mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-06 05:25:41 -05:00
Compare commits
431 Commits
surface-co
...
v0.1.17
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
205c43181b | ||
|
|
05a72abf41 | ||
|
|
14262ba510 | ||
|
|
d847b1e09c | ||
|
|
0086e42a86 | ||
|
|
7474d5a7bf | ||
|
|
5696a36115 | ||
|
|
3cdc1a9c81 | ||
|
|
b095fb9005 | ||
|
|
ce6c16214c | ||
|
|
b6f7f2734e | ||
|
|
4db55e4d77 | ||
|
|
b21f6e80b3 | ||
|
|
a804fb849e | ||
|
|
4ca91cd9f7 | ||
|
|
16e1b587b4 | ||
|
|
5e2756d200 | ||
|
|
ce9ab22ae1 | ||
|
|
72ad35e1f9 | ||
|
|
c0d110cde0 | ||
|
|
b9d5deb2ae | ||
|
|
d4b13ef46b | ||
|
|
748d9e342e | ||
|
|
f49312fc0e | ||
|
|
e0d8bbb243 | ||
|
|
153f2a49f8 | ||
|
|
8b272dc2fd | ||
|
|
87a919bbde | ||
|
|
d3017e98c5 | ||
|
|
5758d7274e | ||
|
|
0e215d69cb | ||
|
|
cbaaa32ce8 | ||
|
|
5c81646397 | ||
|
|
30ca1fb14f | ||
|
|
9fab49984a | ||
|
|
696fa6e4f8 | ||
|
|
921393e84e | ||
|
|
13e894e910 | ||
|
|
7c7e8aaef3 | ||
|
|
7ea3bd9df9 | ||
|
|
7bf7d0afae | ||
|
|
0d329baaca | ||
|
|
941a87b59c | ||
|
|
a9e8ac46d8 | ||
|
|
0d3a294118 | ||
|
|
7a27537632 | ||
|
|
287e778ddb | ||
|
|
ce44edb419 | ||
|
|
9dcd8af7a3 | ||
|
|
76dfcd0ccb | ||
|
|
13a188635d | ||
|
|
cd18fd5aed | ||
|
|
b277bd8014 | ||
|
|
daa0d368ab | ||
|
|
2cc7777e16 | ||
|
|
d276e31f7b | ||
|
|
7f35ba7e21 | ||
|
|
edd54dda84 | ||
|
|
a50a97314d | ||
|
|
4bc05e7083 | ||
|
|
09a45b49a6 | ||
|
|
1c0b71436e | ||
|
|
24f5e9a7e6 | ||
|
|
59d123a4a1 | ||
|
|
ed2afa03f9 | ||
|
|
3c531dc2ec | ||
|
|
83564bd03f | ||
|
|
7146d0d92d | ||
|
|
e842d6761a | ||
|
|
17b405e9dc | ||
|
|
f281513a41 | ||
|
|
63b876479f | ||
|
|
38b833c886 | ||
|
|
d75ea18e9f | ||
|
|
f311b20ef7 | ||
|
|
78f7237422 | ||
|
|
726af3393b | ||
|
|
c772331554 | ||
|
|
80d257b94f | ||
|
|
e2db034959 | ||
|
|
c4e88e5c05 | ||
|
|
e47e7667c6 | ||
|
|
8bb2a64663 | ||
|
|
e056e08fc1 | ||
|
|
342cd55bc0 | ||
|
|
23ef19e683 | ||
|
|
437fd29e96 | ||
|
|
aa7a07fd99 | ||
|
|
5217006dec | ||
|
|
ab4f6baae6 | ||
|
|
1976ea4d49 | ||
|
|
697fc4d2b7 | ||
|
|
38c1f7bbcb | ||
|
|
8cbfaab807 | ||
|
|
f4a4151632 | ||
|
|
5f810fe741 | ||
|
|
adaa0caab8 | ||
|
|
54ef14e765 | ||
|
|
d1383b5d1b | ||
|
|
caa703af99 | ||
|
|
90aab9f4db | ||
|
|
3439030145 | ||
|
|
058c7408d1 | ||
|
|
a4f7fd58f6 | ||
|
|
6f3024c90d | ||
|
|
5f95fa5e79 | ||
|
|
f9cb0506e9 | ||
|
|
2429401d0e | ||
|
|
9ff0d7405f | ||
|
|
5bb5cd296d | ||
|
|
273662e03e | ||
|
|
9c1a89d786 | ||
|
|
524d7ee5c0 | ||
|
|
51d2bc9aae | ||
|
|
0c8a7ff332 | ||
|
|
309b8d9efe | ||
|
|
c2f32b7bdc | ||
|
|
563bc7b359 | ||
|
|
94ca5a5bef | ||
|
|
4464589c0f | ||
|
|
748d4fe2ac | ||
|
|
50b28dc8ca | ||
|
|
118980a9fb | ||
|
|
8aff381676 | ||
|
|
6d0fba1905 | ||
|
|
7e885d3cee | ||
|
|
811daf74ff | ||
|
|
ee755b8bd6 | ||
|
|
1019eb925a | ||
|
|
061bb50b88 | ||
|
|
eb7e665c86 | ||
|
|
80301d1aab | ||
|
|
ea56fb5840 | ||
|
|
692b45c4f0 | ||
|
|
8d53a8826e | ||
|
|
64ea115303 | ||
|
|
b5e29cf50c | ||
|
|
381df1e949 | ||
|
|
13a81eda6f | ||
|
|
a48c39642a | ||
|
|
3be3e622bc | ||
|
|
07fe2ca407 | ||
|
|
9b96dae744 | ||
|
|
0e3d3d1a40 | ||
|
|
cb3274fb0c | ||
|
|
a3ada5b2bb | ||
|
|
3e167a2c52 | ||
|
|
38b3ad2b31 | ||
|
|
56e5cd13b7 | ||
|
|
d63c0fc6f0 | ||
|
|
6814b140fc | ||
|
|
5f7e478118 | ||
|
|
7317024da5 | ||
|
|
9b9fbabc3f | ||
|
|
3c5a23799f | ||
|
|
3bfdc6163c | ||
|
|
cf2f74a38d | ||
|
|
2a7f52c67e | ||
|
|
50fde1e308 | ||
|
|
46fd0ae413 | ||
|
|
65e32dc429 | ||
|
|
413675dfc1 | ||
|
|
a17343f40e | ||
|
|
5d023804c1 | ||
|
|
fa07a846b9 | ||
|
|
5df46b605e | ||
|
|
77cf371a21 | ||
|
|
89802dd040 | ||
|
|
59b95e9dd6 | ||
|
|
b836db5252 | ||
|
|
4dc4b15925 | ||
|
|
5c3062e699 | ||
|
|
3a7777c643 | ||
|
|
71543c35d6 | ||
|
|
f4cf66dc01 | ||
|
|
7870dff0fd | ||
|
|
9fc9c1ed19 | ||
|
|
4d0151350f | ||
|
|
dff10c8d13 | ||
|
|
362bcb9294 | ||
|
|
351b4f8a94 | ||
|
|
90955eb0a1 | ||
|
|
62669747ad | ||
|
|
466d00c666 | ||
|
|
63845ff875 | ||
|
|
d013748a51 | ||
|
|
474af3bc07 | ||
|
|
8e09e155fa | ||
|
|
7f9f4f96b9 | ||
|
|
cd488a8623 | ||
|
|
080b7a28b1 | ||
|
|
6949ed0ebd | ||
|
|
8465fa45bb | ||
|
|
40835ffc89 | ||
|
|
01a42ff330 | ||
|
|
ba49654a64 | ||
|
|
bc6577fe18 | ||
|
|
4ca3f0da67 | ||
|
|
7f2086488b | ||
|
|
3014fd8095 | ||
|
|
27885c8ac3 | ||
|
|
d6be0509ac | ||
|
|
1c85f5e857 | ||
|
|
abe5515aca | ||
|
|
6fba975490 | ||
|
|
2de6798f45 | ||
|
|
04fdfa2a35 | ||
|
|
8f3085290d | ||
|
|
0839fe45f5 | ||
|
|
18f4795fda | ||
|
|
55d9fa622a | ||
|
|
7dc723c764 | ||
|
|
5a63205972 | ||
|
|
a4ceeafb1e | ||
|
|
242e05cc0e | ||
|
|
065dddbe6e | ||
|
|
fa6825252b | ||
|
|
b06e48a444 | ||
|
|
97dbd40f07 | ||
|
|
bc23109f99 | ||
|
|
ecb9675e9c | ||
|
|
e1f9b9e7a4 | ||
|
|
067b485bb3 | ||
|
|
67a4e3074e | ||
|
|
010bc4e8c3 | ||
|
|
9de5e3253e | ||
|
|
e32622ac48 | ||
|
|
5e2371c2cb | ||
|
|
a6ce26ee87 | ||
|
|
2a72c126f1 | ||
|
|
36e1a5d379 | ||
|
|
c12eafa1db | ||
|
|
9e26d8755c | ||
|
|
90bd30e351 | ||
|
|
3fb5d5c4f3 | ||
|
|
9f3685e4d5 | ||
|
|
6565988952 | ||
|
|
10b7a0875b | ||
|
|
bb4b9f1a58 | ||
|
|
981e527560 | ||
|
|
80b2ee719c | ||
|
|
6103d6196f | ||
|
|
ed1a5bfded | ||
|
|
3909ce3350 | ||
|
|
d64cd0b8a4 | ||
|
|
676aa9f93f | ||
|
|
ebd48e2556 | ||
|
|
ad5871aae4 | ||
|
|
e327b1ca5b | ||
|
|
ad43ca11eb | ||
|
|
41ba76e2e2 | ||
|
|
15e773434e | ||
|
|
ed118f4e7a | ||
|
|
3c420f2e30 | ||
|
|
4e271d4f0e | ||
|
|
27f9b3cd0b | ||
|
|
1ed4abd347 | ||
|
|
f71dd1ed54 | ||
|
|
19828d3b06 | ||
|
|
d242e729f0 | ||
|
|
8cd0d5faa5 | ||
|
|
a741d892a9 | ||
|
|
9add3361e0 | ||
|
|
4aac70ab5f | ||
|
|
980aec714a | ||
|
|
af8ee5af0f | ||
|
|
32d9aa0cf2 | ||
|
|
7e49631912 | ||
|
|
43970d34aa | ||
|
|
abb3c40697 | ||
|
|
2757a41102 | ||
|
|
1f8bddaa5e | ||
|
|
4c3b7ca60f | ||
|
|
d9d83e5767 | ||
|
|
b4ebde47be | ||
|
|
ef9f76190d | ||
|
|
71b96efca0 | ||
|
|
7158e09b0e | ||
|
|
8ef125bed2 | ||
|
|
0b11fb2fd5 | ||
|
|
3871f3cf3d | ||
|
|
7c5d1ec0f6 | ||
|
|
b507b08e34 | ||
|
|
1b06090f72 | ||
|
|
5460c20ac3 | ||
|
|
2ccec607a0 | ||
|
|
8a99fcf188 | ||
|
|
1e2489ca76 | ||
|
|
89793d2d62 | ||
|
|
11a1af89f4 | ||
|
|
e24ddb804d | ||
|
|
3524d365be | ||
|
|
2b3b9d037c | ||
|
|
5140cd9d7f | ||
|
|
2df9437b39 | ||
|
|
db440b8a14 | ||
|
|
c3dd70bc99 | ||
|
|
223e783bbc | ||
|
|
ca086dbf16 | ||
|
|
523422cf6c | ||
|
|
2dc310dcbc | ||
|
|
c092cd2921 | ||
|
|
2b14ef76c9 | ||
|
|
fbbf10078f | ||
|
|
2315d423c4 | ||
|
|
9a43465ebf | ||
|
|
fc1444763d | ||
|
|
804bf879ed | ||
|
|
ad44f09421 | ||
|
|
df2469468b | ||
|
|
f8f4fe11eb | ||
|
|
039a370add | ||
|
|
6feebc086d | ||
|
|
bc335c7d72 | ||
|
|
a6dd7254b2 | ||
|
|
7b1026c624 | ||
|
|
4758393cc1 | ||
|
|
52373a3a7d | ||
|
|
c30f9a2841 | ||
|
|
44d6f8f15c | ||
|
|
5ada12f989 | ||
|
|
d213045168 | ||
|
|
d83478239e | ||
|
|
3869955357 | ||
|
|
345d37edf8 | ||
|
|
2788ef28cf | ||
|
|
0d5c1bb3df | ||
|
|
c3d505cdad | ||
|
|
90854e1dd4 | ||
|
|
f96e3b04be | ||
|
|
44449e26a0 | ||
|
|
ddc88fd360 | ||
|
|
fedec450cb | ||
|
|
04ea742830 | ||
|
|
5a5c860cef | ||
|
|
55d06a43f8 | ||
|
|
71eecd6e7b | ||
|
|
6f3019f84b | ||
|
|
e95d3126b2 | ||
|
|
5da265bf0b | ||
|
|
2ce9c43b8c | ||
|
|
740b2f206c | ||
|
|
af622bc7e7 | ||
|
|
7816b50b27 | ||
|
|
4cb7a909f7 | ||
|
|
0fac88e171 | ||
|
|
b4ab9d9650 | ||
|
|
731db13c14 | ||
|
|
414a1ad4d2 | ||
|
|
16055fe96e | ||
|
|
6140c398f0 | ||
|
|
bd02923616 | ||
|
|
6021815fd3 | ||
|
|
8c4aba5479 | ||
|
|
2428b22171 | ||
|
|
a3d30211f6 | ||
|
|
730300d211 | ||
|
|
aaca31276b | ||
|
|
53fb927e36 | ||
|
|
fb5aa0313e | ||
|
|
9b41eecbf1 | ||
|
|
ae461b1caf | ||
|
|
57e36d6710 | ||
|
|
a7c4f09c5b | ||
|
|
554ef16e49 | ||
|
|
082321f860 | ||
|
|
df4f7b8c9e | ||
|
|
3f1742f074 | ||
|
|
4560d5c2d5 | ||
|
|
0ca12d275c | ||
|
|
df9e834309 | ||
|
|
ab1c0bb129 | ||
|
|
5070e4c950 | ||
|
|
c13526ccad | ||
|
|
46e16a6c69 | ||
|
|
53983933dc | ||
|
|
09f3ca39a1 | ||
|
|
42b4c91f35 | ||
|
|
37120776be | ||
|
|
d67bcb66c9 | ||
|
|
acf6e72f35 | ||
|
|
0964b271f5 | ||
|
|
1f7998fc44 | ||
|
|
7ac59f7bd0 | ||
|
|
bf238640af | ||
|
|
6e638cadb7 | ||
|
|
ab0759f441 | ||
|
|
123ec5c78a | ||
|
|
d262c67832 | ||
|
|
fcb9a2838d | ||
|
|
82d1672741 | ||
|
|
657a8b6094 | ||
|
|
165f7e0720 | ||
|
|
91aba84c29 | ||
|
|
02c4ac1bc7 | ||
|
|
c529959027 | ||
|
|
e875d1a5d7 | ||
|
|
d280505b9f | ||
|
|
fdd9d0000b | ||
|
|
74c793eedf | ||
|
|
bd8976c620 | ||
|
|
a3e2563f9b | ||
|
|
a310e3d8fa | ||
|
|
ae9da35af1 | ||
|
|
b45837ff5c | ||
|
|
3dd9ab8a29 | ||
|
|
5ebd2f6b8e | ||
|
|
977043ac92 | ||
|
|
4c6182b79c | ||
|
|
01125e092c | ||
|
|
99ef447a52 | ||
|
|
4722ff9b97 | ||
|
|
23a19df79a | ||
|
|
97b86c6faa | ||
|
|
e6296f20b9 | ||
|
|
0ee9dcc255 | ||
|
|
c7b4e2c49d | ||
|
|
066d1847e4 | ||
|
|
3155bf24c1 | ||
|
|
92bb5b90aa | ||
|
|
a207ed74ff | ||
|
|
9d224113c4 | ||
|
|
934f4b2210 | ||
|
|
a6a41d4de1 | ||
|
|
185ee20f2d | ||
|
|
778b960130 | ||
|
|
184938d10a | ||
|
|
25565af5f9 | ||
|
|
0bd9a4f860 | ||
|
|
fe64dd1dea | ||
|
|
a4a59fd586 |
16
.github/ISSUE_TEMPLATE/bug_report.md
vendored
16
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -26,6 +26,14 @@ assignees: ""
|
|||||||
- [ ] Hyprland
|
- [ ] Hyprland
|
||||||
- [ ] Other (specify)
|
- [ ] Other (specify)
|
||||||
|
|
||||||
|
## Distribution
|
||||||
|
|
||||||
|
<!-- Arch, Fedora, Debian, etc. -->
|
||||||
|
|
||||||
|
## dms version
|
||||||
|
|
||||||
|
<!-- Output of dms version command -->
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
<!-- Brief description of the issue -->
|
<!-- Brief description of the issue -->
|
||||||
@@ -45,6 +53,14 @@ assignees: ""
|
|||||||
## Error Messages/Logs
|
## Error Messages/Logs
|
||||||
|
|
||||||
<!-- Please include any error messages, stack traces, or relevant logs -->
|
<!-- Please include any error messages, stack traces, or relevant logs -->
|
||||||
|
<!-- you can get a log file with the following steps:
|
||||||
|
dms kill
|
||||||
|
mkdir ~/dms_logs
|
||||||
|
nohup dms run > ~/dms_logs/dms-$(date +%s).txt 2>&1 &
|
||||||
|
|
||||||
|
Then trigger your issue, and share the contents of ~/dms_logs/dms-<timestamp>.txt
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
```
|
```
|
||||||
Paste error messages or logs here
|
Paste error messages or logs here
|
||||||
|
|||||||
8
.github/ISSUE_TEMPLATE/support_request.md
vendored
8
.github/ISSUE_TEMPLATE/support_request.md
vendored
@@ -12,6 +12,14 @@ assignees: ""
|
|||||||
- [ ] Hyprland
|
- [ ] Hyprland
|
||||||
- [ ] other
|
- [ ] other
|
||||||
|
|
||||||
|
## Distribution
|
||||||
|
|
||||||
|
<!-- Arch, Fedora, Debian, etc. -->
|
||||||
|
|
||||||
|
## dms version
|
||||||
|
|
||||||
|
<!-- Output of dms version command -->
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
<!-- Brief description of the support needed -->
|
<!-- Brief description of the support needed -->
|
||||||
|
|||||||
290
.github/workflows/copr-release.yml
vendored
Normal file
290
.github/workflows/copr-release.yml
vendored
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
name: DMS Copr Stable Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: 'Versioning (e.g., 0.1.14, leave empty for latest release)'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-upload:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Determine version
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
if [ -n "${{ github.event.inputs.version }}" ]; then
|
||||||
|
VERSION="${{ github.event.inputs.version }}"
|
||||||
|
echo "Using manual version: $VERSION"
|
||||||
|
elif [ "${{ github.event_name }}" = "release" ]; then
|
||||||
|
VERSION="${{ github.event.release.tag_name }}"
|
||||||
|
VERSION="${VERSION#v}"
|
||||||
|
echo "Using release version: $VERSION"
|
||||||
|
elif [ "${{ github.event_name }}" = "push" ] && [[ "${{ github.ref }}" == refs/tags/* ]]; then
|
||||||
|
VERSION="${{ github.ref_name }}"
|
||||||
|
VERSION="${VERSION#v}"
|
||||||
|
echo "Using tag version: $VERSION"
|
||||||
|
else
|
||||||
|
# Fallback to latest release
|
||||||
|
VERSION=$(curl -s https://api.github.com/repos/AvengeMedia/DankMaterialShell/releases/latest | jq -r '.tag_name' | sed 's/^v//')
|
||||||
|
echo "Using latest release version: $VERSION"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
echo "✅ Building DMS stable version: $VERSION"
|
||||||
|
|
||||||
|
- name: Setup build environment
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y rpm wget curl jq gzip
|
||||||
|
mkdir -p ~/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
|
||||||
|
echo "✅ RPM build environment ready"
|
||||||
|
|
||||||
|
- name: Download release assets
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
|
cd ~/rpmbuild/SOURCES
|
||||||
|
|
||||||
|
echo "📦 Downloading DMS QML source for v${VERSION}..."
|
||||||
|
|
||||||
|
# Download DMS QML source
|
||||||
|
wget "https://github.com/AvengeMedia/DankMaterialShell/releases/download/v${VERSION}/dms-qml.tar.gz" || {
|
||||||
|
echo "❌ Failed to download dms-qml.tar.gz for v${VERSION}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "✅ Source downloaded"
|
||||||
|
echo "Note: dms-cli and dgop binaries will be downloaded during build based on target architecture"
|
||||||
|
ls -lh
|
||||||
|
|
||||||
|
- name: Generate stable spec file
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
|
CHANGELOG_DATE="$(date '+%a %b %d %Y')"
|
||||||
|
|
||||||
|
cat > ~/rpmbuild/SPECS/dms.spec <<'SPECEOF'
|
||||||
|
# Spec for DMS stable releases - Generated by GitHub Actions
|
||||||
|
|
||||||
|
%global debug_package %{nil}
|
||||||
|
%global version VERSION_PLACEHOLDER
|
||||||
|
%global pkg_summary DankMaterialShell - Material 3 inspired shell for Wayland compositors
|
||||||
|
|
||||||
|
Name: dms
|
||||||
|
Version: %{version}
|
||||||
|
Release: 1%{?dist}
|
||||||
|
Summary: %{pkg_summary}
|
||||||
|
|
||||||
|
License: GPL-3.0-only
|
||||||
|
URL: https://github.com/AvengeMedia/DankMaterialShell
|
||||||
|
|
||||||
|
Source0: dms-qml.tar.gz
|
||||||
|
|
||||||
|
BuildRequires: gzip
|
||||||
|
BuildRequires: wget
|
||||||
|
|
||||||
|
Requires: (quickshell or quickshell-git)
|
||||||
|
Requires: dms-cli
|
||||||
|
Requires: dgop
|
||||||
|
Requires: fira-code-fonts
|
||||||
|
Requires: material-symbols-fonts
|
||||||
|
Requires: rsms-inter-fonts
|
||||||
|
|
||||||
|
Recommends: brightnessctl
|
||||||
|
Recommends: cava
|
||||||
|
Recommends: cliphist
|
||||||
|
Recommends: hyprpicker
|
||||||
|
Recommends: matugen
|
||||||
|
Recommends: wl-clipboard
|
||||||
|
Recommends: gammastep
|
||||||
|
Recommends: NetworkManager
|
||||||
|
Recommends: qt6-qtmultimedia
|
||||||
|
Suggests: qt6ct
|
||||||
|
|
||||||
|
%description
|
||||||
|
DankMaterialShell (DMS) is a modern Wayland desktop shell built with Quickshell
|
||||||
|
and optimized for the niri and hyprland compositors. Features notifications,
|
||||||
|
app launcher, wallpaper customization, and fully customizable with plugins.
|
||||||
|
|
||||||
|
Includes auto-theming for GTK/Qt apps with matugen, 20+ customizable widgets,
|
||||||
|
process monitoring, notification center, clipboard history, dock, control center,
|
||||||
|
lock screen, and comprehensive plugin system.
|
||||||
|
|
||||||
|
%package -n dms-cli
|
||||||
|
Summary: DankMaterialShell CLI tool
|
||||||
|
License: GPL-3.0-only
|
||||||
|
URL: https://github.com/AvengeMedia/danklinux
|
||||||
|
|
||||||
|
%description -n dms-cli
|
||||||
|
Command-line interface for DankMaterialShell configuration and management.
|
||||||
|
Provides native DBus bindings, NetworkManager integration, and system utilities.
|
||||||
|
|
||||||
|
%package -n dgop
|
||||||
|
Summary: Stateless CPU/GPU monitor for DankMaterialShell
|
||||||
|
License: MIT
|
||||||
|
URL: https://github.com/AvengeMedia/dgop
|
||||||
|
Provides: dgop
|
||||||
|
|
||||||
|
%description -n dgop
|
||||||
|
DGOP is a stateless system monitoring tool that provides CPU, GPU, memory, and
|
||||||
|
network statistics. Designed for integration with DankMaterialShell but can be
|
||||||
|
used standalone. This package always includes the latest stable dgop release.
|
||||||
|
|
||||||
|
%prep
|
||||||
|
%setup -q -c -n dms-qml
|
||||||
|
|
||||||
|
# Download architecture-specific binaries during build
|
||||||
|
# This ensures the correct architecture is used for each build target
|
||||||
|
case "%{_arch}" in
|
||||||
|
x86_64)
|
||||||
|
ARCH_SUFFIX="amd64"
|
||||||
|
;;
|
||||||
|
aarch64)
|
||||||
|
ARCH_SUFFIX="arm64"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unsupported architecture: %{_arch}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Download dms-cli for target architecture
|
||||||
|
wget -O %{_builddir}/dms-cli.gz "https://github.com/AvengeMedia/danklinux/releases/latest/download/dms-distropkg-${ARCH_SUFFIX}.gz" || {
|
||||||
|
echo "Failed to download dms-cli for architecture %{_arch}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
gunzip -c %{_builddir}/dms-cli.gz > %{_builddir}/dms-cli
|
||||||
|
chmod +x %{_builddir}/dms-cli
|
||||||
|
|
||||||
|
# Download dgop for target architecture
|
||||||
|
wget -O %{_builddir}/dgop.gz "https://github.com/AvengeMedia/dgop/releases/latest/download/dgop-linux-${ARCH_SUFFIX}.gz" || {
|
||||||
|
echo "Failed to download dgop for architecture %{_arch}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
gunzip -c %{_builddir}/dgop.gz > %{_builddir}/dgop
|
||||||
|
chmod +x %{_builddir}/dgop
|
||||||
|
|
||||||
|
%build
|
||||||
|
|
||||||
|
%install
|
||||||
|
install -Dm755 %{_builddir}/dms-cli %{buildroot}%{_bindir}/dms
|
||||||
|
install -Dm755 %{_builddir}/dgop %{buildroot}%{_bindir}/dgop
|
||||||
|
|
||||||
|
install -dm755 %{buildroot}%{_sysconfdir}/xdg/quickshell/dms
|
||||||
|
cp -r %{_builddir}/dms-qml/* %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/
|
||||||
|
|
||||||
|
rm -rf %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/.git*
|
||||||
|
rm -f %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/.gitignore
|
||||||
|
rm -rf %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/.github
|
||||||
|
rm -f %{buildroot}%{_sysconfdir}/xdg/quickshell/dms/*.spec
|
||||||
|
|
||||||
|
%files
|
||||||
|
%license LICENSE
|
||||||
|
%doc README.md CONTRIBUTING.md
|
||||||
|
%{_sysconfdir}/xdg/quickshell/dms/
|
||||||
|
|
||||||
|
%files -n dms-cli
|
||||||
|
%{_bindir}/dms
|
||||||
|
|
||||||
|
%files -n dgop
|
||||||
|
%{_bindir}/dgop
|
||||||
|
|
||||||
|
%changelog
|
||||||
|
* CHANGELOG_DATE_PLACEHOLDER AvengeMedia <contact@avengemedia.com> - VERSION_PLACEHOLDER-1
|
||||||
|
- Stable release VERSION_PLACEHOLDER
|
||||||
|
- Built from GitHub release
|
||||||
|
- Includes latest dms-cli and dgop binaries
|
||||||
|
SPECEOF
|
||||||
|
|
||||||
|
sed -i "s/VERSION_PLACEHOLDER/${VERSION}/g" ~/rpmbuild/SPECS/dms.spec
|
||||||
|
sed -i "s/CHANGELOG_DATE_PLACEHOLDER/${CHANGELOG_DATE}/g" ~/rpmbuild/SPECS/dms.spec
|
||||||
|
|
||||||
|
echo "✅ Spec file generated for v${VERSION}"
|
||||||
|
echo ""
|
||||||
|
echo "=== Spec file preview ==="
|
||||||
|
head -40 ~/rpmbuild/SPECS/dms.spec
|
||||||
|
|
||||||
|
- name: Build SRPM
|
||||||
|
id: build
|
||||||
|
run: |
|
||||||
|
cd ~/rpmbuild/SPECS
|
||||||
|
|
||||||
|
echo "🔨 Building SRPM..."
|
||||||
|
rpmbuild -bs dms.spec
|
||||||
|
|
||||||
|
SRPM=$(ls ~/rpmbuild/SRPMS/*.src.rpm | tail -n 1)
|
||||||
|
SRPM_NAME=$(basename "$SRPM")
|
||||||
|
|
||||||
|
echo "srpm_path=$SRPM" >> $GITHUB_OUTPUT
|
||||||
|
echo "srpm_name=$SRPM_NAME" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
echo "✅ SRPM built: $SRPM_NAME"
|
||||||
|
echo ""
|
||||||
|
echo "=== SRPM Info ==="
|
||||||
|
rpm -qpi "$SRPM"
|
||||||
|
|
||||||
|
- name: Upload SRPM artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: dms-stable-srpm-${{ steps.version.outputs.version }}
|
||||||
|
path: ${{ steps.build.outputs.srpm_path }}
|
||||||
|
retention-days: 90
|
||||||
|
|
||||||
|
- name: Install Copr CLI
|
||||||
|
run: |
|
||||||
|
sudo apt-get install -y python3-pip
|
||||||
|
pip3 install copr-cli
|
||||||
|
|
||||||
|
mkdir -p ~/.config
|
||||||
|
cat > ~/.config/copr << EOF
|
||||||
|
[copr-cli]
|
||||||
|
login = ${{ secrets.COPR_LOGIN }}
|
||||||
|
username = avengemedia
|
||||||
|
token = ${{ secrets.COPR_TOKEN }}
|
||||||
|
copr_url = https://copr.fedorainfracloud.org
|
||||||
|
EOF
|
||||||
|
chmod 600 ~/.config/copr
|
||||||
|
|
||||||
|
echo "✅ Copr CLI configured"
|
||||||
|
|
||||||
|
- name: Upload to Copr
|
||||||
|
run: |
|
||||||
|
SRPM="${{ steps.build.outputs.srpm_path }}"
|
||||||
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
|
|
||||||
|
echo "🚀 Uploading SRPM to avengemedia/dms..."
|
||||||
|
echo " SRPM: $(basename $SRPM)"
|
||||||
|
echo " Version: $VERSION"
|
||||||
|
|
||||||
|
BUILD_OUTPUT=$(copr-cli build avengemedia/dms "$SRPM" --nowait 2>&1)
|
||||||
|
echo "$BUILD_OUTPUT"
|
||||||
|
|
||||||
|
BUILD_ID=$(echo "$BUILD_OUTPUT" | grep -oP 'Build was added to.*\K[0-9]+' || echo "unknown")
|
||||||
|
|
||||||
|
if [ "$BUILD_ID" != "unknown" ]; then
|
||||||
|
echo "✅ Build submitted successfully!"
|
||||||
|
echo "🔗 https://copr.fedorainfracloud.org/coprs/avengemedia/dms/build/$BUILD_ID/"
|
||||||
|
else
|
||||||
|
echo "⚠️ Could not extract build ID, but upload may have succeeded"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build summary
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
echo "### 🎉 DMS Stable Build Summary" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "- **Version:** ${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "- **SRPM:** ${{ steps.build.outputs.srpm_name }}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "- **Project:** https://copr.fedorainfracloud.org/coprs/avengemedia/dms/" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Stable release has been built and uploaded to Copr!" >> $GITHUB_STEP_SUMMARY
|
||||||
107
.github/workflows/poeditor-export.yml
vendored
Normal file
107
.github/workflows/poeditor-export.yml
vendored
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
name: POEditor Diff & Sync
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
workflow_dispatch: {}
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: poeditor-sync
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync-translations:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 20
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install jq
|
||||||
|
run: sudo apt-get update && sudo apt-get install -y jq
|
||||||
|
|
||||||
|
- name: Export and update translations from POEditor
|
||||||
|
env:
|
||||||
|
API_TOKEN: ${{ secrets.POEDITOR_API_TOKEN }}
|
||||||
|
PROJECT_ID: ${{ secrets.POEDITOR_PROJECT_ID }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
LANGUAGES=(
|
||||||
|
"ja:translations/poexports/ja.json"
|
||||||
|
"zh-Hans:translations/poexports/zh_CN.json"
|
||||||
|
"pt-br:translations/poexports/pt.json"
|
||||||
|
)
|
||||||
|
|
||||||
|
ANY_CHANGED=false
|
||||||
|
|
||||||
|
for lang_pair in "${LANGUAGES[@]}"; do
|
||||||
|
IFS=':' read -r PO_LANG REPO_FILE <<< "$lang_pair"
|
||||||
|
|
||||||
|
echo "::group::Processing $PO_LANG"
|
||||||
|
|
||||||
|
RESP=$(curl -sS -X POST https://api.poeditor.com/v2/projects/export \
|
||||||
|
-d api_token="$API_TOKEN" \
|
||||||
|
-d id="$PROJECT_ID" \
|
||||||
|
-d language="$PO_LANG" \
|
||||||
|
-d type="key_value_json")
|
||||||
|
|
||||||
|
STATUS=$(echo "$RESP" | jq -r '.response.status')
|
||||||
|
if [[ "$STATUS" != "success" ]]; then
|
||||||
|
echo "POEditor export request failed for $PO_LANG: $RESP" >&2
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
URL=$(echo "$RESP" | jq -r '.result.url')
|
||||||
|
if [[ -z "$URL" || "$URL" == "null" ]]; then
|
||||||
|
echo "No export URL returned for $PO_LANG" >&2
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
curl -sS -L "$URL" -o "/tmp/po_export_${PO_LANG}.json"
|
||||||
|
jq -S . "/tmp/po_export_${PO_LANG}.json" > "/tmp/po_export_${PO_LANG}.norm.json"
|
||||||
|
|
||||||
|
if [[ -f "$REPO_FILE" ]]; then
|
||||||
|
jq -S . "$REPO_FILE" > "/tmp/repo_${PO_LANG}.norm.json" || echo "{}" > "/tmp/repo_${PO_LANG}.norm.json"
|
||||||
|
else
|
||||||
|
echo "{}" > "/tmp/repo_${PO_LANG}.norm.json"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if diff -q "/tmp/po_export_${PO_LANG}.norm.json" "/tmp/repo_${PO_LANG}.norm.json" >/dev/null; then
|
||||||
|
echo "No changes for $PO_LANG"
|
||||||
|
else
|
||||||
|
echo "Detected changes for $PO_LANG"
|
||||||
|
mkdir -p "$(dirname "$REPO_FILE")"
|
||||||
|
cp "/tmp/po_export_${PO_LANG}.norm.json" "$REPO_FILE"
|
||||||
|
ANY_CHANGED=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "::endgroup::"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "any_changed=$ANY_CHANGED" >> "$GITHUB_OUTPUT"
|
||||||
|
id: export
|
||||||
|
|
||||||
|
- name: Commit and push translation updates
|
||||||
|
if: steps.export.outputs.any_changed == 'true'
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
|
||||||
|
git add translations/poexports/*.json
|
||||||
|
git commit -m "i18n: update translations"
|
||||||
|
|
||||||
|
for attempt in 1 2 3; do
|
||||||
|
if git push; then
|
||||||
|
echo "Successfully pushed translation updates"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "Push attempt $attempt failed, pulling and retrying..."
|
||||||
|
git pull --rebase
|
||||||
|
sleep $((attempt*2))
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Failed to push after retries" >&2
|
||||||
|
exit 1
|
||||||
216
.github/workflows/release.yml
vendored
216
.github/workflows/release.yml
vendored
@@ -1,59 +1,219 @@
|
|||||||
name: Create Release
|
# Release from a dispatch event from the danklinux repo
|
||||||
|
name: Create Release from DMS
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
repository_dispatch:
|
||||||
tags:
|
types: [dms_release]
|
||||||
- 'v*'
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: release-${{ github.ref_name }}
|
group: release-${{ github.event.client_payload.tag }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
create_release:
|
create_release_from_dms:
|
||||||
name: 📦 Create GitHub Release
|
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
env:
|
||||||
|
TAG: ${{ github.event.client_payload.tag }}
|
||||||
|
DMS_REPO: ${{ github.event.client_payload.dms_repo }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0 # Fetch full history for changelog generation
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Ensure VERSION and tag
|
||||||
|
run: |
|
||||||
|
set -euxo pipefail
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
|
||||||
|
echo "${TAG}" > VERSION
|
||||||
|
|
||||||
|
git add -A VERSION
|
||||||
|
|
||||||
|
if ! git diff --cached --quiet; then
|
||||||
|
git commit -m "Update VERSION to ${TAG} (from DMS)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
git tag -f "${TAG}"
|
||||||
|
|
||||||
|
git push origin HEAD
|
||||||
|
git push -f origin "${TAG}"
|
||||||
|
|
||||||
# Generate changelog
|
|
||||||
- name: Generate Changelog
|
- name: Generate Changelog
|
||||||
id: changelog
|
id: changelog
|
||||||
run: |
|
run: |
|
||||||
# Get the previous tag
|
set -e
|
||||||
PREVIOUS_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
|
PREVIOUS_TAG=$(git describe --tags --abbrev=0 "${TAG}^" 2>/dev/null || echo "")
|
||||||
|
|
||||||
if [ -z "$PREVIOUS_TAG" ]; then
|
if [ -z "$PREVIOUS_TAG" ]; then
|
||||||
echo "No previous tag found, using all commits"
|
|
||||||
CHANGELOG=$(git log --oneline --pretty=format:"- %s (%h)" | head -50)
|
CHANGELOG=$(git log --oneline --pretty=format:"- %s (%h)" | head -50)
|
||||||
else
|
else
|
||||||
echo "Generating changelog from $PREVIOUS_TAG to HEAD"
|
CHANGELOG=$(git log --oneline --pretty=format:"- %s (%h)" "${PREVIOUS_TAG}..${TAG}")
|
||||||
CHANGELOG=$(git log --oneline --pretty=format:"- %s (%h)" $PREVIOUS_TAG..HEAD)
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create the changelog with proper formatting
|
cat > RELEASE_BODY.md << 'EOF'
|
||||||
cat > CHANGELOG.md << EOF
|
## Assets
|
||||||
|
|
||||||
|
### Complete Packages
|
||||||
|
- **`dms-full-amd64.tar.gz`** - Complete package for x86_64 systems (CLI binaries + QML source + installation guide)
|
||||||
|
- **`dms-full-arm64.tar.gz`** - Complete package for ARM64 systems (CLI binaries + QML source + installation guide)
|
||||||
|
|
||||||
|
### Individual Components
|
||||||
|
- **`dms-cli-amd64.gz`** - DMS CLI binary for x86_64 systems
|
||||||
|
- **`dms-cli-arm64.gz`** - DMS CLI binary for ARM64 systems
|
||||||
|
- **`dms-distropkg-amd64.gz`** - DMS CLI binary built with distro_package tag for AMD64 systems
|
||||||
|
- **`dms-distropkg-arm64.gz`** - DMS CLI binary built with distro_package tag for ARM64 systems
|
||||||
|
- **`dms-qml.tar.gz`** - QML source code only
|
||||||
|
|
||||||
|
### Checksums
|
||||||
|
- **`*.sha256`** - SHA256 checksums for verifying download integrity
|
||||||
|
|
||||||
|
**Installation:** Extract the `dms-full-*.tar.gz` package for your architecture and follow the `INSTALL.md` instructions inside.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat >> RELEASE_BODY.md << EOF
|
||||||
## What's Changed
|
## What's Changed
|
||||||
|
|
||||||
$CHANGELOG
|
$CHANGELOG
|
||||||
|
|
||||||
**Full Changelog**: https://github.com/${{ github.repository }}/compare/${PREVIOUS_TAG}...${{ github.ref_name }}
|
**Full Changelog**: https://github.com/${{ github.repository }}/compare/${PREVIOUS_TAG}...${TAG}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Set output for use in release step
|
|
||||||
echo "changelog<<EOF" >> $GITHUB_OUTPUT
|
echo "changelog<<EOF" >> $GITHUB_OUTPUT
|
||||||
cat CHANGELOG.md >> $GITHUB_OUTPUT
|
cat RELEASE_BODY.md >> $GITHUB_OUTPUT
|
||||||
echo "EOF" >> $GITHUB_OUTPUT
|
echo "EOF" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
# Create GitHub Release
|
- name: Create/Update DankMaterialShell Release
|
||||||
- name: Create GitHub Release
|
uses: softprops/action-gh-release@v2
|
||||||
uses: comnoco/create-release-action@v2.0.5
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ github.ref_name }}
|
tag_name: ${{ env.TAG }}
|
||||||
release_name: Release ${{ github.ref_name }}
|
name: Release ${{ env.TAG }}
|
||||||
body: ${{ steps.changelog.outputs.changelog }}
|
body: ${{ steps.changelog.outputs.changelog }}
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: ${{ contains(github.ref_name, '-') }}
|
prerelease: ${{ contains(env.TAG, '-') }}
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Download and prepare release assets
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
mkdir -p _release_assets
|
||||||
|
|
||||||
|
# Download DMS CLI binaries from the danklinux repo
|
||||||
|
gh release download "${TAG}" -R "${DMS_REPO}" --dir ./_dms_assets
|
||||||
|
|
||||||
|
# Rename CLI binaries to dms-cli-* format and copy distropkg binaries
|
||||||
|
for file in _dms_assets/dms-*.gz*; do
|
||||||
|
if [ -f "$file" ]; then
|
||||||
|
basename=$(basename "$file")
|
||||||
|
if [[ "$basename" == dms-distropkg-* ]]; then
|
||||||
|
cp "$file" "_release_assets/$basename"
|
||||||
|
else
|
||||||
|
newname=$(echo "$basename" | sed 's/^dms-/dms-cli-/')
|
||||||
|
cp "$file" "_release_assets/$newname"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Create QML source package (exclude .git, .github, build artifacts)
|
||||||
|
tar --exclude='.git' \
|
||||||
|
--exclude='.github' \
|
||||||
|
--exclude='_dms_assets' \
|
||||||
|
--exclude='_release_assets' \
|
||||||
|
--exclude='*.tar.gz' \
|
||||||
|
-czf _release_assets/dms-qml.tar.gz .
|
||||||
|
|
||||||
|
# Generate checksum for QML package
|
||||||
|
(cd _release_assets && sha256sum dms-qml.tar.gz > dms-qml.tar.gz.sha256)
|
||||||
|
|
||||||
|
# Create full packages for each architecture
|
||||||
|
for arch in amd64 arm64; do
|
||||||
|
mkdir -p _temp_full/dms
|
||||||
|
mkdir -p _temp_full/bin
|
||||||
|
|
||||||
|
# Extract QML source to temp directory
|
||||||
|
tar -xzf _release_assets/dms-qml.tar.gz -C _temp_full/dms
|
||||||
|
|
||||||
|
# Copy CLI binary if it exists
|
||||||
|
if [ -f "_dms_assets/dms-${arch}.gz" ]; then
|
||||||
|
gunzip -c "_dms_assets/dms-${arch}.gz" > _temp_full/bin/dms
|
||||||
|
chmod +x _temp_full/bin/dms
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy distropkg binary if it exists
|
||||||
|
if [ -f "_dms_assets/dms-distropkg-${arch}.gz" ]; then
|
||||||
|
gunzip -c "_dms_assets/dms-distropkg-${arch}.gz" > _temp_full/bin/dms-distropkg
|
||||||
|
chmod +x _temp_full/bin/dms-distropkg
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create INSTALL.md
|
||||||
|
cat > _temp_full/INSTALL.md << 'EOF'
|
||||||
|
# DankMaterialShell Installation
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Wayland compositor (niri or Hyprland recommended)
|
||||||
|
- Quickshell framework
|
||||||
|
- Qt6
|
||||||
|
|
||||||
|
## Installation Steps
|
||||||
|
|
||||||
|
1. **Install quickshell assets:**
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/.config/quickshell
|
||||||
|
cp -r dms ~/.config/quickshell/
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Install the DMS CLI binaries:**
|
||||||
|
```bash
|
||||||
|
sudo install -m 755 bin/dms /usr/local/bin/dms
|
||||||
|
# or install to a local directory:
|
||||||
|
mkdir -p ~/.local/bin
|
||||||
|
install -m 755 bin/dms ~/.local/bin/dms
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Start the shell:**
|
||||||
|
```bash
|
||||||
|
dms run
|
||||||
|
# or directly with quickshell (will lack some dbus integrations & plugin management):
|
||||||
|
quickshell -p ~/.config/quickshell/dms
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
- Settings are stored in `~/.config/DankMaterialShell/settings.json`
|
||||||
|
- Plugins go in `~/.config/DankMaterialShell/plugins/`
|
||||||
|
- See the documentation in the `dms/` directory for more details
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
- Run with verbose output: `quickshell -v -p ~/.config/quickshell/dms`
|
||||||
|
- Check logs in `~/.local/state/DankMaterialShell/`
|
||||||
|
- Ensure all dependencies are installed
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Create the full package
|
||||||
|
(cd _temp_full && tar -czf "../_release_assets/dms-full-${arch}.tar.gz" .)
|
||||||
|
|
||||||
|
# Generate checksum
|
||||||
|
(cd _release_assets && sha256sum "dms-full-${arch}.tar.gz" > "dms-full-${arch}.tar.gz.sha256")
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
rm -rf _temp_full
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Attach all assets to release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
tag_name: ${{ env.TAG }}
|
||||||
|
files: _release_assets/**
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
233
CLAUDE.md
233
CLAUDE.md
@@ -63,6 +63,9 @@ quickshell -p shell.qml
|
|||||||
# Or use the shorthand
|
# Or use the shorthand
|
||||||
qs -p .
|
qs -p .
|
||||||
|
|
||||||
|
# Run with verbose output for debugging
|
||||||
|
qs -v -p shell.qml
|
||||||
|
|
||||||
# Code formatting and linting
|
# Code formatting and linting
|
||||||
qmlfmt -t 4 -i 4 -b 250 -w /path/to/file.qml # Format a QML file (requires qmlfmt, do not use qmlformat)
|
qmlfmt -t 4 -i 4 -b 250 -w /path/to/file.qml # Format a QML file (requires qmlfmt, do not use qmlformat)
|
||||||
qmllint **/*.qml # Lint all QML files for syntax errors
|
qmllint **/*.qml # Lint all QML files for syntax errors
|
||||||
@@ -89,6 +92,7 @@ shell.qml # Main entry point (minimal orchestration)
|
|||||||
│ ├── DisplayService.qml
|
│ ├── DisplayService.qml
|
||||||
│ ├── NotificationService.qml
|
│ ├── NotificationService.qml
|
||||||
│ ├── WeatherService.qml
|
│ ├── WeatherService.qml
|
||||||
|
│ ├── PluginService.qml
|
||||||
│ └── [14 more services]
|
│ └── [14 more services]
|
||||||
├── Modules/ # UI components (93 files)
|
├── Modules/ # UI components (93 files)
|
||||||
│ ├── TopBar/ # Panel components (13 files)
|
│ ├── TopBar/ # Panel components (13 files)
|
||||||
@@ -104,15 +108,21 @@ shell.qml # Main entry point (minimal orchestration)
|
|||||||
│ ├── SettingsModal.qml
|
│ ├── SettingsModal.qml
|
||||||
│ ├── ClipboardHistoryModal.qml
|
│ ├── ClipboardHistoryModal.qml
|
||||||
│ ├── ProcessListModal.qml
|
│ ├── ProcessListModal.qml
|
||||||
|
│ ├── PluginSettingsModal.qml
|
||||||
│ └── [7 more modals]
|
│ └── [7 more modals]
|
||||||
└── Widgets/ # Reusable UI controls (19 files)
|
├── Widgets/ # Reusable UI controls (19 files)
|
||||||
├── DankIcon.qml
|
│ ├── DankIcon.qml
|
||||||
├── DankSlider.qml
|
│ ├── DankSlider.qml
|
||||||
├── DankToggle.qml
|
│ ├── DankToggle.qml
|
||||||
├── DankTabBar.qml
|
│ ├── DankTabBar.qml
|
||||||
├── DankGridView.qml
|
│ ├── DankGridView.qml
|
||||||
├── DankListView.qml
|
│ ├── DankListView.qml
|
||||||
└── [13 more widgets]
|
│ └── [13 more widgets]
|
||||||
|
└── plugins/ # External plugins directory ($CONFIGPATH/DankMaterialShell/plugins/)
|
||||||
|
└── PluginName/ # Example Plugin structure
|
||||||
|
├── plugin.json # Plugin manifest
|
||||||
|
├── PluginNameWidget.qml # Widget component
|
||||||
|
└── PluginNameSettings.qml # Settings UI
|
||||||
```
|
```
|
||||||
|
|
||||||
### Component Organization
|
### Component Organization
|
||||||
@@ -163,6 +173,12 @@ shell.qml # Main entry point (minimal orchestration)
|
|||||||
- **DankLocationSearch**: Location picker with search
|
- **DankLocationSearch**: Location picker with search
|
||||||
- **SystemLogo**: Animated system branding component
|
- **SystemLogo**: Animated system branding component
|
||||||
|
|
||||||
|
7. **Plugins/** - External plugin system (`$CONFIGPATH/DankMaterialShell/plugins/`)
|
||||||
|
- **PluginService**: Discovers, loads, and manages plugin lifecycle
|
||||||
|
- **Dynamic Loading**: Plugins loaded at runtime from external directory
|
||||||
|
- **DankBar Integration**: Plugin widgets rendered alongside built-in widgets
|
||||||
|
- **Settings System**: Per-plugin settings with persistence
|
||||||
|
|
||||||
### Key Architectural Patterns
|
### Key Architectural Patterns
|
||||||
|
|
||||||
1. **Singleton Services Pattern**:
|
1. **Singleton Services Pattern**:
|
||||||
@@ -408,10 +424,10 @@ When modifying the shell:
|
|||||||
|
|
||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool featureAvailable: false
|
property bool featureAvailable: false
|
||||||
property type currentValue: defaultValue
|
property type currentValue: defaultValue
|
||||||
|
|
||||||
function performAction(param) {
|
function performAction(param) {
|
||||||
// Implementation
|
// Implementation
|
||||||
}
|
}
|
||||||
@@ -422,7 +438,7 @@ When modifying the shell:
|
|||||||
```qml
|
```qml
|
||||||
// In module files
|
// In module files
|
||||||
property alias serviceValue: NewService.currentValue
|
property alias serviceValue: NewService.currentValue
|
||||||
|
|
||||||
SomeControl {
|
SomeControl {
|
||||||
visible: NewService.featureAvailable
|
visible: NewService.featureAvailable
|
||||||
enabled: NewService.featureAvailable
|
enabled: NewService.featureAvailable
|
||||||
@@ -430,6 +446,200 @@ When modifying the shell:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Creating Plugins
|
||||||
|
|
||||||
|
Plugins are external, dynamically-loaded components that extend DankMaterialShell functionality. Plugins are stored in `~/.config/DankMaterialShell/plugins/` and have their settings isolated from core DMS settings.
|
||||||
|
|
||||||
|
**Plugin Types:**
|
||||||
|
- **Widget plugins** (`"type": "widget"` or omit type field): Display UI components in DankBar
|
||||||
|
- **Daemon plugins** (`"type": "daemon"`): Run invisibly in the background without UI
|
||||||
|
|
||||||
|
#### Widget Plugins
|
||||||
|
|
||||||
|
1. **Create plugin directory**:
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/.config/DankMaterialShell/plugins/YourPlugin
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Create manifest** (`plugin.json`):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "yourPlugin",
|
||||||
|
"name": "Your Plugin",
|
||||||
|
"description": "Widget description",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": "Your Name",
|
||||||
|
"icon": "extension",
|
||||||
|
"type": "widget",
|
||||||
|
"component": "./YourWidget.qml",
|
||||||
|
"settings": "./YourSettings.qml",
|
||||||
|
"permissions": ["settings_read", "settings_write"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Create widget component** (`YourWidget.qml`):
|
||||||
|
```qml
|
||||||
|
import QtQuick
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool compactMode: false
|
||||||
|
property string section: "center"
|
||||||
|
property real widgetHeight: 30
|
||||||
|
property var pluginService: null
|
||||||
|
|
||||||
|
width: content.implicitWidth + 16
|
||||||
|
height: widgetHeight
|
||||||
|
radius: 8
|
||||||
|
color: "#20FFFFFF"
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (pluginService) {
|
||||||
|
var data = pluginService.loadPluginData("yourPlugin", "key", defaultValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Create settings component** (`YourSettings.qml`):
|
||||||
|
```qml
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
|
||||||
|
FocusScope {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var pluginService: null
|
||||||
|
|
||||||
|
implicitHeight: settingsColumn.implicitHeight
|
||||||
|
height: implicitHeight
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: settingsColumn
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 16
|
||||||
|
spacing: 12
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Your Plugin Settings"
|
||||||
|
font.pixelSize: 18
|
||||||
|
font.weight: Font.Bold
|
||||||
|
}
|
||||||
|
|
||||||
|
// Your settings UI here
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveSettings(key, value) {
|
||||||
|
if (pluginService) {
|
||||||
|
pluginService.savePluginData("yourPlugin", key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadSettings(key, defaultValue) {
|
||||||
|
if (pluginService) {
|
||||||
|
return pluginService.loadPluginData("yourPlugin", key, defaultValue)
|
||||||
|
}
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Enable plugin**:
|
||||||
|
- Open Settings → Plugins
|
||||||
|
- Click "Scan for Plugins"
|
||||||
|
- Toggle plugin to enable
|
||||||
|
- Add plugin ID to DankBar widget list
|
||||||
|
|
||||||
|
#### Daemon Plugins
|
||||||
|
|
||||||
|
Daemon plugins run invisibly in the background without any UI components. They're useful for monitoring system events, background tasks, or data synchronization.
|
||||||
|
|
||||||
|
1. **Create plugin directory**:
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/.config/DankMaterialShell/plugins/YourDaemon
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Create manifest** (`plugin.json`):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "yourDaemon",
|
||||||
|
"name": "Your Daemon",
|
||||||
|
"description": "Background daemon description",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": "Your Name",
|
||||||
|
"icon": "settings_applications",
|
||||||
|
"type": "daemon",
|
||||||
|
"component": "./YourDaemon.qml",
|
||||||
|
"permissions": ["settings_read", "settings_write"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Create daemon component** (`YourDaemon.qml`):
|
||||||
|
```qml
|
||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var pluginService: null
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: SessionData
|
||||||
|
function onWallpaperPathChanged() {
|
||||||
|
console.log("Wallpaper changed:", SessionData.wallpaperPath)
|
||||||
|
if (pluginService) {
|
||||||
|
pluginService.savePluginData("yourDaemon", "lastEvent", Date.now())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
console.log("Daemon started")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Enable daemon**:
|
||||||
|
- Open Settings → Plugins
|
||||||
|
- Click "Scan for Plugins"
|
||||||
|
- Toggle daemon to enable
|
||||||
|
- Daemon runs automatically in background
|
||||||
|
|
||||||
|
**Example**: See `PLUGINS/WallpaperWatcherDaemon/` for a complete daemon plugin that monitors wallpaper changes
|
||||||
|
|
||||||
|
**Plugin Directory Structure:**
|
||||||
|
```
|
||||||
|
~/.config/DankMaterialShell/
|
||||||
|
├── settings.json # Core DMS settings + plugin settings
|
||||||
|
│ └── pluginSettings: {
|
||||||
|
│ └── yourPlugin: {
|
||||||
|
│ ├── enabled: true,
|
||||||
|
│ └── customData: {...}
|
||||||
|
│ }
|
||||||
|
│ }
|
||||||
|
└── plugins/ # Plugin files directory
|
||||||
|
└── YourPlugin/ # Plugin directory (matches manifest ID)
|
||||||
|
├── plugin.json # Plugin manifest
|
||||||
|
├── YourWidget.qml # Widget component
|
||||||
|
└── YourSettings.qml # Settings UI (optional)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Plugin APIs:**
|
||||||
|
- `pluginService.loadPluginData(pluginId, key, default)` - Load persistent data
|
||||||
|
- `pluginService.savePluginData(pluginId, key, value)` - Save persistent data
|
||||||
|
- `PluginService.enablePlugin(pluginId)` - Load plugin
|
||||||
|
- `PluginService.disablePlugin(pluginId)` - Unload plugin
|
||||||
|
|
||||||
|
**Important Notes:**
|
||||||
|
- Plugin settings are automatically injected by the PluginService via `item.pluginService = PluginService`
|
||||||
|
- Settings are stored in the main settings.json but namespaced under `pluginSettings.{pluginId}`
|
||||||
|
- Plugin directories must match the plugin ID in the manifest
|
||||||
|
- Use the injected `pluginService` property in both widget and settings components
|
||||||
|
|
||||||
### Debugging Common Issues
|
### Debugging Common Issues
|
||||||
|
|
||||||
1. **Import errors**: Check import paths
|
1. **Import errors**: Check import paths
|
||||||
@@ -454,6 +664,7 @@ When modifying the shell:
|
|||||||
- **Function Discovery**: Use grep/search tools to find existing utility functions before implementing new ones
|
- **Function Discovery**: Use grep/search tools to find existing utility functions before implementing new ones
|
||||||
- **Modern QML Patterns**: Leverage new widgets like DankTextField, DankDropdown, CachingImage
|
- **Modern QML Patterns**: Leverage new widgets like DankTextField, DankDropdown, CachingImage
|
||||||
- **Structured Organization**: Follow the established Services/Modules/Widgets/Modals separation
|
- **Structured Organization**: Follow the established Services/Modules/Widgets/Modals separation
|
||||||
|
- **Plugin System**: For user extensions, create plugins instead of modifying core modules - see docs/PLUGINS.md
|
||||||
|
|
||||||
### Common Widget Patterns
|
### Common Widget Patterns
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
Contributions are welcome and encourages.
|
Contributions are welcome and encouraged.
|
||||||
|
|
||||||
## Formatting
|
## Formatting
|
||||||
|
|
||||||
@@ -27,4 +27,4 @@ Sometimes it just breaks code though. Like turning `"_\""` into `"_""`, so you m
|
|||||||
|
|
||||||
## Pull request
|
## Pull request
|
||||||
|
|
||||||
Include screenshots/video if applicable in your pull request if applicable, to visualize what your change is affecting.
|
Include screenshots/video if applicable in your pull request if applicable, to visualize what your change is affecting.
|
||||||
|
|||||||
@@ -72,10 +72,6 @@ Singleton {
|
|||||||
saveSettings()
|
saveSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAppUsageRanking() {
|
|
||||||
return appUsageRanking
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRankedApps() {
|
function getRankedApps() {
|
||||||
var apps = []
|
var apps = []
|
||||||
for (var appId in appUsageRanking) {
|
for (var appId in appUsageRanking) {
|
||||||
|
|||||||
122
Common/CacheData.qml
Normal file
122
Common/CacheData.qml
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtCore
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property int cacheConfigVersion: 1
|
||||||
|
|
||||||
|
readonly property bool isGreeterMode: Quickshell.env("DMS_RUN_GREETER") === "1" || Quickshell.env("DMS_RUN_GREETER") === "true"
|
||||||
|
|
||||||
|
readonly property string _stateUrl: StandardPaths.writableLocation(StandardPaths.GenericCacheLocation)
|
||||||
|
readonly property string _stateDir: Paths.strip(_stateUrl)
|
||||||
|
|
||||||
|
property bool _loading: false
|
||||||
|
|
||||||
|
property string wallpaperLastPath: ""
|
||||||
|
property string profileLastPath: ""
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (!isGreeterMode) {
|
||||||
|
loadCache()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadCache() {
|
||||||
|
_loading = true
|
||||||
|
parseCache(cacheFile.text())
|
||||||
|
_loading = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseCache(content) {
|
||||||
|
_loading = true
|
||||||
|
try {
|
||||||
|
if (content && content.trim()) {
|
||||||
|
const cache = JSON.parse(content)
|
||||||
|
|
||||||
|
wallpaperLastPath = cache.wallpaperLastPath !== undefined ? cache.wallpaperLastPath : ""
|
||||||
|
profileLastPath = cache.profileLastPath !== undefined ? cache.profileLastPath : ""
|
||||||
|
|
||||||
|
if (cache.configVersion === undefined) {
|
||||||
|
migrateFromUndefinedToV1(cache)
|
||||||
|
cleanupUnusedKeys()
|
||||||
|
saveCache()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("CacheData: Failed to parse cache:", e.message)
|
||||||
|
} finally {
|
||||||
|
_loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveCache() {
|
||||||
|
if (_loading)
|
||||||
|
return
|
||||||
|
cacheFile.setText(JSON.stringify({
|
||||||
|
"wallpaperLastPath": wallpaperLastPath,
|
||||||
|
"profileLastPath": profileLastPath,
|
||||||
|
"configVersion": cacheConfigVersion
|
||||||
|
}, null, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
function migrateFromUndefinedToV1(cache) {
|
||||||
|
console.log("CacheData: Migrating configuration from undefined to version 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanupUnusedKeys() {
|
||||||
|
const validKeys = [
|
||||||
|
"wallpaperLastPath",
|
||||||
|
"profileLastPath",
|
||||||
|
"configVersion"
|
||||||
|
]
|
||||||
|
|
||||||
|
try {
|
||||||
|
const content = cacheFile.text()
|
||||||
|
if (!content || !content.trim()) return
|
||||||
|
|
||||||
|
const cache = JSON.parse(content)
|
||||||
|
let needsSave = false
|
||||||
|
|
||||||
|
for (const key in cache) {
|
||||||
|
if (!validKeys.includes(key)) {
|
||||||
|
console.log("CacheData: Removing unused key:", key)
|
||||||
|
delete cache[key]
|
||||||
|
needsSave = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsSave) {
|
||||||
|
cacheFile.setText(JSON.stringify(cache, null, 2))
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("CacheData: Failed to cleanup unused keys:", e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
id: cacheFile
|
||||||
|
|
||||||
|
path: isGreeterMode ? "" : _stateDir + "/DankMaterialShell/cache.json"
|
||||||
|
blockLoading: true
|
||||||
|
blockWrites: true
|
||||||
|
atomicWrites: true
|
||||||
|
watchChanges: !isGreeterMode
|
||||||
|
onLoaded: {
|
||||||
|
if (!isGreeterMode) {
|
||||||
|
parseCache(cacheFile.text())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onLoadFailed: error => {
|
||||||
|
if (!isGreeterMode) {
|
||||||
|
console.log("CacheData: No cache file found, starting fresh")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
62
Common/DankSocket.qml
Normal file
62
Common/DankSocket.qml
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell.Io
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property alias path: socket.path
|
||||||
|
property alias parser: socket.parser
|
||||||
|
property bool connected: false
|
||||||
|
|
||||||
|
property int reconnectBaseMs: 400
|
||||||
|
property int reconnectMaxMs: 15000
|
||||||
|
|
||||||
|
property int _reconnectAttempt: 0
|
||||||
|
|
||||||
|
signal connectionStateChanged()
|
||||||
|
|
||||||
|
onConnectedChanged: {
|
||||||
|
socket.connected = connected
|
||||||
|
}
|
||||||
|
|
||||||
|
Socket {
|
||||||
|
id: socket
|
||||||
|
|
||||||
|
onConnectionStateChanged: {
|
||||||
|
root.connectionStateChanged()
|
||||||
|
if (connected) {
|
||||||
|
root._reconnectAttempt = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (root.connected) {
|
||||||
|
root._scheduleReconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: reconnectTimer
|
||||||
|
interval: 0
|
||||||
|
repeat: false
|
||||||
|
onTriggered: {
|
||||||
|
socket.connected = false
|
||||||
|
Qt.callLater(() => socket.connected = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function send(data) {
|
||||||
|
const json = typeof data === "string" ? data : JSON.stringify(data)
|
||||||
|
const message = json.endsWith("\n") ? json : json + "\n"
|
||||||
|
socket.write(message)
|
||||||
|
socket.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
function _scheduleReconnect() {
|
||||||
|
const pow = Math.min(_reconnectAttempt, 10)
|
||||||
|
const base = Math.min(reconnectBaseMs * Math.pow(2, pow), reconnectMaxMs)
|
||||||
|
const jitter = Math.floor(Math.random() * Math.floor(base / 4))
|
||||||
|
reconnectTimer.interval = base + jitter
|
||||||
|
reconnectTimer.restart()
|
||||||
|
_reconnectAttempt++
|
||||||
|
}
|
||||||
|
}
|
||||||
53
Common/Facts.qml
Normal file
53
Common/Facts.qml
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property var facts: [
|
||||||
|
"A photon takes 100,000 to 200,000 years bouncing through the Sun's dense core, then races to Earth in just 8 minutes 20 seconds.",
|
||||||
|
"A teaspoon of neutron star matter would weigh a billion metric tons here on Earth.",
|
||||||
|
"Right now, 100 trillion solar neutrinos are passing through your body every second.",
|
||||||
|
"The Sun converts 4 million metric tons of matter into pure energy every second—enough to power Earth for 500,000 years.",
|
||||||
|
"The universe still glows with leftover heat from the Big Bang—just 2.7 degrees above absolute zero.",
|
||||||
|
"There's a nebula out there that's actually colder than empty space itself.",
|
||||||
|
"We've detected black holes crashing together by measuring spacetime stretch by less than 1/10,000th the width of a proton.",
|
||||||
|
"Fast radio bursts can release more energy in 5 milliseconds than our Sun produces in 3 days.",
|
||||||
|
"Our galaxy might be crawling with billions of rogue planets drifting alone in the dark.",
|
||||||
|
"Distant galaxies can move away from us faster than light because space itself is stretching.",
|
||||||
|
"The edge of what we can see is 46.5 billion light-years away, even though the universe is only 13.8 billion years old.",
|
||||||
|
"The universe is mostly invisible: 5% regular matter, 27% dark matter, 68% dark energy.",
|
||||||
|
"A day on Venus lasts longer than its entire year around the Sun.",
|
||||||
|
"On Mercury, the time between sunrises is 176 Earth days long.",
|
||||||
|
"In about 4.5 billion years, our galaxy will smash into Andromeda.",
|
||||||
|
"Most of the gold in your jewelry was forged when neutron stars collided somewhere in space.",
|
||||||
|
"PSR J1748-2446ad, the fastest spinning star, rotates 716 times per second—its equator moves at 24% the speed of light.",
|
||||||
|
"Cosmic rays create particles that shouldn't make it to Earth's surface, but time dilation lets them sneak through.",
|
||||||
|
"Jupiter's magnetic field is so huge that if we could see it, it would look bigger than the Moon in our sky.",
|
||||||
|
"Interstellar space is so empty it's like a cube 32 kilometers wide containing just a single grain of sand.",
|
||||||
|
"Voyager 1 is 24 billion kilometers away but won't leave the Sun's gravitational influence for another 30,000 years.",
|
||||||
|
"Counting to a billion at one number per second would take over 31 years.",
|
||||||
|
"Space is so vast, even speeding at light-speed, you'd never return past the cosmic horizon.",
|
||||||
|
"Astronauts on the ISS age about 0.01 seconds less each year than people on Earth.",
|
||||||
|
"Sagittarius B2, a dust cloud near our galaxy's center, contains ethyl formate—the compound that gives raspberries their flavor and rum its smell.",
|
||||||
|
"Beyond 16 billion light-years, the cosmic event horizon marks where space expands too fast for light to ever reach us again.",
|
||||||
|
"Even at light-speed, you'd never catch up to most galaxies—space expands faster.",
|
||||||
|
"Only around 5% of galaxies are ever reachable—even at light-speed.",
|
||||||
|
"If the Sun vanished, we'd still orbit it for 8 minutes before drifting away.",
|
||||||
|
"If a planet 65 million light-years away looked at Earth now, it'd see dinosaurs.",
|
||||||
|
"Our oldest radio signals will reach the Milky Way's center in 26,000 years.",
|
||||||
|
"Every atom in your body heavier than hydrogen was forged in the nuclear furnace of a dying star.",
|
||||||
|
"The Moon moves 3.8 centimeters farther from Earth every year.",
|
||||||
|
"The universe creates 275 million new stars every single day.",
|
||||||
|
"Jupiter's Great Red Spot is a storm twice the size of Earth that has been raging for at least 350 years.",
|
||||||
|
"If you watched someone fall into a black hole, they'd appear frozen at the event horizon forever—time effectively stops from your perspective.",
|
||||||
|
"The Boötes Supervoid is a cosmic desert 1.8 billion light-years across with 60% fewer galaxies than it should have."
|
||||||
|
]
|
||||||
|
|
||||||
|
function getRandomFact() {
|
||||||
|
return facts[Math.floor(Math.random() * facts.length)]
|
||||||
|
}
|
||||||
|
}
|
||||||
113
Common/I18n.qml
Normal file
113
Common/I18n.qml
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Qt.labs.folderlistmodel
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property string _rawLocale: Qt.locale().name
|
||||||
|
readonly property string _lang: _rawLocale.split(/[_-]/)[0]
|
||||||
|
readonly property var _candidates: {
|
||||||
|
const fullUnderscore = _rawLocale;
|
||||||
|
const fullHyphen = _rawLocale.replace("_", "-");
|
||||||
|
return [fullUnderscore, fullHyphen, _lang].filter(c => c && c !== "en");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
readonly property url translationsFolder: Qt.resolvedUrl("../translations/poexports")
|
||||||
|
|
||||||
|
property string currentLocale: "en"
|
||||||
|
property var translations: ({})
|
||||||
|
property bool translationsLoaded: false
|
||||||
|
|
||||||
|
property url _selectedPath: ""
|
||||||
|
|
||||||
|
FolderListModel {
|
||||||
|
id: dir
|
||||||
|
folder: root.translationsFolder
|
||||||
|
nameFilters: ["*.json"]
|
||||||
|
showDirs: false
|
||||||
|
showDotAndDotDot: false
|
||||||
|
|
||||||
|
onStatusChanged: if (status === FolderListModel.Ready) root._pickTranslation()
|
||||||
|
}
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
id: translationLoader
|
||||||
|
path: root._selectedPath
|
||||||
|
|
||||||
|
onLoaded: {
|
||||||
|
try {
|
||||||
|
root.translations = JSON.parse(text())
|
||||||
|
root.translationsLoaded = true
|
||||||
|
console.log(`I18n: Loaded translations for '${root.currentLocale}' ` +
|
||||||
|
`(${Object.keys(root.translations).length} contexts)`)
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(`I18n: Error parsing '${root.currentLocale}':`, e,
|
||||||
|
"- falling back to English")
|
||||||
|
root._fallbackToEnglish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoadFailed: (error) => {
|
||||||
|
console.warn(`I18n: Failed to load '${root.currentLocale}' (${error}), ` +
|
||||||
|
"falling back to English")
|
||||||
|
root._fallbackToEnglish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _pickTranslation() {
|
||||||
|
const present = new Set()
|
||||||
|
for (let i = 0; i < dir.count; i++) {
|
||||||
|
const name = dir.get(i, "fileName") // e.g. "zh_CN.json"
|
||||||
|
if (name && name.endsWith(".json")) {
|
||||||
|
present.add(name.slice(0, -5))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < _candidates.length; i++) {
|
||||||
|
const cand = _candidates[i]
|
||||||
|
if (present.has(cand)) {
|
||||||
|
_useLocale(cand, dir.folder + "/" + cand + ".json")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_fallbackToEnglish()
|
||||||
|
}
|
||||||
|
|
||||||
|
function _useLocale(localeTag, fileUrl) {
|
||||||
|
currentLocale = localeTag
|
||||||
|
_selectedPath = fileUrl
|
||||||
|
translationsLoaded = false
|
||||||
|
translations = ({})
|
||||||
|
console.log(`I18n: Using locale '${localeTag}' from ${fileUrl}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
function _fallbackToEnglish() {
|
||||||
|
currentLocale = "en"
|
||||||
|
_selectedPath = ""
|
||||||
|
translationsLoaded = false
|
||||||
|
translations = ({})
|
||||||
|
console.warn("I18n: Falling back to built-in English strings")
|
||||||
|
}
|
||||||
|
|
||||||
|
function tr(term, context) {
|
||||||
|
if (!translationsLoaded || !translations) return term
|
||||||
|
const ctx = context || term
|
||||||
|
if (translations[ctx] && translations[ctx][term]) return translations[ctx][term]
|
||||||
|
for (const c in translations) {
|
||||||
|
if (translations[c] && translations[c][term]) return translations[c][term]
|
||||||
|
}
|
||||||
|
return term
|
||||||
|
}
|
||||||
|
|
||||||
|
function trContext(context, term) {
|
||||||
|
if (!translationsLoaded || !translations) return term
|
||||||
|
if (translations[context] && translations[context][term]) return translations[context][term]
|
||||||
|
return term
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,6 +38,10 @@ Singleton {
|
|||||||
return stringify(path).replace("file://", "")
|
return stringify(path).replace("file://", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toFileUrl(path: string): string {
|
||||||
|
return path.startsWith("file://") ? path : "file://" + path
|
||||||
|
}
|
||||||
|
|
||||||
function mkdir(path: url): void {
|
function mkdir(path: url): void {
|
||||||
Quickshell.execDetached(["mkdir", "-p", strip(path)])
|
Quickshell.execDetached(["mkdir", "-p", strip(path)])
|
||||||
}
|
}
|
||||||
|
|||||||
70
Common/Proc.qml
Normal file
70
Common/Proc.qml
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property int defaultDebounceMs: 50
|
||||||
|
property var _procDebouncers: ({}) // id -> { timer, command, callback, waitMs }
|
||||||
|
|
||||||
|
function runCommand(id, command, callback, debounceMs) {
|
||||||
|
const wait = (typeof debounceMs === "number" && debounceMs >= 0) ? debounceMs : defaultDebounceMs
|
||||||
|
let procId = id ? id : Math.random()
|
||||||
|
|
||||||
|
if (!_procDebouncers[procId]) {
|
||||||
|
const t = Qt.createQmlObject('import QtQuick; Timer { repeat: false }', root)
|
||||||
|
t.triggered.connect(function() { _launchProc(procId) })
|
||||||
|
_procDebouncers[procId] = { timer: t, command: command, callback: callback, waitMs: wait }
|
||||||
|
} else {
|
||||||
|
_procDebouncers[procId].command = command
|
||||||
|
_procDebouncers[procId].callback = callback
|
||||||
|
_procDebouncers[procId].waitMs = wait
|
||||||
|
}
|
||||||
|
|
||||||
|
const entry = _procDebouncers[procId]
|
||||||
|
entry.timer.interval = entry.waitMs
|
||||||
|
entry.timer.restart()
|
||||||
|
}
|
||||||
|
|
||||||
|
function _launchProc(id) {
|
||||||
|
const entry = _procDebouncers[id]
|
||||||
|
if (!entry) return
|
||||||
|
|
||||||
|
const proc = Qt.createQmlObject('import Quickshell.Io; Process { running: false }', root)
|
||||||
|
const out = Qt.createQmlObject('import Quickshell.Io; StdioCollector {}', proc)
|
||||||
|
const err = Qt.createQmlObject('import Quickshell.Io; StdioCollector {}', proc)
|
||||||
|
|
||||||
|
proc.stdout = out
|
||||||
|
proc.stderr = err
|
||||||
|
proc.command = entry.command
|
||||||
|
|
||||||
|
let capturedOut = ""
|
||||||
|
let exitSeen = false
|
||||||
|
let exitCodeValue = -1
|
||||||
|
|
||||||
|
out.streamFinished.connect(function() {
|
||||||
|
capturedOut = out.text || ""
|
||||||
|
maybeComplete()
|
||||||
|
})
|
||||||
|
|
||||||
|
proc.exited.connect(function(code) {
|
||||||
|
exitSeen = true
|
||||||
|
exitCodeValue = code
|
||||||
|
maybeComplete()
|
||||||
|
})
|
||||||
|
|
||||||
|
function maybeComplete() {
|
||||||
|
if (!exitSeen) return
|
||||||
|
if (typeof entry.callback === "function") {
|
||||||
|
try { entry.callback(capturedOut, exitCodeValue) } catch (e) { console.warn("runCommand callback error:", e) }
|
||||||
|
}
|
||||||
|
try { proc.destroy() } catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
proc.running = true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,24 +9,40 @@ import qs.Common
|
|||||||
import qs.Services
|
import qs.Services
|
||||||
|
|
||||||
Singleton {
|
Singleton {
|
||||||
|
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
readonly property int sessionConfigVersion: 1
|
||||||
|
|
||||||
|
readonly property bool isGreeterMode: Quickshell.env("DMS_RUN_GREETER") === "1" || Quickshell.env("DMS_RUN_GREETER") === "true"
|
||||||
|
property bool hasTriedDefaultSession: false
|
||||||
|
readonly property string _stateUrl: StandardPaths.writableLocation(StandardPaths.GenericStateLocation)
|
||||||
|
readonly property string _stateDir: Paths.strip(_stateUrl)
|
||||||
|
|
||||||
property bool isLightMode: false
|
property bool isLightMode: false
|
||||||
|
property bool doNotDisturb: false
|
||||||
|
|
||||||
property string wallpaperPath: ""
|
property string wallpaperPath: ""
|
||||||
property string wallpaperLastPath: ""
|
|
||||||
property string profileLastPath: ""
|
|
||||||
property bool perMonitorWallpaper: false
|
property bool perMonitorWallpaper: false
|
||||||
property var monitorWallpapers: ({})
|
property var monitorWallpapers: ({})
|
||||||
property bool doNotDisturb: false
|
property bool perModeWallpaper: false
|
||||||
|
property string wallpaperPathLight: ""
|
||||||
|
property string wallpaperPathDark: ""
|
||||||
|
property var monitorWallpapersLight: ({})
|
||||||
|
property var monitorWallpapersDark: ({})
|
||||||
|
property string wallpaperTransition: "fade"
|
||||||
|
readonly property var availableWallpaperTransitions: ["none", "fade", "wipe", "disc", "stripes", "iris bloom", "pixelate", "portal"]
|
||||||
|
property var includedTransitions: availableWallpaperTransitions.filter(t => t !== "none")
|
||||||
|
|
||||||
|
property bool wallpaperCyclingEnabled: false
|
||||||
|
property string wallpaperCyclingMode: "interval"
|
||||||
|
property int wallpaperCyclingInterval: 300
|
||||||
|
property string wallpaperCyclingTime: "06:00"
|
||||||
|
property var monitorCyclingSettings: ({})
|
||||||
|
|
||||||
property bool nightModeEnabled: false
|
property bool nightModeEnabled: false
|
||||||
property int nightModeTemperature: 4500
|
property int nightModeTemperature: 4500
|
||||||
property bool nightModeAutoEnabled: false
|
property bool nightModeAutoEnabled: false
|
||||||
property string nightModeAutoMode: "time"
|
property string nightModeAutoMode: "time"
|
||||||
|
|
||||||
property bool hasTriedDefaultSession: false
|
|
||||||
readonly property string _stateUrl: StandardPaths.writableLocation(StandardPaths.GenericStateLocation)
|
|
||||||
readonly property string _stateDir: Paths.strip(_stateUrl)
|
|
||||||
property int nightModeStartHour: 18
|
property int nightModeStartHour: 18
|
||||||
property int nightModeStartMinute: 0
|
property int nightModeStartMinute: 0
|
||||||
property int nightModeEndHour: 6
|
property int nightModeEndHour: 6
|
||||||
@@ -34,43 +50,30 @@ Singleton {
|
|||||||
property real latitude: 0.0
|
property real latitude: 0.0
|
||||||
property real longitude: 0.0
|
property real longitude: 0.0
|
||||||
property string nightModeLocationProvider: ""
|
property string nightModeLocationProvider: ""
|
||||||
|
|
||||||
property var pinnedApps: []
|
property var pinnedApps: []
|
||||||
|
property var recentColors: []
|
||||||
|
property bool showThirdPartyPlugins: false
|
||||||
|
property string launchPrefix: ""
|
||||||
|
property string lastBrightnessDevice: ""
|
||||||
|
|
||||||
property int selectedGpuIndex: 0
|
property int selectedGpuIndex: 0
|
||||||
property bool nvidiaGpuTempEnabled: false
|
property bool nvidiaGpuTempEnabled: false
|
||||||
property bool nonNvidiaGpuTempEnabled: false
|
property bool nonNvidiaGpuTempEnabled: false
|
||||||
property var enabledGpuPciIds: []
|
property var enabledGpuPciIds: []
|
||||||
property bool wallpaperCyclingEnabled: false
|
|
||||||
property string wallpaperCyclingMode: "interval" // "interval" or "time"
|
|
||||||
property int wallpaperCyclingInterval: 300 // seconds (5 minutes)
|
|
||||||
property string wallpaperCyclingTime: "06:00" // HH:mm format
|
|
||||||
property var monitorCyclingSettings: ({})
|
|
||||||
property string lastBrightnessDevice: ""
|
|
||||||
property string launchPrefix: ""
|
|
||||||
property string wallpaperTransition: "fade"
|
|
||||||
readonly property var availableWallpaperTransitions: ["none", "fade", "wipe", "disc", "stripes", "iris bloom", "pixelate", "portal"]
|
|
||||||
property var includedTransitions: availableWallpaperTransitions.filter(t => t !== "none")
|
|
||||||
|
|
||||||
// Power management settings - AC Power
|
|
||||||
property int acMonitorTimeout: 0 // Never
|
|
||||||
property int acLockTimeout: 0 // Never
|
|
||||||
property int acSuspendTimeout: 0 // Never
|
|
||||||
property int acHibernateTimeout: 0 // Never
|
|
||||||
|
|
||||||
// Power management settings - Battery
|
|
||||||
property int batteryMonitorTimeout: 0 // Never
|
|
||||||
property int batteryLockTimeout: 0 // Never
|
|
||||||
property int batterySuspendTimeout: 0 // Never
|
|
||||||
property int batteryHibernateTimeout: 0 // Never
|
|
||||||
|
|
||||||
property bool lockBeforeSuspend: false
|
|
||||||
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
loadSettings()
|
if (!isGreeterMode) {
|
||||||
|
loadSettings()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadSettings() {
|
function loadSettings() {
|
||||||
parseSettings(settingsFile.text())
|
if (isGreeterMode) {
|
||||||
|
parseSettings(greeterSessionFile.text())
|
||||||
|
} else {
|
||||||
|
parseSettings(settingsFile.text())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseSettings(content) {
|
function parseSettings(content) {
|
||||||
@@ -79,16 +82,18 @@ Singleton {
|
|||||||
var settings = JSON.parse(content)
|
var settings = JSON.parse(content)
|
||||||
isLightMode = settings.isLightMode !== undefined ? settings.isLightMode : false
|
isLightMode = settings.isLightMode !== undefined ? settings.isLightMode : false
|
||||||
wallpaperPath = settings.wallpaperPath !== undefined ? settings.wallpaperPath : ""
|
wallpaperPath = settings.wallpaperPath !== undefined ? settings.wallpaperPath : ""
|
||||||
wallpaperLastPath = settings.wallpaperLastPath !== undefined ? settings.wallpaperLastPath : ""
|
|
||||||
profileLastPath = settings.profileLastPath !== undefined ? settings.profileLastPath : ""
|
|
||||||
perMonitorWallpaper = settings.perMonitorWallpaper !== undefined ? settings.perMonitorWallpaper : false
|
perMonitorWallpaper = settings.perMonitorWallpaper !== undefined ? settings.perMonitorWallpaper : false
|
||||||
monitorWallpapers = settings.monitorWallpapers !== undefined ? settings.monitorWallpapers : {}
|
monitorWallpapers = settings.monitorWallpapers !== undefined ? settings.monitorWallpapers : {}
|
||||||
|
perModeWallpaper = settings.perModeWallpaper !== undefined ? settings.perModeWallpaper : false
|
||||||
|
wallpaperPathLight = settings.wallpaperPathLight !== undefined ? settings.wallpaperPathLight : ""
|
||||||
|
wallpaperPathDark = settings.wallpaperPathDark !== undefined ? settings.wallpaperPathDark : ""
|
||||||
|
monitorWallpapersLight = settings.monitorWallpapersLight !== undefined ? settings.monitorWallpapersLight : {}
|
||||||
|
monitorWallpapersDark = settings.monitorWallpapersDark !== undefined ? settings.monitorWallpapersDark : {}
|
||||||
doNotDisturb = settings.doNotDisturb !== undefined ? settings.doNotDisturb : false
|
doNotDisturb = settings.doNotDisturb !== undefined ? settings.doNotDisturb : false
|
||||||
nightModeEnabled = settings.nightModeEnabled !== undefined ? settings.nightModeEnabled : false
|
nightModeEnabled = settings.nightModeEnabled !== undefined ? settings.nightModeEnabled : false
|
||||||
nightModeTemperature = settings.nightModeTemperature !== undefined ? settings.nightModeTemperature : 4500
|
nightModeTemperature = settings.nightModeTemperature !== undefined ? settings.nightModeTemperature : 4500
|
||||||
nightModeAutoEnabled = settings.nightModeAutoEnabled !== undefined ? settings.nightModeAutoEnabled : false
|
nightModeAutoEnabled = settings.nightModeAutoEnabled !== undefined ? settings.nightModeAutoEnabled : false
|
||||||
nightModeAutoMode = settings.nightModeAutoMode !== undefined ? settings.nightModeAutoMode : "time"
|
nightModeAutoMode = settings.nightModeAutoMode !== undefined ? settings.nightModeAutoMode : "time"
|
||||||
// Handle legacy time format
|
|
||||||
if (settings.nightModeStartTime !== undefined) {
|
if (settings.nightModeStartTime !== undefined) {
|
||||||
const parts = settings.nightModeStartTime.split(":")
|
const parts = settings.nightModeStartTime.split(":")
|
||||||
nightModeStartHour = parseInt(parts[0]) || 18
|
nightModeStartHour = parseInt(parts[0]) || 18
|
||||||
@@ -122,20 +127,20 @@ Singleton {
|
|||||||
launchPrefix = settings.launchPrefix !== undefined ? settings.launchPrefix : ""
|
launchPrefix = settings.launchPrefix !== undefined ? settings.launchPrefix : ""
|
||||||
wallpaperTransition = settings.wallpaperTransition !== undefined ? settings.wallpaperTransition : "fade"
|
wallpaperTransition = settings.wallpaperTransition !== undefined ? settings.wallpaperTransition : "fade"
|
||||||
includedTransitions = settings.includedTransitions !== undefined ? settings.includedTransitions : availableWallpaperTransitions.filter(t => t !== "none")
|
includedTransitions = settings.includedTransitions !== undefined ? settings.includedTransitions : availableWallpaperTransitions.filter(t => t !== "none")
|
||||||
|
recentColors = settings.recentColors !== undefined ? settings.recentColors : []
|
||||||
|
showThirdPartyPlugins = settings.showThirdPartyPlugins !== undefined ? settings.showThirdPartyPlugins : false
|
||||||
|
|
||||||
acMonitorTimeout = settings.acMonitorTimeout !== undefined ? settings.acMonitorTimeout : 0
|
if (settings.configVersion === undefined) {
|
||||||
acLockTimeout = settings.acLockTimeout !== undefined ? settings.acLockTimeout : 0
|
migrateFromUndefinedToV1(settings)
|
||||||
acSuspendTimeout = settings.acSuspendTimeout !== undefined ? settings.acSuspendTimeout : 0
|
saveSettings()
|
||||||
acHibernateTimeout = settings.acHibernateTimeout !== undefined ? settings.acHibernateTimeout : 0
|
} else if (settings.configVersion === sessionConfigVersion) {
|
||||||
batteryMonitorTimeout = settings.batteryMonitorTimeout !== undefined ? settings.batteryMonitorTimeout : 0
|
cleanupUnusedKeys()
|
||||||
batteryLockTimeout = settings.batteryLockTimeout !== undefined ? settings.batteryLockTimeout : 0
|
}
|
||||||
batterySuspendTimeout = settings.batterySuspendTimeout !== undefined ? settings.batterySuspendTimeout : 0
|
|
||||||
batteryHibernateTimeout = settings.batteryHibernateTimeout !== undefined ? settings.batteryHibernateTimeout : 0
|
if (!isGreeterMode) {
|
||||||
lockBeforeSuspend = settings.lockBeforeSuspend !== undefined ? settings.lockBeforeSuspend : false
|
if (typeof Theme !== "undefined") {
|
||||||
|
Theme.generateSystemThemesFromCurrentTheme()
|
||||||
// Generate system themes but don't override user's theme choice
|
}
|
||||||
if (typeof Theme !== "undefined") {
|
|
||||||
Theme.generateSystemThemesFromCurrentTheme()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -144,13 +149,17 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function saveSettings() {
|
function saveSettings() {
|
||||||
|
if (isGreeterMode) return
|
||||||
settingsFile.setText(JSON.stringify({
|
settingsFile.setText(JSON.stringify({
|
||||||
"isLightMode": isLightMode,
|
"isLightMode": isLightMode,
|
||||||
"wallpaperPath": wallpaperPath,
|
"wallpaperPath": wallpaperPath,
|
||||||
"wallpaperLastPath": wallpaperLastPath,
|
|
||||||
"profileLastPath": profileLastPath,
|
|
||||||
"perMonitorWallpaper": perMonitorWallpaper,
|
"perMonitorWallpaper": perMonitorWallpaper,
|
||||||
"monitorWallpapers": monitorWallpapers,
|
"monitorWallpapers": monitorWallpapers,
|
||||||
|
"perModeWallpaper": perModeWallpaper,
|
||||||
|
"wallpaperPathLight": wallpaperPathLight,
|
||||||
|
"wallpaperPathDark": wallpaperPathDark,
|
||||||
|
"monitorWallpapersLight": monitorWallpapersLight,
|
||||||
|
"monitorWallpapersDark": monitorWallpapersDark,
|
||||||
"doNotDisturb": doNotDisturb,
|
"doNotDisturb": doNotDisturb,
|
||||||
"nightModeEnabled": nightModeEnabled,
|
"nightModeEnabled": nightModeEnabled,
|
||||||
"nightModeTemperature": nightModeTemperature,
|
"nightModeTemperature": nightModeTemperature,
|
||||||
@@ -177,20 +186,101 @@ Singleton {
|
|||||||
"launchPrefix": launchPrefix,
|
"launchPrefix": launchPrefix,
|
||||||
"wallpaperTransition": wallpaperTransition,
|
"wallpaperTransition": wallpaperTransition,
|
||||||
"includedTransitions": includedTransitions,
|
"includedTransitions": includedTransitions,
|
||||||
"acMonitorTimeout": acMonitorTimeout,
|
"recentColors": recentColors,
|
||||||
"acLockTimeout": acLockTimeout,
|
"showThirdPartyPlugins": showThirdPartyPlugins,
|
||||||
"acSuspendTimeout": acSuspendTimeout,
|
"configVersion": sessionConfigVersion
|
||||||
"acHibernateTimeout": acHibernateTimeout,
|
|
||||||
"batteryMonitorTimeout": batteryMonitorTimeout,
|
|
||||||
"batteryLockTimeout": batteryLockTimeout,
|
|
||||||
"batterySuspendTimeout": batterySuspendTimeout,
|
|
||||||
"batteryHibernateTimeout": batteryHibernateTimeout,
|
|
||||||
"lockBeforeSuspend": lockBeforeSuspend
|
|
||||||
}, null, 2))
|
}, null, 2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function migrateFromUndefinedToV1(settings) {
|
||||||
|
console.log("SessionData: Migrating configuration from undefined to version 1")
|
||||||
|
if (typeof SettingsData !== "undefined") {
|
||||||
|
if (settings.acMonitorTimeout !== undefined) {
|
||||||
|
SettingsData.setAcMonitorTimeout(settings.acMonitorTimeout)
|
||||||
|
}
|
||||||
|
if (settings.acLockTimeout !== undefined) {
|
||||||
|
SettingsData.setAcLockTimeout(settings.acLockTimeout)
|
||||||
|
}
|
||||||
|
if (settings.acSuspendTimeout !== undefined) {
|
||||||
|
SettingsData.setAcSuspendTimeout(settings.acSuspendTimeout)
|
||||||
|
}
|
||||||
|
if (settings.acHibernateTimeout !== undefined) {
|
||||||
|
SettingsData.setAcHibernateTimeout(settings.acHibernateTimeout)
|
||||||
|
}
|
||||||
|
if (settings.batteryMonitorTimeout !== undefined) {
|
||||||
|
SettingsData.setBatteryMonitorTimeout(settings.batteryMonitorTimeout)
|
||||||
|
}
|
||||||
|
if (settings.batteryLockTimeout !== undefined) {
|
||||||
|
SettingsData.setBatteryLockTimeout(settings.batteryLockTimeout)
|
||||||
|
}
|
||||||
|
if (settings.batterySuspendTimeout !== undefined) {
|
||||||
|
SettingsData.setBatterySuspendTimeout(settings.batterySuspendTimeout)
|
||||||
|
}
|
||||||
|
if (settings.batteryHibernateTimeout !== undefined) {
|
||||||
|
SettingsData.setBatteryHibernateTimeout(settings.batteryHibernateTimeout)
|
||||||
|
}
|
||||||
|
if (settings.lockBeforeSuspend !== undefined) {
|
||||||
|
SettingsData.setLockBeforeSuspend(settings.lockBeforeSuspend)
|
||||||
|
}
|
||||||
|
if (settings.loginctlLockIntegration !== undefined) {
|
||||||
|
SettingsData.setLoginctlLockIntegration(settings.loginctlLockIntegration)
|
||||||
|
}
|
||||||
|
if (settings.launchPrefix !== undefined) {
|
||||||
|
SettingsData.setLaunchPrefix(settings.launchPrefix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof CacheData !== "undefined") {
|
||||||
|
if (settings.wallpaperLastPath !== undefined) {
|
||||||
|
CacheData.wallpaperLastPath = settings.wallpaperLastPath
|
||||||
|
}
|
||||||
|
if (settings.profileLastPath !== undefined) {
|
||||||
|
CacheData.profileLastPath = settings.profileLastPath
|
||||||
|
}
|
||||||
|
CacheData.saveCache()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanupUnusedKeys() {
|
||||||
|
const validKeys = [
|
||||||
|
"isLightMode", "wallpaperPath", "perMonitorWallpaper", "monitorWallpapers", "perModeWallpaper",
|
||||||
|
"wallpaperPathLight", "wallpaperPathDark", "monitorWallpapersLight",
|
||||||
|
"monitorWallpapersDark", "doNotDisturb", "nightModeEnabled",
|
||||||
|
"nightModeTemperature", "nightModeAutoEnabled", "nightModeAutoMode",
|
||||||
|
"nightModeStartHour", "nightModeStartMinute", "nightModeEndHour",
|
||||||
|
"nightModeEndMinute", "latitude", "longitude", "nightModeLocationProvider",
|
||||||
|
"pinnedApps", "selectedGpuIndex", "nvidiaGpuTempEnabled",
|
||||||
|
"nonNvidiaGpuTempEnabled", "enabledGpuPciIds", "wallpaperCyclingEnabled",
|
||||||
|
"wallpaperCyclingMode", "wallpaperCyclingInterval", "wallpaperCyclingTime",
|
||||||
|
"monitorCyclingSettings", "lastBrightnessDevice", "wallpaperTransition",
|
||||||
|
"includedTransitions", "recentColors", "showThirdPartyPlugins", "configVersion"
|
||||||
|
]
|
||||||
|
|
||||||
|
try {
|
||||||
|
const content = settingsFile.text()
|
||||||
|
if (!content || !content.trim()) return
|
||||||
|
|
||||||
|
const settings = JSON.parse(content)
|
||||||
|
let needsSave = false
|
||||||
|
|
||||||
|
for (const key in settings) {
|
||||||
|
if (!validKeys.includes(key)) {
|
||||||
|
console.log("SessionData: Removing unused key:", key)
|
||||||
|
delete settings[key]
|
||||||
|
needsSave = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsSave) {
|
||||||
|
settingsFile.setText(JSON.stringify(settings, null, 2))
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("SessionData: Failed to cleanup unused keys:", e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function setLightMode(lightMode) {
|
function setLightMode(lightMode) {
|
||||||
isLightMode = lightMode
|
isLightMode = lightMode
|
||||||
|
syncWallpaperForCurrentMode()
|
||||||
saveSettings()
|
saveSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,6 +289,212 @@ Singleton {
|
|||||||
saveSettings()
|
saveSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setWallpaperPath(path) {
|
||||||
|
wallpaperPath = path
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setWallpaper(imagePath) {
|
||||||
|
wallpaperPath = imagePath
|
||||||
|
if (perModeWallpaper) {
|
||||||
|
if (isLightMode) {
|
||||||
|
wallpaperPathLight = imagePath
|
||||||
|
} else {
|
||||||
|
wallpaperPathDark = imagePath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
saveSettings()
|
||||||
|
|
||||||
|
if (typeof Theme !== "undefined") {
|
||||||
|
Theme.generateSystemThemesFromCurrentTheme()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setWallpaperColor(color) {
|
||||||
|
wallpaperPath = color
|
||||||
|
if (perModeWallpaper) {
|
||||||
|
if (isLightMode) {
|
||||||
|
wallpaperPathLight = color
|
||||||
|
} else {
|
||||||
|
wallpaperPathDark = color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
saveSettings()
|
||||||
|
|
||||||
|
if (typeof Theme !== "undefined") {
|
||||||
|
Theme.generateSystemThemesFromCurrentTheme()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearWallpaper() {
|
||||||
|
wallpaperPath = ""
|
||||||
|
saveSettings()
|
||||||
|
|
||||||
|
if (typeof Theme !== "undefined") {
|
||||||
|
if (typeof SettingsData !== "undefined" && SettingsData.theme) {
|
||||||
|
Theme.switchTheme(SettingsData.theme)
|
||||||
|
} else {
|
||||||
|
Theme.switchTheme("blue")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setPerMonitorWallpaper(enabled) {
|
||||||
|
perMonitorWallpaper = enabled
|
||||||
|
if (enabled && perModeWallpaper) {
|
||||||
|
syncWallpaperForCurrentMode()
|
||||||
|
}
|
||||||
|
saveSettings()
|
||||||
|
|
||||||
|
if (typeof Theme !== "undefined") {
|
||||||
|
Theme.generateSystemThemesFromCurrentTheme()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setPerModeWallpaper(enabled) {
|
||||||
|
if (enabled && wallpaperCyclingEnabled) {
|
||||||
|
setWallpaperCyclingEnabled(false)
|
||||||
|
}
|
||||||
|
if (enabled && perMonitorWallpaper) {
|
||||||
|
var monitorCyclingAny = false
|
||||||
|
for (var key in monitorCyclingSettings) {
|
||||||
|
if (monitorCyclingSettings[key].enabled) {
|
||||||
|
monitorCyclingAny = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (monitorCyclingAny) {
|
||||||
|
var newSettings = Object.assign({}, monitorCyclingSettings)
|
||||||
|
for (var screenName in newSettings) {
|
||||||
|
newSettings[screenName].enabled = false
|
||||||
|
}
|
||||||
|
monitorCyclingSettings = newSettings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
perModeWallpaper = enabled
|
||||||
|
if (enabled) {
|
||||||
|
if (perMonitorWallpaper) {
|
||||||
|
monitorWallpapersLight = Object.assign({}, monitorWallpapers)
|
||||||
|
monitorWallpapersDark = Object.assign({}, monitorWallpapers)
|
||||||
|
} else {
|
||||||
|
wallpaperPathLight = wallpaperPath
|
||||||
|
wallpaperPathDark = wallpaperPath
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
syncWallpaperForCurrentMode()
|
||||||
|
}
|
||||||
|
saveSettings()
|
||||||
|
|
||||||
|
if (typeof Theme !== "undefined") {
|
||||||
|
Theme.generateSystemThemesFromCurrentTheme()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMonitorWallpaper(screenName, path) {
|
||||||
|
var newMonitorWallpapers = Object.assign({}, monitorWallpapers)
|
||||||
|
if (path && path !== "") {
|
||||||
|
newMonitorWallpapers[screenName] = path
|
||||||
|
} else {
|
||||||
|
delete newMonitorWallpapers[screenName]
|
||||||
|
}
|
||||||
|
monitorWallpapers = newMonitorWallpapers
|
||||||
|
|
||||||
|
if (perModeWallpaper) {
|
||||||
|
if (isLightMode) {
|
||||||
|
var newLight = Object.assign({}, monitorWallpapersLight)
|
||||||
|
if (path && path !== "") {
|
||||||
|
newLight[screenName] = path
|
||||||
|
} else {
|
||||||
|
delete newLight[screenName]
|
||||||
|
}
|
||||||
|
monitorWallpapersLight = newLight
|
||||||
|
} else {
|
||||||
|
var newDark = Object.assign({}, monitorWallpapersDark)
|
||||||
|
if (path && path !== "") {
|
||||||
|
newDark[screenName] = path
|
||||||
|
} else {
|
||||||
|
delete newDark[screenName]
|
||||||
|
}
|
||||||
|
monitorWallpapersDark = newDark
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saveSettings()
|
||||||
|
|
||||||
|
if (typeof Theme !== "undefined" && typeof Quickshell !== "undefined") {
|
||||||
|
var screens = Quickshell.screens
|
||||||
|
if (screens.length > 0 && screenName === screens[0].name) {
|
||||||
|
Theme.generateSystemThemesFromCurrentTheme()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setWallpaperTransition(transition) {
|
||||||
|
wallpaperTransition = transition
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setWallpaperCyclingEnabled(enabled) {
|
||||||
|
wallpaperCyclingEnabled = enabled
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setWallpaperCyclingMode(mode) {
|
||||||
|
wallpaperCyclingMode = mode
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setWallpaperCyclingInterval(interval) {
|
||||||
|
wallpaperCyclingInterval = interval
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setWallpaperCyclingTime(time) {
|
||||||
|
wallpaperCyclingTime = time
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMonitorCyclingEnabled(screenName, enabled) {
|
||||||
|
var newSettings = Object.assign({}, monitorCyclingSettings)
|
||||||
|
if (!newSettings[screenName]) {
|
||||||
|
newSettings[screenName] = { enabled: false, mode: "interval", interval: 300, time: "06:00" }
|
||||||
|
}
|
||||||
|
newSettings[screenName].enabled = enabled
|
||||||
|
monitorCyclingSettings = newSettings
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMonitorCyclingMode(screenName, mode) {
|
||||||
|
var newSettings = Object.assign({}, monitorCyclingSettings)
|
||||||
|
if (!newSettings[screenName]) {
|
||||||
|
newSettings[screenName] = { enabled: false, mode: "interval", interval: 300, time: "06:00" }
|
||||||
|
}
|
||||||
|
newSettings[screenName].mode = mode
|
||||||
|
monitorCyclingSettings = newSettings
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMonitorCyclingInterval(screenName, interval) {
|
||||||
|
var newSettings = Object.assign({}, monitorCyclingSettings)
|
||||||
|
if (!newSettings[screenName]) {
|
||||||
|
newSettings[screenName] = { enabled: false, mode: "interval", interval: 300, time: "06:00" }
|
||||||
|
}
|
||||||
|
newSettings[screenName].interval = interval
|
||||||
|
monitorCyclingSettings = newSettings
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMonitorCyclingTime(screenName, time) {
|
||||||
|
var newSettings = Object.assign({}, monitorCyclingSettings)
|
||||||
|
if (!newSettings[screenName]) {
|
||||||
|
newSettings[screenName] = { enabled: false, mode: "interval", interval: 300, time: "06:00" }
|
||||||
|
}
|
||||||
|
newSettings[screenName].time = time
|
||||||
|
monitorCyclingSettings = newSettings
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
function setNightModeEnabled(enabled) {
|
function setNightModeEnabled(enabled) {
|
||||||
nightModeEnabled = enabled
|
nightModeEnabled = enabled
|
||||||
saveSettings()
|
saveSettings()
|
||||||
@@ -257,58 +553,6 @@ Singleton {
|
|||||||
saveSettings()
|
saveSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
function setWallpaperPath(path) {
|
|
||||||
wallpaperPath = path
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setWallpaper(imagePath) {
|
|
||||||
wallpaperPath = imagePath
|
|
||||||
saveSettings()
|
|
||||||
|
|
||||||
if (typeof Theme !== "undefined") {
|
|
||||||
if (Theme.currentTheme === Theme.dynamic) {
|
|
||||||
Theme.extractColors()
|
|
||||||
}
|
|
||||||
Theme.generateSystemThemesFromCurrentTheme()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setWallpaperColor(color) {
|
|
||||||
wallpaperPath = color
|
|
||||||
saveSettings()
|
|
||||||
|
|
||||||
if (typeof Theme !== "undefined") {
|
|
||||||
if (Theme.currentTheme === Theme.dynamic) {
|
|
||||||
Theme.extractColors()
|
|
||||||
}
|
|
||||||
Theme.generateSystemThemesFromCurrentTheme()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearWallpaper() {
|
|
||||||
wallpaperPath = ""
|
|
||||||
saveSettings()
|
|
||||||
|
|
||||||
if (typeof Theme !== "undefined") {
|
|
||||||
if (typeof SettingsData !== "undefined" && SettingsData.theme) {
|
|
||||||
Theme.switchTheme(SettingsData.theme)
|
|
||||||
} else {
|
|
||||||
Theme.switchTheme("blue")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setWallpaperLastPath(path) {
|
|
||||||
wallpaperLastPath = path
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setProfileLastPath(path) {
|
|
||||||
profileLastPath = path
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setPinnedApps(apps) {
|
function setPinnedApps(apps) {
|
||||||
pinnedApps = apps
|
pinnedApps = apps
|
||||||
saveSettings()
|
saveSettings()
|
||||||
@@ -335,6 +579,31 @@ Singleton {
|
|||||||
return appId && pinnedApps.indexOf(appId) !== -1
|
return appId && pinnedApps.indexOf(appId) !== -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addRecentColor(color) {
|
||||||
|
const colorStr = color.toString()
|
||||||
|
let recent = recentColors.slice()
|
||||||
|
recent = recent.filter(c => c !== colorStr)
|
||||||
|
recent.unshift(colorStr)
|
||||||
|
if (recent.length > 5) recent = recent.slice(0, 5)
|
||||||
|
recentColors = recent
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setShowThirdPartyPlugins(enabled) {
|
||||||
|
showThirdPartyPlugins = enabled
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLaunchPrefix(prefix) {
|
||||||
|
launchPrefix = prefix
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLastBrightnessDevice(device) {
|
||||||
|
lastBrightnessDevice = device
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
function setSelectedGpuIndex(index) {
|
function setSelectedGpuIndex(index) {
|
||||||
selectedGpuIndex = index
|
selectedGpuIndex = index
|
||||||
saveSettings()
|
saveSettings()
|
||||||
@@ -355,24 +624,22 @@ Singleton {
|
|||||||
saveSettings()
|
saveSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
function setWallpaperCyclingEnabled(enabled) {
|
function syncWallpaperForCurrentMode() {
|
||||||
wallpaperCyclingEnabled = enabled
|
if (!perModeWallpaper) return
|
||||||
saveSettings()
|
|
||||||
|
if (perMonitorWallpaper) {
|
||||||
|
monitorWallpapers = isLightMode ? Object.assign({}, monitorWallpapersLight) : Object.assign({}, monitorWallpapersDark)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
wallpaperPath = isLightMode ? wallpaperPathLight : wallpaperPathDark
|
||||||
}
|
}
|
||||||
|
|
||||||
function setWallpaperCyclingMode(mode) {
|
function getMonitorWallpaper(screenName) {
|
||||||
wallpaperCyclingMode = mode
|
if (!perMonitorWallpaper) {
|
||||||
saveSettings()
|
return wallpaperPath
|
||||||
}
|
}
|
||||||
|
return monitorWallpapers[screenName] || wallpaperPath
|
||||||
function setWallpaperCyclingInterval(interval) {
|
|
||||||
wallpaperCyclingInterval = interval
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setWallpaperCyclingTime(time) {
|
|
||||||
wallpaperCyclingTime = time
|
|
||||||
saveSettings()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMonitorCyclingSettings(screenName) {
|
function getMonitorCyclingSettings(screenName) {
|
||||||
@@ -384,172 +651,51 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setMonitorCyclingEnabled(screenName, enabled) {
|
|
||||||
var newSettings = Object.assign({}, monitorCyclingSettings)
|
|
||||||
if (!newSettings[screenName]) {
|
|
||||||
newSettings[screenName] = { enabled: false, mode: "interval", interval: 300, time: "06:00" }
|
|
||||||
}
|
|
||||||
newSettings[screenName].enabled = enabled
|
|
||||||
monitorCyclingSettings = newSettings
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setMonitorCyclingMode(screenName, mode) {
|
|
||||||
var newSettings = Object.assign({}, monitorCyclingSettings)
|
|
||||||
if (!newSettings[screenName]) {
|
|
||||||
newSettings[screenName] = { enabled: false, mode: "interval", interval: 300, time: "06:00" }
|
|
||||||
}
|
|
||||||
newSettings[screenName].mode = mode
|
|
||||||
monitorCyclingSettings = newSettings
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setMonitorCyclingInterval(screenName, interval) {
|
|
||||||
var newSettings = Object.assign({}, monitorCyclingSettings)
|
|
||||||
if (!newSettings[screenName]) {
|
|
||||||
newSettings[screenName] = { enabled: false, mode: "interval", interval: 300, time: "06:00" }
|
|
||||||
}
|
|
||||||
newSettings[screenName].interval = interval
|
|
||||||
monitorCyclingSettings = newSettings
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setMonitorCyclingTime(screenName, time) {
|
|
||||||
var newSettings = Object.assign({}, monitorCyclingSettings)
|
|
||||||
if (!newSettings[screenName]) {
|
|
||||||
newSettings[screenName] = { enabled: false, mode: "interval", interval: 300, time: "06:00" }
|
|
||||||
}
|
|
||||||
newSettings[screenName].time = time
|
|
||||||
monitorCyclingSettings = newSettings
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setPerMonitorWallpaper(enabled) {
|
|
||||||
perMonitorWallpaper = enabled
|
|
||||||
saveSettings()
|
|
||||||
|
|
||||||
// Refresh dynamic theming when per-monitor mode changes
|
|
||||||
if (typeof Theme !== "undefined") {
|
|
||||||
if (Theme.currentTheme === Theme.dynamic) {
|
|
||||||
Theme.extractColors()
|
|
||||||
}
|
|
||||||
Theme.generateSystemThemesFromCurrentTheme()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setMonitorWallpaper(screenName, path) {
|
|
||||||
var newMonitorWallpapers = Object.assign({}, monitorWallpapers)
|
|
||||||
if (path && path !== "") {
|
|
||||||
newMonitorWallpapers[screenName] = path
|
|
||||||
} else {
|
|
||||||
delete newMonitorWallpapers[screenName]
|
|
||||||
}
|
|
||||||
monitorWallpapers = newMonitorWallpapers
|
|
||||||
saveSettings()
|
|
||||||
|
|
||||||
// Trigger dynamic theming if this is the first monitor and dynamic theming is enabled
|
|
||||||
if (typeof Theme !== "undefined" && typeof Quickshell !== "undefined") {
|
|
||||||
var screens = Quickshell.screens
|
|
||||||
if (screens.length > 0 && screenName === screens[0].name) {
|
|
||||||
if (Theme.currentTheme === Theme.dynamic) {
|
|
||||||
Theme.extractColors()
|
|
||||||
}
|
|
||||||
Theme.generateSystemThemesFromCurrentTheme()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMonitorWallpaper(screenName) {
|
|
||||||
if (!perMonitorWallpaper) {
|
|
||||||
return wallpaperPath
|
|
||||||
}
|
|
||||||
return monitorWallpapers[screenName] || wallpaperPath
|
|
||||||
}
|
|
||||||
|
|
||||||
function setLastBrightnessDevice(device) {
|
|
||||||
lastBrightnessDevice = device
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setLaunchPrefix(prefix) {
|
|
||||||
launchPrefix = prefix
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setWallpaperTransition(transition) {
|
|
||||||
wallpaperTransition = transition
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setAcMonitorTimeout(timeout) {
|
|
||||||
acMonitorTimeout = timeout
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setAcLockTimeout(timeout) {
|
|
||||||
acLockTimeout = timeout
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setAcSuspendTimeout(timeout) {
|
|
||||||
acSuspendTimeout = timeout
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setBatteryMonitorTimeout(timeout) {
|
|
||||||
batteryMonitorTimeout = timeout
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setBatteryLockTimeout(timeout) {
|
|
||||||
batteryLockTimeout = timeout
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setBatterySuspendTimeout(timeout) {
|
|
||||||
batterySuspendTimeout = timeout
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setAcHibernateTimeout(timeout) {
|
|
||||||
acHibernateTimeout = timeout
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setBatteryHibernateTimeout(timeout) {
|
|
||||||
batteryHibernateTimeout = timeout
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setLockBeforeSuspend(enabled) {
|
|
||||||
lockBeforeSuspend = enabled
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
FileView {
|
FileView {
|
||||||
id: settingsFile
|
id: settingsFile
|
||||||
|
|
||||||
path: StandardPaths.writableLocation(StandardPaths.GenericStateLocation) + "/DankMaterialShell/session.json"
|
path: isGreeterMode ? "" : StandardPaths.writableLocation(StandardPaths.GenericStateLocation) + "/DankMaterialShell/session.json"
|
||||||
blockLoading: true
|
blockLoading: isGreeterMode
|
||||||
blockWrites: true
|
blockWrites: true
|
||||||
watchChanges: true
|
watchChanges: !isGreeterMode
|
||||||
onLoaded: {
|
onLoaded: {
|
||||||
parseSettings(settingsFile.text())
|
if (!isGreeterMode) {
|
||||||
hasTriedDefaultSession = false
|
parseSettings(settingsFile.text())
|
||||||
|
hasTriedDefaultSession = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
onLoadFailed: error => {
|
onLoadFailed: error => {
|
||||||
if (!hasTriedDefaultSession) {
|
if (!isGreeterMode && !hasTriedDefaultSession) {
|
||||||
hasTriedDefaultSession = true
|
hasTriedDefaultSession = true
|
||||||
defaultSessionCheckProcess.running = true
|
defaultSessionCheckProcess.running = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
id: greeterSessionFile
|
||||||
|
|
||||||
|
path: {
|
||||||
|
const greetCfgDir = Quickshell.env("DMS_GREET_CFG_DIR") || "/etc/greetd/.dms"
|
||||||
|
return greetCfgDir + "/session.json"
|
||||||
|
}
|
||||||
|
preload: isGreeterMode
|
||||||
|
blockLoading: false
|
||||||
|
blockWrites: true
|
||||||
|
watchChanges: false
|
||||||
|
printErrors: true
|
||||||
|
onLoaded: {
|
||||||
|
if (isGreeterMode) {
|
||||||
|
parseSettings(greeterSessionFile.text())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: defaultSessionCheckProcess
|
id: defaultSessionCheckProcess
|
||||||
|
|
||||||
command: ["sh", "-c", "CONFIG_DIR=\"" + _stateDir
|
command: ["sh", "-c", "CONFIG_DIR=\"" + _stateDir
|
||||||
+ "/DankMaterialShell\"; if [ -f \"$CONFIG_DIR/default-session.json\" ] && [ ! -f \"$CONFIG_DIR/session.json\" ]; then cp \"$CONFIG_DIR/default-session.json\" \"$CONFIG_DIR/session.json\" && echo 'copied'; else echo 'not_found'; fi"]
|
+ "/DankMaterialShell\"; if [ -f \"$CONFIG_DIR/default-session.json\" ] && [ ! -f \"$CONFIG_DIR/session.json\" ]; then cp --no-preserve=mode \"$CONFIG_DIR/default-session.json\" \"$CONFIG_DIR/session.json\" && echo 'copied'; else echo 'not_found'; fi"]
|
||||||
running: false
|
running: false
|
||||||
onExited: exitCode => {
|
onExited: exitCode => {
|
||||||
if (exitCode === 0) {
|
if (exitCode === 0) {
|
||||||
@@ -695,4 +841,4 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2,99 +2,101 @@
|
|||||||
// Separated from Theme.qml to keep that file clean
|
// Separated from Theme.qml to keep that file clean
|
||||||
|
|
||||||
const CatppuccinMocha = {
|
const CatppuccinMocha = {
|
||||||
surface: "#45475a",
|
surface: "#313244",
|
||||||
surfaceText: "#cdd6f4",
|
surfaceText: "#cdd6f4",
|
||||||
surfaceVariant: "#45475a",
|
surfaceVariant: "#313244",
|
||||||
surfaceVariantText: "#a6adc8",
|
surfaceVariantText: "#a6adc8",
|
||||||
background: "#1e1e2e",
|
background: "#1e1e2e",
|
||||||
backgroundText: "#cdd6f4",
|
backgroundText: "#cdd6f4",
|
||||||
outline: "#6c7086",
|
outline: "#6c7086",
|
||||||
surfaceContainer: "#313244",
|
surfaceContainer: "#45475a",
|
||||||
surfaceContainerHigh: "#585b70"
|
surfaceContainerHigh: "#585b70",
|
||||||
|
surfaceContainerHighest: "#6c7086"
|
||||||
}
|
}
|
||||||
|
|
||||||
const CatppuccinLatte = {
|
const CatppuccinLatte = {
|
||||||
surface: "#bcc0cc",
|
surface: "#e6e9ef",
|
||||||
surfaceText: "#4c4f69",
|
surfaceText: "#4c4f69",
|
||||||
surfaceVariant: "#bcc0cc",
|
surfaceVariant: "#e6e9ef",
|
||||||
surfaceVariantText: "#6c6f85",
|
surfaceVariantText: "#6c6f85",
|
||||||
background: "#eff1f5",
|
background: "#eff1f5",
|
||||||
backgroundText: "#4c4f69",
|
backgroundText: "#4c4f69",
|
||||||
outline: "#9ca0b0",
|
outline: "#9ca0b0",
|
||||||
surfaceContainer: "#ccd0da",
|
surfaceContainer: "#dce0e8",
|
||||||
surfaceContainerHigh: "#acb0be"
|
surfaceContainerHigh: "#ccd0da",
|
||||||
|
surfaceContainerHighest: "#bcc0cc"
|
||||||
}
|
}
|
||||||
|
|
||||||
const CatppuccinVariants = {
|
const CatppuccinVariants = {
|
||||||
"cat-rosewater": {
|
"cat-rosewater": {
|
||||||
name: "Rosewater",
|
name: "Rosewater",
|
||||||
dark: { primary: "#f5e0dc", secondary: "#f2cdcd", primaryText: "#1e1e2e", primaryContainer: "#8b6b5e", surfaceTint: "#f5e0dc" },
|
dark: { primary: "#f5e0dc", secondary: "#f2cdcd", primaryText: "#1e1e2e", primaryContainer: "#7d5d56", surfaceTint: "#f5e0dc" },
|
||||||
light: { primary: "#dc8a78", secondary: "#dd7878", primaryText: "#ffffff", primaryContainer: "#f4d2ca", surfaceTint: "#dc8a78" }
|
light: { primary: "#dc8a78", secondary: "#dd7878", primaryText: "#ffffff", primaryContainer: "#f6e7e3", surfaceTint: "#dc8a78" }
|
||||||
},
|
},
|
||||||
"cat-flamingo": {
|
"cat-flamingo": {
|
||||||
name: "Flamingo",
|
name: "Flamingo",
|
||||||
dark: { primary: "#f2cdcd", secondary: "#f5e0dc", primaryText: "#1e1e2e", primaryContainer: "#885d62", surfaceTint: "#f2cdcd" },
|
dark: { primary: "#f2cdcd", secondary: "#f5e0dc", primaryText: "#1e1e2e", primaryContainer: "#7a555a", surfaceTint: "#f2cdcd" },
|
||||||
light: { primary: "#dd7878", secondary: "#dc8a78", primaryText: "#ffffff", primaryContainer: "#f4caca", surfaceTint: "#dd7878" }
|
light: { primary: "#dd7878", secondary: "#dc8a78", primaryText: "#ffffff", primaryContainer: "#f6e5e5", surfaceTint: "#dd7878" }
|
||||||
},
|
},
|
||||||
"cat-pink": {
|
"cat-pink": {
|
||||||
name: "Pink",
|
name: "Pink",
|
||||||
dark: { primary: "#f5c2e7", secondary: "#cba6f7", primaryText: "#1e1e2e", primaryContainer: "#8b537a", surfaceTint: "#f5c2e7" },
|
dark: { primary: "#f5c2e7", secondary: "#cba6f7", primaryText: "#1e1e2e", primaryContainer: "#7a3f69", surfaceTint: "#f5c2e7" },
|
||||||
light: { primary: "#ea76cb", secondary: "#8839ef", primaryText: "#ffffff", primaryContainer: "#f7c9e7", surfaceTint: "#ea76cb" }
|
light: { primary: "#ea76cb", secondary: "#8839ef", primaryText: "#ffffff", primaryContainer: "#f7d7ee", surfaceTint: "#ea76cb" }
|
||||||
},
|
},
|
||||||
"cat-mauve": {
|
"cat-mauve": {
|
||||||
name: "Mauve",
|
name: "Mauve",
|
||||||
dark: { primary: "#cba6f7", secondary: "#b4befe", primaryText: "#1e1e2e", primaryContainer: "#61378a", surfaceTint: "#cba6f7" },
|
dark: { primary: "#cba6f7", secondary: "#b4befe", primaryText: "#1e1e2e", primaryContainer: "#55307f", surfaceTint: "#cba6f7" },
|
||||||
light: { primary: "#8839ef", secondary: "#7287fd", primaryText: "#ffffff", primaryContainer: "#e4d3ff", surfaceTint: "#8839ef" }
|
light: { primary: "#8839ef", secondary: "#7287fd", primaryText: "#ffffff", primaryContainer: "#eadcff", surfaceTint: "#8839ef" }
|
||||||
},
|
},
|
||||||
"cat-red": {
|
"cat-red": {
|
||||||
name: "Red",
|
name: "Red",
|
||||||
dark: { primary: "#f38ba8", secondary: "#eba0ac", primaryText: "#1e1e2e", primaryContainer: "#891c3b", surfaceTint: "#f38ba8" },
|
dark: { primary: "#f38ba8", secondary: "#eba0ac", primaryText: "#1e1e2e", primaryContainer: "#6f2438", surfaceTint: "#f38ba8" },
|
||||||
light: { primary: "#d20f39", secondary: "#e64553", primaryText: "#ffffff", primaryContainer: "#f1b8c4", surfaceTint: "#d20f39" }
|
light: { primary: "#d20f39", secondary: "#e64553", primaryText: "#ffffff", primaryContainer: "#f6d0d6", surfaceTint: "#d20f39" }
|
||||||
},
|
},
|
||||||
"cat-maroon": {
|
"cat-maroon": {
|
||||||
name: "Maroon",
|
name: "Maroon",
|
||||||
dark: { primary: "#eba0ac", secondary: "#f38ba8", primaryText: "#1e1e2e", primaryContainer: "#81313f", surfaceTint: "#eba0ac" },
|
dark: { primary: "#eba0ac", secondary: "#f38ba8", primaryText: "#1e1e2e", primaryContainer: "#6d3641", surfaceTint: "#eba0ac" },
|
||||||
light: { primary: "#e64553", secondary: "#d20f39", primaryText: "#ffffff", primaryContainer: "#f4c3c8", surfaceTint: "#e64553" }
|
light: { primary: "#e64553", secondary: "#d20f39", primaryText: "#ffffff", primaryContainer: "#f7d8dc", surfaceTint: "#e64553" }
|
||||||
},
|
},
|
||||||
"cat-peach": {
|
"cat-peach": {
|
||||||
name: "Peach",
|
name: "Peach",
|
||||||
dark: { primary: "#fab387", secondary: "#f9e2af", primaryText: "#1e1e2e", primaryContainer: "#90441a", surfaceTint: "#fab387" },
|
dark: { primary: "#fab387", secondary: "#f9e2af", primaryText: "#1e1e2e", primaryContainer: "#734226", surfaceTint: "#fab387" },
|
||||||
light: { primary: "#fe640b", secondary: "#df8e1d", primaryText: "#ffffff", primaryContainer: "#ffddcc", surfaceTint: "#fe640b" }
|
light: { primary: "#fe640b", secondary: "#df8e1d", primaryText: "#ffffff", primaryContainer: "#ffe4d5", surfaceTint: "#fe640b" }
|
||||||
},
|
},
|
||||||
"cat-yellow": {
|
"cat-yellow": {
|
||||||
name: "Yellow",
|
name: "Yellow",
|
||||||
dark: { primary: "#f9e2af", secondary: "#a6e3a1", primaryText: "#1e1e2e", primaryContainer: "#8f7342", surfaceTint: "#f9e2af" },
|
dark: { primary: "#f9e2af", secondary: "#a6e3a1", primaryText: "#1e1e2e", primaryContainer: "#6e5a2f", surfaceTint: "#f9e2af" },
|
||||||
light: { primary: "#df8e1d", secondary: "#40a02b", primaryText: "#ffffff", primaryContainer: "#fff3cc", surfaceTint: "#df8e1d" }
|
light: { primary: "#df8e1d", secondary: "#40a02b", primaryText: "#ffffff", primaryContainer: "#fff6d6", surfaceTint: "#df8e1d" }
|
||||||
},
|
},
|
||||||
"cat-green": {
|
"cat-green": {
|
||||||
name: "Green",
|
name: "Green",
|
||||||
dark: { primary: "#a6e3a1", secondary: "#94e2d5", primaryText: "#1e1e2e", primaryContainer: "#3c7534", surfaceTint: "#a6e3a1" },
|
dark: { primary: "#a6e3a1", secondary: "#94e2d5", primaryText: "#1e1e2e", primaryContainer: "#2f5f36", surfaceTint: "#a6e3a1" },
|
||||||
light: { primary: "#40a02b", secondary: "#179299", primaryText: "#ffffff", primaryContainer: "#d4f5d4", surfaceTint: "#40a02b" }
|
light: { primary: "#40a02b", secondary: "#179299", primaryText: "#ffffff", primaryContainer: "#dff4e0", surfaceTint: "#40a02b" }
|
||||||
},
|
},
|
||||||
"cat-teal": {
|
"cat-teal": {
|
||||||
name: "Teal",
|
name: "Teal",
|
||||||
dark: { primary: "#94e2d5", secondary: "#89dceb", primaryText: "#1e1e2e", primaryContainer: "#2a7468", surfaceTint: "#94e2d5" },
|
dark: { primary: "#94e2d5", secondary: "#89dceb", primaryText: "#1e1e2e", primaryContainer: "#2e5e59", surfaceTint: "#94e2d5" },
|
||||||
light: { primary: "#179299", secondary: "#04a5e5", primaryText: "#ffffff", primaryContainer: "#ccf2f2", surfaceTint: "#179299" }
|
light: { primary: "#179299", secondary: "#04a5e5", primaryText: "#ffffff", primaryContainer: "#daf3f1", surfaceTint: "#179299" }
|
||||||
},
|
},
|
||||||
"cat-sky": {
|
"cat-sky": {
|
||||||
name: "Sky",
|
name: "Sky",
|
||||||
dark: { primary: "#89dceb", secondary: "#74c7ec", primaryText: "#1e1e2e", primaryContainer: "#196e7e", surfaceTint: "#89dceb" },
|
dark: { primary: "#89dceb", secondary: "#74c7ec", primaryText: "#1e1e2e", primaryContainer: "#24586a", surfaceTint: "#89dceb" },
|
||||||
light: { primary: "#04a5e5", secondary: "#209fb5", primaryText: "#ffffff", primaryContainer: "#ccebff", surfaceTint: "#04a5e5" }
|
light: { primary: "#04a5e5", secondary: "#209fb5", primaryText: "#ffffff", primaryContainer: "#dbf1fb", surfaceTint: "#04a5e5" }
|
||||||
},
|
},
|
||||||
"cat-sapphire": {
|
"cat-sapphire": {
|
||||||
name: "Sapphire",
|
name: "Sapphire",
|
||||||
dark: { primary: "#74c7ec", secondary: "#89b4fa", primaryText: "#1e1e2e", primaryContainer: "#0a597f", surfaceTint: "#74c7ec" },
|
dark: { primary: "#74c7ec", secondary: "#89b4fa", primaryText: "#1e1e2e", primaryContainer: "#1f4d6f", surfaceTint: "#74c7ec" },
|
||||||
light: { primary: "#209fb5", secondary: "#1e66f5", primaryText: "#ffffff", primaryContainer: "#d0f0f5", surfaceTint: "#209fb5" }
|
light: { primary: "#209fb5", secondary: "#1e66f5", primaryText: "#ffffff", primaryContainer: "#def3f8", surfaceTint: "#209fb5" }
|
||||||
},
|
},
|
||||||
"cat-blue": {
|
"cat-blue": {
|
||||||
name: "Blue",
|
name: "Blue",
|
||||||
dark: { primary: "#89b4fa", secondary: "#b4befe", primaryText: "#1e1e2e", primaryContainer: "#19468d", surfaceTint: "#89b4fa" },
|
dark: { primary: "#89b4fa", secondary: "#b4befe", primaryText: "#1e1e2e", primaryContainer: "#243f75", surfaceTint: "#89b4fa" },
|
||||||
light: { primary: "#1e66f5", secondary: "#7287fd", primaryText: "#ffffff", primaryContainer: "#ccd9ff", surfaceTint: "#1e66f5" }
|
light: { primary: "#1e66f5", secondary: "#7287fd", primaryText: "#ffffff", primaryContainer: "#e0e9ff", surfaceTint: "#1e66f5" }
|
||||||
},
|
},
|
||||||
"cat-lavender": {
|
"cat-lavender": {
|
||||||
name: "Lavender",
|
name: "Lavender",
|
||||||
dark: { primary: "#b4befe", secondary: "#cba6f7", primaryText: "#1e1e2e", primaryContainer: "#4a5091", surfaceTint: "#b4befe" },
|
dark: { primary: "#b4befe", secondary: "#cba6f7", primaryText: "#1e1e2e", primaryContainer: "#3f4481", surfaceTint: "#b4befe" },
|
||||||
light: { primary: "#7287fd", secondary: "#8839ef", primaryText: "#ffffff", primaryContainer: "#dde1ff", surfaceTint: "#7287fd" }
|
light: { primary: "#7287fd", secondary: "#8839ef", primaryText: "#ffffff", primaryContainer: "#e5e8ff", surfaceTint: "#7287fd" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,16 +120,17 @@ const StockThemes = {
|
|||||||
primaryText: "#000000",
|
primaryText: "#000000",
|
||||||
primaryContainer: "#0d47a1",
|
primaryContainer: "#0d47a1",
|
||||||
secondary: "#8ab4f8",
|
secondary: "#8ab4f8",
|
||||||
surface: "#1a1c1e",
|
surface: "#101418",
|
||||||
surfaceText: "#e3e8ef",
|
surfaceText: "#e0e2e8",
|
||||||
surfaceVariant: "#44464f",
|
surfaceVariant: "#42474e",
|
||||||
surfaceVariantText: "#c4c7c5",
|
surfaceVariantText: "#c2c7cf",
|
||||||
surfaceTint: "#8ab4f8",
|
surfaceTint: "#8ab4f8",
|
||||||
background: "#1a1c1e",
|
background: "#101418",
|
||||||
backgroundText: "#e3e8ef",
|
backgroundText: "#e0e2e8",
|
||||||
outline: "#8e918f",
|
outline: "#8c9199",
|
||||||
surfaceContainer: "#1e2023",
|
surfaceContainer: "#1d2024",
|
||||||
surfaceContainerHigh: "#292b2f"
|
surfaceContainerHigh: "#272a2f",
|
||||||
|
surfaceContainerHighest: "#32353a"
|
||||||
},
|
},
|
||||||
purple: {
|
purple: {
|
||||||
name: "Purple",
|
name: "Purple",
|
||||||
@@ -135,16 +138,17 @@ const StockThemes = {
|
|||||||
primaryText: "#381E72",
|
primaryText: "#381E72",
|
||||||
primaryContainer: "#4F378B",
|
primaryContainer: "#4F378B",
|
||||||
secondary: "#CCC2DC",
|
secondary: "#CCC2DC",
|
||||||
surface: "#10121E",
|
surface: "#141218",
|
||||||
surfaceText: "#E6E0E9",
|
surfaceText: "#e6e0e9",
|
||||||
surfaceVariant: "#49454F",
|
surfaceVariant: "#49454e",
|
||||||
surfaceVariantText: "#CAC4D0",
|
surfaceVariantText: "#cac4cf",
|
||||||
surfaceTint: "#D0BCFF",
|
surfaceTint: "#D0BCFF",
|
||||||
background: "#10121E",
|
background: "#141218",
|
||||||
backgroundText: "#E6E0E9",
|
backgroundText: "#e6e0e9",
|
||||||
outline: "#938F99",
|
outline: "#948f99",
|
||||||
surfaceContainer: "#1D1B20",
|
surfaceContainer: "#211f24",
|
||||||
surfaceContainerHigh: "#2B2930"
|
surfaceContainerHigh: "#2b292f",
|
||||||
|
surfaceContainerHighest: "#36343a"
|
||||||
},
|
},
|
||||||
green: {
|
green: {
|
||||||
name: "Green",
|
name: "Green",
|
||||||
@@ -152,16 +156,17 @@ const StockThemes = {
|
|||||||
primaryText: "#000000",
|
primaryText: "#000000",
|
||||||
primaryContainer: "#1b5e20",
|
primaryContainer: "#1b5e20",
|
||||||
secondary: "#81c995",
|
secondary: "#81c995",
|
||||||
surface: "#0f1411",
|
surface: "#10140f",
|
||||||
surfaceText: "#e1f5e3",
|
surfaceText: "#e0e4db",
|
||||||
surfaceVariant: "#404943",
|
surfaceVariant: "#424940",
|
||||||
surfaceVariantText: "#c1cbc4",
|
surfaceVariantText: "#c2c9bd",
|
||||||
surfaceTint: "#81c995",
|
surfaceTint: "#81c995",
|
||||||
background: "#0f1411",
|
background: "#10140f",
|
||||||
backgroundText: "#e1f5e3",
|
backgroundText: "#e0e4db",
|
||||||
outline: "#8b938c",
|
outline: "#8c9388",
|
||||||
surfaceContainer: "#1a1f1b",
|
surfaceContainer: "#1d211b",
|
||||||
surfaceContainerHigh: "#252a26"
|
surfaceContainerHigh: "#272b25",
|
||||||
|
surfaceContainerHighest: "#323630"
|
||||||
},
|
},
|
||||||
orange: {
|
orange: {
|
||||||
name: "Orange",
|
name: "Orange",
|
||||||
@@ -169,16 +174,17 @@ const StockThemes = {
|
|||||||
primaryText: "#000000",
|
primaryText: "#000000",
|
||||||
primaryContainer: "#3e2723",
|
primaryContainer: "#3e2723",
|
||||||
secondary: "#ffb74d",
|
secondary: "#ffb74d",
|
||||||
surface: "#1c1410",
|
surface: "#1a120e",
|
||||||
surfaceText: "#f5f1ea",
|
surfaceText: "#f0dfd8",
|
||||||
surfaceVariant: "#4a453a",
|
surfaceVariant: "#52443d",
|
||||||
surfaceVariantText: "#cbc5b8",
|
surfaceVariantText: "#d7c2b9",
|
||||||
surfaceTint: "#ffb74d",
|
surfaceTint: "#ffb74d",
|
||||||
background: "#1c1410",
|
background: "#1a120e",
|
||||||
backgroundText: "#f5f1ea",
|
backgroundText: "#f0dfd8",
|
||||||
outline: "#958f84",
|
outline: "#a08d85",
|
||||||
surfaceContainer: "#211e17",
|
surfaceContainer: "#271e1a",
|
||||||
surfaceContainerHigh: "#2c291f"
|
surfaceContainerHigh: "#322824",
|
||||||
|
surfaceContainerHighest: "#3d332e"
|
||||||
},
|
},
|
||||||
red: {
|
red: {
|
||||||
name: "Red",
|
name: "Red",
|
||||||
@@ -186,16 +192,17 @@ const StockThemes = {
|
|||||||
primaryText: "#000000",
|
primaryText: "#000000",
|
||||||
primaryContainer: "#4a0e0e",
|
primaryContainer: "#4a0e0e",
|
||||||
secondary: "#f28b82",
|
secondary: "#f28b82",
|
||||||
surface: "#1c1011",
|
surface: "#1a1110",
|
||||||
surfaceText: "#f5e8ea",
|
surfaceText: "#f1dedc",
|
||||||
surfaceVariant: "#4a3f41",
|
surfaceVariant: "#534341",
|
||||||
surfaceVariantText: "#cbc2c4",
|
surfaceVariantText: "#d8c2be",
|
||||||
surfaceTint: "#f28b82",
|
surfaceTint: "#f28b82",
|
||||||
background: "#1c1011",
|
background: "#1a1110",
|
||||||
backgroundText: "#f5e8ea",
|
backgroundText: "#f1dedc",
|
||||||
outline: "#958b8d",
|
outline: "#a08c89",
|
||||||
surfaceContainer: "#211b1c",
|
surfaceContainer: "#271d1c",
|
||||||
surfaceContainerHigh: "#2c2426"
|
surfaceContainerHigh: "#322826",
|
||||||
|
surfaceContainerHighest: "#3d3231"
|
||||||
},
|
},
|
||||||
cyan: {
|
cyan: {
|
||||||
name: "Cyan",
|
name: "Cyan",
|
||||||
@@ -203,16 +210,17 @@ const StockThemes = {
|
|||||||
primaryText: "#000000",
|
primaryText: "#000000",
|
||||||
primaryContainer: "#004d5c",
|
primaryContainer: "#004d5c",
|
||||||
secondary: "#4dd0e1",
|
secondary: "#4dd0e1",
|
||||||
surface: "#0f1617",
|
surface: "#0e1416",
|
||||||
surfaceText: "#e8f4f5",
|
surfaceText: "#dee3e5",
|
||||||
surfaceVariant: "#3f474a",
|
surfaceVariant: "#3f484a",
|
||||||
surfaceVariantText: "#c2c9cb",
|
surfaceVariantText: "#bfc8ca",
|
||||||
surfaceTint: "#4dd0e1",
|
surfaceTint: "#4dd0e1",
|
||||||
background: "#0f1617",
|
background: "#0e1416",
|
||||||
backgroundText: "#e8f4f5",
|
backgroundText: "#dee3e5",
|
||||||
outline: "#8c9194",
|
outline: "#899295",
|
||||||
surfaceContainer: "#1a1f20",
|
surfaceContainer: "#1b2122",
|
||||||
surfaceContainerHigh: "#252b2c"
|
surfaceContainerHigh: "#252b2c",
|
||||||
|
surfaceContainerHighest: "#303637"
|
||||||
},
|
},
|
||||||
pink: {
|
pink: {
|
||||||
name: "Pink",
|
name: "Pink",
|
||||||
@@ -220,16 +228,17 @@ const StockThemes = {
|
|||||||
primaryText: "#000000",
|
primaryText: "#000000",
|
||||||
primaryContainer: "#4a0e2f",
|
primaryContainer: "#4a0e2f",
|
||||||
secondary: "#f8bbd9",
|
secondary: "#f8bbd9",
|
||||||
surface: "#1a1014",
|
surface: "#191112",
|
||||||
surfaceText: "#f3e8ee",
|
surfaceText: "#f0dee0",
|
||||||
surfaceVariant: "#483f45",
|
surfaceVariant: "#524345",
|
||||||
surfaceVariantText: "#c9c2c7",
|
surfaceVariantText: "#d6c2c3",
|
||||||
surfaceTint: "#f8bbd9",
|
surfaceTint: "#f8bbd9",
|
||||||
background: "#1a1014",
|
background: "#191112",
|
||||||
backgroundText: "#f3e8ee",
|
backgroundText: "#f0dee0",
|
||||||
outline: "#938a90",
|
outline: "#9f8c8e",
|
||||||
surfaceContainer: "#1f1b1e",
|
surfaceContainer: "#261d1e",
|
||||||
surfaceContainerHigh: "#2a2428"
|
surfaceContainerHigh: "#312829",
|
||||||
|
surfaceContainerHighest: "#3c3233"
|
||||||
},
|
},
|
||||||
amber: {
|
amber: {
|
||||||
name: "Amber",
|
name: "Amber",
|
||||||
@@ -237,16 +246,17 @@ const StockThemes = {
|
|||||||
primaryText: "#000000",
|
primaryText: "#000000",
|
||||||
primaryContainer: "#4a3c00",
|
primaryContainer: "#4a3c00",
|
||||||
secondary: "#ffd54f",
|
secondary: "#ffd54f",
|
||||||
surface: "#1a1710",
|
surface: "#17130b",
|
||||||
surfaceText: "#f3f0e8",
|
surfaceText: "#ebe1d4",
|
||||||
surfaceVariant: "#49453a",
|
surfaceVariant: "#4d4639",
|
||||||
surfaceVariantText: "#cac5b8",
|
surfaceVariantText: "#d0c5b4",
|
||||||
surfaceTint: "#ffd54f",
|
surfaceTint: "#ffd54f",
|
||||||
background: "#1a1710",
|
background: "#17130b",
|
||||||
backgroundText: "#f3f0e8",
|
backgroundText: "#ebe1d4",
|
||||||
outline: "#949084",
|
outline: "#998f80",
|
||||||
surfaceContainer: "#1f1e17",
|
surfaceContainer: "#231f17",
|
||||||
surfaceContainerHigh: "#2a281f"
|
surfaceContainerHigh: "#2e2921",
|
||||||
|
surfaceContainerHighest: "#39342b"
|
||||||
},
|
},
|
||||||
coral: {
|
coral: {
|
||||||
name: "Coral",
|
name: "Coral",
|
||||||
@@ -255,15 +265,16 @@ const StockThemes = {
|
|||||||
primaryContainer: "#8c1d18",
|
primaryContainer: "#8c1d18",
|
||||||
secondary: "#f9dedc",
|
secondary: "#f9dedc",
|
||||||
surface: "#1a1110",
|
surface: "#1a1110",
|
||||||
surfaceText: "#f1e8e7",
|
surfaceText: "#f1dedc",
|
||||||
surfaceVariant: "#4a4142",
|
surfaceVariant: "#534341",
|
||||||
surfaceVariantText: "#cdc2c1",
|
surfaceVariantText: "#d8c2bf",
|
||||||
surfaceTint: "#ffb4ab",
|
surfaceTint: "#ffb4ab",
|
||||||
background: "#1a1110",
|
background: "#1a1110",
|
||||||
backgroundText: "#f1e8e7",
|
backgroundText: "#f1dedc",
|
||||||
outline: "#968b8a",
|
outline: "#a08c8a",
|
||||||
surfaceContainer: "#201a19",
|
surfaceContainer: "#271d1c",
|
||||||
surfaceContainerHigh: "#2b2221"
|
surfaceContainerHigh: "#322826",
|
||||||
|
surfaceContainerHighest: "#3d3231"
|
||||||
},
|
},
|
||||||
monochrome: {
|
monochrome: {
|
||||||
name: "Monochrome",
|
name: "Monochrome",
|
||||||
@@ -279,8 +290,9 @@ const StockThemes = {
|
|||||||
background: "#131315",
|
background: "#131315",
|
||||||
backgroundText: "#e4e2e3",
|
backgroundText: "#e4e2e3",
|
||||||
outline: "#929092",
|
outline: "#929092",
|
||||||
surfaceContainer: "#2a2a2a",
|
surfaceContainer: "#353535",
|
||||||
surfaceContainerHigh: "#2a2a2b",
|
surfaceContainerHigh: "#424242",
|
||||||
|
surfaceContainerHighest: "#505050",
|
||||||
error: "#ffb4ab",
|
error: "#ffb4ab",
|
||||||
warning: "#3f4759",
|
warning: "#3f4759",
|
||||||
info: "#595e6c",
|
info: "#595e6c",
|
||||||
@@ -294,16 +306,17 @@ const StockThemes = {
|
|||||||
primaryText: "#ffffff",
|
primaryText: "#ffffff",
|
||||||
primaryContainer: "#e3f2fd",
|
primaryContainer: "#e3f2fd",
|
||||||
secondary: "#42a5f5",
|
secondary: "#42a5f5",
|
||||||
surface: "#fefefe",
|
surface: "#f7f9ff",
|
||||||
surfaceText: "#1a1c1e",
|
surfaceText: "#181c20",
|
||||||
surfaceVariant: "#e7e0ec",
|
surfaceVariant: "#dee3eb",
|
||||||
surfaceVariantText: "#49454f",
|
surfaceVariantText: "#42474e",
|
||||||
surfaceTint: "#1976d2",
|
surfaceTint: "#1976d2",
|
||||||
background: "#fefefe",
|
background: "#f7f9ff",
|
||||||
backgroundText: "#1a1c1e",
|
backgroundText: "#181c20",
|
||||||
outline: "#79747e",
|
outline: "#72777f",
|
||||||
surfaceContainer: "#f3f3f3",
|
surfaceContainer: "#eceef4",
|
||||||
surfaceContainerHigh: "#ececec"
|
surfaceContainerHigh: "#e6e8ee",
|
||||||
|
surfaceContainerHighest: "#e0e2e8"
|
||||||
},
|
},
|
||||||
purple: {
|
purple: {
|
||||||
name: "Purple Light",
|
name: "Purple Light",
|
||||||
@@ -311,16 +324,17 @@ const StockThemes = {
|
|||||||
primaryText: "#ffffff",
|
primaryText: "#ffffff",
|
||||||
primaryContainer: "#EADDFF",
|
primaryContainer: "#EADDFF",
|
||||||
secondary: "#625B71",
|
secondary: "#625B71",
|
||||||
surface: "#FFFBFE",
|
surface: "#fef7ff",
|
||||||
surfaceText: "#1C1B1F",
|
surfaceText: "#1d1b20",
|
||||||
surfaceVariant: "#E7E0EC",
|
surfaceVariant: "#e7e0eb",
|
||||||
surfaceVariantText: "#49454F",
|
surfaceVariantText: "#49454e",
|
||||||
surfaceTint: "#6750A4",
|
surfaceTint: "#6750A4",
|
||||||
background: "#FFFBFE",
|
background: "#fef7ff",
|
||||||
backgroundText: "#1C1B1F",
|
backgroundText: "#1d1b20",
|
||||||
outline: "#79747E",
|
outline: "#7a757f",
|
||||||
surfaceContainer: "#F3EDF7",
|
surfaceContainer: "#f2ecf4",
|
||||||
surfaceContainerHigh: "#ECE6F0"
|
surfaceContainerHigh: "#ece6ee",
|
||||||
|
surfaceContainerHighest: "#e6e0e9"
|
||||||
},
|
},
|
||||||
green: {
|
green: {
|
||||||
name: "Green Light",
|
name: "Green Light",
|
||||||
@@ -328,16 +342,17 @@ const StockThemes = {
|
|||||||
primaryText: "#ffffff",
|
primaryText: "#ffffff",
|
||||||
primaryContainer: "#e8f5e8",
|
primaryContainer: "#e8f5e8",
|
||||||
secondary: "#4caf50",
|
secondary: "#4caf50",
|
||||||
surface: "#fefefe",
|
surface: "#f7fbf1",
|
||||||
surfaceText: "#1a1c1e",
|
surfaceText: "#191d17",
|
||||||
surfaceVariant: "#e7e0ec",
|
surfaceVariant: "#dee5d8",
|
||||||
surfaceVariantText: "#49454f",
|
surfaceVariantText: "#424940",
|
||||||
surfaceTint: "#2e7d32",
|
surfaceTint: "#2e7d32",
|
||||||
background: "#fefefe",
|
background: "#f7fbf1",
|
||||||
backgroundText: "#1a1c1e",
|
backgroundText: "#191d17",
|
||||||
outline: "#79747e",
|
outline: "#72796f",
|
||||||
surfaceContainer: "#f3f3f3",
|
surfaceContainer: "#ecefe6",
|
||||||
surfaceContainerHigh: "#ececec"
|
surfaceContainerHigh: "#e6e9e0",
|
||||||
|
surfaceContainerHighest: "#e0e4db"
|
||||||
},
|
},
|
||||||
orange: {
|
orange: {
|
||||||
name: "Orange Light",
|
name: "Orange Light",
|
||||||
@@ -345,16 +360,17 @@ const StockThemes = {
|
|||||||
primaryText: "#ffffff",
|
primaryText: "#ffffff",
|
||||||
primaryContainer: "#ffecb3",
|
primaryContainer: "#ffecb3",
|
||||||
secondary: "#ff9800",
|
secondary: "#ff9800",
|
||||||
surface: "#fefefe",
|
surface: "#fff8f6",
|
||||||
surfaceText: "#1a1c1e",
|
surfaceText: "#221a16",
|
||||||
surfaceVariant: "#e7e0ec",
|
surfaceVariant: "#f4ded5",
|
||||||
surfaceVariantText: "#49454f",
|
surfaceVariantText: "#52443d",
|
||||||
surfaceTint: "#e65100",
|
surfaceTint: "#e65100",
|
||||||
background: "#fefefe",
|
background: "#fff8f6",
|
||||||
backgroundText: "#1a1c1e",
|
backgroundText: "#221a16",
|
||||||
outline: "#79747e",
|
outline: "#85736c",
|
||||||
surfaceContainer: "#f3f3f3",
|
surfaceContainer: "#fceae3",
|
||||||
surfaceContainerHigh: "#ececec"
|
surfaceContainerHigh: "#f6e5de",
|
||||||
|
surfaceContainerHighest: "#f0dfd8"
|
||||||
},
|
},
|
||||||
red: {
|
red: {
|
||||||
name: "Red Light",
|
name: "Red Light",
|
||||||
@@ -362,16 +378,17 @@ const StockThemes = {
|
|||||||
primaryText: "#ffffff",
|
primaryText: "#ffffff",
|
||||||
primaryContainer: "#ffebee",
|
primaryContainer: "#ffebee",
|
||||||
secondary: "#f44336",
|
secondary: "#f44336",
|
||||||
surface: "#fefefe",
|
surface: "#fff8f7",
|
||||||
surfaceText: "#1a1c1e",
|
surfaceText: "#231918",
|
||||||
surfaceVariant: "#e7e0ec",
|
surfaceVariant: "#f5ddda",
|
||||||
surfaceVariantText: "#49454f",
|
surfaceVariantText: "#534341",
|
||||||
surfaceTint: "#d32f2f",
|
surfaceTint: "#d32f2f",
|
||||||
background: "#fefefe",
|
background: "#fff8f7",
|
||||||
backgroundText: "#1a1c1e",
|
backgroundText: "#231918",
|
||||||
outline: "#79747e",
|
outline: "#857370",
|
||||||
surfaceContainer: "#f3f3f3",
|
surfaceContainer: "#fceae7",
|
||||||
surfaceContainerHigh: "#ececec"
|
surfaceContainerHigh: "#f7e4e1",
|
||||||
|
surfaceContainerHighest: "#f1dedc"
|
||||||
},
|
},
|
||||||
cyan: {
|
cyan: {
|
||||||
name: "Cyan Light",
|
name: "Cyan Light",
|
||||||
@@ -379,16 +396,17 @@ const StockThemes = {
|
|||||||
primaryText: "#ffffff",
|
primaryText: "#ffffff",
|
||||||
primaryContainer: "#e0f2f1",
|
primaryContainer: "#e0f2f1",
|
||||||
secondary: "#00bcd4",
|
secondary: "#00bcd4",
|
||||||
surface: "#fefefe",
|
surface: "#f5fafc",
|
||||||
surfaceText: "#1a1c1e",
|
surfaceText: "#171d1e",
|
||||||
surfaceVariant: "#e7e0ec",
|
surfaceVariant: "#dbe4e6",
|
||||||
surfaceVariantText: "#49454f",
|
surfaceVariantText: "#3f484a",
|
||||||
surfaceTint: "#0097a7",
|
surfaceTint: "#0097a7",
|
||||||
background: "#fefefe",
|
background: "#f5fafc",
|
||||||
backgroundText: "#1a1c1e",
|
backgroundText: "#171d1e",
|
||||||
outline: "#79747e",
|
outline: "#6f797b",
|
||||||
surfaceContainer: "#f3f3f3",
|
surfaceContainer: "#e9eff0",
|
||||||
surfaceContainerHigh: "#ececec"
|
surfaceContainerHigh: "#e3e9eb",
|
||||||
|
surfaceContainerHighest: "#dee3e5"
|
||||||
},
|
},
|
||||||
pink: {
|
pink: {
|
||||||
name: "Pink Light",
|
name: "Pink Light",
|
||||||
@@ -396,16 +414,17 @@ const StockThemes = {
|
|||||||
primaryText: "#ffffff",
|
primaryText: "#ffffff",
|
||||||
primaryContainer: "#fce4ec",
|
primaryContainer: "#fce4ec",
|
||||||
secondary: "#e91e63",
|
secondary: "#e91e63",
|
||||||
surface: "#fefefe",
|
surface: "#fff8f7",
|
||||||
surfaceText: "#1a1c1e",
|
surfaceText: "#22191a",
|
||||||
surfaceVariant: "#e7e0ec",
|
surfaceVariant: "#f3dddf",
|
||||||
surfaceVariantText: "#49454f",
|
surfaceVariantText: "#524345",
|
||||||
surfaceTint: "#c2185b",
|
surfaceTint: "#c2185b",
|
||||||
background: "#fefefe",
|
background: "#fff8f7",
|
||||||
backgroundText: "#1a1c1e",
|
backgroundText: "#22191a",
|
||||||
outline: "#79747e",
|
outline: "#847375",
|
||||||
surfaceContainer: "#f3f3f3",
|
surfaceContainer: "#fbeaeb",
|
||||||
surfaceContainerHigh: "#ececec"
|
surfaceContainerHigh: "#f5e4e5",
|
||||||
|
surfaceContainerHighest: "#f0dee0"
|
||||||
},
|
},
|
||||||
amber: {
|
amber: {
|
||||||
name: "Amber Light",
|
name: "Amber Light",
|
||||||
@@ -413,16 +432,17 @@ const StockThemes = {
|
|||||||
primaryText: "#000000",
|
primaryText: "#000000",
|
||||||
primaryContainer: "#fff8e1",
|
primaryContainer: "#fff8e1",
|
||||||
secondary: "#ffc107",
|
secondary: "#ffc107",
|
||||||
surface: "#fefefe",
|
surface: "#fff8f2",
|
||||||
surfaceText: "#1a1c1e",
|
surfaceText: "#1f1b13",
|
||||||
surfaceVariant: "#e7e0ec",
|
surfaceVariant: "#ede1cf",
|
||||||
surfaceVariantText: "#49454f",
|
surfaceVariantText: "#4d4639",
|
||||||
surfaceTint: "#ff8f00",
|
surfaceTint: "#ff8f00",
|
||||||
background: "#fefefe",
|
background: "#fff8f2",
|
||||||
backgroundText: "#1a1c1e",
|
backgroundText: "#1f1b13",
|
||||||
outline: "#79747e",
|
outline: "#7f7667",
|
||||||
surfaceContainer: "#f3f3f3",
|
surfaceContainer: "#f6ecdf",
|
||||||
surfaceContainerHigh: "#ececec"
|
surfaceContainerHigh: "#f1e7d9",
|
||||||
|
surfaceContainerHighest: "#ebe1d4"
|
||||||
},
|
},
|
||||||
coral: {
|
coral: {
|
||||||
name: "Coral Light",
|
name: "Coral Light",
|
||||||
@@ -430,16 +450,17 @@ const StockThemes = {
|
|||||||
primaryText: "#ffffff",
|
primaryText: "#ffffff",
|
||||||
primaryContainer: "#ffdad6",
|
primaryContainer: "#ffdad6",
|
||||||
secondary: "#ff5449",
|
secondary: "#ff5449",
|
||||||
surface: "#fefefe",
|
surface: "#fff8f7",
|
||||||
surfaceText: "#1a1c1e",
|
surfaceText: "#231918",
|
||||||
surfaceVariant: "#e7e0ec",
|
surfaceVariant: "#f5ddda",
|
||||||
surfaceVariantText: "#49454f",
|
surfaceVariantText: "#534341",
|
||||||
surfaceTint: "#8c1d18",
|
surfaceTint: "#8c1d18",
|
||||||
background: "#fefefe",
|
background: "#fff8f7",
|
||||||
backgroundText: "#1a1c1e",
|
backgroundText: "#231918",
|
||||||
outline: "#79747e",
|
outline: "#857371",
|
||||||
surfaceContainer: "#f3f3f3",
|
surfaceContainer: "#fceae7",
|
||||||
surfaceContainerHigh: "#ececec"
|
surfaceContainerHigh: "#f6e4e2",
|
||||||
|
surfaceContainerHighest: "#f1dedc"
|
||||||
},
|
},
|
||||||
monochrome: {
|
monochrome: {
|
||||||
name: "Monochrome Light",
|
name: "Monochrome Light",
|
||||||
@@ -455,8 +476,9 @@ const StockThemes = {
|
|||||||
background: "#ffffff",
|
background: "#ffffff",
|
||||||
backgroundText: "#1a1a1a",
|
backgroundText: "#1a1a1a",
|
||||||
outline: "#757577",
|
outline: "#757577",
|
||||||
surfaceContainer: "#f5f5f6",
|
surfaceContainer: "#e8e8ea",
|
||||||
surfaceContainerHigh: "#eaeaeb",
|
surfaceContainerHigh: "#dcdcde",
|
||||||
|
surfaceContainerHighest: "#d0d0d2",
|
||||||
error: "#ba1a1a",
|
error: "#ba1a1a",
|
||||||
warning: "#f9e79f",
|
warning: "#f9e79f",
|
||||||
info: "#5d6475",
|
info: "#5d6475",
|
||||||
|
|||||||
704
Common/Theme.qml
704
Common/Theme.qml
@@ -14,11 +14,19 @@ import "StockThemes.js" as StockThemes
|
|||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
readonly property string stateDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericCacheLocation).toString()) + "/DankMaterialShell"
|
||||||
|
|
||||||
readonly property bool envDisableMatugen: Quickshell.env("DMS_DISABLE_MATUGEN") === "1" || Quickshell.env("DMS_DISABLE_MATUGEN") === "true"
|
readonly property bool envDisableMatugen: Quickshell.env("DMS_DISABLE_MATUGEN") === "1" || Quickshell.env("DMS_DISABLE_MATUGEN") === "true"
|
||||||
|
|
||||||
|
readonly property real popupDistance: {
|
||||||
|
if (typeof SettingsData === "undefined") return 4
|
||||||
|
return SettingsData.popupGapsAuto ? Math.max(4, SettingsData.dankBarSpacing) : SettingsData.popupGapsManual
|
||||||
|
}
|
||||||
|
|
||||||
property string currentTheme: "blue"
|
property string currentTheme: "blue"
|
||||||
property string currentThemeCategory: "generic"
|
property string currentThemeCategory: "generic"
|
||||||
property bool isLightMode: false
|
property bool isLightMode: typeof SessionData !== "undefined" ? SessionData.isLightMode : false
|
||||||
|
property bool colorsFileLoadFailed: false
|
||||||
|
|
||||||
readonly property string dynamic: "dynamic"
|
readonly property string dynamic: "dynamic"
|
||||||
readonly property string custom : "custom"
|
readonly property string custom : "custom"
|
||||||
@@ -28,9 +36,8 @@ Singleton {
|
|||||||
readonly property string shellDir: Paths.strip(Qt.resolvedUrl(".").toString()).replace("/Common/", "")
|
readonly property string shellDir: Paths.strip(Qt.resolvedUrl(".").toString()).replace("/Common/", "")
|
||||||
readonly property string wallpaperPath: {
|
readonly property string wallpaperPath: {
|
||||||
if (typeof SessionData === "undefined") return ""
|
if (typeof SessionData === "undefined") return ""
|
||||||
|
|
||||||
if (SessionData.perMonitorWallpaper) {
|
if (SessionData.perMonitorWallpaper) {
|
||||||
// Use first monitor's wallpaper for dynamic theming
|
|
||||||
var screens = Quickshell.screens
|
var screens = Quickshell.screens
|
||||||
if (screens.length > 0) {
|
if (screens.length > 0) {
|
||||||
var firstMonitorWallpaper = SessionData.getMonitorWallpaper(screens[0].name)
|
var firstMonitorWallpaper = SessionData.getMonitorWallpaper(screens[0].name)
|
||||||
@@ -72,25 +79,81 @@ Singleton {
|
|||||||
property bool qtThemingEnabled: typeof SettingsData !== "undefined" ? (SettingsData.qt5ctAvailable || SettingsData.qt6ctAvailable) : false
|
property bool qtThemingEnabled: typeof SettingsData !== "undefined" ? (SettingsData.qt5ctAvailable || SettingsData.qt6ctAvailable) : false
|
||||||
property var workerRunning: false
|
property var workerRunning: false
|
||||||
property var matugenColors: ({})
|
property var matugenColors: ({})
|
||||||
property bool extractionRequested: false
|
|
||||||
property int colorUpdateTrigger: 0
|
|
||||||
property var customThemeData: null
|
property var customThemeData: null
|
||||||
|
|
||||||
readonly property string stateDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.CacheLocation).toString()) + "/dankshell"
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
Quickshell.execDetached(["mkdir", "-p", stateDir])
|
Quickshell.execDetached(["mkdir", "-p", stateDir])
|
||||||
matugenCheck.running = true
|
Proc.runCommand("matugenCheck", ["which", "matugen"], (output, code) => {
|
||||||
if (typeof SessionData !== "undefined")
|
matugenAvailable = (code === 0) && !envDisableMatugen
|
||||||
|
const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode)
|
||||||
|
|
||||||
|
if (!matugenAvailable || isGreeterMode) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colorsFileLoadFailed && currentTheme === dynamic && wallpaperPath) {
|
||||||
|
console.log("Theme: Matugen now available, regenerating colors for dynamic theme")
|
||||||
|
const isLight = (typeof SessionData !== "undefined" && SessionData.isLightMode)
|
||||||
|
const iconTheme = (typeof SettingsData !== "undefined" && SettingsData.iconTheme) ? SettingsData.iconTheme : "System Default"
|
||||||
|
Quickshell.execDetached(["rm", "-f", stateDir + "/matugen.key"])
|
||||||
|
const selectedMatugenType = (typeof SettingsData !== "undefined" && SettingsData.matugenScheme) ? SettingsData.matugenScheme : "scheme-tonal-spot"
|
||||||
|
if (wallpaperPath.startsWith("#")) {
|
||||||
|
setDesiredTheme("hex", wallpaperPath, isLight, iconTheme, selectedMatugenType)
|
||||||
|
} else {
|
||||||
|
setDesiredTheme("image", wallpaperPath, isLight, iconTheme, selectedMatugenType)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const isLight = (typeof SessionData !== "undefined" && SessionData.isLightMode)
|
||||||
|
const iconTheme = (typeof SettingsData !== "undefined" && SettingsData.iconTheme) ? SettingsData.iconTheme : "System Default"
|
||||||
|
|
||||||
|
if (currentTheme === dynamic) {
|
||||||
|
if (wallpaperPath) {
|
||||||
|
Quickshell.execDetached(["rm", "-f", stateDir + "/matugen.key"])
|
||||||
|
const selectedMatugenType = (typeof SettingsData !== "undefined" && SettingsData.matugenScheme) ? SettingsData.matugenScheme : "scheme-tonal-spot"
|
||||||
|
if (wallpaperPath.startsWith("#")) {
|
||||||
|
setDesiredTheme("hex", wallpaperPath, isLight, iconTheme, selectedMatugenType)
|
||||||
|
} else {
|
||||||
|
setDesiredTheme("image", wallpaperPath, isLight, iconTheme, selectedMatugenType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let primaryColor
|
||||||
|
let matugenType
|
||||||
|
if (currentTheme === "custom") {
|
||||||
|
if (customThemeData && customThemeData.primary) {
|
||||||
|
primaryColor = customThemeData.primary
|
||||||
|
matugenType = customThemeData.matugen_type
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
primaryColor = currentThemeData.primary
|
||||||
|
matugenType = currentThemeData.matugen_type
|
||||||
|
}
|
||||||
|
|
||||||
|
if (primaryColor) {
|
||||||
|
Quickshell.execDetached(["rm", "-f", stateDir + "/matugen.key"])
|
||||||
|
setDesiredTheme("hex", primaryColor, isLight, iconTheme, matugenType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 0)
|
||||||
|
if (typeof SessionData !== "undefined") {
|
||||||
SessionData.isLightModeChanged.connect(root.onLightModeChanged)
|
SessionData.isLightModeChanged.connect(root.onLightModeChanged)
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof SettingsData !== "undefined" && SettingsData.currentThemeName) {
|
if (typeof SettingsData !== "undefined" && SettingsData.currentThemeName) {
|
||||||
switchTheme(SettingsData.currentThemeName, false)
|
switchTheme(SettingsData.currentThemeName, false, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyGreeterTheme(themeName) {
|
||||||
|
switchTheme(themeName, false, false)
|
||||||
|
if (themeName === dynamic && dynamicColorsFileView.path) {
|
||||||
|
dynamicColorsFileView.reload()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMatugenColor(path, fallback) {
|
function getMatugenColor(path, fallback) {
|
||||||
colorUpdateTrigger
|
|
||||||
const colorMode = (typeof SessionData !== "undefined" && SessionData.isLightMode) ? "light" : "dark"
|
const colorMode = (typeof SessionData !== "undefined" && SessionData.isLightMode) ? "light" : "dark"
|
||||||
let cur = matugenColors && matugenColors.colors && matugenColors.colors[colorMode]
|
let cur = matugenColors && matugenColors.colors && matugenColors.colors[colorMode]
|
||||||
for (const part of path.split(".")) {
|
for (const part of path.split(".")) {
|
||||||
@@ -120,6 +183,7 @@ Singleton {
|
|||||||
"outline": getMatugenColor("outline", "#8e918f"),
|
"outline": getMatugenColor("outline", "#8e918f"),
|
||||||
"surfaceContainer": getMatugenColor("surface_container", "#1e2023"),
|
"surfaceContainer": getMatugenColor("surface_container", "#1e2023"),
|
||||||
"surfaceContainerHigh": getMatugenColor("surface_container_high", "#292b2f"),
|
"surfaceContainerHigh": getMatugenColor("surface_container_high", "#292b2f"),
|
||||||
|
"surfaceContainerHighest": getMatugenColor("surface_container_highest", "#343740"),
|
||||||
"error": "#F2B8B5",
|
"error": "#F2B8B5",
|
||||||
"warning": "#FF9800",
|
"warning": "#FF9800",
|
||||||
"info": "#2196F3",
|
"info": "#2196F3",
|
||||||
@@ -154,7 +218,12 @@ Singleton {
|
|||||||
property color primaryText: currentThemeData.primaryText
|
property color primaryText: currentThemeData.primaryText
|
||||||
property color primaryContainer: currentThemeData.primaryContainer
|
property color primaryContainer: currentThemeData.primaryContainer
|
||||||
property color secondary: currentThemeData.secondary
|
property color secondary: currentThemeData.secondary
|
||||||
property color surface: currentThemeData.surface
|
property color surface: {
|
||||||
|
if (typeof SettingsData !== "undefined" && SettingsData.surfaceBase === "s") {
|
||||||
|
return currentThemeData.background
|
||||||
|
}
|
||||||
|
return currentThemeData.surface
|
||||||
|
}
|
||||||
property color surfaceText: currentThemeData.surfaceText
|
property color surfaceText: currentThemeData.surfaceText
|
||||||
property color surfaceVariant: currentThemeData.surfaceVariant
|
property color surfaceVariant: currentThemeData.surfaceVariant
|
||||||
property color surfaceVariantText: currentThemeData.surfaceVariantText
|
property color surfaceVariantText: currentThemeData.surfaceVariantText
|
||||||
@@ -163,8 +232,24 @@ Singleton {
|
|||||||
property color backgroundText: currentThemeData.backgroundText
|
property color backgroundText: currentThemeData.backgroundText
|
||||||
property color outline: currentThemeData.outline
|
property color outline: currentThemeData.outline
|
||||||
property color outlineVariant: currentThemeData.outlineVariant || Qt.rgba(outline.r, outline.g, outline.b, 0.6)
|
property color outlineVariant: currentThemeData.outlineVariant || Qt.rgba(outline.r, outline.g, outline.b, 0.6)
|
||||||
property color surfaceContainer: currentThemeData.surfaceContainer
|
property color surfaceContainer: {
|
||||||
property color surfaceContainerHigh: currentThemeData.surfaceContainerHigh
|
if (typeof SettingsData !== "undefined" && SettingsData.surfaceBase === "s") {
|
||||||
|
return currentThemeData.surface
|
||||||
|
}
|
||||||
|
return currentThemeData.surfaceContainer
|
||||||
|
}
|
||||||
|
property color surfaceContainerHigh: {
|
||||||
|
if (typeof SettingsData !== "undefined" && SettingsData.surfaceBase === "s") {
|
||||||
|
return currentThemeData.surfaceContainer
|
||||||
|
}
|
||||||
|
return currentThemeData.surfaceContainerHigh
|
||||||
|
}
|
||||||
|
property color surfaceContainerHighest: {
|
||||||
|
if (typeof SettingsData !== "undefined" && SettingsData.surfaceBase === "s") {
|
||||||
|
return currentThemeData.surfaceContainerHigh
|
||||||
|
}
|
||||||
|
return currentThemeData.surfaceContainerHighest
|
||||||
|
}
|
||||||
|
|
||||||
property color onSurface: surfaceText
|
property color onSurface: surfaceText
|
||||||
property color onSurfaceVariant: surfaceVariantText
|
property color onSurfaceVariant: surfaceVariantText
|
||||||
@@ -209,11 +294,22 @@ Singleton {
|
|||||||
property color shadowMedium: Qt.rgba(0, 0, 0, 0.08)
|
property color shadowMedium: Qt.rgba(0, 0, 0, 0.08)
|
||||||
property color shadowStrong: Qt.rgba(0, 0, 0, 0.3)
|
property color shadowStrong: Qt.rgba(0, 0, 0, 0.3)
|
||||||
|
|
||||||
property int shorterDuration: 100
|
readonly property var animationDurations: [
|
||||||
property int shortDuration: 150
|
{ shorter: 0, short: 0, medium: 0, long: 0, extraLong: 0 },
|
||||||
property int mediumDuration: 300
|
{ shorter: 50, short: 75, medium: 150, long: 250, extraLong: 500 },
|
||||||
property int longDuration: 500
|
{ shorter: 100, short: 150, medium: 300, long: 500, extraLong: 1000 },
|
||||||
property int extraLongDuration: 1000
|
{ shorter: 150, short: 225, medium: 450, long: 750, extraLong: 1500 },
|
||||||
|
{ shorter: 200, short: 300, medium: 600, long: 1000, extraLong: 2000 }
|
||||||
|
]
|
||||||
|
|
||||||
|
readonly property int currentAnimationSpeed: typeof SettingsData !== "undefined" ? SettingsData.animationSpeed : SettingsData.AnimationSpeed.Short
|
||||||
|
readonly property var currentDurations: animationDurations[currentAnimationSpeed] || animationDurations[SettingsData.AnimationSpeed.Short]
|
||||||
|
|
||||||
|
property int shorterDuration: currentDurations.shorter
|
||||||
|
property int shortDuration: currentDurations.short
|
||||||
|
property int mediumDuration: currentDurations.medium
|
||||||
|
property int longDuration: currentDurations.long
|
||||||
|
property int extraLongDuration: currentDurations.extraLong
|
||||||
property int standardEasing: Easing.OutCubic
|
property int standardEasing: Easing.OutCubic
|
||||||
property int emphasizedEasing: Easing.OutQuart
|
property int emphasizedEasing: Easing.OutQuart
|
||||||
|
|
||||||
@@ -233,8 +329,8 @@ Singleton {
|
|||||||
property real iconSizeLarge: 32
|
property real iconSizeLarge: 32
|
||||||
|
|
||||||
property real panelTransparency: 0.85
|
property real panelTransparency: 0.85
|
||||||
property real widgetTransparency: typeof SettingsData !== "undefined" && SettingsData.topBarWidgetTransparency !== undefined ? SettingsData.topBarWidgetTransparency : 0.85
|
property real widgetTransparency: typeof SettingsData !== "undefined" && SettingsData.dankBarWidgetTransparency !== undefined ? SettingsData.dankBarWidgetTransparency : 1.0
|
||||||
property real popupTransparency: typeof SettingsData !== "undefined" && SettingsData.popupTransparency !== undefined ? SettingsData.popupTransparency : 0.92
|
property real popupTransparency: typeof SettingsData !== "undefined" && SettingsData.popupTransparency !== undefined ? SettingsData.popupTransparency : 1.0
|
||||||
|
|
||||||
function screenTransition() {
|
function screenTransition() {
|
||||||
CompositorService.isNiri && NiriService.doScreenTransition()
|
CompositorService.isNiri && NiriService.doScreenTransition()
|
||||||
@@ -243,11 +339,15 @@ Singleton {
|
|||||||
function switchTheme(themeName, savePrefs = true, enableTransition = true) {
|
function switchTheme(themeName, savePrefs = true, enableTransition = true) {
|
||||||
if (enableTransition) {
|
if (enableTransition) {
|
||||||
screenTransition()
|
screenTransition()
|
||||||
|
themeTransitionTimer.themeName = themeName
|
||||||
|
themeTransitionTimer.savePrefs = savePrefs
|
||||||
|
themeTransitionTimer.restart()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (themeName === dynamic) {
|
if (themeName === dynamic) {
|
||||||
currentTheme = dynamic
|
currentTheme = dynamic
|
||||||
currentThemeCategory = dynamic
|
currentThemeCategory = dynamic
|
||||||
extractColors()
|
|
||||||
} else if (themeName === custom) {
|
} else if (themeName === custom) {
|
||||||
currentTheme = custom
|
currentTheme = custom
|
||||||
currentThemeCategory = custom
|
currentThemeCategory = custom
|
||||||
@@ -256,34 +356,44 @@ Singleton {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
currentTheme = themeName
|
currentTheme = themeName
|
||||||
// Determine category based on theme name
|
|
||||||
if (StockThemes.isCatppuccinVariant(themeName)) {
|
if (StockThemes.isCatppuccinVariant(themeName)) {
|
||||||
currentThemeCategory = "catppuccin"
|
currentThemeCategory = "catppuccin"
|
||||||
} else {
|
} else {
|
||||||
currentThemeCategory = "generic"
|
currentThemeCategory = "generic"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (savePrefs && typeof SettingsData !== "undefined")
|
const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode)
|
||||||
|
if (savePrefs && typeof SettingsData !== "undefined" && !isGreeterMode)
|
||||||
SettingsData.setTheme(currentTheme)
|
SettingsData.setTheme(currentTheme)
|
||||||
|
|
||||||
generateSystemThemesFromCurrentTheme()
|
if (!isGreeterMode) {
|
||||||
|
generateSystemThemesFromCurrentTheme()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setLightMode(light, savePrefs = true) {
|
function setLightMode(light, savePrefs = true, enableTransition = false) {
|
||||||
screenTransition()
|
if (enableTransition) {
|
||||||
isLightMode = light
|
screenTransition()
|
||||||
if (savePrefs && typeof SessionData !== "undefined")
|
lightModeTransitionTimer.lightMode = light
|
||||||
SessionData.setLightMode(isLightMode)
|
lightModeTransitionTimer.savePrefs = savePrefs
|
||||||
PortalService.setLightMode(isLightMode)
|
lightModeTransitionTimer.restart()
|
||||||
generateSystemThemesFromCurrentTheme()
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode)
|
||||||
|
if (savePrefs && typeof SessionData !== "undefined" && !isGreeterMode)
|
||||||
|
SessionData.setLightMode(light)
|
||||||
|
if (!isGreeterMode) {
|
||||||
|
PortalService.setLightMode(light)
|
||||||
|
generateSystemThemesFromCurrentTheme()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleLightMode(savePrefs = true) {
|
function toggleLightMode(savePrefs = true) {
|
||||||
setLightMode(!isLightMode, savePrefs)
|
setLightMode(!isLightMode, savePrefs, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
function forceGenerateSystemThemes() {
|
function forceGenerateSystemThemes() {
|
||||||
screenTransition()
|
|
||||||
if (!matugenAvailable) {
|
if (!matugenAvailable) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -307,8 +417,10 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function switchThemeCategory(category, defaultTheme) {
|
function switchThemeCategory(category, defaultTheme) {
|
||||||
currentThemeCategory = category
|
screenTransition()
|
||||||
switchTheme(defaultTheme, true, false)
|
themeCategoryTransitionTimer.category = category
|
||||||
|
themeCategoryTransitionTimer.defaultTheme = defaultTheme
|
||||||
|
themeCategoryTransitionTimer.restart()
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCatppuccinColor(variantName) {
|
function getCatppuccinColor(variantName) {
|
||||||
@@ -332,7 +444,6 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function loadCustomTheme(themeData) {
|
function loadCustomTheme(themeData) {
|
||||||
screenTransition()
|
|
||||||
if (themeData.dark || themeData.light) {
|
if (themeData.dark || themeData.light) {
|
||||||
const colorMode = (typeof SessionData !== "undefined" && SessionData.isLightMode) ? "light" : "dark"
|
const colorMode = (typeof SessionData !== "undefined" && SessionData.isLightMode) ? "light" : "dark"
|
||||||
const selectedTheme = themeData[colorMode] || themeData.dark || themeData.light
|
const selectedTheme = themeData[colorMode] || themeData.dark || themeData.light
|
||||||
@@ -367,7 +478,7 @@ Singleton {
|
|||||||
property real notepadTransparency: SettingsData.notepadTransparencyOverride >= 0 ? SettingsData.notepadTransparencyOverride : popupTransparency
|
property real notepadTransparency: SettingsData.notepadTransparencyOverride >= 0 ? SettingsData.notepadTransparencyOverride : popupTransparency
|
||||||
|
|
||||||
property var widgetBaseBackgroundColor: {
|
property var widgetBaseBackgroundColor: {
|
||||||
const colorMode = typeof SettingsData !== "undefined" ? SettingsData.widgetBackgroundColor : "sth"
|
const colorMode = typeof SettingsData !== "undefined" ? SettingsData.widgetBackgroundColor : "sch"
|
||||||
switch (colorMode) {
|
switch (colorMode) {
|
||||||
case "s":
|
case "s":
|
||||||
return surface
|
return surface
|
||||||
@@ -388,7 +499,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
property var widgetBackground: {
|
property var widgetBackground: {
|
||||||
const colorMode = typeof SettingsData !== "undefined" ? SettingsData.widgetBackgroundColor : "sth"
|
const colorMode = typeof SettingsData !== "undefined" ? SettingsData.widgetBackgroundColor : "sch"
|
||||||
switch (colorMode) {
|
switch (colorMode) {
|
||||||
case "s":
|
case "s":
|
||||||
return Qt.rgba(surface.r, surface.g, surface.b, widgetTransparency)
|
return Qt.rgba(surface.r, surface.g, surface.b, widgetTransparency)
|
||||||
@@ -414,6 +525,19 @@ Singleton {
|
|||||||
return (0.299 * c.r + 0.587 * c.g + 0.114 * c.b) < 0.5
|
return (0.299 * c.r + 0.587 * c.g + 0.114 * c.b) < 0.5
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function barIconSize(barThickness, offset) {
|
||||||
|
const defaultOffset = offset !== undefined ? offset : -6
|
||||||
|
return Math.round((barThickness / 48) * (iconSize + defaultOffset))
|
||||||
|
}
|
||||||
|
|
||||||
|
function barTextSize(barThickness) {
|
||||||
|
const scale = barThickness / 48
|
||||||
|
const dankBarScale = (typeof SettingsData !== "undefined" ? SettingsData.dankBarFontScale : 1.0)
|
||||||
|
if (scale <= 0.75) return fontSizeSmall * 0.9 * dankBarScale
|
||||||
|
if (scale >= 1.25) return fontSizeMedium * dankBarScale
|
||||||
|
return fontSizeSmall * dankBarScale
|
||||||
|
}
|
||||||
|
|
||||||
function getBatteryIcon(level, isCharging, batteryAvailable) {
|
function getBatteryIcon(level, isCharging, batteryAvailable) {
|
||||||
if (!batteryAvailable)
|
if (!batteryAvailable)
|
||||||
return _getBatteryPowerProfileIcon()
|
return _getBatteryPowerProfileIcon()
|
||||||
@@ -504,23 +628,8 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractColors() {
|
|
||||||
extractionRequested = true
|
|
||||||
if (matugenAvailable)
|
|
||||||
if (rawWallpaperPath.startsWith("we:")) {
|
|
||||||
fileCheckerTimer.start()
|
|
||||||
} else {
|
|
||||||
fileChecker.running = true
|
|
||||||
}
|
|
||||||
else
|
|
||||||
matugenCheck.running = true
|
|
||||||
}
|
|
||||||
|
|
||||||
function onLightModeChanged() {
|
function onLightModeChanged() {
|
||||||
if (matugenColors && Object.keys(matugenColors).length > 0) {
|
|
||||||
colorUpdateTrigger++
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentTheme === "custom" && customThemeFileView.path) {
|
if (currentTheme === "custom" && customThemeFileView.path) {
|
||||||
customThemeFileView.reload()
|
customThemeFileView.reload()
|
||||||
}
|
}
|
||||||
@@ -528,10 +637,12 @@ Singleton {
|
|||||||
|
|
||||||
function setDesiredTheme(kind, value, isLight, iconTheme, matugenType) {
|
function setDesiredTheme(kind, value, isLight, iconTheme, matugenType) {
|
||||||
if (!matugenAvailable) {
|
if (!matugenAvailable) {
|
||||||
console.warn("matugen not available or disabled - cannot set system theme")
|
console.warn("Theme: matugen not available or disabled - cannot set system theme")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("Theme: Setting desired theme -", kind, "mode:", isLight ? "light" : "dark", "type:", matugenType)
|
||||||
|
|
||||||
if (typeof NiriService !== "undefined" && CompositorService.isNiri) {
|
if (typeof NiriService !== "undefined" && CompositorService.isNiri) {
|
||||||
NiriService.suppressNextToast()
|
NiriService.suppressNextToast()
|
||||||
}
|
}
|
||||||
@@ -541,7 +652,9 @@ Singleton {
|
|||||||
"value": value,
|
"value": value,
|
||||||
"mode": isLight ? "light" : "dark",
|
"mode": isLight ? "light" : "dark",
|
||||||
"iconTheme": iconTheme || "System Default",
|
"iconTheme": iconTheme || "System Default",
|
||||||
"matugenType": matugenType || "scheme-tonal-spot"
|
"matugenType": matugenType || "scheme-tonal-spot",
|
||||||
|
"surfaceBase": (typeof SettingsData !== "undefined" && SettingsData.surfaceBase) ? SettingsData.surfaceBase : "sc",
|
||||||
|
"runUserTemplates": (typeof SettingsData !== "undefined") ? SettingsData.runUserMatugenTemplates : true
|
||||||
}
|
}
|
||||||
|
|
||||||
const json = JSON.stringify(desired)
|
const json = JSON.stringify(desired)
|
||||||
@@ -550,19 +663,21 @@ Singleton {
|
|||||||
Quickshell.execDetached(["sh", "-c", `mkdir -p '${stateDir}' && cat > '${desiredPath}' << 'EOF'\n${json}\nEOF`])
|
Quickshell.execDetached(["sh", "-c", `mkdir -p '${stateDir}' && cat > '${desiredPath}' << 'EOF'\n${json}\nEOF`])
|
||||||
workerRunning = true
|
workerRunning = true
|
||||||
if (rawWallpaperPath.startsWith("we:")) {
|
if (rawWallpaperPath.startsWith("we:")) {
|
||||||
console.log("calling matugen worker")
|
console.log("Theme: Starting matugen worker (WE wallpaper)")
|
||||||
systemThemeGenerator.command = [
|
systemThemeGenerator.command = [
|
||||||
"sh", "-c",
|
"sh", "-c",
|
||||||
`sleep 1 && ${shellDir}/scripts/matugen-worker.sh '${stateDir}' '${shellDir}' --run`
|
`sleep 1 && ${shellDir}/scripts/matugen-worker.sh '${stateDir}' '${shellDir}' '${configDir}' --run`
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
systemThemeGenerator.command = [shellDir + "/scripts/matugen-worker.sh", stateDir, shellDir, "--run"]
|
console.log("Theme: Starting matugen worker")
|
||||||
|
systemThemeGenerator.command = [shellDir + "/scripts/matugen-worker.sh", stateDir, shellDir, configDir, "--run"]
|
||||||
}
|
}
|
||||||
systemThemeGenerator.running = true
|
systemThemeGenerator.running = true
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateSystemThemesFromCurrentTheme() {
|
function generateSystemThemesFromCurrentTheme() {
|
||||||
if (!matugenAvailable)
|
const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode)
|
||||||
|
if (!matugenAvailable || isGreeterMode)
|
||||||
return
|
return
|
||||||
|
|
||||||
const isLight = (typeof SessionData !== "undefined" && SessionData.isLightMode)
|
const isLight = (typeof SessionData !== "undefined" && SessionData.isLightMode)
|
||||||
@@ -610,8 +725,17 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isLight = (typeof SessionData !== "undefined" && SessionData.isLightMode) ? "true" : "false"
|
const isLight = (typeof SessionData !== "undefined" && SessionData.isLightMode) ? "true" : "false"
|
||||||
gtkApplier.command = [shellDir + "/scripts/gtk.sh", configDir, isLight, shellDir]
|
Proc.runCommand("gtkApplier", [shellDir + "/scripts/gtk.sh", configDir, isLight, shellDir], (output, exitCode) => {
|
||||||
gtkApplier.running = true
|
if (exitCode === 0) {
|
||||||
|
if (typeof ToastService !== "undefined" && typeof NiriService !== "undefined" && !NiriService.matugenSuppression) {
|
||||||
|
ToastService.showInfo("GTK colors applied successfully")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (typeof ToastService !== "undefined") {
|
||||||
|
ToastService.showError("Failed to apply GTK colors")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyQtColors() {
|
function applyQtColors() {
|
||||||
@@ -622,244 +746,84 @@ Singleton {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
qtApplier.command = [shellDir + "/scripts/qt.sh", configDir]
|
Proc.runCommand("qtApplier", [shellDir + "/scripts/qt.sh", configDir], (output, exitCode) => {
|
||||||
qtApplier.running = true
|
if (exitCode === 0) {
|
||||||
}
|
if (typeof ToastService !== "undefined") {
|
||||||
|
ToastService.showInfo("Qt colors applied successfully")
|
||||||
function extractJsonFromText(text) {
|
|
||||||
if (!text)
|
|
||||||
return null
|
|
||||||
|
|
||||||
const start = text.search(/[{\[]/)
|
|
||||||
if (start === -1)
|
|
||||||
return null
|
|
||||||
|
|
||||||
const open = text[start]
|
|
||||||
const pairs = {
|
|
||||||
"{": '}',
|
|
||||||
"[": ']'
|
|
||||||
}
|
|
||||||
const close = pairs[open]
|
|
||||||
if (!close)
|
|
||||||
return null
|
|
||||||
|
|
||||||
let inString = false
|
|
||||||
let escape = false
|
|
||||||
const stack = [open]
|
|
||||||
|
|
||||||
for (var i = start + 1; i < text.length; i++) {
|
|
||||||
const ch = text[i]
|
|
||||||
|
|
||||||
if (inString) {
|
|
||||||
if (escape) {
|
|
||||||
escape = false
|
|
||||||
} else if (ch === '\\') {
|
|
||||||
escape = true
|
|
||||||
} else if (ch === '"') {
|
|
||||||
inString = false
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch === '"') {
|
|
||||||
inString = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (ch === '{' || ch === '[') {
|
|
||||||
stack.push(ch)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (ch === '}' || ch === ']') {
|
|
||||||
const last = stack.pop()
|
|
||||||
if (!last || pairs[last] !== ch) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
if (stack.length === 0) {
|
|
||||||
return text.slice(start, i + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: matugenCheck
|
|
||||||
command: ["which", "matugen"]
|
|
||||||
onExited: code => {
|
|
||||||
matugenAvailable = (code === 0) && !envDisableMatugen
|
|
||||||
if (!matugenAvailable) {
|
|
||||||
console.log("matugen not not available in path or disabled via DMS_DISABLE_MATUGEN")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (extractionRequested) {
|
|
||||||
if (rawWallpaperPath.startsWith("we:")) {
|
|
||||||
fileCheckerTimer.start()
|
|
||||||
} else {
|
|
||||||
fileChecker.running = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const isLight = (typeof SessionData !== "undefined" && SessionData.isLightMode)
|
|
||||||
const iconTheme = (typeof SettingsData !== "undefined" && SettingsData.iconTheme) ? SettingsData.iconTheme : "System Default"
|
|
||||||
|
|
||||||
if (currentTheme === dynamic) {
|
|
||||||
if (wallpaperPath) {
|
|
||||||
Quickshell.execDetached(["rm", "-f", stateDir + "/matugen.key"])
|
|
||||||
const selectedMatugenType = (typeof SettingsData !== "undefined" && SettingsData.matugenScheme) ? SettingsData.matugenScheme : "scheme-tonal-spot"
|
|
||||||
if (wallpaperPath.startsWith("#")) {
|
|
||||||
setDesiredTheme("hex", wallpaperPath, isLight, iconTheme, selectedMatugenType)
|
|
||||||
} else {
|
|
||||||
setDesiredTheme("image", wallpaperPath, isLight, iconTheme, selectedMatugenType)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let primaryColor
|
|
||||||
let matugenType
|
|
||||||
if (currentTheme === "custom") {
|
|
||||||
if (customThemeData && customThemeData.primary) {
|
|
||||||
primaryColor = customThemeData.primary
|
|
||||||
matugenType = customThemeData.matugen_type
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
primaryColor = currentThemeData.primary
|
|
||||||
matugenType = currentThemeData.matugen_type
|
|
||||||
}
|
|
||||||
|
|
||||||
if (primaryColor) {
|
|
||||||
Quickshell.execDetached(["rm", "-f", stateDir + "/matugen.key"])
|
|
||||||
setDesiredTheme("hex", primaryColor, isLight, iconTheme, matugenType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: fileChecker
|
|
||||||
command: ["test", "-r", wallpaperPath]
|
|
||||||
onExited: code => {
|
|
||||||
if (code === 0) {
|
|
||||||
matugenProcess.running = true
|
|
||||||
} else if (wallpaperPath.startsWith("#")) {
|
|
||||||
colorMatugenProcess.running = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: fileCheckerTimer
|
|
||||||
interval: 1000
|
|
||||||
repeat: false
|
|
||||||
onTriggered: {
|
|
||||||
fileChecker.running = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: matugenProcess
|
|
||||||
command: {
|
|
||||||
const scheme = (typeof SettingsData !== "undefined" && SettingsData.matugenScheme) ? SettingsData.matugenScheme : "scheme-tonal-spot"
|
|
||||||
return ["matugen", "image", wallpaperPath, "--json", "hex", "-t", scheme]
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
id: matugenCollector
|
|
||||||
onStreamFinished: {
|
|
||||||
if (!matugenCollector.text) {
|
|
||||||
if (typeof ToastService !== "undefined") {
|
|
||||||
ToastService.wallpaperErrorStatus = "error"
|
|
||||||
ToastService.showError("Wallpaper Processing Failed: Empty JSON extracted from matugen output.")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const extractedJson = extractJsonFromText(matugenCollector.text)
|
|
||||||
if (!extractedJson) {
|
|
||||||
if (typeof ToastService !== "undefined") {
|
|
||||||
ToastService.wallpaperErrorStatus = "error"
|
|
||||||
ToastService.showError("Wallpaper Processing Failed: Invalid JSON extracted from matugen output.")
|
|
||||||
}
|
|
||||||
console.log("Raw matugen output:", matugenCollector.text)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
root.matugenColors = JSON.parse(extractedJson)
|
|
||||||
root.colorUpdateTrigger++
|
|
||||||
if (typeof ToastService !== "undefined") {
|
|
||||||
ToastService.clearWallpaperError()
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
if (typeof ToastService !== "undefined") {
|
|
||||||
ToastService.wallpaperErrorStatus = "error"
|
|
||||||
ToastService.showError("Wallpaper processing failed (JSON parse error after extraction)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onExited: code => {
|
|
||||||
if (code !== 0) {
|
|
||||||
if (typeof ToastService !== "undefined") {
|
if (typeof ToastService !== "undefined") {
|
||||||
ToastService.wallpaperErrorStatus = "error"
|
ToastService.showError("Failed to apply Qt colors")
|
||||||
ToastService.showError("Matugen command failed with exit code " + code)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
Process {
|
function withAlpha(c, a) { return Qt.rgba(c.r, c.g, c.b, a); }
|
||||||
id: colorMatugenProcess
|
|
||||||
command: {
|
|
||||||
const scheme = (typeof SettingsData !== "undefined" && SettingsData.matugenScheme) ? SettingsData.matugenScheme : "scheme-tonal-spot"
|
|
||||||
return ["matugen", "color", "hex", wallpaperPath, "--json", "hex", "-t", scheme]
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
function snap(value, dpr) {
|
||||||
id: colorMatugenCollector
|
const s = dpr || 1
|
||||||
onStreamFinished: {
|
return Math.round(value * s) / s
|
||||||
if (!colorMatugenCollector.text) {
|
|
||||||
if (typeof ToastService !== "undefined") {
|
|
||||||
ToastService.wallpaperErrorStatus = "error"
|
|
||||||
ToastService.showError("Color Processing Failed: Empty JSON extracted from matugen output.")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const extractedJson = extractJsonFromText(colorMatugenCollector.text)
|
|
||||||
if (!extractedJson) {
|
|
||||||
if (typeof ToastService !== "undefined") {
|
|
||||||
ToastService.wallpaperErrorStatus = "error"
|
|
||||||
ToastService.showError("Color Processing Failed: Invalid JSON extracted from matugen output.")
|
|
||||||
}
|
|
||||||
console.log("Raw matugen output:", colorMatugenCollector.text)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
root.matugenColors = JSON.parse(extractedJson)
|
|
||||||
root.colorUpdateTrigger++
|
|
||||||
if (typeof ToastService !== "undefined") {
|
|
||||||
ToastService.clearWallpaperError()
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
if (typeof ToastService !== "undefined") {
|
|
||||||
ToastService.wallpaperErrorStatus = "error"
|
|
||||||
ToastService.showError("Color processing failed (JSON parse error after extraction)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onExited: code => {
|
|
||||||
if (code !== 0) {
|
|
||||||
if (typeof ToastService !== "undefined") {
|
|
||||||
ToastService.wallpaperErrorStatus = "error"
|
|
||||||
ToastService.showError("Matugen color command failed with exit code " + code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Process {
|
function px(value, dpr) {
|
||||||
id: ensureStateDir
|
const s = dpr || 1
|
||||||
|
return Math.round(value * s) / s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hairline(dpr) {
|
||||||
|
return 1 / (dpr || 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function invertHex(hex) {
|
||||||
|
hex = hex.replace('#', '');
|
||||||
|
|
||||||
|
if (!/^[0-9A-Fa-f]{6}$/.test(hex)) {
|
||||||
|
return hex;
|
||||||
|
}
|
||||||
|
|
||||||
|
const r = parseInt(hex.substr(0, 2), 16);
|
||||||
|
const g = parseInt(hex.substr(2, 2), 16);
|
||||||
|
const b = parseInt(hex.substr(4, 2), 16);
|
||||||
|
|
||||||
|
const invR = (255 - r).toString(16).padStart(2, '0');
|
||||||
|
const invG = (255 - g).toString(16).padStart(2, '0');
|
||||||
|
const invB = (255 - b).toString(16).padStart(2, '0');
|
||||||
|
|
||||||
|
return `#${invR}${invG}${invB}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
property string baseLogoColor: {
|
||||||
|
if (typeof SettingsData === "undefined") return ""
|
||||||
|
const colorOverride = SettingsData.launcherLogoColorOverride
|
||||||
|
if (!colorOverride || colorOverride === "") return ""
|
||||||
|
if (colorOverride === "primary") return primary
|
||||||
|
if (colorOverride === "surface") return surfaceText
|
||||||
|
return colorOverride
|
||||||
|
}
|
||||||
|
|
||||||
|
property string effectiveLogoColor: {
|
||||||
|
if (typeof SettingsData === "undefined") return ""
|
||||||
|
|
||||||
|
const colorOverride = SettingsData.launcherLogoColorOverride
|
||||||
|
if (!colorOverride || colorOverride === "") return ""
|
||||||
|
|
||||||
|
if (colorOverride === "primary") return primary
|
||||||
|
if (colorOverride === "surface") return surfaceText
|
||||||
|
|
||||||
|
if (!SettingsData.launcherLogoColorInvertOnMode) {
|
||||||
|
return colorOverride
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLightMode) {
|
||||||
|
return invertHex(colorOverride)
|
||||||
|
}
|
||||||
|
|
||||||
|
return colorOverride
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: systemThemeGenerator
|
id: systemThemeGenerator
|
||||||
running: false
|
running: false
|
||||||
@@ -867,64 +831,19 @@ Singleton {
|
|||||||
onExited: exitCode => {
|
onExited: exitCode => {
|
||||||
workerRunning = false
|
workerRunning = false
|
||||||
|
|
||||||
if (exitCode === 2) {
|
if (exitCode === 0) {
|
||||||
// Exit code 2 means wallpaper/color not found - this is expected on first run
|
console.log("Theme: Matugen worker completed successfully")
|
||||||
console.log("Theme worker: wallpaper/color not found, skipping theme generation")
|
if (currentTheme === dynamic) {
|
||||||
} else if (exitCode !== 0) {
|
console.log("Theme: Reloading dynamic colors file")
|
||||||
|
dynamicColorsFileView.reload()
|
||||||
|
}
|
||||||
|
} else if (exitCode === 2) {
|
||||||
|
console.log("Theme: Matugen worker completed with code 2 (no changes needed)")
|
||||||
|
} else {
|
||||||
if (typeof ToastService !== "undefined") {
|
if (typeof ToastService !== "undefined") {
|
||||||
ToastService.showError("Theme worker failed (" + exitCode + ")")
|
ToastService.showError("Theme worker failed (" + exitCode + ")")
|
||||||
}
|
}
|
||||||
console.warn("Theme worker failed with exit code:", exitCode)
|
console.warn("Theme: Matugen worker failed with exit code:", exitCode)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: gtkApplier
|
|
||||||
running: false
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
id: gtkStdout
|
|
||||||
}
|
|
||||||
|
|
||||||
stderr: StdioCollector {
|
|
||||||
id: gtkStderr
|
|
||||||
}
|
|
||||||
|
|
||||||
onExited: exitCode => {
|
|
||||||
if (exitCode === 0) {
|
|
||||||
if (typeof ToastService !== "undefined" && typeof NiriService !== "undefined" && !NiriService.matugenSuppression) {
|
|
||||||
ToastService.showInfo("GTK colors applied successfully")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (typeof ToastService !== "undefined") {
|
|
||||||
ToastService.showError("Failed to apply GTK colors: " + gtkStderr.text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: qtApplier
|
|
||||||
running: false
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
id: qtStdout
|
|
||||||
}
|
|
||||||
|
|
||||||
stderr: StdioCollector {
|
|
||||||
id: qtStderr
|
|
||||||
}
|
|
||||||
|
|
||||||
onExited: exitCode => {
|
|
||||||
if (exitCode === 0) {
|
|
||||||
if (typeof ToastService !== "undefined") {
|
|
||||||
ToastService.showInfo("Qt colors applied successfully")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (typeof ToastService !== "undefined") {
|
|
||||||
ToastService.showError("Failed to apply Qt colors: " + qtStderr.text)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -957,21 +876,81 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
id: dynamicColorsFileView
|
||||||
|
path: {
|
||||||
|
const greetCfgDir = Quickshell.env("DMS_GREET_CFG_DIR") || "/etc/greetd/.dms"
|
||||||
|
const colorsPath = SessionData.isGreeterMode
|
||||||
|
? greetCfgDir + "/colors.json"
|
||||||
|
: stateDir + "/dms-colors.json"
|
||||||
|
return colorsPath
|
||||||
|
}
|
||||||
|
watchChanges: currentTheme === dynamic && !SessionData.isGreeterMode
|
||||||
|
|
||||||
|
function parseAndLoadColors() {
|
||||||
|
try {
|
||||||
|
const colorsText = dynamicColorsFileView.text()
|
||||||
|
if (colorsText) {
|
||||||
|
root.matugenColors = JSON.parse(colorsText)
|
||||||
|
if (typeof ToastService !== "undefined") {
|
||||||
|
ToastService.clearWallpaperError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Theme: Failed to parse dynamic colors:", e)
|
||||||
|
if (typeof ToastService !== "undefined") {
|
||||||
|
ToastService.wallpaperErrorStatus = "error"
|
||||||
|
ToastService.showError("Dynamic colors parse error: " + e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoaded: {
|
||||||
|
if (currentTheme === dynamic) {
|
||||||
|
console.log("Theme: Dynamic colors file loaded successfully")
|
||||||
|
colorsFileLoadFailed = false
|
||||||
|
parseAndLoadColors()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onFileChanged: {
|
||||||
|
if (currentTheme === dynamic) {
|
||||||
|
dynamicColorsFileView.reload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoadFailed: function (error) {
|
||||||
|
if (currentTheme === dynamic) {
|
||||||
|
console.log("Theme: Dynamic colors file load failed, marking for regeneration")
|
||||||
|
colorsFileLoadFailed = true
|
||||||
|
const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode)
|
||||||
|
if (!isGreeterMode && matugenAvailable && wallpaperPath) {
|
||||||
|
console.log("Theme: Matugen available, triggering immediate regeneration")
|
||||||
|
generateSystemThemesFromCurrentTheme()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onPathChanged: {
|
||||||
|
colorsFileLoadFailed = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
target: "theme"
|
target: "theme"
|
||||||
|
|
||||||
function toggle(): string {
|
function toggle(): string {
|
||||||
root.toggleLightMode()
|
root.toggleLightMode()
|
||||||
return root.isLightMode ? "light" : "dark"
|
return root.isLightMode ? "dark" : "light"
|
||||||
}
|
}
|
||||||
|
|
||||||
function light(): string {
|
function light(): string {
|
||||||
root.setLightMode(true)
|
root.setLightMode(true, true, true)
|
||||||
return "light"
|
return "light"
|
||||||
}
|
}
|
||||||
|
|
||||||
function dark(): string {
|
function dark(): string {
|
||||||
root.setLightMode(false)
|
root.setLightMode(false, true, true)
|
||||||
return "dark"
|
return "dark"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -979,4 +958,35 @@ Singleton {
|
|||||||
return root.isLightMode ? "light" : "dark"
|
return root.isLightMode ? "light" : "dark"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// These timers are for screen transitions, since sometimes QML still beats the niri call
|
||||||
|
Timer {
|
||||||
|
id: themeTransitionTimer
|
||||||
|
interval: 50
|
||||||
|
repeat: false
|
||||||
|
property string themeName: ""
|
||||||
|
property bool savePrefs: true
|
||||||
|
onTriggered: root.switchTheme(themeName, savePrefs, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: lightModeTransitionTimer
|
||||||
|
interval: 100
|
||||||
|
repeat: false
|
||||||
|
property bool lightMode: false
|
||||||
|
property bool savePrefs: true
|
||||||
|
onTriggered: root.setLightMode(lightMode, savePrefs, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: themeCategoryTransitionTimer
|
||||||
|
interval: 50
|
||||||
|
repeat: false
|
||||||
|
property string category: ""
|
||||||
|
property string defaultTheme: ""
|
||||||
|
onTriggered: {
|
||||||
|
root.currentThemeCategory = category
|
||||||
|
root.switchTheme(defaultTheme, true, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
29
DMSGreeter.qml
Normal file
29
DMSGreeter.qml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Services.Greetd
|
||||||
|
import qs.Common
|
||||||
|
import qs.Modules.Greetd
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
WlSessionLock {
|
||||||
|
id: sessionLock
|
||||||
|
locked: false
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
Qt.callLater(() => { locked = true })
|
||||||
|
}
|
||||||
|
|
||||||
|
onLockedChanged: {
|
||||||
|
if (!locked) {
|
||||||
|
console.log("Greetd session unlocked, exiting")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GreeterSurface {
|
||||||
|
lock: sessionLock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
541
DMSShell.qml
Normal file
541
DMSShell.qml
Normal file
@@ -0,0 +1,541 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import qs.Common
|
||||||
|
import qs.Modals
|
||||||
|
import qs.Modals.Clipboard
|
||||||
|
import qs.Modals.Common
|
||||||
|
import qs.Modals.Settings
|
||||||
|
import qs.Modals.Spotlight
|
||||||
|
import qs.Modules
|
||||||
|
import qs.Modules.AppDrawer
|
||||||
|
import qs.Modules.DankDash
|
||||||
|
import qs.Modules.ControlCenter
|
||||||
|
import qs.Modules.Dock
|
||||||
|
import qs.Modules.Lock
|
||||||
|
import qs.Modules.Notepad
|
||||||
|
import qs.Modules.Notifications.Center
|
||||||
|
import qs.Widgets
|
||||||
|
import qs.Modules.Notifications.Popup
|
||||||
|
import qs.Modules.OSD
|
||||||
|
import qs.Modules.ProcessList
|
||||||
|
import qs.Modules.Settings
|
||||||
|
import qs.Modules.DankBar
|
||||||
|
import qs.Modules.DankBar.Popouts
|
||||||
|
import qs.Modules.Plugins
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
Instantiator {
|
||||||
|
id: daemonPluginInstantiator
|
||||||
|
asynchronous: true
|
||||||
|
model: Object.keys(PluginService.pluginDaemonComponents)
|
||||||
|
|
||||||
|
delegate: Loader {
|
||||||
|
id: daemonLoader
|
||||||
|
property string pluginId: modelData
|
||||||
|
sourceComponent: PluginService.pluginDaemonComponents[pluginId]
|
||||||
|
|
||||||
|
onLoaded: {
|
||||||
|
if (item) {
|
||||||
|
item.pluginService = PluginService
|
||||||
|
if (item.popoutService !== undefined) {
|
||||||
|
item.popoutService = PopoutService
|
||||||
|
}
|
||||||
|
item.pluginId = pluginId
|
||||||
|
console.log("Daemon plugin loaded:", pluginId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WallpaperBackground {}
|
||||||
|
|
||||||
|
Lock {
|
||||||
|
id: lock
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: dankBarLoader
|
||||||
|
asynchronous: false
|
||||||
|
|
||||||
|
property var currentPosition: SettingsData.dankBarPosition
|
||||||
|
property bool initialized: false
|
||||||
|
|
||||||
|
sourceComponent: DankBar {
|
||||||
|
onColorPickerRequested: {
|
||||||
|
if (colorPickerModal.shouldBeVisible) {
|
||||||
|
colorPickerModal.close()
|
||||||
|
} else {
|
||||||
|
colorPickerModal.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
initialized = true
|
||||||
|
}
|
||||||
|
|
||||||
|
onCurrentPositionChanged: {
|
||||||
|
if (!initialized)
|
||||||
|
return
|
||||||
|
|
||||||
|
const component = sourceComponent
|
||||||
|
sourceComponent = null
|
||||||
|
sourceComponent = component
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: dockLoader
|
||||||
|
active: true
|
||||||
|
asynchronous: false
|
||||||
|
|
||||||
|
property var currentPosition: SettingsData.dockPosition
|
||||||
|
property bool initialized: false
|
||||||
|
|
||||||
|
sourceComponent: Dock {
|
||||||
|
contextMenu: dockContextMenuLoader.item ? dockContextMenuLoader.item : null
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoaded: {
|
||||||
|
if (item) {
|
||||||
|
dockContextMenuLoader.active = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
initialized = true
|
||||||
|
}
|
||||||
|
|
||||||
|
onCurrentPositionChanged: {
|
||||||
|
if (!initialized)
|
||||||
|
return
|
||||||
|
|
||||||
|
console.log("DEBUG: Dock position changed to:", currentPosition, "- recreating dock")
|
||||||
|
const comp = sourceComponent
|
||||||
|
sourceComponent = null
|
||||||
|
sourceComponent = comp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: dankDashPopoutLoader
|
||||||
|
|
||||||
|
active: false
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
|
sourceComponent: Component {
|
||||||
|
DankDashPopout {
|
||||||
|
id: dankDashPopout
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
PopoutService.dankDashPopout = dankDashPopout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: dockContextMenuLoader
|
||||||
|
|
||||||
|
active: false
|
||||||
|
|
||||||
|
DockContextMenu {
|
||||||
|
id: dockContextMenu
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: notificationCenterLoader
|
||||||
|
|
||||||
|
active: false
|
||||||
|
|
||||||
|
NotificationCenterPopout {
|
||||||
|
id: notificationCenter
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
PopoutService.notificationCenterPopout = notificationCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Variants {
|
||||||
|
model: SettingsData.getFilteredScreens("notifications")
|
||||||
|
|
||||||
|
delegate: NotificationPopupManager {
|
||||||
|
modelData: item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: controlCenterLoader
|
||||||
|
|
||||||
|
active: false
|
||||||
|
|
||||||
|
property var modalRef: colorPickerModal
|
||||||
|
property LazyLoader powerModalLoaderRef: powerMenuModalLoader
|
||||||
|
|
||||||
|
ControlCenterPopout {
|
||||||
|
id: controlCenterPopout
|
||||||
|
colorPickerModal: controlCenterLoader.modalRef
|
||||||
|
powerMenuModalLoader: controlCenterLoader.powerModalLoaderRef
|
||||||
|
|
||||||
|
onLockRequested: {
|
||||||
|
lock.activate()
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
PopoutService.controlCenterPopout = controlCenterPopout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: wifiPasswordModalLoader
|
||||||
|
|
||||||
|
active: false
|
||||||
|
|
||||||
|
WifiPasswordModal {
|
||||||
|
id: wifiPasswordModal
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
PopoutService.wifiPasswordModal = wifiPasswordModal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: networkInfoModalLoader
|
||||||
|
|
||||||
|
active: false
|
||||||
|
|
||||||
|
NetworkInfoModal {
|
||||||
|
id: networkInfoModal
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
PopoutService.networkInfoModal = networkInfoModal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: batteryPopoutLoader
|
||||||
|
|
||||||
|
active: false
|
||||||
|
|
||||||
|
BatteryPopout {
|
||||||
|
id: batteryPopout
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
PopoutService.batteryPopout = batteryPopout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: vpnPopoutLoader
|
||||||
|
|
||||||
|
active: false
|
||||||
|
|
||||||
|
VpnPopout {
|
||||||
|
id: vpnPopout
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
PopoutService.vpnPopout = vpnPopout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: powerMenuLoader
|
||||||
|
|
||||||
|
active: false
|
||||||
|
|
||||||
|
PowerMenu {
|
||||||
|
id: powerMenu
|
||||||
|
|
||||||
|
onPowerActionRequested: (action, title, message) => {
|
||||||
|
if (SettingsData.powerActionConfirm) {
|
||||||
|
powerConfirmModalLoader.active = true
|
||||||
|
if (powerConfirmModalLoader.item) {
|
||||||
|
powerConfirmModalLoader.item.confirmButtonColor = action === "poweroff" ? Theme.error : action === "reboot" ? Theme.warning : Theme.primary
|
||||||
|
powerConfirmModalLoader.item.show(title, message, () => actionApply(action), function () {})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
actionApply(action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function actionApply(action) {
|
||||||
|
switch (action) {
|
||||||
|
case "logout":
|
||||||
|
SessionService.logout()
|
||||||
|
break
|
||||||
|
case "suspend":
|
||||||
|
SessionService.suspend()
|
||||||
|
break
|
||||||
|
case "hibernate":
|
||||||
|
SessionService.hibernate()
|
||||||
|
break
|
||||||
|
case "reboot":
|
||||||
|
SessionService.reboot()
|
||||||
|
break
|
||||||
|
case "poweroff":
|
||||||
|
SessionService.poweroff()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: powerConfirmModalLoader
|
||||||
|
|
||||||
|
active: false
|
||||||
|
|
||||||
|
ConfirmModal {
|
||||||
|
id: powerConfirmModal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: processListPopoutLoader
|
||||||
|
|
||||||
|
active: false
|
||||||
|
|
||||||
|
ProcessListPopout {
|
||||||
|
id: processListPopout
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
PopoutService.processListPopout = processListPopout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsModal {
|
||||||
|
id: settingsModal
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
PopoutService.settingsModal = settingsModal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: appDrawerLoader
|
||||||
|
|
||||||
|
active: false
|
||||||
|
|
||||||
|
AppDrawerPopout {
|
||||||
|
id: appDrawerPopout
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
PopoutService.appDrawerPopout = appDrawerPopout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SpotlightModal {
|
||||||
|
id: spotlightModal
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
PopoutService.spotlightModal = spotlightModal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ClipboardHistoryModal {
|
||||||
|
id: clipboardHistoryModalPopup
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
PopoutService.clipboardHistoryModal = clipboardHistoryModalPopup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationModal {
|
||||||
|
id: notificationModal
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
PopoutService.notificationModal = notificationModal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankColorPickerModal {
|
||||||
|
id: colorPickerModal
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
PopoutService.colorPickerModal = colorPickerModal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: processListModalLoader
|
||||||
|
|
||||||
|
active: false
|
||||||
|
|
||||||
|
ProcessListModal {
|
||||||
|
id: processListModal
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
PopoutService.processListModal = processListModal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: systemUpdateLoader
|
||||||
|
|
||||||
|
active: false
|
||||||
|
|
||||||
|
SystemUpdatePopout {
|
||||||
|
id: systemUpdatePopout
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
PopoutService.systemUpdatePopout = systemUpdatePopout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Variants {
|
||||||
|
id: notepadSlideoutVariants
|
||||||
|
model: SettingsData.getFilteredScreens("notepad")
|
||||||
|
|
||||||
|
delegate: DankSlideout {
|
||||||
|
id: notepadSlideout
|
||||||
|
modelData: item
|
||||||
|
title: I18n.tr("Notepad")
|
||||||
|
slideoutWidth: 480
|
||||||
|
expandable: true
|
||||||
|
expandedWidthValue: 960
|
||||||
|
customTransparency: SettingsData.notepadTransparencyOverride
|
||||||
|
|
||||||
|
content: Component {
|
||||||
|
Notepad {
|
||||||
|
onHideRequested: {
|
||||||
|
notepadSlideout.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle() {
|
||||||
|
if (isVisible) {
|
||||||
|
hide()
|
||||||
|
} else {
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: powerMenuModalLoader
|
||||||
|
|
||||||
|
active: false
|
||||||
|
|
||||||
|
PowerMenuModal {
|
||||||
|
id: powerMenuModal
|
||||||
|
|
||||||
|
onPowerActionRequested: (action, title, message) => {
|
||||||
|
if (SettingsData.powerActionConfirm) {
|
||||||
|
powerConfirmModalLoader.active = true
|
||||||
|
if (powerConfirmModalLoader.item) {
|
||||||
|
powerConfirmModalLoader.item.confirmButtonColor = action === "poweroff" ? Theme.error : action === "reboot" ? Theme.warning : Theme.primary
|
||||||
|
powerConfirmModalLoader.item.show(title, message, () => actionApply(action), function () {})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
actionApply(action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function actionApply(action) {
|
||||||
|
switch (action) {
|
||||||
|
case "logout":
|
||||||
|
SessionService.logout()
|
||||||
|
break
|
||||||
|
case "suspend":
|
||||||
|
SessionService.suspend()
|
||||||
|
break
|
||||||
|
case "hibernate":
|
||||||
|
SessionService.hibernate()
|
||||||
|
break
|
||||||
|
case "reboot":
|
||||||
|
SessionService.reboot()
|
||||||
|
break
|
||||||
|
case "poweroff":
|
||||||
|
SessionService.poweroff()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
PopoutService.powerMenuModal = powerMenuModal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: hyprKeybindsModalLoader
|
||||||
|
|
||||||
|
active: false
|
||||||
|
|
||||||
|
HyprKeybindsModal {
|
||||||
|
id: hyprKeybindsModal
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
PopoutService.hyprKeybindsModal = hyprKeybindsModal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DMSShellIPC {
|
||||||
|
powerMenuModalLoader: powerMenuModalLoader
|
||||||
|
processListModalLoader: processListModalLoader
|
||||||
|
controlCenterLoader: controlCenterLoader
|
||||||
|
dankDashPopoutLoader: dankDashPopoutLoader
|
||||||
|
notepadSlideoutVariants: notepadSlideoutVariants
|
||||||
|
hyprKeybindsModalLoader: hyprKeybindsModalLoader
|
||||||
|
dankBarLoader: dankBarLoader
|
||||||
|
}
|
||||||
|
|
||||||
|
Variants {
|
||||||
|
model: SettingsData.getFilteredScreens("toast")
|
||||||
|
|
||||||
|
delegate: Toast {
|
||||||
|
modelData: item
|
||||||
|
visible: ToastService.toastVisible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Variants {
|
||||||
|
model: SettingsData.getFilteredScreens("osd")
|
||||||
|
|
||||||
|
delegate: VolumeOSD {
|
||||||
|
modelData: item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Variants {
|
||||||
|
model: SettingsData.getFilteredScreens("osd")
|
||||||
|
|
||||||
|
delegate: MicMuteOSD {
|
||||||
|
modelData: item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Variants {
|
||||||
|
model: SettingsData.getFilteredScreens("osd")
|
||||||
|
|
||||||
|
delegate: BrightnessOSD {
|
||||||
|
modelData: item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Variants {
|
||||||
|
model: SettingsData.getFilteredScreens("osd")
|
||||||
|
|
||||||
|
delegate: IdleInhibitorOSD {
|
||||||
|
modelData: item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
352
DMSShellIPC.qml
Normal file
352
DMSShellIPC.qml
Normal file
@@ -0,0 +1,352 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property var powerMenuModalLoader
|
||||||
|
required property var processListModalLoader
|
||||||
|
required property var controlCenterLoader
|
||||||
|
required property var dankDashPopoutLoader
|
||||||
|
required property var notepadSlideoutVariants
|
||||||
|
required property var hyprKeybindsModalLoader
|
||||||
|
required property var dankBarLoader
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
function open() {
|
||||||
|
root.powerMenuModalLoader.active = true
|
||||||
|
if (root.powerMenuModalLoader.item)
|
||||||
|
root.powerMenuModalLoader.item.openCentered()
|
||||||
|
|
||||||
|
return "POWERMENU_OPEN_SUCCESS"
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
if (root.powerMenuModalLoader.item)
|
||||||
|
root.powerMenuModalLoader.item.close()
|
||||||
|
|
||||||
|
return "POWERMENU_CLOSE_SUCCESS"
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle() {
|
||||||
|
root.powerMenuModalLoader.active = true
|
||||||
|
if (root.powerMenuModalLoader.item) {
|
||||||
|
if (root.powerMenuModalLoader.item.shouldBeVisible) {
|
||||||
|
root.powerMenuModalLoader.item.close()
|
||||||
|
} else {
|
||||||
|
root.powerMenuModalLoader.item.openCentered()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "POWERMENU_TOGGLE_SUCCESS"
|
||||||
|
}
|
||||||
|
|
||||||
|
target: "powermenu"
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
function open(): string {
|
||||||
|
root.processListModalLoader.active = true
|
||||||
|
if (root.processListModalLoader.item)
|
||||||
|
root.processListModalLoader.item.show()
|
||||||
|
|
||||||
|
return "PROCESSLIST_OPEN_SUCCESS"
|
||||||
|
}
|
||||||
|
|
||||||
|
function close(): string {
|
||||||
|
if (root.processListModalLoader.item)
|
||||||
|
root.processListModalLoader.item.hide()
|
||||||
|
|
||||||
|
return "PROCESSLIST_CLOSE_SUCCESS"
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle(): string {
|
||||||
|
root.processListModalLoader.active = true
|
||||||
|
if (root.processListModalLoader.item)
|
||||||
|
root.processListModalLoader.item.toggle()
|
||||||
|
|
||||||
|
return "PROCESSLIST_TOGGLE_SUCCESS"
|
||||||
|
}
|
||||||
|
|
||||||
|
target: "processlist"
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
function open(): string {
|
||||||
|
if (root.dankBarLoader.item) {
|
||||||
|
root.dankBarLoader.item.triggerControlCenterOnFocusedScreen()
|
||||||
|
return "CONTROL_CENTER_OPEN_SUCCESS"
|
||||||
|
}
|
||||||
|
return "CONTROL_CENTER_OPEN_FAILED"
|
||||||
|
}
|
||||||
|
|
||||||
|
function close(): string {
|
||||||
|
if (root.controlCenterLoader.item) {
|
||||||
|
root.controlCenterLoader.item.close()
|
||||||
|
return "CONTROL_CENTER_CLOSE_SUCCESS"
|
||||||
|
}
|
||||||
|
return "CONTROL_CENTER_CLOSE_FAILED"
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle(): string {
|
||||||
|
if (root.dankBarLoader.item) {
|
||||||
|
root.dankBarLoader.item.triggerControlCenterOnFocusedScreen()
|
||||||
|
return "CONTROL_CENTER_TOGGLE_SUCCESS"
|
||||||
|
}
|
||||||
|
return "CONTROL_CENTER_TOGGLE_FAILED"
|
||||||
|
}
|
||||||
|
|
||||||
|
target: "control-center"
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
function open(tab: string): string {
|
||||||
|
root.dankDashPopoutLoader.active = true
|
||||||
|
if (root.dankDashPopoutLoader.item) {
|
||||||
|
switch (tab.toLowerCase()) {
|
||||||
|
case "media":
|
||||||
|
root.dankDashPopoutLoader.item.currentTabIndex = 1
|
||||||
|
break
|
||||||
|
case "weather":
|
||||||
|
root.dankDashPopoutLoader.item.currentTabIndex = SettingsData.weatherEnabled ? 2 : 0
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
root.dankDashPopoutLoader.item.currentTabIndex = 0
|
||||||
|
break
|
||||||
|
}
|
||||||
|
root.dankDashPopoutLoader.item.setTriggerPosition(Screen.width / 2, Theme.barHeight + Theme.spacingS, 100, "center", Screen)
|
||||||
|
root.dankDashPopoutLoader.item.dashVisible = true
|
||||||
|
return "DASH_OPEN_SUCCESS"
|
||||||
|
}
|
||||||
|
return "DASH_OPEN_FAILED"
|
||||||
|
}
|
||||||
|
|
||||||
|
function close(): string {
|
||||||
|
if (root.dankDashPopoutLoader.item) {
|
||||||
|
root.dankDashPopoutLoader.item.dashVisible = false
|
||||||
|
return "DASH_CLOSE_SUCCESS"
|
||||||
|
}
|
||||||
|
return "DASH_CLOSE_FAILED"
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle(tab: string): string {
|
||||||
|
root.dankDashPopoutLoader.active = true
|
||||||
|
if (root.dankDashPopoutLoader.item) {
|
||||||
|
if (root.dankDashPopoutLoader.item.dashVisible) {
|
||||||
|
root.dankDashPopoutLoader.item.dashVisible = false
|
||||||
|
} else {
|
||||||
|
switch (tab.toLowerCase()) {
|
||||||
|
case "media":
|
||||||
|
root.dankDashPopoutLoader.item.currentTabIndex = 1
|
||||||
|
break
|
||||||
|
case "weather":
|
||||||
|
root.dankDashPopoutLoader.item.currentTabIndex = SettingsData.weatherEnabled ? 2 : 0
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
root.dankDashPopoutLoader.item.currentTabIndex = 0
|
||||||
|
break
|
||||||
|
}
|
||||||
|
root.dankDashPopoutLoader.item.setTriggerPosition(Screen.width / 2, Theme.barHeight + Theme.spacingS, 100, "center", Screen)
|
||||||
|
root.dankDashPopoutLoader.item.dashVisible = true
|
||||||
|
}
|
||||||
|
return "DASH_TOGGLE_SUCCESS"
|
||||||
|
}
|
||||||
|
return "DASH_TOGGLE_FAILED"
|
||||||
|
}
|
||||||
|
|
||||||
|
target: "dash"
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
function getFocusedScreenName() {
|
||||||
|
if (CompositorService.isHyprland && Hyprland.focusedWorkspace && Hyprland.focusedWorkspace.monitor) {
|
||||||
|
return Hyprland.focusedWorkspace.monitor.name
|
||||||
|
}
|
||||||
|
if (CompositorService.isNiri && NiriService.currentOutput) {
|
||||||
|
return NiriService.currentOutput
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function getActiveNotepadInstance() {
|
||||||
|
if (root.notepadSlideoutVariants.instances.length === 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root.notepadSlideoutVariants.instances.length === 1) {
|
||||||
|
return root.notepadSlideoutVariants.instances[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
var focusedScreen = getFocusedScreenName()
|
||||||
|
if (focusedScreen && root.notepadSlideoutVariants.instances.length > 0) {
|
||||||
|
for (var i = 0; i < root.notepadSlideoutVariants.instances.length; i++) {
|
||||||
|
var slideout = root.notepadSlideoutVariants.instances[i]
|
||||||
|
if (slideout.modelData && slideout.modelData.name === focusedScreen) {
|
||||||
|
return slideout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < root.notepadSlideoutVariants.instances.length; i++) {
|
||||||
|
var slideout = root.notepadSlideoutVariants.instances[i]
|
||||||
|
if (slideout.isVisible) {
|
||||||
|
return slideout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.notepadSlideoutVariants.instances[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
function open(): string {
|
||||||
|
var instance = getActiveNotepadInstance()
|
||||||
|
if (instance) {
|
||||||
|
instance.show()
|
||||||
|
return "NOTEPAD_OPEN_SUCCESS"
|
||||||
|
}
|
||||||
|
return "NOTEPAD_OPEN_FAILED"
|
||||||
|
}
|
||||||
|
|
||||||
|
function close(): string {
|
||||||
|
var instance = getActiveNotepadInstance()
|
||||||
|
if (instance) {
|
||||||
|
instance.hide()
|
||||||
|
return "NOTEPAD_CLOSE_SUCCESS"
|
||||||
|
}
|
||||||
|
return "NOTEPAD_CLOSE_FAILED"
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle(): string {
|
||||||
|
var instance = getActiveNotepadInstance()
|
||||||
|
if (instance) {
|
||||||
|
instance.toggle()
|
||||||
|
return "NOTEPAD_TOGGLE_SUCCESS"
|
||||||
|
}
|
||||||
|
return "NOTEPAD_TOGGLE_FAILED"
|
||||||
|
}
|
||||||
|
|
||||||
|
target: "notepad"
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
function toggle(): string {
|
||||||
|
SessionService.toggleIdleInhibit()
|
||||||
|
return SessionService.idleInhibited ? "Idle inhibit enabled" : "Idle inhibit disabled"
|
||||||
|
}
|
||||||
|
|
||||||
|
function enable(): string {
|
||||||
|
SessionService.enableIdleInhibit()
|
||||||
|
return "Idle inhibit enabled"
|
||||||
|
}
|
||||||
|
|
||||||
|
function disable(): string {
|
||||||
|
SessionService.disableIdleInhibit()
|
||||||
|
return "Idle inhibit disabled"
|
||||||
|
}
|
||||||
|
|
||||||
|
function status(): string {
|
||||||
|
return SessionService.idleInhibited ? "Idle inhibit is enabled" : "Idle inhibit is disabled"
|
||||||
|
}
|
||||||
|
|
||||||
|
function reason(newReason: string): string {
|
||||||
|
if (!newReason) {
|
||||||
|
return `Current reason: ${SessionService.inhibitReason}`
|
||||||
|
}
|
||||||
|
|
||||||
|
SessionService.setInhibitReason(newReason)
|
||||||
|
return `Inhibit reason set to: ${newReason}`
|
||||||
|
}
|
||||||
|
|
||||||
|
target: "inhibit"
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
function list(): string {
|
||||||
|
return MprisController.availablePlayers.map(p => p.identity).join("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
function play(): void {
|
||||||
|
if (MprisController.activePlayer && MprisController.activePlayer.canPlay) {
|
||||||
|
MprisController.activePlayer.play()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function pause(): void {
|
||||||
|
if (MprisController.activePlayer && MprisController.activePlayer.canPause) {
|
||||||
|
MprisController.activePlayer.pause()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function playPause(): void {
|
||||||
|
if (MprisController.activePlayer && MprisController.activePlayer.canTogglePlaying) {
|
||||||
|
MprisController.activePlayer.togglePlaying()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function previous(): void {
|
||||||
|
if (MprisController.activePlayer && MprisController.activePlayer.canGoPrevious) {
|
||||||
|
MprisController.activePlayer.previous()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function next(): void {
|
||||||
|
if (MprisController.activePlayer && MprisController.activePlayer.canGoNext) {
|
||||||
|
MprisController.activePlayer.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stop(): void {
|
||||||
|
if (MprisController.activePlayer) {
|
||||||
|
MprisController.activePlayer.stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
target: "mpris"
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
function openBinds(): string {
|
||||||
|
if (!CompositorService.isHyprland) {
|
||||||
|
return "HYPR_NOT_AVAILABLE"
|
||||||
|
}
|
||||||
|
root.hyprKeybindsModalLoader.active = true
|
||||||
|
if (root.hyprKeybindsModalLoader.item) {
|
||||||
|
root.hyprKeybindsModalLoader.item.open()
|
||||||
|
return "HYPR_KEYBINDS_OPEN_SUCCESS"
|
||||||
|
}
|
||||||
|
return "HYPR_KEYBINDS_OPEN_FAILED"
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeBinds(): string {
|
||||||
|
if (!CompositorService.isHyprland) {
|
||||||
|
return "HYPR_NOT_AVAILABLE"
|
||||||
|
}
|
||||||
|
if (root.hyprKeybindsModalLoader.item) {
|
||||||
|
root.hyprKeybindsModalLoader.item.close()
|
||||||
|
return "HYPR_KEYBINDS_CLOSE_SUCCESS"
|
||||||
|
}
|
||||||
|
return "HYPR_KEYBINDS_CLOSE_FAILED"
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleBinds(): string {
|
||||||
|
if (!CompositorService.isHyprland) {
|
||||||
|
return "HYPR_NOT_AVAILABLE"
|
||||||
|
}
|
||||||
|
root.hyprKeybindsModalLoader.active = true
|
||||||
|
if (root.hyprKeybindsModalLoader.item) {
|
||||||
|
if (root.hyprKeybindsModalLoader.item.shouldBeVisible) {
|
||||||
|
root.hyprKeybindsModalLoader.item.close()
|
||||||
|
} else {
|
||||||
|
root.hyprKeybindsModalLoader.item.open()
|
||||||
|
}
|
||||||
|
return "HYPR_KEYBINDS_TOGGLE_SUCCESS"
|
||||||
|
}
|
||||||
|
return "HYPR_KEYBINDS_TOGGLE_FAILED"
|
||||||
|
}
|
||||||
|
|
||||||
|
target: "hypr"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,7 +30,7 @@ Item {
|
|||||||
showKeyboardHints: modal.showKeyboardHints
|
showKeyboardHints: modal.showKeyboardHints
|
||||||
onKeyboardHintsToggled: modal.showKeyboardHints = !modal.showKeyboardHints
|
onKeyboardHintsToggled: modal.showKeyboardHints = !modal.showKeyboardHints
|
||||||
onClearAllClicked: {
|
onClearAllClicked: {
|
||||||
clearConfirmDialog.show("Clear All History?", "This will permanently delete all clipboard history.", function () {
|
clearConfirmDialog.show(I18n.tr("Clear All History?"), I18n.tr("This will permanently delete all clipboard history."), function () {
|
||||||
modal.clearAll()
|
modal.clearAll()
|
||||||
modal.hide()
|
modal.hide()
|
||||||
}, function () {})
|
}, function () {})
|
||||||
@@ -46,7 +46,7 @@ Item {
|
|||||||
leftIconName: "search"
|
leftIconName: "search"
|
||||||
showClearButton: true
|
showClearButton: true
|
||||||
focus: true
|
focus: true
|
||||||
ignoreLeftRightKeys: true
|
ignoreTabKeys: true
|
||||||
keyForwardTargets: [modal.modalFocusScope]
|
keyForwardTargets: [modal.modalFocusScope]
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
modal.searchText = text
|
modal.searchText = text
|
||||||
@@ -77,15 +77,12 @@ Item {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height - ClipboardConstants.headerHeight - 70
|
height: parent.height - ClipboardConstants.headerHeight - 70
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Theme.surfaceLight
|
color: "transparent"
|
||||||
border.color: Theme.outlineLight
|
|
||||||
border.width: 1
|
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
DankListView {
|
DankListView {
|
||||||
id: clipboardListView
|
id: clipboardListView
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingS
|
|
||||||
model: filteredModel
|
model: filteredModel
|
||||||
|
|
||||||
currentIndex: clipboardContent.modal ? clipboardContent.modal.selectedIndex : 0
|
currentIndex: clipboardContent.modal ? clipboardContent.modal.selectedIndex : 0
|
||||||
@@ -119,7 +116,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "No clipboard entries found"
|
text: I18n.tr("No clipboard entries found")
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
|
|||||||
@@ -24,17 +24,10 @@ Rectangle {
|
|||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: {
|
color: {
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.2)
|
return Theme.primaryPressed
|
||||||
}
|
}
|
||||||
return mouseArea.containsMouse ? Theme.primaryHover : Theme.primaryBackground
|
return mouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceContainerHigh
|
||||||
}
|
}
|
||||||
border.color: {
|
|
||||||
if (isSelected) {
|
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.5)
|
|
||||||
}
|
|
||||||
return Theme.outlineStrong
|
|
||||||
}
|
|
||||||
border.width: isSelected ? 1.5 : 1
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -87,11 +80,11 @@ Rectangle {
|
|||||||
text: {
|
text: {
|
||||||
switch (entryType) {
|
switch (entryType) {
|
||||||
case "image":
|
case "image":
|
||||||
return "Image • " + entryPreview
|
return I18n.tr("Image") + " • " + entryPreview
|
||||||
case "long_text":
|
case "long_text":
|
||||||
return "Long Text"
|
return I18n.tr("Long Text")
|
||||||
default:
|
default:
|
||||||
return "Text"
|
return I18n.tr("Text")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: `Clipboard History (${totalCount})`
|
text: I18n.tr("Clipboard History") + ` (${totalCount})`
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ DankModal {
|
|||||||
open()
|
open()
|
||||||
clipboardHistoryModal.searchText = ""
|
clipboardHistoryModal.searchText = ""
|
||||||
clipboardHistoryModal.activeImageLoads = 0
|
clipboardHistoryModal.activeImageLoads = 0
|
||||||
|
clipboardHistoryModal.shouldHaveFocus = true
|
||||||
refreshClipboard()
|
refreshClipboard()
|
||||||
keyboardController.reset()
|
keyboardController.reset()
|
||||||
|
|
||||||
@@ -91,7 +92,7 @@ DankModal {
|
|||||||
function copyEntry(entry) {
|
function copyEntry(entry) {
|
||||||
const entryId = entry.split('\t')[0]
|
const entryId = entry.split('\t')[0]
|
||||||
Quickshell.execDetached(["sh", "-c", `cliphist decode ${entryId} | wl-copy`])
|
Quickshell.execDetached(["sh", "-c", `cliphist decode ${entryId} | wl-copy`])
|
||||||
ToastService.showInfo("Copied to clipboard")
|
ToastService.showInfo(I18n.tr("Copied to clipboard"))
|
||||||
hide()
|
hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +154,7 @@ DankModal {
|
|||||||
|
|
||||||
ConfirmModal {
|
ConfirmModal {
|
||||||
id: clearConfirmDialog
|
id: clearConfirmDialog
|
||||||
confirmButtonText: "Clear All"
|
confirmButtonText: I18n.tr("Clear All")
|
||||||
confirmButtonColor: Theme.primary
|
confirmButtonColor: Theme.primary
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ QtObject {
|
|||||||
modal.hide()
|
modal.hide()
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
}
|
}
|
||||||
} else if (event.key === Qt.Key_Down) {
|
} else if (event.key === Qt.Key_Down || event.key === Qt.Key_Tab) {
|
||||||
if (!modal.keyboardNavigationActive) {
|
if (!modal.keyboardNavigationActive) {
|
||||||
modal.keyboardNavigationActive = true
|
modal.keyboardNavigationActive = true
|
||||||
modal.selectedIndex = 0
|
modal.selectedIndex = 0
|
||||||
@@ -62,7 +62,7 @@ QtObject {
|
|||||||
selectNext()
|
selectNext()
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
}
|
}
|
||||||
} else if (event.key === Qt.Key_Up) {
|
} else if (event.key === Qt.Key_Up || event.key === Qt.Key_Backtab) {
|
||||||
if (!modal.keyboardNavigationActive) {
|
if (!modal.keyboardNavigationActive) {
|
||||||
modal.keyboardNavigationActive = true
|
modal.keyboardNavigationActive = true
|
||||||
modal.selectedIndex = 0
|
modal.selectedIndex = 0
|
||||||
@@ -74,6 +74,42 @@ QtObject {
|
|||||||
selectPrevious()
|
selectPrevious()
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
}
|
}
|
||||||
|
} else if (event.key === Qt.Key_N && event.modifiers & Qt.ControlModifier) {
|
||||||
|
if (!modal.keyboardNavigationActive) {
|
||||||
|
modal.keyboardNavigationActive = true
|
||||||
|
modal.selectedIndex = 0
|
||||||
|
} else {
|
||||||
|
selectNext()
|
||||||
|
}
|
||||||
|
event.accepted = true
|
||||||
|
} else if (event.key === Qt.Key_P && event.modifiers & Qt.ControlModifier) {
|
||||||
|
if (!modal.keyboardNavigationActive) {
|
||||||
|
modal.keyboardNavigationActive = true
|
||||||
|
modal.selectedIndex = 0
|
||||||
|
} else if (modal.selectedIndex === 0) {
|
||||||
|
modal.keyboardNavigationActive = false
|
||||||
|
} else {
|
||||||
|
selectPrevious()
|
||||||
|
}
|
||||||
|
event.accepted = true
|
||||||
|
} else if (event.key === Qt.Key_J && event.modifiers & Qt.ControlModifier) {
|
||||||
|
if (!modal.keyboardNavigationActive) {
|
||||||
|
modal.keyboardNavigationActive = true
|
||||||
|
modal.selectedIndex = 0
|
||||||
|
} else {
|
||||||
|
selectNext()
|
||||||
|
}
|
||||||
|
event.accepted = true
|
||||||
|
} else if (event.key === Qt.Key_K && event.modifiers & Qt.ControlModifier) {
|
||||||
|
if (!modal.keyboardNavigationActive) {
|
||||||
|
modal.keyboardNavigationActive = true
|
||||||
|
modal.selectedIndex = 0
|
||||||
|
} else if (modal.selectedIndex === 0) {
|
||||||
|
modal.keyboardNavigationActive = false
|
||||||
|
} else {
|
||||||
|
selectPrevious()
|
||||||
|
}
|
||||||
|
event.accepted = true
|
||||||
} else if (event.key === Qt.Key_Delete && (event.modifiers & Qt.ShiftModifier)) {
|
} else if (event.key === Qt.Key_Delete && (event.modifiers & Qt.ShiftModifier)) {
|
||||||
modal.clearAll()
|
modal.clearAll()
|
||||||
modal.hide()
|
modal.hide()
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import qs.Modals.Clipboard
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
id: keyboardHints
|
id: keyboardHints
|
||||||
|
|
||||||
|
readonly property string hintsText: I18n.tr("Shift+Del: Clear All • Esc: Close")
|
||||||
|
|
||||||
height: ClipboardConstants.keyboardHintsHeight
|
height: ClipboardConstants.keyboardHintsHeight
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95)
|
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95)
|
||||||
@@ -26,7 +28,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Shift+Del: Clear All • Esc: Close"
|
text: keyboardHints.hintsText
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import Qt.labs.platform
|
|
||||||
import Quickshell
|
|
||||||
import qs.Common
|
|
||||||
import qs.Services
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: colorPickerModal
|
|
||||||
|
|
||||||
signal colorSelected(color selectedColor)
|
|
||||||
|
|
||||||
function show() {
|
|
||||||
colorDialog.open()
|
|
||||||
}
|
|
||||||
|
|
||||||
function hide() {
|
|
||||||
colorDialog.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
function copyColorToClipboard(colorValue) {
|
|
||||||
Quickshell.execDetached(["sh", "-c", `echo "${colorValue}" | wl-copy`])
|
|
||||||
ToastService.showInfo(`Color ${colorValue} copied to clipboard`)
|
|
||||||
console.log("Copied color to clipboard:", colorValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
ColorDialog {
|
|
||||||
id: colorDialog
|
|
||||||
title: "Color Picker - Select and copy color"
|
|
||||||
color: Theme.primary
|
|
||||||
|
|
||||||
onAccepted: {
|
|
||||||
const colorString = color.toString()
|
|
||||||
copyColorToClipboard(colorString)
|
|
||||||
colorSelected(color)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -68,9 +68,11 @@ DankModal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
onOpened: {
|
onOpened: {
|
||||||
modalFocusScope.forceActiveFocus()
|
Qt.callLater(function () {
|
||||||
modalFocusScope.focus = true
|
modalFocusScope.forceActiveFocus()
|
||||||
shouldHaveFocus = true
|
modalFocusScope.focus = true
|
||||||
|
shouldHaveFocus = true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
modalFocusScope.Keys.onPressed: function (event) {
|
modalFocusScope.Keys.onPressed: function (event) {
|
||||||
switch (event.key) {
|
switch (event.key) {
|
||||||
@@ -93,6 +95,48 @@ DankModal {
|
|||||||
selectedButton = 1
|
selectedButton = 1
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
break
|
break
|
||||||
|
case Qt.Key_N:
|
||||||
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
|
keyboardNavigation = true
|
||||||
|
selectedButton = (selectedButton + 1) % 2
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case Qt.Key_P:
|
||||||
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
|
keyboardNavigation = true
|
||||||
|
selectedButton = selectedButton === -1 ? 1 : (selectedButton - 1 + 2) % 2
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case Qt.Key_J:
|
||||||
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
|
keyboardNavigation = true
|
||||||
|
selectedButton = 1
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case Qt.Key_K:
|
||||||
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
|
keyboardNavigation = true
|
||||||
|
selectedButton = 0
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case Qt.Key_H:
|
||||||
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
|
keyboardNavigation = true
|
||||||
|
selectedButton = 0
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case Qt.Key_L:
|
||||||
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
|
keyboardNavigation = true
|
||||||
|
selectedButton = 1
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
break
|
||||||
case Qt.Key_Tab:
|
case Qt.Key_Tab:
|
||||||
keyboardNavigation = true
|
keyboardNavigation = true
|
||||||
selectedButton = selectedButton === -1 ? 0 : (selectedButton + 1) % 2
|
selectedButton = selectedButton === -1 ? 0 : (selectedButton + 1) % 2
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Effects
|
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
import Quickshell.Hyprland
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: root
|
id: root
|
||||||
@@ -11,10 +12,22 @@ PanelWindow {
|
|||||||
|
|
||||||
property alias content: contentLoader.sourceComponent
|
property alias content: contentLoader.sourceComponent
|
||||||
property alias contentLoader: contentLoader
|
property alias contentLoader: contentLoader
|
||||||
|
property Item directContent: null
|
||||||
property real width: 400
|
property real width: 400
|
||||||
property real height: 300
|
property real height: 300
|
||||||
readonly property real screenWidth: screen ? screen.width : 1920
|
readonly property real screenWidth: screen ? screen.width : 1920
|
||||||
readonly property real screenHeight: screen ? screen.height : 1080
|
readonly property real screenHeight: screen ? screen.height : 1080
|
||||||
|
readonly property real dpr: {
|
||||||
|
if (CompositorService.isNiri && screen) {
|
||||||
|
const niriScale = NiriService.displayScales[screen.name]
|
||||||
|
if (niriScale !== undefined) return niriScale
|
||||||
|
}
|
||||||
|
if (CompositorService.isHyprland && screen) {
|
||||||
|
const hyprlandMonitor = Hyprland.monitors.values.find(m => m.name === screen.name)
|
||||||
|
if (hyprlandMonitor?.scale !== undefined) return hyprlandMonitor.scale
|
||||||
|
}
|
||||||
|
return (screen?.devicePixelRatio) || 1
|
||||||
|
}
|
||||||
property bool showBackground: true
|
property bool showBackground: true
|
||||||
property real backgroundOpacity: 0.5
|
property real backgroundOpacity: 0.5
|
||||||
property string positioning: "center"
|
property string positioning: "center"
|
||||||
@@ -22,7 +35,7 @@ PanelWindow {
|
|||||||
property bool closeOnEscapeKey: true
|
property bool closeOnEscapeKey: true
|
||||||
property bool closeOnBackgroundClick: true
|
property bool closeOnBackgroundClick: true
|
||||||
property string animationType: "scale"
|
property string animationType: "scale"
|
||||||
property int animationDuration: Theme.shorterDuration
|
property int animationDuration: Theme.shortDuration
|
||||||
property var animationEasing: Theme.emphasizedEasing
|
property var animationEasing: Theme.emphasizedEasing
|
||||||
property color backgroundColor: Theme.surfaceContainer
|
property color backgroundColor: Theme.surfaceContainer
|
||||||
property color borderColor: Theme.outlineMedium
|
property color borderColor: Theme.outlineMedium
|
||||||
@@ -34,6 +47,7 @@ PanelWindow {
|
|||||||
property bool shouldHaveFocus: shouldBeVisible
|
property bool shouldHaveFocus: shouldBeVisible
|
||||||
property bool allowFocusOverride: false
|
property bool allowFocusOverride: false
|
||||||
property bool allowStacking: false
|
property bool allowStacking: false
|
||||||
|
property bool keepContentLoaded: false
|
||||||
|
|
||||||
signal opened
|
signal opened
|
||||||
signal dialogClosed
|
signal dialogClosed
|
||||||
@@ -90,7 +104,7 @@ PanelWindow {
|
|||||||
Timer {
|
Timer {
|
||||||
id: closeTimer
|
id: closeTimer
|
||||||
|
|
||||||
interval: animationDuration + 50
|
interval: animationDuration + 100
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
visible = false
|
visible = false
|
||||||
}
|
}
|
||||||
@@ -133,22 +147,26 @@ PanelWindow {
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
id: contentContainer
|
id: contentContainer
|
||||||
|
|
||||||
width: root.width
|
width: Theme.px(root.width, dpr)
|
||||||
height: root.height
|
height: Theme.px(root.height, dpr)
|
||||||
anchors.centerIn: positioning === "center" ? parent : undefined
|
anchors.centerIn: undefined
|
||||||
x: {
|
x: {
|
||||||
if (positioning === "top-right") {
|
if (positioning === "center") {
|
||||||
return Math.max(Theme.spacingL, root.screenWidth - width - Theme.spacingL)
|
return Theme.snap((root.screenWidth - width) / 2, dpr)
|
||||||
|
} else if (positioning === "top-right") {
|
||||||
|
return Theme.px(Math.max(Theme.spacingL, root.screenWidth - width - Theme.spacingL), dpr)
|
||||||
} else if (positioning === "custom") {
|
} else if (positioning === "custom") {
|
||||||
return root.customPosition.x
|
return Theme.snap(root.customPosition.x, dpr)
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
y: {
|
y: {
|
||||||
if (positioning === "top-right") {
|
if (positioning === "center") {
|
||||||
return Theme.barHeight + Theme.spacingXS
|
return Theme.snap((root.screenHeight - height) / 2, dpr)
|
||||||
|
} else if (positioning === "top-right") {
|
||||||
|
return Theme.px(Theme.barHeight + Theme.spacingXS, dpr)
|
||||||
} else if (positioning === "custom") {
|
} else if (positioning === "custom") {
|
||||||
return root.customPosition.y
|
return Theme.snap(root.customPosition.y, dpr)
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -156,49 +174,78 @@ PanelWindow {
|
|||||||
radius: root.cornerRadius
|
radius: root.cornerRadius
|
||||||
border.color: root.borderColor
|
border.color: root.borderColor
|
||||||
border.width: root.borderWidth
|
border.width: root.borderWidth
|
||||||
layer.enabled: root.enableShadow
|
clip: false
|
||||||
|
layer.enabled: true
|
||||||
opacity: root.shouldBeVisible ? 1 : 0
|
opacity: root.shouldBeVisible ? 1 : 0
|
||||||
scale: root.animationType === "scale" ? (root.shouldBeVisible ? 1 : 0.9) : 1
|
|
||||||
transform: root.animationType === "slide" ? slideTransform : null
|
transform: root.animationType === "slide" ? slideTransform : null
|
||||||
|
|
||||||
Translate {
|
Translate {
|
||||||
id: slideTransform
|
id: slideTransform
|
||||||
|
|
||||||
x: root.shouldBeVisible ? 0 : 15
|
readonly property real rawX: root.shouldBeVisible ? 0 : 15
|
||||||
y: root.shouldBeVisible ? 0 : -30
|
readonly property real rawY: root.shouldBeVisible ? 0 : -30
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
x: Theme.snap(rawX, root.dpr)
|
||||||
id: contentLoader
|
y: Theme.snap(rawY, root.dpr)
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
active: root.visible
|
|
||||||
asynchronous: false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: root.animationDuration
|
duration: animationDuration
|
||||||
easing.type: root.animationEasing
|
easing.type: animationEasing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on scale {
|
FocusScope {
|
||||||
enabled: root.animationType === "scale"
|
anchors.fill: parent
|
||||||
|
focus: root.shouldBeVisible
|
||||||
|
clip: false
|
||||||
|
|
||||||
NumberAnimation {
|
Item {
|
||||||
duration: root.animationDuration
|
id: directContentWrapper
|
||||||
easing.type: root.animationEasing
|
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: root.directContent !== null
|
||||||
|
focus: true
|
||||||
|
clip: false
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (root.directContent) {
|
||||||
|
root.directContent.parent = directContentWrapper
|
||||||
|
root.directContent.anchors.fill = directContentWrapper
|
||||||
|
Qt.callLater(() => root.directContent.forceActiveFocus())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
function onDirectContentChanged() {
|
||||||
|
if (root.directContent) {
|
||||||
|
root.directContent.parent = directContentWrapper
|
||||||
|
root.directContent.anchors.fill = directContentWrapper
|
||||||
|
Qt.callLater(() => root.directContent.forceActiveFocus())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
target: root
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
layer.effect: MultiEffect {
|
Loader {
|
||||||
shadowEnabled: true
|
id: contentLoader
|
||||||
shadowHorizontalOffset: 0
|
|
||||||
shadowVerticalOffset: 8
|
anchors.fill: parent
|
||||||
shadowBlur: 1
|
active: root.directContent === null && (root.keepContentLoaded || root.shouldBeVisible || root.visible)
|
||||||
shadowColor: Theme.shadowStrong
|
asynchronous: false
|
||||||
shadowOpacity: 0.3
|
focus: true
|
||||||
|
clip: false
|
||||||
|
visible: root.directContent === null
|
||||||
|
|
||||||
|
onLoaded: {
|
||||||
|
if (item) {
|
||||||
|
Qt.callLater(() => item.forceActiveFocus())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,8 +254,8 @@ PanelWindow {
|
|||||||
|
|
||||||
objectName: "modalFocusScope"
|
objectName: "modalFocusScope"
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: root.visible // Only active when the modal is visible
|
visible: root.shouldBeVisible || root.visible
|
||||||
focus: root.visible
|
focus: root.shouldBeVisible
|
||||||
Keys.onEscapePressed: event => {
|
Keys.onEscapePressed: event => {
|
||||||
if (root.closeOnEscapeKey && shouldHaveFocus) {
|
if (root.closeOnEscapeKey && shouldHaveFocus) {
|
||||||
root.close()
|
root.close()
|
||||||
@@ -223,7 +270,7 @@ PanelWindow {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onShouldHaveFocusChanged() {
|
function onShouldHaveFocusChanged() {
|
||||||
if (shouldHaveFocus && visible) {
|
if (shouldHaveFocus && shouldBeVisible) {
|
||||||
Qt.callLater(() => focusScope.forceActiveFocus())
|
Qt.callLater(() => focusScope.forceActiveFocus())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
542
Modals/DankColorPickerModal.qml
Normal file
542
Modals/DankColorPickerModal.qml
Normal file
@@ -0,0 +1,542 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import qs.Common
|
||||||
|
import qs.Modals.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
DankModal {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string pickerTitle: "Choose Color"
|
||||||
|
property color selectedColor: SessionData.recentColors.length > 0 ? SessionData.recentColors[0] : Theme.primary
|
||||||
|
property var onColorSelectedCallback: null
|
||||||
|
|
||||||
|
signal colorSelected(color selectedColor)
|
||||||
|
|
||||||
|
property color currentColor: Theme.primary
|
||||||
|
property real hue: 0
|
||||||
|
property real saturation: 1
|
||||||
|
property real value: 1
|
||||||
|
property real alpha: 1
|
||||||
|
property real gradientX: 0
|
||||||
|
property real gradientY: 0
|
||||||
|
|
||||||
|
readonly property var standardColors: [
|
||||||
|
"#f44336", "#e91e63", "#9c27b0", "#673ab7", "#3f51b5", "#2196f3", "#03a9f4", "#00bcd4",
|
||||||
|
"#009688", "#4caf50", "#8bc34a", "#cddc39", "#ffeb3b", "#ffc107", "#ff9800", "#ff5722",
|
||||||
|
"#d32f2f", "#c2185b", "#7b1fa2", "#512da8", "#303f9f", "#1976d2", "#0288d1", "#0097a7",
|
||||||
|
"#00796b", "#388e3c", "#689f38", "#afb42b", "#fbc02d", "#ffa000", "#f57c00", "#e64a19",
|
||||||
|
"#c62828", "#ad1457", "#6a1b9a", "#4527a0", "#283593", "#1565c0", "#0277bd", "#00838f",
|
||||||
|
"#00695c", "#2e7d32", "#558b2f", "#9e9d24", "#f9a825", "#ff8f00", "#ef6c00", "#d84315",
|
||||||
|
"#ffffff", "#9e9e9e", "#212121"
|
||||||
|
]
|
||||||
|
|
||||||
|
function show() {
|
||||||
|
currentColor = selectedColor
|
||||||
|
updateFromColor(currentColor)
|
||||||
|
open()
|
||||||
|
}
|
||||||
|
|
||||||
|
function hide() {
|
||||||
|
onColorSelectedCallback = null
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
|
||||||
|
onColorSelected: (color) => {
|
||||||
|
if (onColorSelectedCallback) {
|
||||||
|
onColorSelectedCallback(color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyColorToClipboard(colorValue) {
|
||||||
|
Quickshell.execDetached(["sh", "-c", `echo "${colorValue}" | wl-copy`])
|
||||||
|
ToastService.showInfo(`Color ${colorValue} copied`)
|
||||||
|
SessionData.addRecentColor(currentColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateFromColor(color) {
|
||||||
|
hue = color.hsvHue
|
||||||
|
saturation = color.hsvSaturation
|
||||||
|
value = color.hsvValue
|
||||||
|
alpha = color.a
|
||||||
|
gradientX = saturation
|
||||||
|
gradientY = 1 - value
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateColor() {
|
||||||
|
currentColor = Qt.hsva(hue, saturation, value, alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateColorFromGradient(x, y) {
|
||||||
|
saturation = Math.max(0, Math.min(1, x))
|
||||||
|
value = Math.max(0, Math.min(1, 1 - y))
|
||||||
|
updateColor()
|
||||||
|
selectedColor = currentColor
|
||||||
|
}
|
||||||
|
|
||||||
|
function pickColorFromScreen() {
|
||||||
|
hide()
|
||||||
|
Proc.runCommand("hyprpicker", ["hyprpicker", "--format=hex"], (output, errorCode) => {
|
||||||
|
if (errorCode !== 0) {
|
||||||
|
console.warn("hyprpicker exited with code:", errorCode)
|
||||||
|
root.show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const colorStr = output.trim()
|
||||||
|
if (colorStr.length >= 7 && colorStr.startsWith('#')) {
|
||||||
|
const pickedColor = Qt.color(colorStr)
|
||||||
|
root.selectedColor = pickedColor
|
||||||
|
root.currentColor = pickedColor
|
||||||
|
root.updateFromColor(pickedColor)
|
||||||
|
copyColorToClipboard(colorStr)
|
||||||
|
root.show()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
width: 680
|
||||||
|
height: 680
|
||||||
|
backgroundColor: Theme.surfaceContainer
|
||||||
|
cornerRadius: Theme.cornerRadius
|
||||||
|
borderColor: Theme.outlineMedium
|
||||||
|
borderWidth: 1
|
||||||
|
keepContentLoaded: true
|
||||||
|
|
||||||
|
onBackgroundClicked: hide()
|
||||||
|
|
||||||
|
content: Component {
|
||||||
|
FocusScope {
|
||||||
|
id: colorContent
|
||||||
|
|
||||||
|
property alias hexInput: hexInput
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
focus: true
|
||||||
|
|
||||||
|
Keys.onEscapePressed: event => {
|
||||||
|
root.hide()
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width - 90
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: root.pickerTitle
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Select a color from the palette or use custom sliders")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceTextMedium
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
iconName: "colorize"
|
||||||
|
iconSize: Theme.iconSize - 4
|
||||||
|
iconColor: Theme.surfaceText
|
||||||
|
onClicked: () => {
|
||||||
|
root.pickColorFromScreen()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
iconName: "close"
|
||||||
|
iconSize: Theme.iconSize - 4
|
||||||
|
iconColor: Theme.surfaceText
|
||||||
|
onClicked: () => {
|
||||||
|
root.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: gradientPicker
|
||||||
|
width: parent.width - 70
|
||||||
|
height: 280
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
border.color: Theme.outlineStrong
|
||||||
|
border.width: 1
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: Qt.hsva(root.hue, 1, 1, 1)
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
gradient: Gradient {
|
||||||
|
orientation: Gradient.Horizontal
|
||||||
|
GradientStop { position: 0.0; color: "#ffffff" }
|
||||||
|
GradientStop { position: 1.0; color: "transparent" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
gradient: Gradient {
|
||||||
|
orientation: Gradient.Vertical
|
||||||
|
GradientStop { position: 0.0; color: "transparent" }
|
||||||
|
GradientStop { position: 1.0; color: "#000000" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: pickerCircle
|
||||||
|
width: 16
|
||||||
|
height: 16
|
||||||
|
radius: 8
|
||||||
|
border.color: "white"
|
||||||
|
border.width: 2
|
||||||
|
color: "transparent"
|
||||||
|
x: root.gradientX * parent.width - width / 2
|
||||||
|
y: root.gradientY * parent.height - height / 2
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width - 4
|
||||||
|
height: parent.height - 4
|
||||||
|
radius: width / 2
|
||||||
|
border.color: "black"
|
||||||
|
border.width: 1
|
||||||
|
color: "transparent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.CrossCursor
|
||||||
|
onPressed: mouse => {
|
||||||
|
const x = Math.max(0, Math.min(1, mouse.x / width))
|
||||||
|
const y = Math.max(0, Math.min(1, mouse.y / height))
|
||||||
|
root.gradientX = x
|
||||||
|
root.gradientY = y
|
||||||
|
root.updateColorFromGradient(x, y)
|
||||||
|
}
|
||||||
|
onPositionChanged: mouse => {
|
||||||
|
if (pressed) {
|
||||||
|
const x = Math.max(0, Math.min(1, mouse.x / width))
|
||||||
|
const y = Math.max(0, Math.min(1, mouse.y / height))
|
||||||
|
root.gradientX = x
|
||||||
|
root.gradientY = y
|
||||||
|
root.updateColorFromGradient(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: hueSlider
|
||||||
|
width: 50
|
||||||
|
height: 280
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
border.color: Theme.outlineStrong
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
gradient: Gradient {
|
||||||
|
orientation: Gradient.Vertical
|
||||||
|
GradientStop { position: 0.00; color: "#ff0000" }
|
||||||
|
GradientStop { position: 0.17; color: "#ffff00" }
|
||||||
|
GradientStop { position: 0.33; color: "#00ff00" }
|
||||||
|
GradientStop { position: 0.50; color: "#00ffff" }
|
||||||
|
GradientStop { position: 0.67; color: "#0000ff" }
|
||||||
|
GradientStop { position: 0.83; color: "#ff00ff" }
|
||||||
|
GradientStop { position: 1.00; color: "#ff0000" }
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: hueIndicator
|
||||||
|
width: parent.width
|
||||||
|
height: 4
|
||||||
|
color: "white"
|
||||||
|
border.color: "black"
|
||||||
|
border.width: 1
|
||||||
|
y: root.hue * parent.height - height / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.SizeVerCursor
|
||||||
|
onPressed: mouse => {
|
||||||
|
const h = Math.max(0, Math.min(1, mouse.y / height))
|
||||||
|
root.hue = h
|
||||||
|
root.updateColor()
|
||||||
|
root.selectedColor = root.currentColor
|
||||||
|
}
|
||||||
|
onPositionChanged: mouse => {
|
||||||
|
if (pressed) {
|
||||||
|
const h = Math.max(0, Math.min(1, mouse.y / height))
|
||||||
|
root.hue = h
|
||||||
|
root.updateColor()
|
||||||
|
root.selectedColor = root.currentColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Material Colors")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
GridView {
|
||||||
|
width: parent.width
|
||||||
|
height: 140
|
||||||
|
cellWidth: 38
|
||||||
|
cellHeight: 38
|
||||||
|
clip: true
|
||||||
|
interactive: false
|
||||||
|
model: root.standardColors
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
width: 36
|
||||||
|
height: 36
|
||||||
|
color: modelData
|
||||||
|
radius: 4
|
||||||
|
border.color: Theme.outlineStrong
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: () => {
|
||||||
|
const pickedColor = Qt.color(modelData)
|
||||||
|
root.selectedColor = pickedColor
|
||||||
|
root.currentColor = pickedColor
|
||||||
|
root.updateFromColor(pickedColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: 210
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Recent Colors")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: 5
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 36
|
||||||
|
height: 36
|
||||||
|
radius: 4
|
||||||
|
border.color: Theme.outlineStrong
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
color: {
|
||||||
|
if (index < SessionData.recentColors.length) {
|
||||||
|
return SessionData.recentColors[index]
|
||||||
|
}
|
||||||
|
return Theme.surfaceContainerHigh
|
||||||
|
}
|
||||||
|
|
||||||
|
opacity: index < SessionData.recentColors.length ? 1.0 : 0.3
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: index < SessionData.recentColors.length ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
|
enabled: index < SessionData.recentColors.length
|
||||||
|
onClicked: () => {
|
||||||
|
if (index < SessionData.recentColors.length) {
|
||||||
|
const pickedColor = SessionData.recentColors[index]
|
||||||
|
root.selectedColor = pickedColor
|
||||||
|
root.currentColor = pickedColor
|
||||||
|
root.updateFromColor(pickedColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width - 330
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Opacity")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
DankSlider {
|
||||||
|
width: parent.width
|
||||||
|
value: Math.round(root.alpha * 100)
|
||||||
|
minimum: 0
|
||||||
|
maximum: 100
|
||||||
|
showValue: false
|
||||||
|
onSliderValueChanged: (newValue) => {
|
||||||
|
root.alpha = newValue / 100
|
||||||
|
root.updateColor()
|
||||||
|
root.selectedColor = root.currentColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 100
|
||||||
|
height: 50
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: root.currentColor
|
||||||
|
border.color: Theme.outlineStrong
|
||||||
|
border.width: 2
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Hex:")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceTextMedium
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
DankTextField {
|
||||||
|
id: hexInput
|
||||||
|
width: 120
|
||||||
|
height: 38
|
||||||
|
text: root.currentColor.toString()
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
textColor: {
|
||||||
|
if (text.length === 0) return Theme.surfaceText
|
||||||
|
const hexPattern = /^#?[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$/
|
||||||
|
return hexPattern.test(text) ? Theme.surfaceText : Theme.error
|
||||||
|
}
|
||||||
|
placeholderText: "#000000"
|
||||||
|
backgroundColor: Theme.surfaceHover
|
||||||
|
borderWidth: 1
|
||||||
|
focusedBorderWidth: 2
|
||||||
|
topPadding: Theme.spacingS
|
||||||
|
bottomPadding: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
onAccepted: () => {
|
||||||
|
const hexPattern = /^#?[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$/
|
||||||
|
if (!hexPattern.test(text)) return
|
||||||
|
const color = Qt.color(text)
|
||||||
|
if (color) {
|
||||||
|
root.selectedColor = color
|
||||||
|
root.currentColor = color
|
||||||
|
root.updateFromColor(color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankButton {
|
||||||
|
width: 80
|
||||||
|
buttonHeight: 36
|
||||||
|
text: I18n.tr("Apply")
|
||||||
|
backgroundColor: Theme.primary
|
||||||
|
textColor: Theme.background
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
onClicked: {
|
||||||
|
const hexPattern = /^#?[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$/
|
||||||
|
if (!hexPattern.test(hexInput.text)) return
|
||||||
|
const color = Qt.color(hexInput.text)
|
||||||
|
if (color) {
|
||||||
|
root.currentColor = color
|
||||||
|
root.updateFromColor(color)
|
||||||
|
root.selectedColor = root.currentColor
|
||||||
|
root.colorSelected(root.currentColor)
|
||||||
|
SessionData.addRecentColor(root.currentColor)
|
||||||
|
root.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width - 460
|
||||||
|
height: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
DankButton {
|
||||||
|
width: 70
|
||||||
|
buttonHeight: 36
|
||||||
|
text: I18n.tr("Cancel")
|
||||||
|
backgroundColor: "transparent"
|
||||||
|
textColor: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
onClicked: root.hide()
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: "transparent"
|
||||||
|
border.color: Theme.surfaceVariantAlpha
|
||||||
|
border.width: 1
|
||||||
|
z: -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankButton {
|
||||||
|
width: 70
|
||||||
|
buttonHeight: 36
|
||||||
|
text: I18n.tr("Copy")
|
||||||
|
backgroundColor: Theme.primary
|
||||||
|
textColor: Theme.background
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
onClicked: {
|
||||||
|
const colorString = root.currentColor.toString()
|
||||||
|
root.copyColorToClipboard(colorString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,6 +35,7 @@ DankModal {
|
|||||||
property bool weAvailable: false
|
property bool weAvailable: false
|
||||||
property string wePath: ""
|
property string wePath: ""
|
||||||
property bool weMode: false
|
property bool weMode: false
|
||||||
|
property var parentModal: null
|
||||||
|
|
||||||
signal fileSelected(string path)
|
signal fileSelected(string path)
|
||||||
|
|
||||||
@@ -47,15 +48,17 @@ DankModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getLastPath() {
|
function getLastPath() {
|
||||||
const lastPath = browserType === "wallpaper" ? SessionData.wallpaperLastPath : browserType === "profile" ? SessionData.profileLastPath : ""
|
const lastPath = browserType === "wallpaper" ? CacheData.wallpaperLastPath : browserType === "profile" ? CacheData.profileLastPath : ""
|
||||||
return (lastPath && lastPath !== "") ? lastPath : homeDir
|
return (lastPath && lastPath !== "") ? lastPath : homeDir
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveLastPath(path) {
|
function saveLastPath(path) {
|
||||||
if (browserType === "wallpaper") {
|
if (browserType === "wallpaper") {
|
||||||
SessionData.setWallpaperLastPath(path)
|
CacheData.wallpaperLastPath = path
|
||||||
|
CacheData.saveCache()
|
||||||
} else if (browserType === "profile") {
|
} else if (browserType === "profile") {
|
||||||
SessionData.setProfileLastPath(path)
|
CacheData.profileLastPath = path
|
||||||
|
CacheData.saveCache()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,6 +134,8 @@ DankModal {
|
|||||||
|
|
||||||
objectName: "fileBrowserModal"
|
objectName: "fileBrowserModal"
|
||||||
allowStacking: true
|
allowStacking: true
|
||||||
|
closeOnEscapeKey: false
|
||||||
|
shouldHaveFocus: shouldBeVisible
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
currentPath = getLastPath()
|
currentPath = getLastPath()
|
||||||
}
|
}
|
||||||
@@ -165,10 +170,23 @@ DankModal {
|
|||||||
visible: false
|
visible: false
|
||||||
onBackgroundClicked: close()
|
onBackgroundClicked: close()
|
||||||
onOpened: {
|
onOpened: {
|
||||||
modalFocusScope.forceActiveFocus()
|
if (parentModal) {
|
||||||
|
parentModal.shouldHaveFocus = false
|
||||||
|
parentModal.allowFocusOverride = true
|
||||||
|
}
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (contentLoader && contentLoader.item) {
|
||||||
|
contentLoader.item.forceActiveFocus()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
modalFocusScope.Keys.onPressed: function (event) {
|
onDialogClosed: {
|
||||||
keyboardController.handleKey(event)
|
if (parentModal) {
|
||||||
|
parentModal.allowFocusOverride = false
|
||||||
|
parentModal.shouldHaveFocus = Qt.binding(() => {
|
||||||
|
return parentModal.shouldBeVisible
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
@@ -239,7 +257,12 @@ DankModal {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!keyboardNavigationActive) {
|
if (!keyboardNavigationActive) {
|
||||||
if (event.key === Qt.Key_Tab || event.key === Qt.Key_Down || event.key === Qt.Key_Right) {
|
const isInitKey = event.key === Qt.Key_Tab || event.key === Qt.Key_Down || event.key === Qt.Key_Right ||
|
||||||
|
(event.key === Qt.Key_N && event.modifiers & Qt.ControlModifier) ||
|
||||||
|
(event.key === Qt.Key_J && event.modifiers & Qt.ControlModifier) ||
|
||||||
|
(event.key === Qt.Key_L && event.modifiers & Qt.ControlModifier)
|
||||||
|
|
||||||
|
if (isInitKey) {
|
||||||
keyboardNavigationActive = true
|
keyboardNavigationActive = true
|
||||||
if (currentPath !== homeDir) {
|
if (currentPath !== homeDir) {
|
||||||
backButtonFocused = true
|
backButtonFocused = true
|
||||||
@@ -281,6 +304,69 @@ DankModal {
|
|||||||
}
|
}
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
break
|
break
|
||||||
|
case Qt.Key_N:
|
||||||
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
|
if (backButtonFocused) {
|
||||||
|
backButtonFocused = false
|
||||||
|
selectedIndex = 0
|
||||||
|
} else if (selectedIndex < totalItems - 1) {
|
||||||
|
selectedIndex++
|
||||||
|
}
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case Qt.Key_P:
|
||||||
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
|
if (selectedIndex > 0) {
|
||||||
|
selectedIndex--
|
||||||
|
} else if (currentPath !== homeDir) {
|
||||||
|
backButtonFocused = true
|
||||||
|
selectedIndex = -1
|
||||||
|
}
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case Qt.Key_J:
|
||||||
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
|
if (selectedIndex < totalItems - 1) {
|
||||||
|
selectedIndex++
|
||||||
|
}
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case Qt.Key_K:
|
||||||
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
|
if (selectedIndex > 0) {
|
||||||
|
selectedIndex--
|
||||||
|
} else if (currentPath !== homeDir) {
|
||||||
|
backButtonFocused = true
|
||||||
|
selectedIndex = -1
|
||||||
|
}
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case Qt.Key_H:
|
||||||
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
|
if (!backButtonFocused && selectedIndex > 0) {
|
||||||
|
selectedIndex--
|
||||||
|
} else if (currentPath !== homeDir) {
|
||||||
|
backButtonFocused = true
|
||||||
|
selectedIndex = -1
|
||||||
|
}
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case Qt.Key_L:
|
||||||
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
|
if (backButtonFocused) {
|
||||||
|
backButtonFocused = false
|
||||||
|
selectedIndex = 0
|
||||||
|
} else if (selectedIndex < totalItems - 1) {
|
||||||
|
selectedIndex++
|
||||||
|
}
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
break
|
||||||
case Qt.Key_Left:
|
case Qt.Key_Left:
|
||||||
if (backButtonFocused)
|
if (backButtonFocused)
|
||||||
return
|
return
|
||||||
@@ -387,6 +473,16 @@ DankModal {
|
|||||||
Item {
|
Item {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Keys.onPressed: event => {
|
||||||
|
keyboardController.handleKey(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
onVisibleChanged: {
|
||||||
|
if (visible) {
|
||||||
|
forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingM
|
anchors.margins: Theme.spacingM
|
||||||
@@ -527,7 +623,6 @@ DankModal {
|
|||||||
required property bool fileIsDir
|
required property bool fileIsDir
|
||||||
required property string filePath
|
required property string filePath
|
||||||
required property string fileName
|
required property string fileName
|
||||||
required property url fileURL
|
|
||||||
required property int index
|
required property int index
|
||||||
|
|
||||||
width: weMode ? 245 : 140
|
width: weMode ? 245 : 140
|
||||||
@@ -687,7 +782,7 @@ DankModal {
|
|||||||
width: parent.width - saveButton.width - Theme.spacingM
|
width: parent.width - saveButton.width - Theme.spacingM
|
||||||
height: 40
|
height: 40
|
||||||
text: defaultFileName
|
text: defaultFileName
|
||||||
placeholderText: "Enter filename..."
|
placeholderText: I18n.tr("Enter filename...")
|
||||||
ignoreLeftRightKeys: false
|
ignoreLeftRightKeys: false
|
||||||
focus: saveMode
|
focus: saveMode
|
||||||
topPadding: Theme.spacingS
|
topPadding: Theme.spacingS
|
||||||
@@ -720,7 +815,7 @@ DankModal {
|
|||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: "Save"
|
text: I18n.tr("Save")
|
||||||
color: fileNameInput.text.trim() !== "" ? Theme.primaryText : Theme.surfaceVariantText
|
color: fileNameInput.text.trim() !== "" ? Theme.primaryText : Theme.surfaceVariantText
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
}
|
}
|
||||||
@@ -824,7 +919,7 @@ DankModal {
|
|||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: qsTr("File Already Exists")
|
text: I18n.tr("File Already Exists")
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
@@ -832,7 +927,7 @@ DankModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: qsTr("A file with this name already exists. Do you want to overwrite it?")
|
text: I18n.tr("A file with this name already exists. Do you want to overwrite it?")
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceTextMedium
|
color: Theme.surfaceTextMedium
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -854,7 +949,7 @@ DankModal {
|
|||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: qsTr("Cancel")
|
text: I18n.tr("Cancel")
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -880,7 +975,7 @@ DankModal {
|
|||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: qsTr("Overwrite")
|
text: I18n.tr("Overwrite")
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.background
|
color: Theme.background
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "File Information"
|
text: I18n.tr("File Information")
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -197,7 +197,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "F1/I: Toggle • F10: Help"
|
text: I18n.tr("F1/I: Toggle • F10: Help")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceTextMedium
|
color: Theme.surfaceTextMedium
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ Rectangle {
|
|||||||
spacing: 2
|
spacing: 2
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Tab/Shift+Tab: Nav • ←→↑↓: Grid Nav • Enter/Space: Select"
|
text: I18n.tr("Tab/Shift+Tab: Nav • ←→↑↓: Grid Nav • Enter/Space: Select")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -32,7 +32,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Alt+←/Backspace: Back • F1/I: File Info • F10: Help • Esc: Close"
|
text: I18n.tr("Alt+←/Backspace: Back • F1/I: File Info • F10: Help • Esc: Close")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|||||||
266
Modals/HyprKeybindsModal.qml
Normal file
266
Modals/HyprKeybindsModal.qml
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Quickshell
|
||||||
|
import qs.Common
|
||||||
|
import qs.Modals.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
DankModal {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
width: 1400
|
||||||
|
height: 900
|
||||||
|
onBackgroundClicked: close()
|
||||||
|
|
||||||
|
function categorizeKeybinds() {
|
||||||
|
const categories = {
|
||||||
|
"Workspace": [],
|
||||||
|
"Window": [],
|
||||||
|
"Monitor": [],
|
||||||
|
"Execute": [],
|
||||||
|
"System": [],
|
||||||
|
"Other": []
|
||||||
|
}
|
||||||
|
|
||||||
|
function addKeybind(keybind) {
|
||||||
|
const dispatcher = keybind.dispatcher || ""
|
||||||
|
if (dispatcher.includes("workspace")) {
|
||||||
|
categories["Workspace"].push(keybind)
|
||||||
|
} else if (dispatcher.includes("monitor")) {
|
||||||
|
categories["Monitor"].push(keybind)
|
||||||
|
} else if (dispatcher.includes("window") || dispatcher.includes("focus") || dispatcher.includes("move") || dispatcher.includes("swap") || dispatcher.includes("resize") || dispatcher === "killactive" || dispatcher === "fullscreen" || dispatcher === "togglefloating") {
|
||||||
|
categories["Window"].push(keybind)
|
||||||
|
} else if (dispatcher === "exec") {
|
||||||
|
categories["Execute"].push(keybind)
|
||||||
|
} else if (dispatcher === "exit" || dispatcher.includes("dpms")) {
|
||||||
|
categories["System"].push(keybind)
|
||||||
|
} else {
|
||||||
|
categories["Other"].push(keybind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const allKeybinds = HyprKeybindsService.keybinds.keybinds || []
|
||||||
|
for (let i = 0; i < allKeybinds.length; i++) {
|
||||||
|
addKeybind(allKeybinds[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
const children = HyprKeybindsService.keybinds.children || []
|
||||||
|
for (let i = 0; i < children.length; i++) {
|
||||||
|
const child = children[i]
|
||||||
|
const childKeybinds = child.keybinds || []
|
||||||
|
for (let j = 0; j < childKeybinds.length; j++) {
|
||||||
|
addKeybind(childKeybinds[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
categories["Workspace"].sort((a, b) => {
|
||||||
|
const dispA = a.dispatcher || ""
|
||||||
|
const dispB = b.dispatcher || ""
|
||||||
|
return dispA.localeCompare(dispB)
|
||||||
|
})
|
||||||
|
|
||||||
|
categories["Window"].sort((a, b) => {
|
||||||
|
const dispA = a.dispatcher || ""
|
||||||
|
const dispB = b.dispatcher || ""
|
||||||
|
return dispA.localeCompare(dispB)
|
||||||
|
})
|
||||||
|
|
||||||
|
categories["Monitor"].sort((a, b) => {
|
||||||
|
const dispA = a.dispatcher || ""
|
||||||
|
const dispB = b.dispatcher || ""
|
||||||
|
return dispA.localeCompare(dispB)
|
||||||
|
})
|
||||||
|
|
||||||
|
categories["Execute"].sort((a, b) => {
|
||||||
|
const modsA = a.mods || []
|
||||||
|
const keyA = a.key || ""
|
||||||
|
const bindA = [...modsA, keyA].join("+")
|
||||||
|
|
||||||
|
const modsB = b.mods || []
|
||||||
|
const keyB = b.key || ""
|
||||||
|
const bindB = [...modsB, keyB].join("+")
|
||||||
|
|
||||||
|
return bindA.localeCompare(bindB)
|
||||||
|
})
|
||||||
|
|
||||||
|
return categories
|
||||||
|
}
|
||||||
|
|
||||||
|
content: Component {
|
||||||
|
Item {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
DankFlickable {
|
||||||
|
id: mainFlickable
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingL
|
||||||
|
contentWidth: rowLayout.implicitWidth
|
||||||
|
contentHeight: rowLayout.implicitHeight
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: rowLayout
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
property var categories: root.categorizeKeybinds()
|
||||||
|
property real columnWidth: (mainFlickable.width - spacing * 2) / 3
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: rowLayout.columnWidth
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "Window / Monitor"
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.primary
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
color: Theme.primary
|
||||||
|
opacity: 0.3
|
||||||
|
}
|
||||||
|
|
||||||
|
Item { width: 1; height: Theme.spacingXS }
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: [...(rowLayout.categories["Window"] || []), ...(rowLayout.categories["Monitor"] || [])]
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
width: Math.min(140, parent.width * 0.42)
|
||||||
|
height: 22
|
||||||
|
radius: 4
|
||||||
|
opacity: 0.3
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
anchors.margins: 2
|
||||||
|
width: parent.width - 4
|
||||||
|
text: {
|
||||||
|
const mods = modelData.mods || []
|
||||||
|
const key = modelData.key || ""
|
||||||
|
const parts = [...mods, key]
|
||||||
|
return parts.join("+")
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.weight: Font.Medium
|
||||||
|
isMonospace: true
|
||||||
|
elide: Text.ElideRight
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
width: parent.width - 150
|
||||||
|
text: {
|
||||||
|
const comment = modelData.comment || ""
|
||||||
|
if (comment) return comment
|
||||||
|
|
||||||
|
const dispatcher = modelData.dispatcher || ""
|
||||||
|
const params = modelData.params || ""
|
||||||
|
return params ? `${dispatcher} ${params}` : dispatcher
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
opacity: 0.9
|
||||||
|
elide: Text.ElideRight
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: ["Workspace", "Execute"]
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: rowLayout.columnWidth
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.primary
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
color: Theme.primary
|
||||||
|
opacity: 0.3
|
||||||
|
}
|
||||||
|
|
||||||
|
Item { width: 1; height: Theme.spacingXS }
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: rowLayout.categories[modelData] || []
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
width: Math.min(140, parent.width * 0.42)
|
||||||
|
height: 22
|
||||||
|
radius: 4
|
||||||
|
opacity: 0.3
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
anchors.margins: 2
|
||||||
|
width: parent.width - 4
|
||||||
|
text: {
|
||||||
|
const mods = modelData.mods || []
|
||||||
|
const key = modelData.key || ""
|
||||||
|
const parts = [...mods, key]
|
||||||
|
return parts.join("+")
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.weight: Font.Medium
|
||||||
|
isMonospace: true
|
||||||
|
elide: Text.ElideRight
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
width: parent.width - 150
|
||||||
|
text: {
|
||||||
|
const comment = modelData.comment || ""
|
||||||
|
if (comment) return comment
|
||||||
|
|
||||||
|
const dispatcher = modelData.dispatcher || ""
|
||||||
|
const params = modelData.params || ""
|
||||||
|
return params ? `${dispatcher} ${params}` : dispatcher
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
opacity: 0.9
|
||||||
|
elide: Text.ElideRight
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -56,7 +56,7 @@ DankModal {
|
|||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Network Information"
|
text: I18n.tr("Network Information")
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -126,7 +126,7 @@ DankModal {
|
|||||||
id: closeText
|
id: closeText
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: "Close"
|
text: I18n.tr("Close")
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.background
|
color: Theme.background
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
|
|||||||
162
Modals/NetworkWiredInfoModal.qml
Normal file
162
Modals/NetworkWiredInfoModal.qml
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import qs.Common
|
||||||
|
import qs.Modals.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
DankModal {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool networkWiredInfoModalVisible: false
|
||||||
|
property string networkID: ""
|
||||||
|
property var networkData: null
|
||||||
|
|
||||||
|
function showNetworkInfo(id, data) {
|
||||||
|
networkID = id
|
||||||
|
networkData = data
|
||||||
|
networkWiredInfoModalVisible = true
|
||||||
|
open()
|
||||||
|
NetworkService.fetchWiredNetworkInfo(data.uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideDialog() {
|
||||||
|
networkWiredInfoModalVisible = false
|
||||||
|
close()
|
||||||
|
networkID = ""
|
||||||
|
networkData = null
|
||||||
|
}
|
||||||
|
|
||||||
|
visible: networkWiredInfoModalVisible
|
||||||
|
width: 600
|
||||||
|
height: 500
|
||||||
|
enableShadow: true
|
||||||
|
onBackgroundClicked: hideDialog()
|
||||||
|
onVisibleChanged: {
|
||||||
|
if (!visible) {
|
||||||
|
networkID = ""
|
||||||
|
networkData = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
content: Component {
|
||||||
|
Item {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingL
|
||||||
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width - 40
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Network Information")
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: `Details for "${networkID}"`
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceTextMedium
|
||||||
|
width: parent.width
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
iconName: "close"
|
||||||
|
iconSize: Theme.iconSize - 4
|
||||||
|
iconColor: Theme.surfaceText
|
||||||
|
onClicked: root.hideDialog()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: detailsRect
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height - 140
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.surfaceHover
|
||||||
|
border.color: Theme.outlineStrong
|
||||||
|
border.width: 1
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
DankFlickable {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
contentHeight: detailsText.contentHeight
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: detailsText
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
text: NetworkService.networkWiredInfoDetails && NetworkService.networkWiredInfoDetails.replace(/\\n/g, '\n') || "No information available"
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: 40
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: Math.max(70, closeText.contentWidth + Theme.spacingM * 2)
|
||||||
|
height: 36
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: closeArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: closeText
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: I18n.tr("Close")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.background
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: closeArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: root.hideDialog()
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -54,6 +54,9 @@ DankModal {
|
|||||||
height: 700
|
height: 700
|
||||||
visible: false
|
visible: false
|
||||||
onBackgroundClicked: hide()
|
onBackgroundClicked: hide()
|
||||||
|
onOpened: () => {
|
||||||
|
Qt.callLater(() => modalFocusScope.forceActiveFocus());
|
||||||
|
}
|
||||||
onShouldBeVisibleChanged: (shouldBeVisible) => {
|
onShouldBeVisibleChanged: (shouldBeVisible) => {
|
||||||
if (!shouldBeVisible) {
|
if (!shouldBeVisible) {
|
||||||
notificationModalOpen = false
|
notificationModalOpen = false
|
||||||
|
|||||||
@@ -9,31 +9,47 @@ DankModal {
|
|||||||
|
|
||||||
property int selectedIndex: 0
|
property int selectedIndex: 0
|
||||||
property int optionCount: SessionService.hibernateSupported ? 5 : 4
|
property int optionCount: SessionService.hibernateSupported ? 5 : 4
|
||||||
|
property rect parentBounds: Qt.rect(0, 0, 0, 0)
|
||||||
|
property var parentScreen: null
|
||||||
|
|
||||||
signal powerActionRequested(string action, string title, string message)
|
signal powerActionRequested(string action, string title, string message)
|
||||||
|
|
||||||
|
function openCentered() {
|
||||||
|
parentBounds = Qt.rect(0, 0, 0, 0)
|
||||||
|
parentScreen = null
|
||||||
|
backgroundOpacity = 0.5
|
||||||
|
open()
|
||||||
|
}
|
||||||
|
|
||||||
|
function openFromControlCenter(bounds, targetScreen) {
|
||||||
|
parentBounds = bounds
|
||||||
|
parentScreen = targetScreen
|
||||||
|
backgroundOpacity = 0
|
||||||
|
open()
|
||||||
|
}
|
||||||
|
|
||||||
function selectOption(action) {
|
function selectOption(action) {
|
||||||
close();
|
close();
|
||||||
const actions = {
|
const actions = {
|
||||||
"logout": {
|
"logout": {
|
||||||
"title": "Log Out",
|
"title": I18n.tr("Log Out"),
|
||||||
"message": "Are you sure you want to log out?"
|
"message": I18n.tr("Are you sure you want to log out?")
|
||||||
},
|
},
|
||||||
"suspend": {
|
"suspend": {
|
||||||
"title": "Suspend",
|
"title": I18n.tr("Suspend"),
|
||||||
"message": "Are you sure you want to suspend the system?"
|
"message": I18n.tr("Are you sure you want to suspend the system?")
|
||||||
},
|
},
|
||||||
"hibernate": {
|
"hibernate": {
|
||||||
"title": "Hibernate",
|
"title": I18n.tr("Hibernate"),
|
||||||
"message": "Are you sure you want to hibernate the system?"
|
"message": I18n.tr("Are you sure you want to hibernate the system?")
|
||||||
},
|
},
|
||||||
"reboot": {
|
"reboot": {
|
||||||
"title": "Reboot",
|
"title": I18n.tr("Reboot"),
|
||||||
"message": "Are you sure you want to reboot the system?"
|
"message": I18n.tr("Are you sure you want to reboot the system?")
|
||||||
},
|
},
|
||||||
"poweroff": {
|
"poweroff": {
|
||||||
"title": "Power Off",
|
"title": I18n.tr("Power Off"),
|
||||||
"message": "Are you sure you want to power off the system?"
|
"message": I18n.tr("Are you sure you want to power off the system?")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const selected = actions[action]
|
const selected = actions[action]
|
||||||
@@ -47,23 +63,31 @@ DankModal {
|
|||||||
width: 320
|
width: 320
|
||||||
height: contentLoader.item ? contentLoader.item.implicitHeight : 300
|
height: contentLoader.item ? contentLoader.item.implicitHeight : 300
|
||||||
enableShadow: true
|
enableShadow: true
|
||||||
|
screen: parentScreen
|
||||||
|
positioning: parentBounds.width > 0 ? "custom" : "center"
|
||||||
|
customPosition: {
|
||||||
|
if (parentBounds.width > 0) {
|
||||||
|
const centerX = parentBounds.x + (parentBounds.width - width) / 2
|
||||||
|
const centerY = parentBounds.y + (parentBounds.height - height) / 2
|
||||||
|
return Qt.point(centerX, centerY)
|
||||||
|
}
|
||||||
|
return Qt.point(0, 0)
|
||||||
|
}
|
||||||
onBackgroundClicked: () => {
|
onBackgroundClicked: () => {
|
||||||
return close();
|
return close();
|
||||||
}
|
}
|
||||||
onOpened: () => {
|
onOpened: () => {
|
||||||
selectedIndex = 0;
|
selectedIndex = 0;
|
||||||
modalFocusScope.forceActiveFocus();
|
Qt.callLater(() => modalFocusScope.forceActiveFocus());
|
||||||
}
|
}
|
||||||
modalFocusScope.Keys.onPressed: (event) => {
|
modalFocusScope.Keys.onPressed: (event) => {
|
||||||
switch (event.key) {
|
switch (event.key) {
|
||||||
case Qt.Key_Up:
|
case Qt.Key_Up:
|
||||||
|
case Qt.Key_Backtab:
|
||||||
selectedIndex = (selectedIndex - 1 + optionCount) % optionCount;
|
selectedIndex = (selectedIndex - 1 + optionCount) % optionCount;
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
break;
|
break;
|
||||||
case Qt.Key_Down:
|
case Qt.Key_Down:
|
||||||
selectedIndex = (selectedIndex + 1) % optionCount;
|
|
||||||
event.accepted = true;
|
|
||||||
break;
|
|
||||||
case Qt.Key_Tab:
|
case Qt.Key_Tab:
|
||||||
selectedIndex = (selectedIndex + 1) % optionCount;
|
selectedIndex = (selectedIndex + 1) % optionCount;
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
@@ -78,6 +102,30 @@ DankModal {
|
|||||||
}
|
}
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
break;
|
break;
|
||||||
|
case Qt.Key_N:
|
||||||
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
|
selectedIndex = (selectedIndex + 1) % optionCount;
|
||||||
|
event.accepted = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Qt.Key_P:
|
||||||
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
|
selectedIndex = (selectedIndex - 1 + optionCount) % optionCount;
|
||||||
|
event.accepted = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Qt.Key_J:
|
||||||
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
|
selectedIndex = (selectedIndex + 1) % optionCount;
|
||||||
|
event.accepted = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Qt.Key_K:
|
||||||
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
|
selectedIndex = (selectedIndex - 1 + optionCount) % optionCount;
|
||||||
|
event.accepted = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +144,7 @@ DankModal {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Power Options"
|
text: I18n.tr("Power Options")
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -153,7 +201,7 @@ DankModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Log Out"
|
text: I18n.tr("Log Out")
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -206,7 +254,7 @@ DankModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Suspend"
|
text: I18n.tr("Suspend")
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -260,7 +308,7 @@ DankModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Hibernate"
|
text: I18n.tr("Hibernate")
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -314,7 +362,7 @@ DankModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Reboot"
|
text: I18n.tr("Reboot")
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: rebootArea.containsMouse ? Theme.warning : Theme.surfaceText
|
color: rebootArea.containsMouse ? Theme.warning : Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -368,7 +416,7 @@ DankModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Power Off"
|
text: I18n.tr("Power Off")
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: powerOffArea.containsMouse ? Theme.error : Theme.surfaceText
|
color: powerOffArea.containsMouse ? Theme.error : Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ DankModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "System Monitor Unavailable"
|
text: I18n.tr("System Monitor Unavailable")
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
color: Theme.error
|
color: Theme.error
|
||||||
@@ -131,7 +131,7 @@ DankModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "The 'dgop' tool is required for system monitoring.\nPlease install dgop to use this feature."
|
text: I18n.tr("The 'dgop' tool is required for system monitoring.\nPlease install dgop to use this feature.")
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
@@ -154,7 +154,7 @@ DankModal {
|
|||||||
height: 40
|
height: 40
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "System Monitor"
|
text: I18n.tr("System Monitor")
|
||||||
font.pixelSize: Theme.fontSizeLarge + 4
|
font.pixelSize: Theme.fontSizeLarge + 4
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
@@ -181,7 +181,7 @@ DankModal {
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
height: 52
|
height: 52
|
||||||
color: Theme.surfaceSelected
|
color: Theme.surfaceContainerHigh
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.color: Theme.outlineLight
|
border.color: Theme.outlineLight
|
||||||
border.width: 1
|
border.width: 1
|
||||||
@@ -281,7 +281,7 @@ DankModal {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Theme.surfaceLight
|
color: Theme.surfaceContainerHigh
|
||||||
border.color: Theme.outlineLight
|
border.color: Theme.outlineLight
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
|
|||||||
@@ -19,19 +19,103 @@ Item {
|
|||||||
spacing: Theme.spacingXL
|
spacing: Theme.spacingXL
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Battery not detected - only AC power settings available"
|
text: I18n.tr("Battery not detected - only AC power settings available")
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
visible: !BatteryService.batteryAvailable
|
visible: !BatteryService.batteryAvailable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
width: parent.width
|
||||||
|
height: lockScreenSection.implicitHeight + Theme.spacingL * 2
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
||||||
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||||
|
border.width: 0
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: lockScreenSection
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingL
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "lock"
|
||||||
|
size: Theme.iconSize
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Lock Screen")
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankToggle {
|
||||||
|
width: parent.width
|
||||||
|
text: I18n.tr("Show Power Actions")
|
||||||
|
description: I18n.tr("Show power, restart, and logout buttons on the lock screen")
|
||||||
|
checked: SettingsData.lockScreenShowPowerActions
|
||||||
|
onToggled: checked => SettingsData.setLockScreenShowPowerActions(checked)
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("loginctl not available - lock integration requires DMS socket connection")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.warning
|
||||||
|
visible: !SessionService.loginctlAvailable
|
||||||
|
width: parent.width
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
|
||||||
|
DankToggle {
|
||||||
|
width: parent.width
|
||||||
|
text: I18n.tr("Enable loginctl lock integration")
|
||||||
|
description: I18n.tr("Bind lock screen to dbus signals from loginctl. Disable if using an external lock screen")
|
||||||
|
checked: SessionService.loginctlAvailable && SettingsData.loginctlLockIntegration
|
||||||
|
enabled: SessionService.loginctlAvailable
|
||||||
|
onToggled: checked => {
|
||||||
|
if (SessionService.loginctlAvailable) {
|
||||||
|
SettingsData.setLoginctlLockIntegration(checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankToggle {
|
||||||
|
width: parent.width
|
||||||
|
text: I18n.tr("Lock before suspend")
|
||||||
|
description: I18n.tr("Automatically lock the screen when the system prepares to suspend")
|
||||||
|
checked: SettingsData.lockBeforeSuspend
|
||||||
|
visible: SessionService.loginctlAvailable && SettingsData.loginctlLockIntegration
|
||||||
|
onToggled: checked => SettingsData.setLockBeforeSuspend(checked)
|
||||||
|
}
|
||||||
|
|
||||||
|
DankToggle {
|
||||||
|
width: parent.width
|
||||||
|
text: I18n.tr("Enable fingerprint authentication")
|
||||||
|
description: I18n.tr("Use fingerprint reader for lock screen authentication (requires enrolled fingerprints)")
|
||||||
|
checked: SettingsData.enableFprint
|
||||||
|
visible: SettingsData.fprintdAvailable
|
||||||
|
onToggled: checked => SettingsData.setEnableFprint(checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
StyledRect {
|
StyledRect {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: timeoutSection.implicitHeight + Theme.spacingL * 2
|
height: timeoutSection.implicitHeight + Theme.spacingL * 2
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||||
border.width: 1
|
border.width: 0
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: timeoutSection
|
id: timeoutSection
|
||||||
@@ -51,7 +135,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Idle Settings"
|
text: I18n.tr("Idle Settings")
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
@@ -79,21 +163,20 @@ Item {
|
|||||||
property var timeoutOptions: ["Never", "1 minute", "2 minutes", "3 minutes", "5 minutes", "10 minutes", "15 minutes", "20 minutes", "30 minutes", "1 hour", "1 hour 30 minutes", "2 hours", "3 hours"]
|
property var timeoutOptions: ["Never", "1 minute", "2 minutes", "3 minutes", "5 minutes", "10 minutes", "15 minutes", "20 minutes", "30 minutes", "1 hour", "1 hour 30 minutes", "2 hours", "3 hours"]
|
||||||
property var timeoutValues: [0, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800]
|
property var timeoutValues: [0, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800]
|
||||||
|
|
||||||
width: parent.width
|
text: I18n.tr("Automatically lock after")
|
||||||
text: "Automatically lock after"
|
|
||||||
options: timeoutOptions
|
options: timeoutOptions
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: powerCategory
|
target: powerCategory
|
||||||
function onCurrentIndexChanged() {
|
function onCurrentIndexChanged() {
|
||||||
const currentTimeout = powerCategory.currentIndex === 0 ? SessionData.acLockTimeout : SessionData.batteryLockTimeout
|
const currentTimeout = powerCategory.currentIndex === 0 ? SettingsData.acLockTimeout : SettingsData.batteryLockTimeout
|
||||||
const index = lockDropdown.timeoutValues.indexOf(currentTimeout)
|
const index = lockDropdown.timeoutValues.indexOf(currentTimeout)
|
||||||
lockDropdown.currentValue = index >= 0 ? lockDropdown.timeoutOptions[index] : "Never"
|
lockDropdown.currentValue = index >= 0 ? lockDropdown.timeoutOptions[index] : "Never"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
const currentTimeout = powerCategory.currentIndex === 0 ? SessionData.acLockTimeout : SessionData.batteryLockTimeout
|
const currentTimeout = powerCategory.currentIndex === 0 ? SettingsData.acLockTimeout : SettingsData.batteryLockTimeout
|
||||||
const index = timeoutValues.indexOf(currentTimeout)
|
const index = timeoutValues.indexOf(currentTimeout)
|
||||||
currentValue = index >= 0 ? timeoutOptions[index] : "Never"
|
currentValue = index >= 0 ? timeoutOptions[index] : "Never"
|
||||||
}
|
}
|
||||||
@@ -103,9 +186,9 @@ Item {
|
|||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
const timeout = timeoutValues[index]
|
const timeout = timeoutValues[index]
|
||||||
if (powerCategory.currentIndex === 0) {
|
if (powerCategory.currentIndex === 0) {
|
||||||
SessionData.setAcLockTimeout(timeout)
|
SettingsData.setAcLockTimeout(timeout)
|
||||||
} else {
|
} else {
|
||||||
SessionData.setBatteryLockTimeout(timeout)
|
SettingsData.setBatteryLockTimeout(timeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,21 +199,20 @@ Item {
|
|||||||
property var timeoutOptions: ["Never", "1 minute", "2 minutes", "3 minutes", "5 minutes", "10 minutes", "15 minutes", "20 minutes", "30 minutes", "1 hour", "1 hour 30 minutes", "2 hours", "3 hours"]
|
property var timeoutOptions: ["Never", "1 minute", "2 minutes", "3 minutes", "5 minutes", "10 minutes", "15 minutes", "20 minutes", "30 minutes", "1 hour", "1 hour 30 minutes", "2 hours", "3 hours"]
|
||||||
property var timeoutValues: [0, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800]
|
property var timeoutValues: [0, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800]
|
||||||
|
|
||||||
width: parent.width
|
text: I18n.tr("Turn off monitors after")
|
||||||
text: "Turn off monitors after"
|
|
||||||
options: timeoutOptions
|
options: timeoutOptions
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: powerCategory
|
target: powerCategory
|
||||||
function onCurrentIndexChanged() {
|
function onCurrentIndexChanged() {
|
||||||
const currentTimeout = powerCategory.currentIndex === 0 ? SessionData.acMonitorTimeout : SessionData.batteryMonitorTimeout
|
const currentTimeout = powerCategory.currentIndex === 0 ? SettingsData.acMonitorTimeout : SettingsData.batteryMonitorTimeout
|
||||||
const index = monitorDropdown.timeoutValues.indexOf(currentTimeout)
|
const index = monitorDropdown.timeoutValues.indexOf(currentTimeout)
|
||||||
monitorDropdown.currentValue = index >= 0 ? monitorDropdown.timeoutOptions[index] : "Never"
|
monitorDropdown.currentValue = index >= 0 ? monitorDropdown.timeoutOptions[index] : "Never"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
const currentTimeout = powerCategory.currentIndex === 0 ? SessionData.acMonitorTimeout : SessionData.batteryMonitorTimeout
|
const currentTimeout = powerCategory.currentIndex === 0 ? SettingsData.acMonitorTimeout : SettingsData.batteryMonitorTimeout
|
||||||
const index = timeoutValues.indexOf(currentTimeout)
|
const index = timeoutValues.indexOf(currentTimeout)
|
||||||
currentValue = index >= 0 ? timeoutOptions[index] : "Never"
|
currentValue = index >= 0 ? timeoutOptions[index] : "Never"
|
||||||
}
|
}
|
||||||
@@ -140,9 +222,9 @@ Item {
|
|||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
const timeout = timeoutValues[index]
|
const timeout = timeoutValues[index]
|
||||||
if (powerCategory.currentIndex === 0) {
|
if (powerCategory.currentIndex === 0) {
|
||||||
SessionData.setAcMonitorTimeout(timeout)
|
SettingsData.setAcMonitorTimeout(timeout)
|
||||||
} else {
|
} else {
|
||||||
SessionData.setBatteryMonitorTimeout(timeout)
|
SettingsData.setBatteryMonitorTimeout(timeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,21 +235,20 @@ Item {
|
|||||||
property var timeoutOptions: ["Never", "1 minute", "2 minutes", "3 minutes", "5 minutes", "10 minutes", "15 minutes", "20 minutes", "30 minutes", "1 hour", "1 hour 30 minutes", "2 hours", "3 hours"]
|
property var timeoutOptions: ["Never", "1 minute", "2 minutes", "3 minutes", "5 minutes", "10 minutes", "15 minutes", "20 minutes", "30 minutes", "1 hour", "1 hour 30 minutes", "2 hours", "3 hours"]
|
||||||
property var timeoutValues: [0, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800]
|
property var timeoutValues: [0, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800]
|
||||||
|
|
||||||
width: parent.width
|
text: I18n.tr("Suspend system after")
|
||||||
text: "Suspend system after"
|
|
||||||
options: timeoutOptions
|
options: timeoutOptions
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: powerCategory
|
target: powerCategory
|
||||||
function onCurrentIndexChanged() {
|
function onCurrentIndexChanged() {
|
||||||
const currentTimeout = powerCategory.currentIndex === 0 ? SessionData.acSuspendTimeout : SessionData.batterySuspendTimeout
|
const currentTimeout = powerCategory.currentIndex === 0 ? SettingsData.acSuspendTimeout : SettingsData.batterySuspendTimeout
|
||||||
const index = suspendDropdown.timeoutValues.indexOf(currentTimeout)
|
const index = suspendDropdown.timeoutValues.indexOf(currentTimeout)
|
||||||
suspendDropdown.currentValue = index >= 0 ? suspendDropdown.timeoutOptions[index] : "Never"
|
suspendDropdown.currentValue = index >= 0 ? suspendDropdown.timeoutOptions[index] : "Never"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
const currentTimeout = powerCategory.currentIndex === 0 ? SessionData.acSuspendTimeout : SessionData.batterySuspendTimeout
|
const currentTimeout = powerCategory.currentIndex === 0 ? SettingsData.acSuspendTimeout : SettingsData.batterySuspendTimeout
|
||||||
const index = timeoutValues.indexOf(currentTimeout)
|
const index = timeoutValues.indexOf(currentTimeout)
|
||||||
currentValue = index >= 0 ? timeoutOptions[index] : "Never"
|
currentValue = index >= 0 ? timeoutOptions[index] : "Never"
|
||||||
}
|
}
|
||||||
@@ -177,9 +258,9 @@ Item {
|
|||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
const timeout = timeoutValues[index]
|
const timeout = timeoutValues[index]
|
||||||
if (powerCategory.currentIndex === 0) {
|
if (powerCategory.currentIndex === 0) {
|
||||||
SessionData.setAcSuspendTimeout(timeout)
|
SettingsData.setAcSuspendTimeout(timeout)
|
||||||
} else {
|
} else {
|
||||||
SessionData.setBatterySuspendTimeout(timeout)
|
SettingsData.setBatterySuspendTimeout(timeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,22 +271,21 @@ Item {
|
|||||||
property var timeoutOptions: ["Never", "1 minute", "2 minutes", "3 minutes", "5 minutes", "10 minutes", "15 minutes", "20 minutes", "30 minutes", "1 hour", "1 hour 30 minutes", "2 hours", "3 hours"]
|
property var timeoutOptions: ["Never", "1 minute", "2 minutes", "3 minutes", "5 minutes", "10 minutes", "15 minutes", "20 minutes", "30 minutes", "1 hour", "1 hour 30 minutes", "2 hours", "3 hours"]
|
||||||
property var timeoutValues: [0, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800]
|
property var timeoutValues: [0, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800]
|
||||||
|
|
||||||
width: parent.width
|
text: I18n.tr("Hibernate system after")
|
||||||
text: "Hibernate system after"
|
|
||||||
options: timeoutOptions
|
options: timeoutOptions
|
||||||
visible: SessionService.hibernateSupported
|
visible: SessionService.hibernateSupported
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: powerCategory
|
target: powerCategory
|
||||||
function onCurrentIndexChanged() {
|
function onCurrentIndexChanged() {
|
||||||
const currentTimeout = powerCategory.currentIndex === 0 ? SessionData.acHibernateTimeout : SessionData.batteryHibernateTimeout
|
const currentTimeout = powerCategory.currentIndex === 0 ? SettingsData.acHibernateTimeout : SettingsData.batteryHibernateTimeout
|
||||||
const index = hibernateDropdown.timeoutValues.indexOf(currentTimeout)
|
const index = hibernateDropdown.timeoutValues.indexOf(currentTimeout)
|
||||||
hibernateDropdown.currentValue = index >= 0 ? hibernateDropdown.timeoutOptions[index] : "Never"
|
hibernateDropdown.currentValue = index >= 0 ? hibernateDropdown.timeoutOptions[index] : "Never"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
const currentTimeout = powerCategory.currentIndex === 0 ? SessionData.acHibernateTimeout : SessionData.batteryHibernateTimeout
|
const currentTimeout = powerCategory.currentIndex === 0 ? SettingsData.acHibernateTimeout : SettingsData.batteryHibernateTimeout
|
||||||
const index = timeoutValues.indexOf(currentTimeout)
|
const index = timeoutValues.indexOf(currentTimeout)
|
||||||
currentValue = index >= 0 ? timeoutOptions[index] : "Never"
|
currentValue = index >= 0 ? timeoutOptions[index] : "Never"
|
||||||
}
|
}
|
||||||
@@ -215,24 +295,16 @@ Item {
|
|||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
const timeout = timeoutValues[index]
|
const timeout = timeoutValues[index]
|
||||||
if (powerCategory.currentIndex === 0) {
|
if (powerCategory.currentIndex === 0) {
|
||||||
SessionData.setAcHibernateTimeout(timeout)
|
SettingsData.setAcHibernateTimeout(timeout)
|
||||||
} else {
|
} else {
|
||||||
SessionData.setBatteryHibernateTimeout(timeout)
|
SettingsData.setBatteryHibernateTimeout(timeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DankToggle {
|
|
||||||
width: parent.width
|
|
||||||
text: "Lock before suspend"
|
|
||||||
description: "Automatically lock the screen when the system prepares to suspend"
|
|
||||||
checked: SessionData.lockBeforeSuspend
|
|
||||||
onToggled: checked => SessionData.setLockBeforeSuspend(checked)
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Idle monitoring not supported - requires newer Quickshell version"
|
text: I18n.tr("Idle monitoring not supported - requires newer Quickshell version")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.error
|
color: Theme.error
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
@@ -241,6 +313,277 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
width: parent.width
|
||||||
|
height: powerCommandConfirmSection.implicitHeight + Theme.spacingL * 2
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
||||||
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||||
|
border.width: 0
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: powerCommandConfirmSection
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingL
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "check_circle"
|
||||||
|
size: Theme.iconSize
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Power Action Confirmation")
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankToggle {
|
||||||
|
width: parent.width
|
||||||
|
text: I18n.tr("Show Confirmation on Power Actions")
|
||||||
|
description: I18n.tr("Request confirmation on power off, restart, suspend, hibernate and logout actions")
|
||||||
|
checked: SettingsData.powerActionConfirm
|
||||||
|
onToggled: checked => SettingsData.setPowerActionConfirm(checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
width: parent.width
|
||||||
|
height: powerCommandCustomization.implicitHeight + Theme.spacingL * 2
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
||||||
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||||
|
border.width: 0
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: powerCommandCustomization
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingL
|
||||||
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "developer_mode"
|
||||||
|
size: Theme.iconSize
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Custom Power Actions")
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
anchors.left: parent.left
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Command or script to run instead of the standard lock procedure")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
DankTextField {
|
||||||
|
id: customLockCommand
|
||||||
|
width: parent.width
|
||||||
|
height: 48
|
||||||
|
placeholderText: "/usr/bin/myLock.sh"
|
||||||
|
backgroundColor: Theme.surfaceVariant
|
||||||
|
normalBorderColor: Theme.primarySelected
|
||||||
|
focusedBorderColor: Theme.primary
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (SettingsData.customPowerActionLock) {
|
||||||
|
text = SettingsData.customPowerActionLock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onTextEdited: {
|
||||||
|
SettingsData.setCustomPowerActionLock(text.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
anchors.left: parent.left
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Command or script to run instead of the standard logout procedure")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
DankTextField {
|
||||||
|
id: customLogoutCommand
|
||||||
|
width: parent.width
|
||||||
|
height: 48
|
||||||
|
placeholderText: "/usr/bin/myLogout.sh"
|
||||||
|
backgroundColor: Theme.surfaceVariant
|
||||||
|
normalBorderColor: Theme.primarySelected
|
||||||
|
focusedBorderColor: Theme.primary
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (SettingsData.customPowerActionLogout) {
|
||||||
|
text = SettingsData.customPowerActionLogout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onTextEdited: {
|
||||||
|
SettingsData.setCustomPowerActionLogout(text.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
anchors.left: parent.left
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Command or script to run instead of the standard suspend procedure")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
DankTextField {
|
||||||
|
id: customSuspendCommand
|
||||||
|
width: parent.width
|
||||||
|
height: 48
|
||||||
|
placeholderText: "/usr/bin/mySuspend.sh"
|
||||||
|
backgroundColor: Theme.surfaceVariant
|
||||||
|
normalBorderColor: Theme.primarySelected
|
||||||
|
focusedBorderColor: Theme.primary
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (SettingsData.customPowerActionSuspend) {
|
||||||
|
text = SettingsData.customPowerActionSuspend;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onTextEdited: {
|
||||||
|
SettingsData.setCustomPowerActionSuspend(text.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
anchors.left: parent.left
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Command or script to run instead of the standard hibernate procedure")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
DankTextField {
|
||||||
|
id: customHibernateCommand
|
||||||
|
width: parent.width
|
||||||
|
height: 48
|
||||||
|
placeholderText: "/usr/bin/myHibernate.sh"
|
||||||
|
backgroundColor: Theme.surfaceVariant
|
||||||
|
normalBorderColor: Theme.primarySelected
|
||||||
|
focusedBorderColor: Theme.primary
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (SettingsData.customPowerActionHibernate) {
|
||||||
|
text = SettingsData.customPowerActionHibernate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onTextEdited: {
|
||||||
|
SettingsData.setCustomPowerActionHibernate(text.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
anchors.left: parent.left
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Command or script to run instead of the standard reboot procedure")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
DankTextField {
|
||||||
|
id: customRebootCommand
|
||||||
|
width: parent.width
|
||||||
|
height: 48
|
||||||
|
placeholderText: "/usr/bin/myReboot.sh"
|
||||||
|
backgroundColor: Theme.surfaceVariant
|
||||||
|
normalBorderColor: Theme.primarySelected
|
||||||
|
focusedBorderColor: Theme.primary
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (SettingsData.customPowerActionReboot) {
|
||||||
|
text = SettingsData.customPowerActionReboot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onTextEdited: {
|
||||||
|
SettingsData.setCustomPowerActionReboot(text.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
anchors.left: parent.left
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Command or script to run instead of the standard power off procedure")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
DankTextField {
|
||||||
|
id: customPowerOffCommand
|
||||||
|
width: parent.width
|
||||||
|
height: 48
|
||||||
|
placeholderText: "/usr/bin/myPowerOff.sh"
|
||||||
|
backgroundColor: Theme.surfaceVariant
|
||||||
|
normalBorderColor: Theme.primarySelected
|
||||||
|
focusedBorderColor: Theme.primary
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (SettingsData.customPowerActionPowerOff) {
|
||||||
|
text = SettingsData.customPowerActionPowerOff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onTextEdited: {
|
||||||
|
SettingsData.setCustomPowerActionPowerOff(text.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ import QtQuick
|
|||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modules.Settings
|
import qs.Modules.Settings
|
||||||
|
|
||||||
Item {
|
FocusScope {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property int currentIndex: 0
|
property int currentIndex: 0
|
||||||
property var parentModal: null
|
property var parentModal: null
|
||||||
|
focus: true
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -34,27 +35,14 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: timeLoader
|
id: timeWeatherLoader
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: root.currentIndex === 1
|
active: root.currentIndex === 1
|
||||||
visible: active
|
visible: active
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
|
|
||||||
sourceComponent: TimeTab {
|
sourceComponent: TimeWeatherTab {
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
id: weatherLoader
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
active: root.currentIndex === 2
|
|
||||||
visible: active
|
|
||||||
asynchronous: true
|
|
||||||
|
|
||||||
sourceComponent: WeatherTab {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -63,11 +51,12 @@ Item {
|
|||||||
id: topBarLoader
|
id: topBarLoader
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: root.currentIndex === 3
|
active: root.currentIndex === 2
|
||||||
visible: active
|
visible: active
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
|
|
||||||
sourceComponent: TopBarTab {
|
sourceComponent: DankBarTab {
|
||||||
|
parentModal: root.parentModal
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -76,7 +65,7 @@ Item {
|
|||||||
id: widgetsLoader
|
id: widgetsLoader
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: root.currentIndex === 4
|
active: root.currentIndex === 3
|
||||||
visible: active
|
visible: active
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
|
|
||||||
@@ -89,7 +78,7 @@ Item {
|
|||||||
id: dockLoader
|
id: dockLoader
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: root.currentIndex === 5
|
active: root.currentIndex === 4
|
||||||
visible: active
|
visible: active
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
|
|
||||||
@@ -105,7 +94,7 @@ Item {
|
|||||||
id: displaysLoader
|
id: displaysLoader
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: root.currentIndex === 6
|
active: root.currentIndex === 5
|
||||||
visible: active
|
visible: active
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
|
|
||||||
@@ -118,7 +107,7 @@ Item {
|
|||||||
id: launcherLoader
|
id: launcherLoader
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: root.currentIndex === 7
|
active: root.currentIndex === 6
|
||||||
visible: active
|
visible: active
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
|
|
||||||
@@ -131,7 +120,7 @@ Item {
|
|||||||
id: themeColorsLoader
|
id: themeColorsLoader
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: root.currentIndex === 8
|
active: root.currentIndex === 7
|
||||||
visible: active
|
visible: active
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
|
|
||||||
@@ -144,7 +133,7 @@ Item {
|
|||||||
id: powerLoader
|
id: powerLoader
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: root.currentIndex === 9
|
active: root.currentIndex === 8
|
||||||
visible: active
|
visible: active
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
|
|
||||||
@@ -153,6 +142,20 @@ Item {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: pluginsLoader
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
active: root.currentIndex === 9
|
||||||
|
visible: active
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
|
sourceComponent: PluginsTab {
|
||||||
|
parentModal: root.parentModal
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: aboutLoader
|
id: aboutLoader
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ DankModal {
|
|||||||
|
|
||||||
objectName: "settingsModal"
|
objectName: "settingsModal"
|
||||||
width: 800
|
width: 800
|
||||||
height: 750
|
height: 800
|
||||||
visible: false
|
visible: false
|
||||||
onBackgroundClicked: () => {
|
onBackgroundClicked: () => {
|
||||||
return hide();
|
return hide();
|
||||||
@@ -78,6 +78,7 @@ DankModal {
|
|||||||
id: profileBrowser
|
id: profileBrowser
|
||||||
|
|
||||||
allowStacking: true
|
allowStacking: true
|
||||||
|
parentModal: settingsModal
|
||||||
browserTitle: "Select Profile Image"
|
browserTitle: "Select Profile Image"
|
||||||
browserIcon: "person"
|
browserIcon: "person"
|
||||||
browserType: "profile"
|
browserType: "profile"
|
||||||
@@ -87,12 +88,6 @@ DankModal {
|
|||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
onDialogClosed: () => {
|
onDialogClosed: () => {
|
||||||
if (settingsModal) {
|
|
||||||
settingsModal.allowFocusOverride = false;
|
|
||||||
settingsModal.shouldHaveFocus = Qt.binding(() => {
|
|
||||||
return settingsModal.shouldBeVisible;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
allowStacking = true;
|
allowStacking = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,6 +96,7 @@ DankModal {
|
|||||||
id: wallpaperBrowser
|
id: wallpaperBrowser
|
||||||
|
|
||||||
allowStacking: true
|
allowStacking: true
|
||||||
|
parentModal: settingsModal
|
||||||
browserTitle: "Select Wallpaper"
|
browserTitle: "Select Wallpaper"
|
||||||
browserIcon: "wallpaper"
|
browserIcon: "wallpaper"
|
||||||
browserType: "wallpaper"
|
browserType: "wallpaper"
|
||||||
@@ -115,7 +111,7 @@ DankModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
settingsContent: Component {
|
settingsContent: Component {
|
||||||
Item {
|
FocusScope {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
focus: true
|
focus: true
|
||||||
|
|
||||||
@@ -144,7 +140,7 @@ DankModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Settings"
|
text: I18n.tr("Settings")
|
||||||
font.pixelSize: Theme.fontSizeXLarge
|
font.pixelSize: Theme.fontSizeXLarge
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
|
|||||||
@@ -9,37 +9,37 @@ Rectangle {
|
|||||||
property int currentIndex: 0
|
property int currentIndex: 0
|
||||||
property var parentModal: null
|
property var parentModal: null
|
||||||
readonly property var sidebarItems: [{
|
readonly property var sidebarItems: [{
|
||||||
"text": "Personalization",
|
"text": I18n.tr("Personalization"),
|
||||||
"icon": "person"
|
"icon": "person"
|
||||||
}, {
|
}, {
|
||||||
"text": "Time & Date",
|
"text": I18n.tr("Time & Weather"),
|
||||||
"icon": "schedule"
|
"icon": "schedule"
|
||||||
}, {
|
}, {
|
||||||
"text": "Weather",
|
"text": I18n.tr("Dank Bar"),
|
||||||
"icon": "cloud"
|
|
||||||
}, {
|
|
||||||
"text": "Top Bar",
|
|
||||||
"icon": "toolbar"
|
"icon": "toolbar"
|
||||||
}, {
|
}, {
|
||||||
"text": "Widgets",
|
"text": I18n.tr("Widgets"),
|
||||||
"icon": "widgets"
|
"icon": "widgets"
|
||||||
}, {
|
}, {
|
||||||
"text": "Dock",
|
"text": I18n.tr("Dock"),
|
||||||
"icon": "dock_to_bottom"
|
"icon": "dock_to_bottom"
|
||||||
}, {
|
}, {
|
||||||
"text": "Displays",
|
"text": I18n.tr("Displays"),
|
||||||
"icon": "monitor"
|
"icon": "monitor"
|
||||||
}, {
|
}, {
|
||||||
"text": "Launcher",
|
"text": I18n.tr("Launcher"),
|
||||||
"icon": "apps"
|
"icon": "apps"
|
||||||
}, {
|
}, {
|
||||||
"text": "Theme & Colors",
|
"text": I18n.tr("Theme & Colors"),
|
||||||
"icon": "palette"
|
"icon": "palette"
|
||||||
}, {
|
}, {
|
||||||
"text": "Power",
|
"text": I18n.tr("Power & Security"),
|
||||||
"icon": "power_settings_new"
|
"icon": "power"
|
||||||
}, {
|
}, {
|
||||||
"text": "About",
|
"text": I18n.tr("Plugins"),
|
||||||
|
"icon": "extension"
|
||||||
|
}, {
|
||||||
|
"text": I18n.tr("About"),
|
||||||
"icon": "info"
|
"icon": "info"
|
||||||
}]
|
}]
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ Rectangle {
|
|||||||
width: parent.width - Theme.spacingS * 2
|
width: parent.width - Theme.spacingS * 2
|
||||||
height: 44
|
height: 44
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: isActive ? Theme.primaryContainer : tabMouseArea.containsMouse ? Theme.surfaceHover : "transparent"
|
color: isActive ? Theme.primary : tabMouseArea.containsMouse ? Theme.surfaceHover : "transparent"
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -94,14 +94,14 @@ Rectangle {
|
|||||||
DankIcon {
|
DankIcon {
|
||||||
name: modelData.icon || ""
|
name: modelData.icon || ""
|
||||||
size: Theme.iconSize - 2
|
size: Theme.iconSize - 2
|
||||||
color: parent.parent.isActive ? Theme.surfaceText : Theme.surfaceText
|
color: parent.parent.isActive ? Theme.primaryText : Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: modelData.text || ""
|
text: modelData.text || ""
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: parent.parent.isActive ? Theme.surfaceText : Theme.surfaceText
|
color: parent.parent.isActive ? Theme.primaryText : Theme.surfaceText
|
||||||
font.weight: parent.parent.isActive ? Font.Medium : Font.Normal
|
font.weight: parent.parent.isActive ? Font.Medium : Font.Normal
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,13 @@ Item {
|
|||||||
property alias searchField: searchField
|
property alias searchField: searchField
|
||||||
property var parentModal: null
|
property var parentModal: null
|
||||||
|
|
||||||
|
function resetScroll() {
|
||||||
|
resultsView.resetScroll()
|
||||||
|
}
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
focus: true
|
focus: true
|
||||||
|
clip: false
|
||||||
Keys.onPressed: event => {
|
Keys.onPressed: event => {
|
||||||
if (event.key === Qt.Key_Escape) {
|
if (event.key === Qt.Key_Escape) {
|
||||||
if (parentModal)
|
if (parentModal)
|
||||||
@@ -33,13 +38,49 @@ Item {
|
|||||||
} else if (event.key === Qt.Key_Left && appLauncher.viewMode === "grid") {
|
} else if (event.key === Qt.Key_Left && appLauncher.viewMode === "grid") {
|
||||||
appLauncher.selectPreviousInRow()
|
appLauncher.selectPreviousInRow()
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
|
} else if (event.key == Qt.Key_J && event.modifiers & Qt.ControlModifier) {
|
||||||
|
appLauncher.selectNext()
|
||||||
|
event.accepted = true
|
||||||
|
} else if (event.key == Qt.Key_K && event.modifiers & Qt.ControlModifier) {
|
||||||
|
appLauncher.selectPrevious()
|
||||||
|
event.accepted = true
|
||||||
|
} else if (event.key == Qt.Key_L && event.modifiers & Qt.ControlModifier && appLauncher.viewMode === "grid") {
|
||||||
|
appLauncher.selectNextInRow()
|
||||||
|
event.accepted = true
|
||||||
|
} else if (event.key == Qt.Key_H && event.modifiers & Qt.ControlModifier && appLauncher.viewMode === "grid") {
|
||||||
|
appLauncher.selectPreviousInRow()
|
||||||
|
event.accepted = true
|
||||||
|
} else if (event.key === Qt.Key_Tab) {
|
||||||
|
if (appLauncher.viewMode === "grid") {
|
||||||
|
appLauncher.selectNextInRow()
|
||||||
|
} else {
|
||||||
|
appLauncher.selectNext()
|
||||||
|
}
|
||||||
|
event.accepted = true
|
||||||
|
} else if (event.key === Qt.Key_Backtab) {
|
||||||
|
if (appLauncher.viewMode === "grid") {
|
||||||
|
appLauncher.selectPreviousInRow()
|
||||||
|
} else {
|
||||||
|
appLauncher.selectPrevious()
|
||||||
|
}
|
||||||
|
event.accepted = true
|
||||||
|
} else if (event.key === Qt.Key_N && event.modifiers & Qt.ControlModifier) {
|
||||||
|
if (appLauncher.viewMode === "grid") {
|
||||||
|
appLauncher.selectNextInRow()
|
||||||
|
} else {
|
||||||
|
appLauncher.selectNext()
|
||||||
|
}
|
||||||
|
event.accepted = true
|
||||||
|
} else if (event.key === Qt.Key_P && event.modifiers & Qt.ControlModifier) {
|
||||||
|
if (appLauncher.viewMode === "grid") {
|
||||||
|
appLauncher.selectPreviousInRow()
|
||||||
|
} else {
|
||||||
|
appLauncher.selectPrevious()
|
||||||
|
}
|
||||||
|
event.accepted = true
|
||||||
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||||
appLauncher.launchSelected()
|
appLauncher.launchSelected()
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
} else if (!searchField.activeFocus && event.text && event.text.length > 0 && event.text.match(/[a-zA-Z0-9\\s]/)) {
|
|
||||||
searchField.forceActiveFocus()
|
|
||||||
searchField.insertText(event.text)
|
|
||||||
event.accepted = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,43 +100,22 @@ Item {
|
|||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingL
|
anchors.margins: Theme.spacingM
|
||||||
spacing: Theme.spacingL
|
spacing: Theme.spacingM
|
||||||
|
clip: false
|
||||||
Rectangle {
|
|
||||||
width: parent.width
|
|
||||||
height: categorySelector.height + Theme.spacingM * 2
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: Theme.surfaceVariantAlpha
|
|
||||||
border.color: Theme.outlineMedium
|
|
||||||
border.width: 1
|
|
||||||
visible: appLauncher.categories.length > 1 || appLauncher.model.count > 0
|
|
||||||
|
|
||||||
CategorySelector {
|
|
||||||
id: categorySelector
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: parent.width - Theme.spacingM * 2
|
|
||||||
categories: appLauncher.categories
|
|
||||||
selectedCategory: appLauncher.selectedCategory
|
|
||||||
compact: false
|
|
||||||
onCategorySelected: category => {
|
|
||||||
appLauncher.setCategory(category)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
leftPadding: Theme.spacingS
|
||||||
|
|
||||||
DankTextField {
|
DankTextField {
|
||||||
id: searchField
|
id: searchField
|
||||||
|
|
||||||
width: parent.width - 80 - Theme.spacingM
|
width: parent.width - 80 - Theme.spacingL
|
||||||
height: 56
|
height: 56
|
||||||
cornerRadius: Theme.cornerRadius
|
cornerRadius: Theme.cornerRadius
|
||||||
backgroundColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.7)
|
backgroundColor: Theme.surfaceContainerHigh
|
||||||
normalBorderColor: Theme.outlineMedium
|
normalBorderColor: Theme.outlineMedium
|
||||||
focusedBorderColor: Theme.primary
|
focusedBorderColor: Theme.primary
|
||||||
leftIconName: "search"
|
leftIconName: "search"
|
||||||
@@ -107,7 +127,8 @@ Item {
|
|||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
enabled: parentModal ? parentModal.spotlightOpen : true
|
enabled: parentModal ? parentModal.spotlightOpen : true
|
||||||
placeholderText: ""
|
placeholderText: ""
|
||||||
ignoreLeftRightKeys: true
|
ignoreLeftRightKeys: appLauncher.viewMode !== "list"
|
||||||
|
ignoreTabKeys: true
|
||||||
keyForwardTargets: [spotlightKeyHandler]
|
keyForwardTargets: [spotlightKeyHandler]
|
||||||
text: appLauncher.searchQuery
|
text: appLauncher.searchQuery
|
||||||
onTextEdited: () => {
|
onTextEdited: () => {
|
||||||
@@ -125,7 +146,7 @@ Item {
|
|||||||
else if (appLauncher.model.count > 0)
|
else if (appLauncher.model.count > 0)
|
||||||
appLauncher.launchApp(appLauncher.model.get(0))
|
appLauncher.launchApp(appLauncher.model.get(0))
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
} else if (event.key === Qt.Key_Down || event.key === Qt.Key_Up || event.key === Qt.Key_Left || event.key === Qt.Key_Right || ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && text.length === 0)) {
|
} else if (event.key === Qt.Key_Down || event.key === Qt.Key_Up || event.key === Qt.Key_Left || event.key === Qt.Key_Right || event.key === Qt.Key_Tab || event.key === Qt.Key_Backtab || ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && text.length === 0)) {
|
||||||
event.accepted = false
|
event.accepted = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,8 +162,6 @@ Item {
|
|||||||
height: 36
|
height: 36
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: appLauncher.viewMode === "list" ? Theme.primaryHover : listViewArea.containsMouse ? Theme.surfaceHover : "transparent"
|
color: appLauncher.viewMode === "list" ? Theme.primaryHover : listViewArea.containsMouse ? Theme.surfaceHover : "transparent"
|
||||||
border.color: appLauncher.viewMode === "list" ? Theme.primarySelected : "transparent"
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -168,8 +187,6 @@ Item {
|
|||||||
height: 36
|
height: 36
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: appLauncher.viewMode === "grid" ? Theme.primaryHover : gridViewArea.containsMouse ? Theme.surfaceHover : "transparent"
|
color: appLauncher.viewMode === "grid" ? Theme.primaryHover : gridViewArea.containsMouse ? Theme.surfaceHover : "transparent"
|
||||||
border.color: appLauncher.viewMode === "grid" ? Theme.primarySelected : "transparent"
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -193,6 +210,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SpotlightResults {
|
SpotlightResults {
|
||||||
|
id: resultsView
|
||||||
appLauncher: spotlightKeyHandler.appLauncher
|
appLauncher: spotlightKeyHandler.appLauncher
|
||||||
contextMenu: contextMenu
|
contextMenu: contextMenu
|
||||||
}
|
}
|
||||||
@@ -210,7 +228,7 @@ Item {
|
|||||||
visible: contextMenu.visible
|
visible: contextMenu.visible
|
||||||
z: 999
|
z: 999
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
contextMenu.close()
|
contextMenu.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
|||||||
@@ -1,65 +1,73 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
Popup {
|
||||||
id: contextMenu
|
id: contextMenu
|
||||||
|
|
||||||
property var currentApp: null
|
property var currentApp: null
|
||||||
property bool menuVisible: false
|
|
||||||
property var appLauncher: null
|
property var appLauncher: null
|
||||||
property var parentHandler: null
|
property var parentHandler: null
|
||||||
|
readonly property var desktopEntry: (currentApp && !currentApp.isPlugin && appLauncher && appLauncher._uniqueApps && currentApp.appIndex >= 0 && currentApp.appIndex < appLauncher._uniqueApps.length) ? appLauncher._uniqueApps[currentApp.appIndex] : null
|
||||||
|
|
||||||
function show(x, y, app) {
|
function show(x, y, app) {
|
||||||
currentApp = app
|
currentApp = app
|
||||||
const menuWidth = 180
|
contextMenu.x = x + 4
|
||||||
const menuHeight = menuColumn.implicitHeight + Theme.spacingS * 2
|
contextMenu.y = y + 4
|
||||||
let finalX = x + 8
|
contextMenu.open()
|
||||||
let finalY = y + 8
|
|
||||||
if (parentHandler) {
|
|
||||||
if (finalX + menuWidth > parentHandler.width)
|
|
||||||
finalX = x - menuWidth - 8
|
|
||||||
|
|
||||||
if (finalY + menuHeight > parentHandler.height)
|
|
||||||
finalY = y - menuHeight - 8
|
|
||||||
|
|
||||||
finalX = Math.max(8, Math.min(finalX, parentHandler.width - menuWidth - 8))
|
|
||||||
finalY = Math.max(8, Math.min(finalY, parentHandler.height - menuHeight - 8))
|
|
||||||
}
|
|
||||||
contextMenu.x = finalX
|
|
||||||
contextMenu.y = finalY
|
|
||||||
contextMenu.visible = true
|
|
||||||
contextMenu.menuVisible = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function close() {
|
function hide() {
|
||||||
contextMenu.menuVisible = false
|
contextMenu.close()
|
||||||
Qt.callLater(() => {
|
|
||||||
contextMenu.visible = false
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
visible: false
|
width: Math.max(180, menuColumn.implicitWidth + Theme.spacingS * 2)
|
||||||
width: 180
|
|
||||||
height: menuColumn.implicitHeight + Theme.spacingS * 2
|
height: menuColumn.implicitHeight + Theme.spacingS * 2
|
||||||
radius: Theme.cornerRadius
|
padding: 0
|
||||||
color: Theme.popupBackground()
|
closePolicy: Popup.CloseOnPressOutside
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
modal: false
|
||||||
border.width: 1
|
dim: false
|
||||||
z: 1000
|
|
||||||
opacity: menuVisible ? 1 : 0
|
|
||||||
scale: menuVisible ? 1 : 0.85
|
|
||||||
|
|
||||||
Rectangle {
|
background: Rectangle {
|
||||||
anchors.fill: parent
|
radius: Theme.cornerRadius
|
||||||
anchors.topMargin: 4
|
color: Theme.popupBackground()
|
||||||
anchors.leftMargin: 2
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
anchors.rightMargin: -2
|
border.width: 1
|
||||||
anchors.bottomMargin: -4
|
|
||||||
radius: parent.radius
|
Rectangle {
|
||||||
color: Qt.rgba(0, 0, 0, 0.15)
|
anchors.fill: parent
|
||||||
z: parent.z - 1
|
anchors.topMargin: 4
|
||||||
|
anchors.leftMargin: 2
|
||||||
|
anchors.rightMargin: -2
|
||||||
|
anchors.bottomMargin: -4
|
||||||
|
radius: parent.radius
|
||||||
|
color: Qt.rgba(0, 0, 0, 0.15)
|
||||||
|
z: -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enter: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
from: 0
|
||||||
|
to: 1
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
from: 1
|
||||||
|
to: 0
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -76,6 +84,7 @@ Rectangle {
|
|||||||
color: pinMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
color: pinMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
|
id: pinRow
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: Theme.spacingS
|
anchors.leftMargin: Theme.spacingS
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
@@ -83,10 +92,10 @@ Rectangle {
|
|||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
name: {
|
name: {
|
||||||
if (!contextMenu.currentApp || !contextMenu.currentApp.desktopEntry)
|
if (!desktopEntry)
|
||||||
return "push_pin"
|
return "push_pin"
|
||||||
|
|
||||||
const appId = contextMenu.currentApp.desktopEntry.id || contextMenu.currentApp.desktopEntry.execString || ""
|
const appId = desktopEntry.id || desktopEntry.execString || ""
|
||||||
return SessionData.isPinnedApp(appId) ? "keep_off" : "push_pin"
|
return SessionData.isPinnedApp(appId) ? "keep_off" : "push_pin"
|
||||||
}
|
}
|
||||||
size: Theme.iconSize - 2
|
size: Theme.iconSize - 2
|
||||||
@@ -97,11 +106,11 @@ Rectangle {
|
|||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: {
|
text: {
|
||||||
if (!contextMenu.currentApp || !contextMenu.currentApp.desktopEntry)
|
if (!desktopEntry)
|
||||||
return "Pin to Dock"
|
return I18n.tr("Pin to Dock")
|
||||||
|
|
||||||
const appId = contextMenu.currentApp.desktopEntry.id || contextMenu.currentApp.desktopEntry.execString || ""
|
const appId = desktopEntry.id || desktopEntry.execString || ""
|
||||||
return SessionData.isPinnedApp(appId) ? "Unpin from Dock" : "Pin to Dock"
|
return SessionData.isPinnedApp(appId) ? I18n.tr("Unpin from Dock") : I18n.tr("Pin to Dock")
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
@@ -117,15 +126,15 @@ Rectangle {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
if (!contextMenu.currentApp || !contextMenu.currentApp.desktopEntry)
|
if (!desktopEntry)
|
||||||
return
|
return
|
||||||
|
|
||||||
const appId = contextMenu.currentApp.desktopEntry.id || contextMenu.currentApp.desktopEntry.execString || ""
|
const appId = desktopEntry.id || desktopEntry.execString || ""
|
||||||
if (SessionData.isPinnedApp(appId))
|
if (SessionData.isPinnedApp(appId))
|
||||||
SessionData.removePinnedApp(appId)
|
SessionData.removePinnedApp(appId)
|
||||||
else
|
else
|
||||||
SessionData.addPinnedApp(appId)
|
SessionData.addPinnedApp(appId)
|
||||||
contextMenu.close()
|
contextMenu.hide()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,6 +153,79 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: desktopEntry && desktopEntry.actions ? desktopEntry.actions : []
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: actionMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: actionRow
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: Theme.iconSize - 2
|
||||||
|
height: Theme.iconSize - 2
|
||||||
|
visible: modelData.icon && modelData.icon !== ""
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: modelData.icon ? Quickshell.iconPath(modelData.icon, true) : ""
|
||||||
|
smooth: true
|
||||||
|
asynchronous: true
|
||||||
|
visible: status === Image.Ready
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.name || ""
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Normal
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: actionMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (modelData && desktopEntry) {
|
||||||
|
SessionService.launchDesktopAction(desktopEntry, modelData)
|
||||||
|
if (appLauncher && contextMenu.currentApp) {
|
||||||
|
appLauncher.appLaunched(contextMenu.currentApp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contextMenu.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
visible: desktopEntry && desktopEntry.actions && desktopEntry.actions.length > 0
|
||||||
|
width: parent.width - Theme.spacingS * 2
|
||||||
|
height: 5
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 32
|
height: 32
|
||||||
@@ -151,6 +233,7 @@ Rectangle {
|
|||||||
color: launchMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
color: launchMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
|
id: launchRow
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: Theme.spacingS
|
anchors.leftMargin: Theme.spacingS
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
@@ -165,7 +248,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Launch"
|
text: I18n.tr("Launch")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Normal
|
font.weight: Font.Normal
|
||||||
@@ -183,23 +266,73 @@ Rectangle {
|
|||||||
if (contextMenu.currentApp && appLauncher)
|
if (contextMenu.currentApp && appLauncher)
|
||||||
appLauncher.launchApp(contextMenu.currentApp)
|
appLauncher.launchApp(contextMenu.currentApp)
|
||||||
|
|
||||||
contextMenu.close()
|
contextMenu.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
visible: SessionService.hasPrimeRun
|
||||||
|
width: parent.width - Theme.spacingS * 2
|
||||||
|
height: 5
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
visible: SessionService.hasPrimeRun
|
||||||
|
width: parent.width
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: primeRunMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: primeRunRow
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "memory"
|
||||||
|
size: Theme.iconSize - 2
|
||||||
|
color: Theme.surfaceText
|
||||||
|
opacity: 0.7
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Launch on dGPU")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Normal
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: primeRunMouseArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: () => {
|
||||||
|
if (desktopEntry) {
|
||||||
|
SessionService.launchDesktopEntry(desktopEntry, true)
|
||||||
|
if (appLauncher && contextMenu.currentApp) {
|
||||||
|
appLauncher.appLaunched(contextMenu.currentApp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contextMenu.hide()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on scale {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,29 +13,57 @@ DankModal {
|
|||||||
id: spotlightModal
|
id: spotlightModal
|
||||||
|
|
||||||
property bool spotlightOpen: false
|
property bool spotlightOpen: false
|
||||||
property Component spotlightContent
|
property alias spotlightContent: spotlightContentInstance
|
||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
spotlightOpen = true
|
spotlightOpen = true
|
||||||
open()
|
open()
|
||||||
if (contentLoader.item && contentLoader.item.appLauncher) {
|
|
||||||
contentLoader.item.appLauncher.searchQuery = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (contentLoader.item && contentLoader.item.searchField) {
|
if (spotlightContent && spotlightContent.searchField) {
|
||||||
contentLoader.item.searchField.forceActiveFocus()
|
spotlightContent.searchField.forceActiveFocus()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function showWithQuery(query) {
|
||||||
|
if (spotlightContent) {
|
||||||
|
if (spotlightContent.appLauncher) {
|
||||||
|
spotlightContent.appLauncher.searchQuery = query
|
||||||
|
}
|
||||||
|
if (spotlightContent.searchField) {
|
||||||
|
spotlightContent.searchField.text = query
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spotlightOpen = true
|
||||||
|
open()
|
||||||
|
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (spotlightContent && spotlightContent.searchField) {
|
||||||
|
spotlightContent.searchField.forceActiveFocus()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function hide() {
|
function hide() {
|
||||||
spotlightOpen = false
|
spotlightOpen = false
|
||||||
close()
|
close()
|
||||||
if (contentLoader.item && contentLoader.item.appLauncher) {
|
}
|
||||||
contentLoader.item.appLauncher.searchQuery = ""
|
|
||||||
contentLoader.item.appLauncher.selectedIndex = 0
|
onDialogClosed: {
|
||||||
contentLoader.item.appLauncher.setCategory("All")
|
if (spotlightContent) {
|
||||||
|
if (spotlightContent.appLauncher) {
|
||||||
|
spotlightContent.appLauncher.searchQuery = ""
|
||||||
|
spotlightContent.appLauncher.selectedIndex = 0
|
||||||
|
spotlightContent.appLauncher.setCategory(I18n.tr("All"))
|
||||||
|
}
|
||||||
|
if (spotlightContent.resetScroll) {
|
||||||
|
spotlightContent.resetScroll()
|
||||||
|
}
|
||||||
|
if (spotlightContent.searchField) {
|
||||||
|
spotlightContent.searchField.text = ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,20 +77,21 @@ DankModal {
|
|||||||
|
|
||||||
shouldBeVisible: spotlightOpen
|
shouldBeVisible: spotlightOpen
|
||||||
width: 550
|
width: 550
|
||||||
height: 600
|
height: 700
|
||||||
backgroundColor: Theme.popupBackground()
|
backgroundColor: Theme.popupBackground()
|
||||||
cornerRadius: Theme.cornerRadius
|
cornerRadius: Theme.cornerRadius
|
||||||
borderColor: Theme.outlineMedium
|
borderColor: Theme.outlineMedium
|
||||||
borderWidth: 1
|
borderWidth: 1
|
||||||
enableShadow: true
|
enableShadow: true
|
||||||
|
keepContentLoaded: true
|
||||||
onVisibleChanged: () => {
|
onVisibleChanged: () => {
|
||||||
if (visible && !spotlightOpen) {
|
if (visible && !spotlightOpen) {
|
||||||
show()
|
show()
|
||||||
}
|
}
|
||||||
if (visible && contentLoader.item) {
|
if (visible && spotlightContent) {
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (contentLoader.item.searchField) {
|
if (spotlightContent.searchField) {
|
||||||
contentLoader.item.searchField.forceActiveFocus()
|
spotlightContent.searchField.forceActiveFocus()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -70,7 +99,6 @@ DankModal {
|
|||||||
onBackgroundClicked: () => {
|
onBackgroundClicked: () => {
|
||||||
return hide()
|
return hide()
|
||||||
}
|
}
|
||||||
content: spotlightContent
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onCloseAllModalsExcept(excludedModal) {
|
function onCloseAllModalsExcept(excludedModal) {
|
||||||
@@ -98,12 +126,28 @@ DankModal {
|
|||||||
return "SPOTLIGHT_TOGGLE_SUCCESS"
|
return "SPOTLIGHT_TOGGLE_SUCCESS"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openQuery(query: string): string {
|
||||||
|
spotlightModal.showWithQuery(query)
|
||||||
|
return "SPOTLIGHT_OPEN_QUERY_SUCCESS"
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleQuery(query: string): string {
|
||||||
|
if (spotlightModal.spotlightOpen) {
|
||||||
|
spotlightModal.hide()
|
||||||
|
} else {
|
||||||
|
spotlightModal.showWithQuery(query)
|
||||||
|
}
|
||||||
|
return "SPOTLIGHT_TOGGLE_QUERY_SUCCESS"
|
||||||
|
}
|
||||||
|
|
||||||
target: "spotlight"
|
target: "spotlight"
|
||||||
}
|
}
|
||||||
|
|
||||||
spotlightContent: Component {
|
SpotlightContent {
|
||||||
SpotlightContent {
|
id: spotlightContentInstance
|
||||||
parentModal: spotlightModal
|
|
||||||
}
|
parentModal: spotlightModal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
directContent: spotlightContentInstance
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,12 +10,16 @@ Rectangle {
|
|||||||
property var appLauncher: null
|
property var appLauncher: null
|
||||||
property var contextMenu: null
|
property var contextMenu: null
|
||||||
|
|
||||||
|
function resetScroll() {
|
||||||
|
resultsList.contentY = 0
|
||||||
|
resultsGrid.contentY = 0
|
||||||
|
}
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height - y
|
height: parent.height - y
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Theme.surfaceLight
|
color: "transparent"
|
||||||
border.color: Theme.outlineLight
|
clip: true
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
DankListView {
|
DankListView {
|
||||||
id: resultsList
|
id: resultsList
|
||||||
@@ -75,9 +79,7 @@ Rectangle {
|
|||||||
width: ListView.view.width
|
width: ListView.view.width
|
||||||
height: resultsList.itemHeight
|
height: resultsList.itemHeight
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: ListView.isCurrentItem ? Theme.primaryPressed : listMouseArea.containsMouse ? Theme.primaryHoverLight : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.03)
|
color: ListView.isCurrentItem ? Theme.primaryPressed : listMouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceContainerHigh
|
||||||
border.color: ListView.isCurrentItem ? Theme.primarySelected : Theme.outlineMedium
|
|
||||||
border.width: ListView.isCurrentItem ? 2 : 1
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -128,6 +130,8 @@ Rectangle {
|
|||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
maximumLineCount: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
@@ -160,8 +164,9 @@ Rectangle {
|
|||||||
onClicked: mouse => {
|
onClicked: mouse => {
|
||||||
if (mouse.button === Qt.LeftButton) {
|
if (mouse.button === Qt.LeftButton) {
|
||||||
resultsList.itemClicked(index, model)
|
resultsList.itemClicked(index, model)
|
||||||
} else if (mouse.button === Qt.RightButton) {
|
} else if (mouse.button === Qt.RightButton && !model.isPlugin) {
|
||||||
const modalPos = mapToItem(resultsContainer.parent, mouse.x, mouse.y)
|
const globalPos = mapToItem(null, mouse.x, mouse.y)
|
||||||
|
const modalPos = resultsContainer.parent.mapFromItem(null, globalPos.x, globalPos.y)
|
||||||
resultsList.itemRightClicked(index, model, modalPos.x, modalPos.y)
|
resultsList.itemRightClicked(index, model, modalPos.x, modalPos.y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,9 +243,7 @@ Rectangle {
|
|||||||
width: resultsGrid.cellWidth - resultsGrid.cellPadding
|
width: resultsGrid.cellWidth - resultsGrid.cellPadding
|
||||||
height: resultsGrid.cellHeight - resultsGrid.cellPadding
|
height: resultsGrid.cellHeight - resultsGrid.cellPadding
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: resultsGrid.currentIndex === index ? Theme.primaryPressed : gridMouseArea.containsMouse ? Theme.primaryHoverLight : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.03)
|
color: resultsGrid.currentIndex === index ? Theme.primaryPressed : gridMouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceContainerHigh
|
||||||
border.color: resultsGrid.currentIndex === index ? Theme.primarySelected : Theme.outlineMedium
|
|
||||||
border.width: resultsGrid.currentIndex === index ? 2 : 1
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -290,8 +293,8 @@ Rectangle {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
maximumLineCount: 2
|
maximumLineCount: 1
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.NoWrap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,8 +316,9 @@ Rectangle {
|
|||||||
onClicked: mouse => {
|
onClicked: mouse => {
|
||||||
if (mouse.button === Qt.LeftButton) {
|
if (mouse.button === Qt.LeftButton) {
|
||||||
resultsGrid.itemClicked(index, model)
|
resultsGrid.itemClicked(index, model)
|
||||||
} else if (mouse.button === Qt.RightButton) {
|
} else if (mouse.button === Qt.RightButton && !model.isPlugin) {
|
||||||
const modalPos = mapToItem(resultsContainer.parent, mouse.x, mouse.y)
|
const globalPos = mapToItem(null, mouse.x, mouse.y)
|
||||||
|
const modalPos = resultsContainer.parent.mapFromItem(null, globalPos.x, globalPos.y)
|
||||||
resultsGrid.itemRightClicked(index, model, modalPos.x, modalPos.y)
|
resultsGrid.itemRightClicked(index, model, modalPos.x, modalPos.y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,33 +9,62 @@ DankModal {
|
|||||||
|
|
||||||
property string wifiPasswordSSID: ""
|
property string wifiPasswordSSID: ""
|
||||||
property string wifiPasswordInput: ""
|
property string wifiPasswordInput: ""
|
||||||
|
property string wifiUsernameInput: ""
|
||||||
|
property bool requiresEnterprise: false
|
||||||
|
|
||||||
|
property string wifiAnonymousIdentityInput: ""
|
||||||
|
property string wifiDomainInput: ""
|
||||||
|
|
||||||
function show(ssid) {
|
function show(ssid) {
|
||||||
wifiPasswordSSID = ssid
|
wifiPasswordSSID = ssid
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
|
wifiUsernameInput = ""
|
||||||
|
wifiAnonymousIdentityInput = ""
|
||||||
|
wifiDomainInput = ""
|
||||||
|
|
||||||
|
const network = NetworkService.wifiNetworks.find(n => n.ssid === ssid)
|
||||||
|
requiresEnterprise = network?.enterprise || false
|
||||||
|
|
||||||
open()
|
open()
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (contentLoader.item && contentLoader.item.passwordInput)
|
if (contentLoader.item) {
|
||||||
contentLoader.item.passwordInput.forceActiveFocus()
|
if (requiresEnterprise && contentLoader.item.usernameInput) {
|
||||||
|
contentLoader.item.usernameInput.forceActiveFocus()
|
||||||
|
} else if (contentLoader.item.passwordInput) {
|
||||||
|
contentLoader.item.passwordInput.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldBeVisible: false
|
shouldBeVisible: false
|
||||||
width: 420
|
width: 420
|
||||||
height: 230
|
height: requiresEnterprise ? 430 : 230
|
||||||
onShouldBeVisibleChanged: () => {
|
onShouldBeVisibleChanged: () => {
|
||||||
if (!shouldBeVisible)
|
if (!shouldBeVisible) {
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
|
wifiUsernameInput = ""
|
||||||
|
wifiAnonymousIdentityInput = ""
|
||||||
|
wifiDomainInput = ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
onOpened: {
|
onOpened: {
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (contentLoader.item && contentLoader.item.passwordInput)
|
if (contentLoader.item) {
|
||||||
contentLoader.item.passwordInput.forceActiveFocus()
|
if (requiresEnterprise && contentLoader.item.usernameInput) {
|
||||||
|
contentLoader.item.usernameInput.forceActiveFocus()
|
||||||
|
} else if (contentLoader.item.passwordInput) {
|
||||||
|
contentLoader.item.passwordInput.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
onBackgroundClicked: () => {
|
onBackgroundClicked: () => {
|
||||||
close()
|
close()
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
|
wifiUsernameInput = ""
|
||||||
|
wifiAnonymousIdentityInput = ""
|
||||||
|
wifiDomainInput = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
@@ -55,6 +84,7 @@ DankModal {
|
|||||||
FocusScope {
|
FocusScope {
|
||||||
id: wifiContent
|
id: wifiContent
|
||||||
|
|
||||||
|
property alias usernameInput: usernameInput
|
||||||
property alias passwordInput: passwordInput
|
property alias passwordInput: passwordInput
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -62,6 +92,9 @@ DankModal {
|
|||||||
Keys.onEscapePressed: event => {
|
Keys.onEscapePressed: event => {
|
||||||
close()
|
close()
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
|
wifiUsernameInput = ""
|
||||||
|
wifiAnonymousIdentityInput = ""
|
||||||
|
wifiDomainInput = ""
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,14 +111,14 @@ DankModal {
|
|||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Connect to Wi-Fi"
|
text: I18n.tr("Connect to Wi-Fi")
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: `Enter password for "${wifiPasswordSSID}"`
|
text: requiresEnterprise ? I18n.tr("Enter credentials for ") + wifiPasswordSSID : I18n.tr("Enter password for ") + wifiPasswordSSID
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceTextMedium
|
color: Theme.surfaceTextMedium
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -100,10 +133,50 @@ DankModal {
|
|||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
close()
|
close()
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
|
wifiUsernameInput = ""
|
||||||
|
wifiAnonymousIdentityInput = ""
|
||||||
|
wifiDomainInput = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 50
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.surfaceHover
|
||||||
|
border.color: usernameInput.activeFocus ? Theme.primary : Theme.outlineStrong
|
||||||
|
border.width: usernameInput.activeFocus ? 2 : 1
|
||||||
|
visible: requiresEnterprise
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: () => {
|
||||||
|
usernameInput.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankTextField {
|
||||||
|
id: usernameInput
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
textColor: Theme.surfaceText
|
||||||
|
text: wifiUsernameInput
|
||||||
|
placeholderText: I18n.tr("Username")
|
||||||
|
backgroundColor: "transparent"
|
||||||
|
enabled: root.shouldBeVisible
|
||||||
|
onTextEdited: () => {
|
||||||
|
wifiUsernameInput = text
|
||||||
|
}
|
||||||
|
onAccepted: () => {
|
||||||
|
if (passwordInput) {
|
||||||
|
passwordInput.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 50
|
height: 50
|
||||||
@@ -127,21 +200,32 @@ DankModal {
|
|||||||
textColor: Theme.surfaceText
|
textColor: Theme.surfaceText
|
||||||
text: wifiPasswordInput
|
text: wifiPasswordInput
|
||||||
echoMode: showPasswordCheckbox.checked ? TextInput.Normal : TextInput.Password
|
echoMode: showPasswordCheckbox.checked ? TextInput.Normal : TextInput.Password
|
||||||
placeholderText: ""
|
placeholderText: requiresEnterprise ? I18n.tr("Password") : ""
|
||||||
backgroundColor: "transparent"
|
backgroundColor: "transparent"
|
||||||
focus: true
|
focus: !requiresEnterprise
|
||||||
enabled: root.shouldBeVisible
|
enabled: root.shouldBeVisible
|
||||||
onTextEdited: () => {
|
onTextEdited: () => {
|
||||||
wifiPasswordInput = text
|
wifiPasswordInput = text
|
||||||
}
|
}
|
||||||
onAccepted: () => {
|
onAccepted: () => {
|
||||||
NetworkService.connectToWifi(wifiPasswordSSID, passwordInput.text)
|
const username = requiresEnterprise ? usernameInput.text : ""
|
||||||
|
NetworkService.connectToWifi(
|
||||||
|
wifiPasswordSSID,
|
||||||
|
passwordInput.text,
|
||||||
|
username,
|
||||||
|
wifiAnonymousIdentityInput,
|
||||||
|
wifiDomainInput
|
||||||
|
)
|
||||||
close()
|
close()
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
|
wifiUsernameInput = ""
|
||||||
|
wifiAnonymousIdentityInput = ""
|
||||||
|
wifiDomainInput = ""
|
||||||
passwordInput.text = ""
|
passwordInput.text = ""
|
||||||
|
if (requiresEnterprise) usernameInput.text = ""
|
||||||
}
|
}
|
||||||
Component.onCompleted: () => {
|
Component.onCompleted: () => {
|
||||||
if (root.shouldBeVisible)
|
if (root.shouldBeVisible && !requiresEnterprise)
|
||||||
focusDelayTimer.start()
|
focusDelayTimer.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,8 +235,13 @@ DankModal {
|
|||||||
interval: 100
|
interval: 100
|
||||||
repeat: false
|
repeat: false
|
||||||
onTriggered: () => {
|
onTriggered: () => {
|
||||||
if (root.shouldBeVisible)
|
if (root.shouldBeVisible) {
|
||||||
passwordInput.forceActiveFocus()
|
if (requiresEnterprise && usernameInput) {
|
||||||
|
usernameInput.forceActiveFocus()
|
||||||
|
} else {
|
||||||
|
passwordInput.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,6 +256,70 @@ DankModal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
visible: requiresEnterprise
|
||||||
|
width: parent.width
|
||||||
|
height: 50
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.surfaceHover
|
||||||
|
border.color: anonInput.activeFocus ? Theme.primary : Theme.outlineStrong
|
||||||
|
border.width: anonInput.activeFocus ? 2 : 1
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: () => {
|
||||||
|
anonInput.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankTextField {
|
||||||
|
id: anonInput
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
textColor: Theme.surfaceText
|
||||||
|
text: wifiAnonymousIdentityInput
|
||||||
|
placeholderText: I18n.tr("Anonymous Identity (optional)")
|
||||||
|
backgroundColor: "transparent"
|
||||||
|
enabled: root.shouldBeVisible
|
||||||
|
onTextEdited: () => {
|
||||||
|
wifiAnonymousIdentityInput = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
visible: requiresEnterprise
|
||||||
|
width: parent.width
|
||||||
|
height: 50
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.surfaceHover
|
||||||
|
border.color: domainMatchInput.activeFocus ? Theme.primary : Theme.outlineStrong
|
||||||
|
border.width: domainMatchInput.activeFocus ? 2 : 1
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: () => {
|
||||||
|
domainMatchInput.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankTextField {
|
||||||
|
id: domainMatchInput
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
textColor: Theme.surfaceText
|
||||||
|
text: wifiDomainInput
|
||||||
|
placeholderText: I18n.tr("Domain (optional)")
|
||||||
|
backgroundColor: "transparent"
|
||||||
|
enabled: root.shouldBeVisible
|
||||||
|
onTextEdited: () => {
|
||||||
|
wifiDomainInput = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
@@ -201,7 +354,7 @@ DankModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Show password"
|
text: I18n.tr("Show password")
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
@@ -229,7 +382,7 @@ DankModal {
|
|||||||
id: cancelText
|
id: cancelText
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: "Cancel"
|
text: I18n.tr("Cancel")
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -244,6 +397,9 @@ DankModal {
|
|||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
close()
|
close()
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
|
wifiUsernameInput = ""
|
||||||
|
wifiAnonymousIdentityInput = ""
|
||||||
|
wifiDomainInput = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -253,14 +409,14 @@ DankModal {
|
|||||||
height: 36
|
height: 36
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: connectArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
|
color: connectArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
|
||||||
enabled: passwordInput.text.length > 0
|
enabled: requiresEnterprise ? (usernameInput.text.length > 0 && passwordInput.text.length > 0) : passwordInput.text.length > 0
|
||||||
opacity: enabled ? 1 : 0.5
|
opacity: enabled ? 1 : 0.5
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
id: connectText
|
id: connectText
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: "Connect"
|
text: I18n.tr("Connect")
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.background
|
color: Theme.background
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -274,10 +430,21 @@ DankModal {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
enabled: parent.enabled
|
enabled: parent.enabled
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
NetworkService.connectToWifi(wifiPasswordSSID, passwordInput.text)
|
const username = requiresEnterprise ? usernameInput.text : ""
|
||||||
|
NetworkService.connectToWifi(
|
||||||
|
wifiPasswordSSID,
|
||||||
|
passwordInput.text,
|
||||||
|
username,
|
||||||
|
wifiAnonymousIdentityInput,
|
||||||
|
wifiDomainInput
|
||||||
|
)
|
||||||
close()
|
close()
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
|
wifiUsernameInput = ""
|
||||||
|
wifiAnonymousIdentityInput = ""
|
||||||
|
wifiDomainInput = ""
|
||||||
passwordInput.text = ""
|
passwordInput.text = ""
|
||||||
|
if (requiresEnterprise) usernameInput.text = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
import QtQuick.Effects
|
import QtQuick.Effects
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
@@ -12,7 +13,6 @@ import qs.Widgets
|
|||||||
DankPopout {
|
DankPopout {
|
||||||
id: appDrawerPopout
|
id: appDrawerPopout
|
||||||
|
|
||||||
property string triggerSection: "left"
|
|
||||||
property var triggerScreen: null
|
property var triggerScreen: null
|
||||||
|
|
||||||
// Setting to Exclusive, so virtual keyboards can send input to app drawer
|
// Setting to Exclusive, so virtual keyboards can send input to app drawer
|
||||||
@@ -33,16 +33,16 @@ DankPopout {
|
|||||||
popupWidth: 520
|
popupWidth: 520
|
||||||
popupHeight: 600
|
popupHeight: 600
|
||||||
triggerX: Theme.spacingL
|
triggerX: Theme.spacingL
|
||||||
triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.spacingXS
|
triggerY: Math.max(26 + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding)) + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap - 2
|
||||||
triggerWidth: 40
|
triggerWidth: 40
|
||||||
positioning: "center"
|
positioning: ""
|
||||||
screen: triggerScreen
|
screen: triggerScreen
|
||||||
|
|
||||||
onShouldBeVisibleChanged: {
|
onShouldBeVisibleChanged: {
|
||||||
if (shouldBeVisible) {
|
if (shouldBeVisible) {
|
||||||
appLauncher.searchQuery = ""
|
appLauncher.searchQuery = ""
|
||||||
appLauncher.selectedIndex = 0
|
appLauncher.selectedIndex = 0
|
||||||
appLauncher.setCategory("All")
|
appLauncher.setCategory(I18n.tr("All"))
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (contentLoader.item && contentLoader.item.searchField) {
|
if (contentLoader.item && contentLoader.item.searchField) {
|
||||||
contentLoader.item.searchField.text = ""
|
contentLoader.item.searchField.text = ""
|
||||||
@@ -95,7 +95,7 @@ DankPopout {
|
|||||||
color: "transparent"
|
color: "transparent"
|
||||||
radius: parent.radius + Math.abs(modelData.margin)
|
radius: parent.radius + Math.abs(modelData.margin)
|
||||||
border.color: modelData.color
|
border.color: modelData.color
|
||||||
border.width: 1
|
border.width: 0
|
||||||
z: modelData.z
|
z: modelData.z
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,6 +112,8 @@ DankPopout {
|
|||||||
mappings[Qt.Key_Up] = () => appLauncher.selectPrevious()
|
mappings[Qt.Key_Up] = () => appLauncher.selectPrevious()
|
||||||
mappings[Qt.Key_Return] = () => appLauncher.launchSelected()
|
mappings[Qt.Key_Return] = () => appLauncher.launchSelected()
|
||||||
mappings[Qt.Key_Enter] = () => appLauncher.launchSelected()
|
mappings[Qt.Key_Enter] = () => appLauncher.launchSelected()
|
||||||
|
mappings[Qt.Key_Tab] = () => appLauncher.viewMode === "grid" ? appLauncher.selectNextInRow() : appLauncher.selectNext()
|
||||||
|
mappings[Qt.Key_Backtab] = () => appLauncher.viewMode === "grid" ? appLauncher.selectPreviousInRow() : appLauncher.selectPrevious()
|
||||||
|
|
||||||
if (appLauncher.viewMode === "grid") {
|
if (appLauncher.viewMode === "grid") {
|
||||||
mappings[Qt.Key_Right] = () => appLauncher.selectNextInRow()
|
mappings[Qt.Key_Right] = () => appLauncher.selectNextInRow()
|
||||||
@@ -128,38 +130,70 @@ DankPopout {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!searchField.activeFocus && event.text && /[a-zA-Z0-9\s]/.test(event.text)) {
|
if (event.key === Qt.Key_N && event.modifiers & Qt.ControlModifier) {
|
||||||
searchField.forceActiveFocus()
|
appLauncher.selectNext()
|
||||||
searchField.insertText(event.text)
|
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (event.key === Qt.Key_P && event.modifiers & Qt.ControlModifier) {
|
||||||
|
appLauncher.selectPrevious()
|
||||||
|
event.accepted = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === Qt.Key_J && event.modifiers & Qt.ControlModifier) {
|
||||||
|
appLauncher.selectNext()
|
||||||
|
event.accepted = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === Qt.Key_K && event.modifiers & Qt.ControlModifier) {
|
||||||
|
appLauncher.selectPrevious()
|
||||||
|
event.accepted = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (appLauncher.viewMode === "grid") {
|
||||||
|
if (event.key === Qt.Key_L && event.modifiers & Qt.ControlModifier) {
|
||||||
|
appLauncher.selectNextInRow()
|
||||||
|
event.accepted = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === Qt.Key_H && event.modifiers & Qt.ControlModifier) {
|
||||||
|
appLauncher.selectPreviousInRow()
|
||||||
|
event.accepted = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
width: parent.width - Theme.spacingL * 2
|
width: parent.width - Theme.spacingS * 2
|
||||||
height: parent.height - Theme.spacingL * 2
|
height: parent.height - Theme.spacingS * 2
|
||||||
x: Theme.spacingL
|
x: Theme.spacingS
|
||||||
y: Theme.spacingL
|
y: Theme.spacingS
|
||||||
spacing: Theme.spacingL
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
Row {
|
Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 40
|
height: 40
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
text: "Applications"
|
text: I18n.tr("Applications")
|
||||||
font.pixelSize: Theme.fontSizeLarge + 4
|
font.pixelSize: Theme.fontSizeLarge + 4
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
|
||||||
width: parent.width - 200
|
|
||||||
height: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
text: appLauncher.model.count + " apps"
|
text: appLauncher.model.count + " apps"
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
@@ -170,7 +204,8 @@ DankPopout {
|
|||||||
DankTextField {
|
DankTextField {
|
||||||
id: searchField
|
id: searchField
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width - Theme.spacingS * 2
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
height: 52
|
height: 52
|
||||||
cornerRadius: Theme.cornerRadius
|
cornerRadius: Theme.cornerRadius
|
||||||
backgroundColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.7)
|
backgroundColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.7)
|
||||||
@@ -183,7 +218,8 @@ DankPopout {
|
|||||||
showClearButton: true
|
showClearButton: true
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
enabled: appDrawerPopout.shouldBeVisible
|
enabled: appDrawerPopout.shouldBeVisible
|
||||||
ignoreLeftRightKeys: true
|
ignoreLeftRightKeys: appLauncher.viewMode !== "list"
|
||||||
|
ignoreTabKeys: true
|
||||||
keyForwardTargets: [keyHandler]
|
keyForwardTargets: [keyHandler]
|
||||||
onTextEdited: {
|
onTextEdited: {
|
||||||
appLauncher.searchQuery = text
|
appLauncher.searchQuery = text
|
||||||
@@ -208,7 +244,7 @@ DankPopout {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const navigationKeys = [Qt.Key_Down, Qt.Key_Up, Qt.Key_Left, Qt.Key_Right]
|
const navigationKeys = [Qt.Key_Down, Qt.Key_Up, Qt.Key_Left, Qt.Key_Right, Qt.Key_Tab, Qt.Key_Backtab]
|
||||||
const isNavigationKey = navigationKeys.includes(event.key)
|
const isNavigationKey = navigationKeys.includes(event.key)
|
||||||
const isEmptyEnter = isEnterKey && !hasText
|
const isEmptyEnter = isEnterKey && !hasText
|
||||||
|
|
||||||
@@ -231,14 +267,18 @@ DankPopout {
|
|||||||
height: 40
|
height: 40
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
visible: searchField.text.length === 0
|
visible: searchField.text.length === 0
|
||||||
|
leftPadding: Theme.spacingS
|
||||||
|
|
||||||
Item {
|
Rectangle {
|
||||||
width: 200
|
width: 180
|
||||||
height: 36
|
height: 40
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
DankDropdown {
|
DankDropdown {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
text: ""
|
text: ""
|
||||||
|
dropdownWidth: 180
|
||||||
currentValue: appLauncher.selectedCategory
|
currentValue: appLauncher.selectedCategory
|
||||||
options: appLauncher.categories
|
options: appLauncher.categories
|
||||||
optionIcons: appLauncher.categoryIcons
|
optionIcons: appLauncher.categoryIcons
|
||||||
@@ -249,7 +289,7 @@ DankPopout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
width: parent.width - 300
|
width: parent.width - 290
|
||||||
height: 1
|
height: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,15 +326,13 @@ DankPopout {
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: {
|
height: {
|
||||||
let usedHeight = 40 + Theme.spacingL
|
let usedHeight = 40 + Theme.spacingS
|
||||||
usedHeight += 52 + Theme.spacingL
|
usedHeight += 52 + Theme.spacingS
|
||||||
usedHeight += (searchField.text.length === 0 ? 40 + Theme.spacingL : 0)
|
usedHeight += (searchField.text.length === 0 ? 40 : 0)
|
||||||
return parent.height - usedHeight
|
return parent.height - usedHeight
|
||||||
}
|
}
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.1)
|
color: "transparent"
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.05)
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
DankListView {
|
DankListView {
|
||||||
id: appList
|
id: appList
|
||||||
@@ -323,7 +361,9 @@ DankPopout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingS
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
anchors.bottomMargin: Theme.spacingS
|
||||||
visible: appLauncher.viewMode === "list"
|
visible: appLauncher.viewMode === "list"
|
||||||
model: appLauncher.model
|
model: appLauncher.model
|
||||||
currentIndex: appLauncher.selectedIndex
|
currentIndex: appLauncher.selectedIndex
|
||||||
@@ -353,9 +393,7 @@ DankPopout {
|
|||||||
width: ListView.view.width
|
width: ListView.view.width
|
||||||
height: appList.itemHeight
|
height: appList.itemHeight
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: ListView.isCurrentItem ? Theme.primaryPressed : listMouseArea.containsMouse ? Theme.primaryHoverLight : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.03)
|
color: ListView.isCurrentItem ? Theme.primaryPressed : listMouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceContainerHigh
|
||||||
border.color: ListView.isCurrentItem ? Theme.primarySelected : Theme.outlineMedium
|
|
||||||
border.width: ListView.isCurrentItem ? 2 : 1
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -371,6 +409,7 @@ DankPopout {
|
|||||||
id: listIconImg
|
id: listIconImg
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingXS
|
||||||
source: Quickshell.iconPath(model.icon, true)
|
source: Quickshell.iconPath(model.icon, true)
|
||||||
smooth: true
|
smooth: true
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
@@ -379,10 +418,13 @@ DankPopout {
|
|||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
anchors.bottomMargin: Theme.spacingM
|
||||||
visible: !listIconImg.visible
|
visible: !listIconImg.visible
|
||||||
color: Theme.surfaceLight
|
color: Theme.surfaceLight
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.width: 1
|
border.width: 0
|
||||||
border.color: Theme.primarySelected
|
border.color: Theme.primarySelected
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
@@ -407,6 +449,8 @@ DankPopout {
|
|||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
maximumLineCount: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
@@ -425,6 +469,9 @@ DankPopout {
|
|||||||
id: listMouseArea
|
id: listMouseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
anchors.bottomMargin: Theme.spacingM
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
@@ -440,7 +487,8 @@ DankPopout {
|
|||||||
if (mouse.button === Qt.LeftButton) {
|
if (mouse.button === Qt.LeftButton) {
|
||||||
appList.itemClicked(index, model)
|
appList.itemClicked(index, model)
|
||||||
} else if (mouse.button === Qt.RightButton) {
|
} else if (mouse.button === Qt.RightButton) {
|
||||||
var panelPos = mapToItem(contextMenu.parent, mouse.x, mouse.y)
|
var globalPos = mapToItem(null, mouse.x, mouse.y)
|
||||||
|
var panelPos = contextMenu.parent.mapFromItem(null, globalPos.x, globalPos.y)
|
||||||
appList.itemRightClicked(index, model, panelPos.x, panelPos.y)
|
appList.itemRightClicked(index, model, panelPos.x, panelPos.y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -484,7 +532,9 @@ DankPopout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingS
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
anchors.bottomMargin: Theme.spacingS
|
||||||
visible: appLauncher.viewMode === "grid"
|
visible: appLauncher.viewMode === "grid"
|
||||||
model: appLauncher.model
|
model: appLauncher.model
|
||||||
clip: true
|
clip: true
|
||||||
@@ -516,9 +566,7 @@ DankPopout {
|
|||||||
width: appGrid.cellWidth - appGrid.cellPadding
|
width: appGrid.cellWidth - appGrid.cellPadding
|
||||||
height: appGrid.cellHeight - appGrid.cellPadding
|
height: appGrid.cellHeight - appGrid.cellPadding
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: appGrid.currentIndex === index ? Theme.primaryPressed : gridMouseArea.containsMouse ? Theme.primaryHoverLight : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.03)
|
color: appGrid.currentIndex === index ? Theme.primaryPressed : gridMouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceContainerHigh
|
||||||
border.color: appGrid.currentIndex === index ? Theme.primarySelected : Theme.outlineMedium
|
|
||||||
border.width: appGrid.currentIndex === index ? 2 : 1
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -535,6 +583,9 @@ DankPopout {
|
|||||||
id: gridIconImg
|
id: gridIconImg
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
anchors.bottomMargin: Theme.spacingS
|
||||||
source: Quickshell.iconPath(model.icon, true)
|
source: Quickshell.iconPath(model.icon, true)
|
||||||
smooth: true
|
smooth: true
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
@@ -543,10 +594,13 @@ DankPopout {
|
|||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
anchors.bottomMargin: Theme.spacingS
|
||||||
visible: !gridIconImg.visible
|
visible: !gridIconImg.visible
|
||||||
color: Theme.surfaceLight
|
color: Theme.surfaceLight
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.width: 1
|
border.width: 0
|
||||||
border.color: Theme.primarySelected
|
border.color: Theme.primarySelected
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
@@ -568,8 +622,8 @@ DankPopout {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
maximumLineCount: 2
|
maximumLineCount: 1
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.NoWrap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -577,6 +631,9 @@ DankPopout {
|
|||||||
id: gridMouseArea
|
id: gridMouseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
anchors.bottomMargin: Theme.spacingS
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
@@ -592,7 +649,8 @@ DankPopout {
|
|||||||
if (mouse.button === Qt.LeftButton) {
|
if (mouse.button === Qt.LeftButton) {
|
||||||
appGrid.itemClicked(index, model)
|
appGrid.itemClicked(index, model)
|
||||||
} else if (mouse.button === Qt.RightButton) {
|
} else if (mouse.button === Qt.RightButton) {
|
||||||
var panelPos = mapToItem(contextMenu.parent, mouse.x, mouse.y)
|
var globalPos = mapToItem(null, mouse.x, mouse.y)
|
||||||
|
var panelPos = contextMenu.parent.mapFromItem(null, globalPos.x, globalPos.y)
|
||||||
appGrid.itemRightClicked(index, model, panelPos.x, panelPos.y)
|
appGrid.itemRightClicked(index, model, panelPos.x, panelPos.y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -605,68 +663,68 @@ DankPopout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Popup {
|
||||||
id: contextMenu
|
id: contextMenu
|
||||||
|
|
||||||
property var currentApp: null
|
property var currentApp: null
|
||||||
property bool menuVisible: false
|
readonly property var desktopEntry: (currentApp && !currentApp.isPlugin && appLauncher && appLauncher._uniqueApps && currentApp.appIndex >= 0 && currentApp.appIndex < appLauncher._uniqueApps.length) ? appLauncher._uniqueApps[currentApp.appIndex] : null
|
||||||
|
readonly property string appId: desktopEntry ? (desktopEntry.id || desktopEntry.execString || "") : ""
|
||||||
readonly property string appId: (currentApp && currentApp.desktopEntry) ? (currentApp.desktopEntry.id || currentApp.desktopEntry.execString || "") : ""
|
|
||||||
readonly property bool isPinned: appId && SessionData.isPinnedApp(appId)
|
readonly property bool isPinned: appId && SessionData.isPinnedApp(appId)
|
||||||
|
|
||||||
function show(x, y, app) {
|
function show(x, y, app) {
|
||||||
currentApp = app
|
currentApp = app
|
||||||
|
contextMenu.x = x + 4
|
||||||
const menuWidth = 180
|
contextMenu.y = y + 4
|
||||||
const menuHeight = menuColumn.implicitHeight + Theme.spacingS * 2
|
contextMenu.open()
|
||||||
|
|
||||||
let finalX = x + 8
|
|
||||||
let finalY = y + 8
|
|
||||||
|
|
||||||
if (finalX + menuWidth > appDrawerPopout.popupWidth) {
|
|
||||||
finalX = x - menuWidth - 8
|
|
||||||
}
|
|
||||||
|
|
||||||
if (finalY + menuHeight > appDrawerPopout.popupHeight) {
|
|
||||||
finalY = y - menuHeight - 8
|
|
||||||
}
|
|
||||||
|
|
||||||
finalX = Math.max(8, Math.min(finalX, appDrawerPopout.popupWidth - menuWidth - 8))
|
|
||||||
finalY = Math.max(8, Math.min(finalY, appDrawerPopout.popupHeight - menuHeight - 8))
|
|
||||||
|
|
||||||
contextMenu.x = finalX
|
|
||||||
contextMenu.y = finalY
|
|
||||||
contextMenu.visible = true
|
|
||||||
contextMenu.menuVisible = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function close() {
|
function hide() {
|
||||||
contextMenu.menuVisible = false
|
contextMenu.close()
|
||||||
Qt.callLater(() => {
|
|
||||||
contextMenu.visible = false
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
visible: false
|
width: Math.max(180, menuColumn.implicitWidth + Theme.spacingS * 2)
|
||||||
width: 180
|
|
||||||
height: menuColumn.implicitHeight + Theme.spacingS * 2
|
height: menuColumn.implicitHeight + Theme.spacingS * 2
|
||||||
radius: Theme.cornerRadius
|
padding: 0
|
||||||
color: Theme.popupBackground()
|
closePolicy: Popup.CloseOnPressOutside
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
modal: false
|
||||||
border.width: 1
|
dim: false
|
||||||
z: 1000
|
|
||||||
opacity: menuVisible ? 1 : 0
|
|
||||||
scale: menuVisible ? 1 : 0.85
|
|
||||||
|
|
||||||
Rectangle {
|
background: Rectangle {
|
||||||
anchors.fill: parent
|
radius: Theme.cornerRadius
|
||||||
anchors.topMargin: 4
|
color: Theme.popupBackground()
|
||||||
anchors.leftMargin: 2
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
anchors.rightMargin: -2
|
border.width: 1
|
||||||
anchors.bottomMargin: -4
|
|
||||||
radius: parent.radius
|
Rectangle {
|
||||||
color: Qt.rgba(0, 0, 0, 0.15)
|
anchors.fill: parent
|
||||||
z: parent.z - 1
|
anchors.topMargin: 4
|
||||||
|
anchors.leftMargin: 2
|
||||||
|
anchors.rightMargin: -2
|
||||||
|
anchors.bottomMargin: -4
|
||||||
|
radius: parent.radius
|
||||||
|
color: Qt.rgba(0, 0, 0, 0.15)
|
||||||
|
z: -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enter: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
from: 0
|
||||||
|
to: 1
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
from: 1
|
||||||
|
to: 0
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -697,7 +755,7 @@ DankPopout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: contextMenu.isPinned ? "Unpin from Dock" : "Pin to Dock"
|
text: contextMenu.isPinned ? I18n.tr("Unpin from Dock") : I18n.tr("Pin to Dock")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Normal
|
font.weight: Font.Normal
|
||||||
@@ -712,7 +770,7 @@ DankPopout {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!contextMenu.currentApp || !contextMenu.currentApp.desktopEntry) {
|
if (!contextMenu.desktopEntry) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -721,7 +779,7 @@ DankPopout {
|
|||||||
} else {
|
} else {
|
||||||
SessionData.addPinnedApp(contextMenu.appId)
|
SessionData.addPinnedApp(contextMenu.appId)
|
||||||
}
|
}
|
||||||
contextMenu.close()
|
contextMenu.hide()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -740,6 +798,79 @@ DankPopout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: contextMenu.desktopEntry && contextMenu.desktopEntry.actions ? contextMenu.desktopEntry.actions : []
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: Math.max(parent.width, actionRow.implicitWidth + Theme.spacingS * 2)
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: actionMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: actionRow
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: Theme.iconSize - 2
|
||||||
|
height: Theme.iconSize - 2
|
||||||
|
visible: modelData.icon && modelData.icon !== ""
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: modelData.icon ? Quickshell.iconPath(modelData.icon, true) : ""
|
||||||
|
smooth: true
|
||||||
|
asynchronous: true
|
||||||
|
visible: status === Image.Ready
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.name || ""
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Normal
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: actionMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (modelData && contextMenu.desktopEntry) {
|
||||||
|
SessionService.launchDesktopAction(contextMenu.desktopEntry, modelData)
|
||||||
|
if (contextMenu.currentApp) {
|
||||||
|
appLauncher.appLaunched(contextMenu.currentApp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contextMenu.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
visible: contextMenu.desktopEntry && contextMenu.desktopEntry.actions && contextMenu.desktopEntry.actions.length > 0
|
||||||
|
width: parent.width - Theme.spacingS * 2
|
||||||
|
height: 5
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 32
|
height: 32
|
||||||
@@ -761,7 +892,7 @@ DankPopout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Launch"
|
text: I18n.tr("Launch")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Normal
|
font.weight: Font.Normal
|
||||||
@@ -779,23 +910,72 @@ DankPopout {
|
|||||||
if (contextMenu.currentApp)
|
if (contextMenu.currentApp)
|
||||||
appLauncher.launchApp(contextMenu.currentApp)
|
appLauncher.launchApp(contextMenu.currentApp)
|
||||||
|
|
||||||
contextMenu.close()
|
contextMenu.hide()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on opacity {
|
Rectangle {
|
||||||
NumberAnimation {
|
visible: SessionService.hasPrimeRun
|
||||||
duration: Theme.mediumDuration
|
width: parent.width - Theme.spacingS * 2
|
||||||
easing.type: Theme.emphasizedEasing
|
height: 5
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on scale {
|
Rectangle {
|
||||||
NumberAnimation {
|
visible: SessionService.hasPrimeRun
|
||||||
duration: Theme.mediumDuration
|
width: parent.width
|
||||||
easing.type: Theme.emphasizedEasing
|
height: 32
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: primeRunMouseArea.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: "memory"
|
||||||
|
size: Theme.iconSize - 2
|
||||||
|
color: Theme.surfaceText
|
||||||
|
opacity: 0.7
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Launch on dGPU")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Normal
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: primeRunMouseArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (contextMenu.desktopEntry) {
|
||||||
|
SessionService.launchDesktopEntry(contextMenu.desktopEntry, true)
|
||||||
|
if (contextMenu.currentApp) {
|
||||||
|
appLauncher.appLaunched(contextMenu.currentApp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contextMenu.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -805,7 +985,7 @@ DankPopout {
|
|||||||
visible: contextMenu.visible
|
visible: contextMenu.visible
|
||||||
z: 999
|
z: 999
|
||||||
onClicked: {
|
onClicked: {
|
||||||
contextMenu.close()
|
contextMenu.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Item {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string searchQuery: ""
|
property string searchQuery: ""
|
||||||
property string selectedCategory: "All"
|
property string selectedCategory: I18n.tr("All")
|
||||||
property string viewMode: "list" // "list" or "grid"
|
property string viewMode: "list" // "list" or "grid"
|
||||||
property int selectedIndex: 0
|
property int selectedIndex: 0
|
||||||
property int maxResults: 50
|
property int maxResults: 50
|
||||||
@@ -18,20 +18,46 @@ Item {
|
|||||||
property int debounceInterval: 50
|
property int debounceInterval: 50
|
||||||
property bool keyboardNavigationActive: false
|
property bool keyboardNavigationActive: false
|
||||||
property bool suppressUpdatesWhileLaunching: false
|
property bool suppressUpdatesWhileLaunching: false
|
||||||
readonly property var categories: {
|
property var categories: {
|
||||||
const allCategories = AppSearchService.getAllCategories().filter(cat => cat !== "Education" && cat !== "Science")
|
const allCategories = AppSearchService.getAllCategories().filter(cat => cat !== "Education" && cat !== "Science")
|
||||||
const result = ["All"]
|
const result = [I18n.tr("All")]
|
||||||
return result.concat(allCategories.filter(cat => cat !== "All"))
|
return result.concat(allCategories.filter(cat => cat !== I18n.tr("All")))
|
||||||
}
|
}
|
||||||
readonly property var categoryIcons: categories.map(category => AppSearchService.getCategoryIcon(category))
|
readonly property var categoryIcons: categories.map(category => AppSearchService.getCategoryIcon(category))
|
||||||
property var appUsageRanking: AppUsageHistoryData.appUsageRanking || {}
|
property var appUsageRanking: AppUsageHistoryData.appUsageRanking || {}
|
||||||
property alias model: filteredModel
|
property alias model: filteredModel
|
||||||
property var _watchApplications: AppSearchService.applications
|
property var _watchApplications: AppSearchService.applications
|
||||||
|
property var _uniqueApps: []
|
||||||
|
property bool _isTriggered: false
|
||||||
|
property string _triggeredCategory: ""
|
||||||
|
property bool _updatingFromTrigger: false
|
||||||
|
|
||||||
signal appLaunched(var app)
|
signal appLaunched(var app)
|
||||||
signal categorySelected(string category)
|
signal categorySelected(string category)
|
||||||
signal viewModeSelected(string mode)
|
signal viewModeSelected(string mode)
|
||||||
|
|
||||||
|
function updateCategories() {
|
||||||
|
const allCategories = AppSearchService.getAllCategories().filter(cat => cat !== "Education" && cat !== "Science")
|
||||||
|
const result = [I18n.tr("All")]
|
||||||
|
categories = result.concat(allCategories.filter(cat => cat !== I18n.tr("All")))
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: PluginService
|
||||||
|
function onPluginLoaded() { updateCategories() }
|
||||||
|
function onPluginUnloaded() { updateCategories() }
|
||||||
|
function onPluginListUpdated() { updateCategories() }
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: SettingsData
|
||||||
|
function onSortAppsAlphabeticallyChanged() {
|
||||||
|
updateFilteredModel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function updateFilteredModel() {
|
function updateFilteredModel() {
|
||||||
if (suppressUpdatesWhileLaunching) {
|
if (suppressUpdatesWhileLaunching) {
|
||||||
suppressUpdatesWhileLaunching = false
|
suppressUpdatesWhileLaunching = false
|
||||||
@@ -41,49 +67,112 @@ Item {
|
|||||||
selectedIndex = 0
|
selectedIndex = 0
|
||||||
keyboardNavigationActive = false
|
keyboardNavigationActive = false
|
||||||
|
|
||||||
|
const triggerResult = checkPluginTriggers(searchQuery)
|
||||||
|
if (triggerResult.triggered) {
|
||||||
|
console.log("AppLauncher: Plugin trigger detected:", triggerResult.trigger, "for plugin:", triggerResult.pluginId)
|
||||||
|
}
|
||||||
|
|
||||||
let apps = []
|
let apps = []
|
||||||
if (searchQuery.length === 0) {
|
const allCategory = I18n.tr("All")
|
||||||
apps = selectedCategory === "All" ? AppSearchService.getAppsInCategory("All") : AppSearchService.getAppsInCategory(selectedCategory).slice(0, maxResults)
|
const emptyTriggerPlugins = typeof PluginService !== "undefined" ? PluginService.getPluginsWithEmptyTrigger() : []
|
||||||
|
|
||||||
|
if (triggerResult.triggered) {
|
||||||
|
_isTriggered = true
|
||||||
|
_triggeredCategory = triggerResult.pluginCategory
|
||||||
|
_updatingFromTrigger = true
|
||||||
|
selectedCategory = triggerResult.pluginCategory
|
||||||
|
_updatingFromTrigger = false
|
||||||
|
apps = AppSearchService.getPluginItems(triggerResult.pluginCategory, triggerResult.query)
|
||||||
} else {
|
} else {
|
||||||
if (selectedCategory === "All") {
|
if (_isTriggered) {
|
||||||
apps = AppSearchService.searchApplications(searchQuery)
|
_updatingFromTrigger = true
|
||||||
} else {
|
selectedCategory = allCategory
|
||||||
const categoryApps = AppSearchService.getAppsInCategory(selectedCategory)
|
_updatingFromTrigger = false
|
||||||
if (categoryApps.length > 0) {
|
_isTriggered = false
|
||||||
const allSearchResults = AppSearchService.searchApplications(searchQuery)
|
_triggeredCategory = ""
|
||||||
const categoryNames = new Set(categoryApps.map(app => app.name))
|
}
|
||||||
apps = allSearchResults.filter(searchApp => categoryNames.has(searchApp.name)).slice(0, maxResults)
|
if (searchQuery.length === 0) {
|
||||||
|
if (selectedCategory === allCategory) {
|
||||||
|
let emptyTriggerItems = []
|
||||||
|
emptyTriggerPlugins.forEach(pluginId => {
|
||||||
|
const plugin = PluginService.getLauncherPlugin(pluginId)
|
||||||
|
const pluginCategory = plugin.name || pluginId
|
||||||
|
const items = AppSearchService.getPluginItems(pluginCategory, "")
|
||||||
|
emptyTriggerItems = emptyTriggerItems.concat(items)
|
||||||
|
})
|
||||||
|
apps = AppSearchService.applications.concat(emptyTriggerItems)
|
||||||
} else {
|
} else {
|
||||||
apps = []
|
apps = AppSearchService.getAppsInCategory(selectedCategory).slice(0, maxResults)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (selectedCategory === allCategory) {
|
||||||
|
apps = AppSearchService.searchApplications(searchQuery)
|
||||||
|
|
||||||
|
let emptyTriggerItems = []
|
||||||
|
emptyTriggerPlugins.forEach(pluginId => {
|
||||||
|
const plugin = PluginService.getLauncherPlugin(pluginId)
|
||||||
|
const pluginCategory = plugin.name || pluginId
|
||||||
|
const items = AppSearchService.getPluginItems(pluginCategory, searchQuery)
|
||||||
|
emptyTriggerItems = emptyTriggerItems.concat(items)
|
||||||
|
})
|
||||||
|
apps = apps.concat(emptyTriggerItems)
|
||||||
|
} else {
|
||||||
|
const categoryApps = AppSearchService.getAppsInCategory(selectedCategory)
|
||||||
|
if (categoryApps.length > 0) {
|
||||||
|
const allSearchResults = AppSearchService.searchApplications(searchQuery)
|
||||||
|
const categoryNames = new Set(categoryApps.map(app => app.name))
|
||||||
|
apps = allSearchResults.filter(searchApp => categoryNames.has(searchApp.name)).slice(0, maxResults)
|
||||||
|
} else {
|
||||||
|
apps = []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (searchQuery.length === 0) {
|
if (searchQuery.length === 0) {
|
||||||
apps = apps.sort((a, b) => {
|
if (SettingsData.sortAppsAlphabetically) {
|
||||||
const aId = a.id || a.execString || a.exec || ""
|
apps = apps.sort((a, b) => {
|
||||||
const bId = b.id || b.execString || b.exec || ""
|
return (a.name || "").localeCompare(b.name || "")
|
||||||
const aUsage = appUsageRanking[aId] ? appUsageRanking[aId].usageCount : 0
|
})
|
||||||
const bUsage = appUsageRanking[bId] ? appUsageRanking[bId].usageCount : 0
|
} else {
|
||||||
if (aUsage !== bUsage) {
|
apps = apps.sort((a, b) => {
|
||||||
return bUsage - aUsage
|
const aId = a.id || a.execString || a.exec || ""
|
||||||
}
|
const bId = b.id || b.execString || b.exec || ""
|
||||||
return (a.name || "").localeCompare(b.name || "")
|
const aUsage = appUsageRanking[aId] ? appUsageRanking[aId].usageCount : 0
|
||||||
})
|
const bUsage = appUsageRanking[bId] ? appUsageRanking[bId].usageCount : 0
|
||||||
|
if (aUsage !== bUsage) {
|
||||||
|
return bUsage - aUsage
|
||||||
|
}
|
||||||
|
return (a.name || "").localeCompare(b.name || "")
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const seenNames = new Set()
|
||||||
|
const uniqueApps = []
|
||||||
apps.forEach(app => {
|
apps.forEach(app => {
|
||||||
if (app) {
|
if (app) {
|
||||||
|
const itemKey = app.name + "|" + (app.execString || app.exec || app.action || "")
|
||||||
|
if (seenNames.has(itemKey)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
seenNames.add(itemKey)
|
||||||
|
uniqueApps.push(app)
|
||||||
|
|
||||||
|
const isPluginItem = app.action !== undefined
|
||||||
filteredModel.append({
|
filteredModel.append({
|
||||||
"name": app.name || "",
|
"name": app.name || "",
|
||||||
"exec": app.execString || "",
|
"exec": app.execString || app.exec || app.action || "",
|
||||||
"icon": app.icon || "application-x-executable",
|
"icon": app.icon || "application-x-executable",
|
||||||
"comment": app.comment || "",
|
"comment": app.comment || "",
|
||||||
"categories": app.categories || [],
|
"categories": app.categories || [],
|
||||||
"desktopEntry": app
|
"isPlugin": isPluginItem,
|
||||||
|
"appIndex": uniqueApps.length - 1
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
root._uniqueApps = uniqueApps
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectNext() {
|
function selectNext() {
|
||||||
@@ -127,13 +216,25 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function launchApp(appData) {
|
function launchApp(appData) {
|
||||||
if (!appData) {
|
if (!appData || typeof appData.appIndex === "undefined" || appData.appIndex < 0 || appData.appIndex >= _uniqueApps.length) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
suppressUpdatesWhileLaunching = true
|
suppressUpdatesWhileLaunching = true
|
||||||
SessionService.launchDesktopEntry(appData.desktopEntry)
|
|
||||||
appLaunched(appData)
|
const actualApp = _uniqueApps[appData.appIndex]
|
||||||
AppUsageHistoryData.addAppUsage(appData.desktopEntry)
|
|
||||||
|
if (appData.isPlugin) {
|
||||||
|
const pluginId = getPluginIdForItem(actualApp)
|
||||||
|
if (pluginId) {
|
||||||
|
AppSearchService.executePluginItem(actualApp, pluginId)
|
||||||
|
appLaunched(appData)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SessionService.launchDesktopEntry(actualApp)
|
||||||
|
appLaunched(appData)
|
||||||
|
AppUsageHistoryData.addAppUsage(actualApp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setCategory(category) {
|
function setCategory(category) {
|
||||||
@@ -153,7 +254,12 @@ Item {
|
|||||||
updateFilteredModel()
|
updateFilteredModel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onSelectedCategoryChanged: updateFilteredModel()
|
onSelectedCategoryChanged: {
|
||||||
|
if (_updatingFromTrigger) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
updateFilteredModel()
|
||||||
|
}
|
||||||
onAppUsageRankingChanged: updateFilteredModel()
|
onAppUsageRankingChanged: updateFilteredModel()
|
||||||
on_WatchApplicationsChanged: updateFilteredModel()
|
on_WatchApplicationsChanged: updateFilteredModel()
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
@@ -171,4 +277,63 @@ Item {
|
|||||||
repeat: false
|
repeat: false
|
||||||
onTriggered: updateFilteredModel()
|
onTriggered: updateFilteredModel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Plugin trigger system functions
|
||||||
|
function checkPluginTriggers(query) {
|
||||||
|
if (!query || typeof PluginService === "undefined") {
|
||||||
|
return { triggered: false, pluginCategory: "", query: "" }
|
||||||
|
}
|
||||||
|
|
||||||
|
const triggers = PluginService.getAllPluginTriggers()
|
||||||
|
|
||||||
|
for (const trigger in triggers) {
|
||||||
|
if (query.startsWith(trigger)) {
|
||||||
|
const pluginId = triggers[trigger]
|
||||||
|
const plugin = PluginService.getLauncherPlugin(pluginId)
|
||||||
|
|
||||||
|
if (plugin) {
|
||||||
|
const remainingQuery = query.substring(trigger.length).trim()
|
||||||
|
const result = {
|
||||||
|
triggered: true,
|
||||||
|
pluginId: pluginId,
|
||||||
|
pluginCategory: plugin.name || pluginId,
|
||||||
|
query: remainingQuery,
|
||||||
|
trigger: trigger
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { triggered: false, pluginCategory: "", query: "" }
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPluginIdForItem(item) {
|
||||||
|
if (!item || !item.categories || typeof PluginService === "undefined") {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const launchers = PluginService.getLauncherPlugins()
|
||||||
|
for (const pluginId in launchers) {
|
||||||
|
const plugin = launchers[pluginId]
|
||||||
|
const pluginCategory = plugin.name || pluginId
|
||||||
|
|
||||||
|
let hasCategory = false
|
||||||
|
if (Array.isArray(item.categories)) {
|
||||||
|
hasCategory = item.categories.includes(pluginCategory)
|
||||||
|
} else if (item.categories && typeof item.categories.count !== "undefined") {
|
||||||
|
for (let i = 0; i < item.categories.count; i++) {
|
||||||
|
if (item.categories.get(i) === pluginCategory) {
|
||||||
|
hasCategory = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasCategory) {
|
||||||
|
return pluginId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property var categories: []
|
property var categories: []
|
||||||
property string selectedCategory: "All"
|
property string selectedCategory: I18n.tr("All")
|
||||||
property bool compact: false
|
property bool compact: false
|
||||||
|
|
||||||
signal categorySelected(string category)
|
signal categorySelected(string category)
|
||||||
@@ -15,10 +16,9 @@ Item {
|
|||||||
readonly property int maxCompactItems: 8
|
readonly property int maxCompactItems: 8
|
||||||
readonly property int itemHeight: 36
|
readonly property int itemHeight: 36
|
||||||
readonly property color selectedBorderColor: "transparent"
|
readonly property color selectedBorderColor: "transparent"
|
||||||
readonly property color unselectedBorderColor: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
|
readonly property color unselectedBorderColor: "transparent"
|
||||||
|
|
||||||
function handleCategoryClick(category) {
|
function handleCategoryClick(category) {
|
||||||
selectedCategory = category
|
|
||||||
categorySelected(category)
|
categorySelected(category)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,8 +42,7 @@ Item {
|
|||||||
height: root.itemHeight
|
height: root.itemHeight
|
||||||
width: root.getButtonWidth(itemCount, parent.width)
|
width: root.getButtonWidth(itemCount, parent.width)
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: selectedCategory === modelData ? Theme.primary : "transparent"
|
color: selectedCategory === modelData ? Theme.primary : Theme.surfaceContainerHigh
|
||||||
border.color: selectedCategory === modelData ? selectedBorderColor : unselectedBorderColor
|
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -82,7 +81,7 @@ Item {
|
|||||||
height: root.itemHeight
|
height: root.itemHeight
|
||||||
width: root.getButtonWidth(itemCount, parent.width)
|
width: root.getButtonWidth(itemCount, parent.width)
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: selectedCategory === modelData ? Theme.primary : "transparent"
|
color: selectedCategory === modelData ? Theme.primary : Theme.surfaceContainerHigh
|
||||||
border.color: selectedCategory === modelData ? selectedBorderColor : unselectedBorderColor
|
border.color: selectedCategory === modelData ? selectedBorderColor : unselectedBorderColor
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
@@ -118,7 +117,7 @@ Item {
|
|||||||
height: root.itemHeight
|
height: root.itemHeight
|
||||||
width: root.getButtonWidth(itemCount, parent.width)
|
width: root.getButtonWidth(itemCount, parent.width)
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: selectedCategory === modelData ? Theme.primary : "transparent"
|
color: selectedCategory === modelData ? Theme.primary : Theme.surfaceContainerHigh
|
||||||
border.color: selectedCategory === modelData ? selectedBorderColor : unselectedBorderColor
|
border.color: selectedCategory === modelData ? selectedBorderColor : unselectedBorderColor
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
|
|||||||
246
Modules/ControlCenter/BuiltinPlugins/VpnWidget.qml
Normal file
246
Modules/ControlCenter/BuiltinPlugins/VpnWidget.qml
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
import qs.Modules.Plugins
|
||||||
|
|
||||||
|
PluginComponent {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
Ref {
|
||||||
|
service: VpnService
|
||||||
|
}
|
||||||
|
|
||||||
|
ccWidgetIcon: VpnService.isBusy ? "sync" : (VpnService.connected ? "vpn_lock" : "vpn_key_off")
|
||||||
|
ccWidgetPrimaryText: "VPN"
|
||||||
|
ccWidgetSecondaryText: {
|
||||||
|
if (!VpnService.connected)
|
||||||
|
return "Disconnected"
|
||||||
|
const names = VpnService.activeNames || []
|
||||||
|
if (names.length <= 1)
|
||||||
|
return names[0] || "Connected"
|
||||||
|
return names[0] + " +" + (names.length - 1)
|
||||||
|
}
|
||||||
|
ccWidgetIsActive: VpnService.connected
|
||||||
|
|
||||||
|
onCcWidgetToggled: {
|
||||||
|
if (VpnService.connected) {
|
||||||
|
VpnService.disconnectAllActive()
|
||||||
|
} else if (VpnService.profiles.length > 0) {
|
||||||
|
VpnService.connect(VpnService.profiles[0].uuid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ccDetailContent: Component {
|
||||||
|
Rectangle {
|
||||||
|
id: detailRoot
|
||||||
|
implicitHeight: detailColumn.implicitHeight + Theme.spacingM * 2
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.surfaceContainerHigh
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: detailColumn
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
if (!VpnService.connected)
|
||||||
|
return "Active: None"
|
||||||
|
const names = VpnService.activeNames || []
|
||||||
|
if (names.length <= 1)
|
||||||
|
return "Active: " + (names[0] || "VPN")
|
||||||
|
return "Active: " + names[0] + " +" + (names.length - 1)
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
height: 28
|
||||||
|
radius: 14
|
||||||
|
color: discAllArea.containsMouse ? Theme.errorHover : Theme.surfaceLight
|
||||||
|
visible: VpnService.connected
|
||||||
|
width: 110
|
||||||
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "link_off"
|
||||||
|
size: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Disconnect")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: discAllArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: VpnService.disconnectAllActive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
height: 1
|
||||||
|
width: parent.width
|
||||||
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
|
}
|
||||||
|
|
||||||
|
DankFlickable {
|
||||||
|
width: parent.width
|
||||||
|
height: 160
|
||||||
|
contentHeight: listCol.height
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: listCol
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: VpnService.profiles.length === 0 ? 120 : 0
|
||||||
|
visible: height > 0
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "playlist_remove"
|
||||||
|
size: 36
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("No VPN profiles found")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Add a VPN in NetworkManager")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: VpnService.profiles
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
width: parent ? parent.width : 300
|
||||||
|
height: 50
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: rowArea.containsMouse ? Theme.primaryHoverLight : (VpnService.isActiveUuid(modelData.uuid) ? Theme.primaryPressed : Theme.surfaceLight)
|
||||||
|
border.width: VpnService.isActiveUuid(modelData.uuid) ? 2 : 1
|
||||||
|
border.color: VpnService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.outlineLight
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: VpnService.isActiveUuid(modelData.uuid) ? "vpn_lock" : "vpn_key_off"
|
||||||
|
size: Theme.iconSize - 4
|
||||||
|
color: VpnService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
spacing: 2
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.name
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: VpnService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
if (modelData.type === "wireguard")
|
||||||
|
return "WireGuard"
|
||||||
|
const svc = modelData.serviceType || ""
|
||||||
|
if (svc.indexOf("openvpn") !== -1)
|
||||||
|
return "OpenVPN"
|
||||||
|
if (svc.indexOf("wireguard") !== -1)
|
||||||
|
return "WireGuard (plugin)"
|
||||||
|
if (svc.indexOf("openconnect") !== -1)
|
||||||
|
return "OpenConnect"
|
||||||
|
if (svc.indexOf("fortissl") !== -1 || svc.indexOf("forti") !== -1)
|
||||||
|
return "Fortinet"
|
||||||
|
if (svc.indexOf("strongswan") !== -1)
|
||||||
|
return "IPsec (strongSwan)"
|
||||||
|
if (svc.indexOf("libreswan") !== -1)
|
||||||
|
return "IPsec (Libreswan)"
|
||||||
|
if (svc.indexOf("l2tp") !== -1)
|
||||||
|
return "L2TP/IPsec"
|
||||||
|
if (svc.indexOf("pptp") !== -1)
|
||||||
|
return "PPTP"
|
||||||
|
if (svc.indexOf("vpnc") !== -1)
|
||||||
|
return "Cisco (vpnc)"
|
||||||
|
if (svc.indexOf("sstp") !== -1)
|
||||||
|
return "SSTP"
|
||||||
|
if (svc)
|
||||||
|
return svc.split('.').pop()
|
||||||
|
return "VPN"
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceTextMedium
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: rowArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: VpnService.toggle(modelData.uuid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,8 +25,7 @@ Rectangle {
|
|||||||
|
|
||||||
readonly property color _tileBgActive: Theme.primary
|
readonly property color _tileBgActive: Theme.primary
|
||||||
readonly property color _tileBgInactive:
|
readonly property color _tileBgInactive:
|
||||||
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b,
|
Theme.surfaceContainerHigh
|
||||||
Theme.getContentBackgroundAlpha() * 0.60)
|
|
||||||
readonly property color _tileRingActive:
|
readonly property color _tileRingActive:
|
||||||
Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
|
Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
|
||||||
|
|
||||||
@@ -60,7 +59,7 @@ Rectangle {
|
|||||||
DankIcon {
|
DankIcon {
|
||||||
name: root.iconName
|
name: root.iconName
|
||||||
size: Theme.iconSize
|
size: Theme.iconSize
|
||||||
color: isActive ? Theme.primaryContainer : Theme.primary
|
color: isActive ? Theme.primaryText : Theme.primary
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +77,7 @@ Rectangle {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
text: root.text
|
text: root.text
|
||||||
style: Typography.Style.Body
|
style: Typography.Style.Body
|
||||||
color: isActive ? Theme.primaryContainer : Theme.surfaceText
|
color: isActive ? Theme.primaryText : Theme.surfaceText
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
wrapMode: Text.NoWrap
|
wrapMode: Text.NoWrap
|
||||||
}
|
}
|
||||||
@@ -87,7 +86,7 @@ Rectangle {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
text: root.secondaryText
|
text: root.secondaryText
|
||||||
style: Typography.Style.Caption
|
style: Typography.Style.Caption
|
||||||
color: isActive ? Theme.primaryContainer : Theme.surfaceVariantText
|
color: isActive ? Theme.primaryText : Theme.surfaceVariantText
|
||||||
visible: text.length > 0
|
visible: text.length > 0
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
wrapMode: Text.NoWrap
|
wrapMode: Text.NoWrap
|
||||||
|
|||||||
@@ -1,40 +1,164 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
import qs.Modules.ControlCenter.Details
|
import qs.Modules.ControlCenter.Details
|
||||||
|
import qs.Modules.ControlCenter.Models
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string expandedSection: ""
|
property string expandedSection: ""
|
||||||
property var expandedWidgetData: null
|
property var expandedWidgetData: null
|
||||||
|
property var bluetoothCodecSelector: null
|
||||||
|
|
||||||
|
property var pluginDetailInstance: null
|
||||||
|
property var widgetModel: null
|
||||||
|
property var collapseCallback: null
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
|
id: pluginDetailLoader
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 250
|
height: 250
|
||||||
y: Theme.spacingS
|
y: Theme.spacingS
|
||||||
active: parent.height > 0
|
active: false
|
||||||
property string sectionKey: root.expandedSection
|
sourceComponent: null
|
||||||
sourceComponent: {
|
}
|
||||||
switch (root.expandedSection) {
|
|
||||||
case "network":
|
Loader {
|
||||||
case "wifi": return networkDetailComponent
|
id: coreDetailLoader
|
||||||
case "bluetooth": return bluetoothDetailComponent
|
width: parent.width
|
||||||
case "audioOutput": return audioOutputDetailComponent
|
height: 250
|
||||||
case "audioInput": return audioInputDetailComponent
|
y: Theme.spacingS
|
||||||
case "battery": return batteryDetailComponent
|
active: false
|
||||||
default:
|
sourceComponent: null
|
||||||
if (root.expandedSection.startsWith("diskUsage_")) {
|
}
|
||||||
return diskUsageDetailComponent
|
|
||||||
|
Connections {
|
||||||
|
target: coreDetailLoader.item
|
||||||
|
enabled: root.expandedSection.startsWith("brightnessSlider_")
|
||||||
|
ignoreUnknownSignals: true
|
||||||
|
|
||||||
|
function onDeviceNameChanged(newDeviceName) {
|
||||||
|
if (root.expandedWidgetData && root.expandedWidgetData.id === "brightnessSlider") {
|
||||||
|
const widgets = SettingsData.controlCenterWidgets || []
|
||||||
|
const newWidgets = widgets.map(w => {
|
||||||
|
if (w.id === "brightnessSlider" && w.instanceId === root.expandedWidgetData.instanceId) {
|
||||||
|
const updatedWidget = Object.assign({}, w)
|
||||||
|
updatedWidget.deviceName = newDeviceName
|
||||||
|
return updatedWidget
|
||||||
|
}
|
||||||
|
return w
|
||||||
|
})
|
||||||
|
SettingsData.setControlCenterWidgets(newWidgets)
|
||||||
|
if (root.collapseCallback) {
|
||||||
|
root.collapseCallback()
|
||||||
}
|
}
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onSectionKeyChanged: {
|
}
|
||||||
active = false
|
|
||||||
active = true
|
Connections {
|
||||||
|
target: coreDetailLoader.item
|
||||||
|
enabled: root.expandedSection.startsWith("diskUsage_")
|
||||||
|
ignoreUnknownSignals: true
|
||||||
|
|
||||||
|
function onMountPathChanged(newMountPath) {
|
||||||
|
if (root.expandedWidgetData && root.expandedWidgetData.id === "diskUsage") {
|
||||||
|
const widgets = SettingsData.controlCenterWidgets || []
|
||||||
|
const newWidgets = widgets.map(w => {
|
||||||
|
if (w.id === "diskUsage" && w.instanceId === root.expandedWidgetData.instanceId) {
|
||||||
|
const updatedWidget = Object.assign({}, w)
|
||||||
|
updatedWidget.mountPath = newMountPath
|
||||||
|
return updatedWidget
|
||||||
|
}
|
||||||
|
return w
|
||||||
|
})
|
||||||
|
SettingsData.setControlCenterWidgets(newWidgets)
|
||||||
|
if (root.collapseCallback) {
|
||||||
|
root.collapseCallback()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onExpandedSectionChanged: {
|
||||||
|
if (pluginDetailInstance) {
|
||||||
|
pluginDetailInstance.destroy()
|
||||||
|
pluginDetailInstance = null
|
||||||
|
}
|
||||||
|
pluginDetailLoader.active = false
|
||||||
|
coreDetailLoader.active = false
|
||||||
|
|
||||||
|
if (!root.expandedSection) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root.expandedSection.startsWith("builtin_")) {
|
||||||
|
const builtinId = root.expandedSection
|
||||||
|
let builtinInstance = null
|
||||||
|
|
||||||
|
if (builtinId === "builtin_vpn") {
|
||||||
|
if (widgetModel?.vpnLoader) {
|
||||||
|
widgetModel.vpnLoader.active = true
|
||||||
|
}
|
||||||
|
builtinInstance = widgetModel.vpnBuiltinInstance
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!builtinInstance || !builtinInstance.ccDetailContent) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginDetailLoader.sourceComponent = builtinInstance.ccDetailContent
|
||||||
|
pluginDetailLoader.active = parent.height > 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root.expandedSection.startsWith("plugin_")) {
|
||||||
|
const pluginId = root.expandedSection.replace("plugin_", "")
|
||||||
|
const pluginComponent = PluginService.pluginWidgetComponents[pluginId]
|
||||||
|
if (!pluginComponent) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginDetailInstance = pluginComponent.createObject(null)
|
||||||
|
if (!pluginDetailInstance || !pluginDetailInstance.ccDetailContent) {
|
||||||
|
if (pluginDetailInstance) {
|
||||||
|
pluginDetailInstance.destroy()
|
||||||
|
pluginDetailInstance = null
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginDetailLoader.sourceComponent = pluginDetailInstance.ccDetailContent
|
||||||
|
pluginDetailLoader.active = parent.height > 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root.expandedSection.startsWith("diskUsage_")) {
|
||||||
|
coreDetailLoader.sourceComponent = diskUsageDetailComponent
|
||||||
|
coreDetailLoader.active = parent.height > 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root.expandedSection.startsWith("brightnessSlider_")) {
|
||||||
|
coreDetailLoader.sourceComponent = brightnessDetailComponent
|
||||||
|
coreDetailLoader.active = parent.height > 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (root.expandedSection) {
|
||||||
|
case "network":
|
||||||
|
case "wifi": coreDetailLoader.sourceComponent = networkDetailComponent; break
|
||||||
|
case "bluetooth": coreDetailLoader.sourceComponent = bluetoothDetailComponent; break
|
||||||
|
case "audioOutput": coreDetailLoader.sourceComponent = audioOutputDetailComponent; break
|
||||||
|
case "audioInput": coreDetailLoader.sourceComponent = audioInputDetailComponent; break
|
||||||
|
case "battery": coreDetailLoader.sourceComponent = batteryDetailComponent; break
|
||||||
|
default: return
|
||||||
|
}
|
||||||
|
|
||||||
|
coreDetailLoader.active = parent.height > 0
|
||||||
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: networkDetailComponent
|
id: networkDetailComponent
|
||||||
NetworkDetail {}
|
NetworkDetail {}
|
||||||
@@ -42,7 +166,17 @@ Item {
|
|||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: bluetoothDetailComponent
|
id: bluetoothDetailComponent
|
||||||
BluetoothDetail {}
|
BluetoothDetail {
|
||||||
|
id: bluetoothDetail
|
||||||
|
onShowCodecSelector: function(device) {
|
||||||
|
if (root.bluetoothCodecSelector) {
|
||||||
|
root.bluetoothCodecSelector.show(device)
|
||||||
|
root.bluetoothCodecSelector.codecSelected.connect(function(deviceAddress, codecName) {
|
||||||
|
bluetoothDetail.updateDeviceCodecDisplay(deviceAddress, codecName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
@@ -65,22 +199,14 @@ Item {
|
|||||||
DiskUsageDetail {
|
DiskUsageDetail {
|
||||||
currentMountPath: root.expandedWidgetData?.mountPath || "/"
|
currentMountPath: root.expandedWidgetData?.mountPath || "/"
|
||||||
instanceId: root.expandedWidgetData?.instanceId || ""
|
instanceId: root.expandedWidgetData?.instanceId || ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
onMountPathChanged: (newMountPath) => {
|
id: brightnessDetailComponent
|
||||||
if (root.expandedWidgetData && root.expandedWidgetData.id === "diskUsage") {
|
BrightnessDetail {
|
||||||
const widgets = SettingsData.controlCenterWidgets || []
|
currentDeviceName: root.expandedWidgetData?.deviceName || ""
|
||||||
const newWidgets = widgets.map(w => {
|
instanceId: root.expandedWidgetData?.instanceId || ""
|
||||||
if (w.id === "diskUsage" && w.instanceId === root.expandedWidgetData.instanceId) {
|
|
||||||
const updatedWidget = Object.assign({}, w)
|
|
||||||
updatedWidget.mountPath = newMountPath
|
|
||||||
return updatedWidget
|
|
||||||
}
|
|
||||||
return w
|
|
||||||
})
|
|
||||||
SettingsData.setControlCenterWidgets(newWidgets)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
91
Modules/ControlCenter/Components/DragDropDetailHost.qml
Normal file
91
Modules/ControlCenter/Components/DragDropDetailHost.qml
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
import qs.Modules.ControlCenter.Details
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string expandedSection: ""
|
||||||
|
property var expandedWidgetData: null
|
||||||
|
|
||||||
|
height: active ? 250 : 0
|
||||||
|
visible: active
|
||||||
|
|
||||||
|
readonly property bool active: expandedSection !== ""
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.topMargin: Theme.spacingS
|
||||||
|
sourceComponent: {
|
||||||
|
if (!root.active) return null
|
||||||
|
|
||||||
|
if (expandedSection.startsWith("diskUsage_")) {
|
||||||
|
return diskUsageDetailComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (expandedSection) {
|
||||||
|
case "wifi": return networkDetailComponent
|
||||||
|
case "bluetooth": return bluetoothDetailComponent
|
||||||
|
case "audioOutput": return audioOutputDetailComponent
|
||||||
|
case "audioInput": return audioInputDetailComponent
|
||||||
|
case "battery": return batteryDetailComponent
|
||||||
|
default: return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: networkDetailComponent
|
||||||
|
NetworkDetail {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: bluetoothDetailComponent
|
||||||
|
BluetoothDetail {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: audioOutputDetailComponent
|
||||||
|
AudioOutputDetail {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: audioInputDetailComponent
|
||||||
|
AudioInputDetail {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: batteryDetailComponent
|
||||||
|
BatteryDetail {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: diskUsageDetailComponent
|
||||||
|
DiskUsageDetail {
|
||||||
|
currentMountPath: root.expandedWidgetData?.mountPath || "/"
|
||||||
|
instanceId: root.expandedWidgetData?.instanceId || ""
|
||||||
|
|
||||||
|
onMountPathChanged: (newMountPath) => {
|
||||||
|
if (root.expandedWidgetData && root.expandedWidgetData.id === "diskUsage") {
|
||||||
|
const widgets = SettingsData.controlCenterWidgets || []
|
||||||
|
const newWidgets = widgets.map(w => {
|
||||||
|
if (w.id === "diskUsage" && w.instanceId === root.expandedWidgetData.instanceId) {
|
||||||
|
const updatedWidget = Object.assign({}, w)
|
||||||
|
updatedWidget.mountPath = newMountPath
|
||||||
|
return updatedWidget
|
||||||
|
}
|
||||||
|
return w
|
||||||
|
})
|
||||||
|
SettingsData.setControlCenterWidgets(newWidgets)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1001
Modules/ControlCenter/Components/DragDropGrid.qml
Normal file
1001
Modules/ControlCenter/Components/DragDropGrid.qml
Normal file
File diff suppressed because it is too large
Load Diff
289
Modules/ControlCenter/Components/DragDropWidgetWrapper.qml
Normal file
289
Modules/ControlCenter/Components/DragDropWidgetWrapper.qml
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool editMode: false
|
||||||
|
property var widgetData: null
|
||||||
|
property int widgetIndex: -1
|
||||||
|
property bool isSlider: false
|
||||||
|
property Component widgetComponent: null
|
||||||
|
property real gridCellWidth: 100
|
||||||
|
property real gridCellHeight: 60
|
||||||
|
property int gridColumns: 4
|
||||||
|
property var gridLayout: null
|
||||||
|
|
||||||
|
z: dragArea.drag.active ? 10000 : 1
|
||||||
|
|
||||||
|
signal widgetMoved(int fromIndex, int toIndex)
|
||||||
|
signal removeWidget(int index)
|
||||||
|
signal toggleWidgetSize(int index)
|
||||||
|
|
||||||
|
width: {
|
||||||
|
const widgetWidth = widgetData?.width || 50
|
||||||
|
if (widgetWidth <= 25) return gridCellWidth
|
||||||
|
else if (widgetWidth <= 50) return gridCellWidth * 2
|
||||||
|
else if (widgetWidth <= 75) return gridCellWidth * 3
|
||||||
|
else return gridCellWidth * 4
|
||||||
|
}
|
||||||
|
height: isSlider ? 16 : gridCellHeight
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: dragIndicator
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "transparent"
|
||||||
|
border.color: Theme.primary
|
||||||
|
border.width: dragArea.drag.active ? 2 : 0
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
opacity: dragArea.drag.active ? 0.8 : 1.0
|
||||||
|
z: dragArea.drag.active ? 10000 : 1
|
||||||
|
|
||||||
|
Behavior on border.width {
|
||||||
|
NumberAnimation { duration: 150 }
|
||||||
|
}
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation { duration: 150 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: widgetLoader
|
||||||
|
anchors.fill: parent
|
||||||
|
sourceComponent: widgetComponent
|
||||||
|
property var widgetData: root.widgetData
|
||||||
|
property int widgetIndex: root.widgetIndex
|
||||||
|
property int globalWidgetIndex: root.widgetIndex
|
||||||
|
property int widgetWidth: root.widgetData?.width || 50
|
||||||
|
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: editModeBlocker
|
||||||
|
anchors.fill: parent
|
||||||
|
enabled: root.editMode
|
||||||
|
acceptedButtons: Qt.AllButtons
|
||||||
|
onPressed: function(mouse) { mouse.accepted = true }
|
||||||
|
onWheel: function(wheel) { wheel.accepted = true }
|
||||||
|
z: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: dragArea
|
||||||
|
anchors.fill: parent
|
||||||
|
enabled: editMode
|
||||||
|
cursorShape: editMode ? Qt.OpenHandCursor : Qt.PointingHandCursor
|
||||||
|
drag.target: editMode ? root : null
|
||||||
|
drag.axis: Drag.XAndYAxis
|
||||||
|
drag.smoothed: true
|
||||||
|
|
||||||
|
onPressed: function(mouse) {
|
||||||
|
if (editMode) {
|
||||||
|
cursorShape = Qt.ClosedHandCursor
|
||||||
|
if (root.gridLayout && root.gridLayout.moveToTop) {
|
||||||
|
root.gridLayout.moveToTop(root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onReleased: function(mouse) {
|
||||||
|
if (editMode) {
|
||||||
|
cursorShape = Qt.OpenHandCursor
|
||||||
|
root.snapToGrid()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Drag.active: dragArea.drag.active
|
||||||
|
Drag.hotSpot.x: width / 2
|
||||||
|
Drag.hotSpot.y: height / 2
|
||||||
|
|
||||||
|
function swapIndices(i, j) {
|
||||||
|
if (i === j) return;
|
||||||
|
const arr = SettingsData.controlCenterWidgets;
|
||||||
|
if (!arr || i < 0 || j < 0 || i >= arr.length || j >= arr.length) return;
|
||||||
|
|
||||||
|
const copy = arr.slice();
|
||||||
|
const tmp = copy[i];
|
||||||
|
copy[i] = copy[j];
|
||||||
|
copy[j] = tmp;
|
||||||
|
|
||||||
|
SettingsData.setControlCenterWidgets(copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
function snapToGrid() {
|
||||||
|
if (!editMode || !gridLayout) return
|
||||||
|
|
||||||
|
const globalPos = root.mapToItem(gridLayout, 0, 0)
|
||||||
|
const cellWidth = gridLayout.width / gridColumns
|
||||||
|
const cellHeight = gridCellHeight + Theme.spacingS
|
||||||
|
|
||||||
|
const centerX = globalPos.x + (root.width / 2)
|
||||||
|
const centerY = globalPos.y + (root.height / 2)
|
||||||
|
|
||||||
|
let targetCol = Math.max(0, Math.floor(centerX / cellWidth))
|
||||||
|
let targetRow = Math.max(0, Math.floor(centerY / cellHeight))
|
||||||
|
|
||||||
|
targetCol = Math.min(targetCol, gridColumns - 1)
|
||||||
|
|
||||||
|
const newIndex = findBestInsertionIndex(targetRow, targetCol)
|
||||||
|
|
||||||
|
if (newIndex !== widgetIndex && newIndex >= 0 && newIndex < (SettingsData.controlCenterWidgets?.length || 0)) {
|
||||||
|
swapIndices(widgetIndex, newIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function findBestInsertionIndex(targetRow, targetCol) {
|
||||||
|
const widgets = SettingsData.controlCenterWidgets || [];
|
||||||
|
const n = widgets.length;
|
||||||
|
if (!n || widgetIndex < 0 || widgetIndex >= n) return -1;
|
||||||
|
|
||||||
|
function spanFor(width) {
|
||||||
|
const w = width ?? 50;
|
||||||
|
if (w <= 25) return 1;
|
||||||
|
if (w <= 50) return 2;
|
||||||
|
if (w <= 75) return 3;
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cols = gridColumns || 4;
|
||||||
|
|
||||||
|
let row = 0, col = 0;
|
||||||
|
let draggedOrigKey = null;
|
||||||
|
|
||||||
|
const pos = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
const span = Math.min(spanFor(widgets[i].width), cols);
|
||||||
|
|
||||||
|
if (col + span > cols) {
|
||||||
|
row++;
|
||||||
|
col = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const startCol = col;
|
||||||
|
const centerKey = row * cols + (startCol + (span - 1) / 2);
|
||||||
|
|
||||||
|
if (i === widgetIndex) {
|
||||||
|
draggedOrigKey = centerKey;
|
||||||
|
} else {
|
||||||
|
pos.push({ index: i, row, startCol, span, centerKey });
|
||||||
|
}
|
||||||
|
|
||||||
|
col += span;
|
||||||
|
if (col >= cols) {
|
||||||
|
row++;
|
||||||
|
col = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos.length === 0) return -1;
|
||||||
|
|
||||||
|
const centerColCoord = targetCol + 0.5;
|
||||||
|
const targetKey = targetRow * cols + centerColCoord;
|
||||||
|
|
||||||
|
for (let k = 0; k < pos.length; k++) {
|
||||||
|
const p = pos[k];
|
||||||
|
if (p.row === targetRow && centerColCoord >= p.startCol && centerColCoord < (p.startCol + p.span)) {
|
||||||
|
return p.index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let lo = 0, hi = pos.length - 1;
|
||||||
|
if (targetKey <= pos[0].centerKey) return pos[0].index;
|
||||||
|
if (targetKey >= pos[hi].centerKey) return pos[hi].index;
|
||||||
|
|
||||||
|
while (lo <= hi) {
|
||||||
|
const mid = (lo + hi) >> 1;
|
||||||
|
const mk = pos[mid].centerKey;
|
||||||
|
if (targetKey < mk) hi = mid - 1;
|
||||||
|
else if (targetKey > mk) lo = mid + 1;
|
||||||
|
else return pos[mid].index;
|
||||||
|
}
|
||||||
|
const movingUp = (draggedOrigKey != null) ? (targetKey < draggedOrigKey) : false;
|
||||||
|
return (movingUp ? pos[lo].index : pos[hi].index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 16
|
||||||
|
height: 16
|
||||||
|
radius: 8
|
||||||
|
color: Theme.error
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.margins: -4
|
||||||
|
visible: editMode
|
||||||
|
z: 10
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: "close"
|
||||||
|
size: 12
|
||||||
|
color: Theme.primaryText
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: removeWidget(widgetIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SizeControls {
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.margins: -6
|
||||||
|
visible: editMode
|
||||||
|
z: 10
|
||||||
|
currentSize: root.widgetData?.width || 50
|
||||||
|
isSlider: root.isSlider
|
||||||
|
widgetIndex: root.widgetIndex
|
||||||
|
onSizeChanged: (newSize) => {
|
||||||
|
var widgets = SettingsData.controlCenterWidgets.slice()
|
||||||
|
if (widgetIndex >= 0 && widgetIndex < widgets.length) {
|
||||||
|
widgets[widgetIndex].width = newSize
|
||||||
|
SettingsData.setControlCenterWidgets(widgets)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: dragHandle
|
||||||
|
width: 16
|
||||||
|
height: 12
|
||||||
|
radius: 2
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.margins: 4
|
||||||
|
visible: editMode
|
||||||
|
z: 15
|
||||||
|
opacity: dragArea.drag.active ? 1.0 : 0.7
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: "drag_indicator"
|
||||||
|
size: 10
|
||||||
|
color: Theme.primaryText
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation { duration: 150 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: editMode ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
border.color: "transparent"
|
||||||
|
border.width: 0
|
||||||
|
z: -1
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation { duration: Theme.shortDuration }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ Row {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
property var availableWidgets: []
|
property var availableWidgets: []
|
||||||
|
property Item popoutContent: null
|
||||||
|
|
||||||
signal addWidget(string widgetId)
|
signal addWidget(string widgetId)
|
||||||
signal resetToDefault()
|
signal resetToDefault()
|
||||||
@@ -19,7 +20,9 @@ Row {
|
|||||||
|
|
||||||
Popup {
|
Popup {
|
||||||
id: addWidgetPopup
|
id: addWidgetPopup
|
||||||
anchors.centerIn: parent
|
parent: popoutContent
|
||||||
|
x: parent ? Math.round((parent.width - width) / 2) : 0
|
||||||
|
y: parent ? Math.round((parent.height - height) / 2) : 0
|
||||||
width: 400
|
width: 400
|
||||||
height: 300
|
height: 300
|
||||||
modal: true
|
modal: true
|
||||||
@@ -29,7 +32,7 @@ Row {
|
|||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: Theme.surfaceContainer
|
color: Theme.surfaceContainer
|
||||||
border.color: Theme.primarySelected
|
border.color: Theme.primarySelected
|
||||||
border.width: 1
|
border.width: 0
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +55,7 @@ Row {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Typography {
|
Typography {
|
||||||
text: "Add Widget"
|
text: I18n.tr("Add Widget")
|
||||||
style: Typography.Style.Subtitle
|
style: Typography.Style.Subtitle
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
@@ -66,15 +69,16 @@ Row {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
clip: true
|
||||||
model: root.availableWidgets
|
model: root.availableWidgets
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
width: 400 - Theme.spacingL * 2
|
width: 400 - Theme.spacingL * 2
|
||||||
height: 50
|
height: 50
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: widgetMouseArea.containsMouse ? Theme.primaryHover : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
color: widgetMouseArea.containsMouse ? Theme.primaryHover : Theme.surfaceContainerHigh
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||||
border.width: 1
|
border.width: 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -138,7 +142,7 @@ Row {
|
|||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
||||||
border.color: Theme.primary
|
border.color: Theme.primary
|
||||||
border.width: 1
|
border.width: 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -152,7 +156,7 @@ Row {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Typography {
|
Typography {
|
||||||
text: "Add Widget"
|
text: I18n.tr("Add Widget")
|
||||||
style: Typography.Style.Button
|
style: Typography.Style.Button
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
@@ -172,7 +176,7 @@ Row {
|
|||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12)
|
color: Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12)
|
||||||
border.color: Theme.warning
|
border.color: Theme.warning
|
||||||
border.width: 1
|
border.width: 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -186,7 +190,7 @@ Row {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Typography {
|
Typography {
|
||||||
text: "Defaults"
|
text: I18n.tr("Defaults")
|
||||||
style: Typography.Style.Button
|
style: Typography.Style.Button
|
||||||
color: Theme.warning
|
color: Theme.warning
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
@@ -206,7 +210,7 @@ Row {
|
|||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
|
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
|
||||||
border.color: Theme.error
|
border.color: Theme.error
|
||||||
border.width: 1
|
border.width: 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -220,7 +224,7 @@ Row {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Typography {
|
Typography {
|
||||||
text: "Reset"
|
text: I18n.tr("Reset")
|
||||||
style: Typography.Style.Button
|
style: Typography.Style.Button
|
||||||
color: Theme.error
|
color: Theme.error
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|||||||
@@ -1,241 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import qs.Common
|
|
||||||
import qs.Widgets
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property bool editMode: false
|
|
||||||
property var widgetData: null
|
|
||||||
property int widgetIndex: -1
|
|
||||||
property bool showSizeControls: true
|
|
||||||
property bool isSlider: false
|
|
||||||
|
|
||||||
signal removeWidget(int index)
|
|
||||||
signal toggleWidgetSize(int index)
|
|
||||||
signal moveWidget(int fromIndex, int toIndex)
|
|
||||||
|
|
||||||
// Delete button in top-right
|
|
||||||
Rectangle {
|
|
||||||
width: 16
|
|
||||||
height: 16
|
|
||||||
radius: 8
|
|
||||||
color: Theme.error
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.margins: -4
|
|
||||||
visible: editMode
|
|
||||||
z: 10
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
name: "close"
|
|
||||||
size: 12
|
|
||||||
color: Theme.primaryText
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: root.removeWidget(widgetIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size control buttons in bottom-right
|
|
||||||
Row {
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.margins: -8
|
|
||||||
spacing: 4
|
|
||||||
visible: editMode && showSizeControls
|
|
||||||
z: 10
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: 24
|
|
||||||
height: 24
|
|
||||||
radius: 12
|
|
||||||
color: (widgetData?.width || 50) === 25 ? Theme.primary : Theme.primaryContainer
|
|
||||||
border.color: Theme.primary
|
|
||||||
border.width: 1
|
|
||||||
visible: !isSlider
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
text: "25"
|
|
||||||
font.pixelSize: 10
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: (widgetData?.width || 50) === 25 ? Theme.primaryText : Theme.primary
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: {
|
|
||||||
var widgets = SettingsData.controlCenterWidgets.slice()
|
|
||||||
if (widgetIndex >= 0 && widgetIndex < widgets.length) {
|
|
||||||
widgets[widgetIndex].width = 25
|
|
||||||
SettingsData.setControlCenterWidgets(widgets)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: 24
|
|
||||||
height: 24
|
|
||||||
radius: 12
|
|
||||||
color: (widgetData?.width || 50) === 50 ? Theme.primary : Theme.primaryContainer
|
|
||||||
border.color: Theme.primary
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
text: "50"
|
|
||||||
font.pixelSize: 10
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: (widgetData?.width || 50) === 50 ? Theme.primaryText : Theme.primary
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: {
|
|
||||||
var widgets = SettingsData.controlCenterWidgets.slice()
|
|
||||||
if (widgetIndex >= 0 && widgetIndex < widgets.length) {
|
|
||||||
widgets[widgetIndex].width = 50
|
|
||||||
SettingsData.setControlCenterWidgets(widgets)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: 24
|
|
||||||
height: 24
|
|
||||||
radius: 12
|
|
||||||
color: (widgetData?.width || 50) === 75 ? Theme.primary : Theme.primaryContainer
|
|
||||||
border.color: Theme.primary
|
|
||||||
border.width: 1
|
|
||||||
visible: !isSlider
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
text: "75"
|
|
||||||
font.pixelSize: 10
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: (widgetData?.width || 50) === 75 ? Theme.primaryText : Theme.primary
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: {
|
|
||||||
var widgets = SettingsData.controlCenterWidgets.slice()
|
|
||||||
if (widgetIndex >= 0 && widgetIndex < widgets.length) {
|
|
||||||
widgets[widgetIndex].width = 75
|
|
||||||
SettingsData.setControlCenterWidgets(widgets)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: 24
|
|
||||||
height: 24
|
|
||||||
radius: 12
|
|
||||||
color: (widgetData?.width || 50) === 100 ? Theme.primary : Theme.primaryContainer
|
|
||||||
border.color: Theme.primary
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
text: "100"
|
|
||||||
font.pixelSize: 9
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: (widgetData?.width || 50) === 100 ? Theme.primaryText : Theme.primary
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: {
|
|
||||||
var widgets = SettingsData.controlCenterWidgets.slice()
|
|
||||||
if (widgetIndex >= 0 && widgetIndex < widgets.length) {
|
|
||||||
widgets[widgetIndex].width = 100
|
|
||||||
SettingsData.setControlCenterWidgets(widgets)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arrow buttons for reordering in top-left
|
|
||||||
Row {
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.margins: 4
|
|
||||||
spacing: 2
|
|
||||||
visible: editMode
|
|
||||||
z: 20
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: 16
|
|
||||||
height: 16
|
|
||||||
radius: 8
|
|
||||||
color: Theme.surfaceContainer
|
|
||||||
border.color: Theme.outline
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
name: "keyboard_arrow_left"
|
|
||||||
size: 12
|
|
||||||
color: Theme.surfaceText
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
enabled: widgetIndex > 0
|
|
||||||
opacity: enabled ? 1.0 : 0.5
|
|
||||||
onClicked: root.moveWidget(widgetIndex, widgetIndex - 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: 16
|
|
||||||
height: 16
|
|
||||||
radius: 8
|
|
||||||
color: Theme.surfaceContainer
|
|
||||||
border.color: Theme.outline
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
name: "keyboard_arrow_right"
|
|
||||||
size: 12
|
|
||||||
color: Theme.surfaceText
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
enabled: widgetIndex < ((SettingsData.controlCenterWidgets?.length ?? 0) - 1)
|
|
||||||
opacity: enabled ? 1.0 : 0.5
|
|
||||||
onClicked: root.moveWidget(widgetIndex, widgetIndex + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Border highlight
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.1)
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
border.color: Theme.primary
|
|
||||||
border.width: editMode ? 1 : 0
|
|
||||||
visible: editMode
|
|
||||||
z: -1
|
|
||||||
|
|
||||||
Behavior on border.width {
|
|
||||||
NumberAnimation { duration: Theme.shortDuration }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,22 +6,19 @@ import qs.Widgets
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool powerOptionsExpanded: false
|
|
||||||
property bool editMode: false
|
property bool editMode: false
|
||||||
|
|
||||||
signal powerActionRequested(string action, string title, string message)
|
signal powerButtonClicked()
|
||||||
signal lockRequested()
|
signal lockRequested()
|
||||||
signal editModeToggled()
|
signal editModeToggled()
|
||||||
|
signal settingsButtonClicked()
|
||||||
|
|
||||||
implicitHeight: 70
|
implicitHeight: 70
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.surfaceVariant.r,
|
color: Theme.surfaceContainerHigh
|
||||||
Theme.surfaceVariant.g,
|
|
||||||
Theme.surfaceVariant.b,
|
|
||||||
Theme.getContentBackgroundAlpha() * 0.4)
|
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
|
||||||
Theme.outline.b, 0.08)
|
Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -86,13 +83,11 @@ Rectangle {
|
|||||||
|
|
||||||
DankActionButton {
|
DankActionButton {
|
||||||
buttonSize: 36
|
buttonSize: 36
|
||||||
iconName: root.powerOptionsExpanded ? "expand_less" : "power_settings_new"
|
iconName: "power_settings_new"
|
||||||
iconSize: Theme.iconSize - 4
|
iconSize: Theme.iconSize - 4
|
||||||
iconColor: root.powerOptionsExpanded ? Theme.primary : Theme.surfaceText
|
iconColor: Theme.surfaceText
|
||||||
backgroundColor: "transparent"
|
backgroundColor: "transparent"
|
||||||
onClicked: {
|
onClicked: root.powerButtonClicked()
|
||||||
root.powerOptionsExpanded = !root.powerOptionsExpanded
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DankActionButton {
|
DankActionButton {
|
||||||
@@ -102,6 +97,7 @@ Rectangle {
|
|||||||
iconColor: Theme.surfaceText
|
iconColor: Theme.surfaceText
|
||||||
backgroundColor: "transparent"
|
backgroundColor: "transparent"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
root.settingsButtonClicked()
|
||||||
settingsModal.show()
|
settingsModal.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import qs.Common
|
|
||||||
import qs.Services
|
|
||||||
import qs.Widgets
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property bool expanded: false
|
|
||||||
|
|
||||||
signal powerActionRequested(string action, string title, string message)
|
|
||||||
|
|
||||||
implicitHeight: expanded ? 60 : 0
|
|
||||||
height: implicitHeight
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: parent.width
|
|
||||||
height: 60
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: Qt.rgba(Theme.surfaceVariant.r,
|
|
||||||
Theme.surfaceVariant.g,
|
|
||||||
Theme.surfaceVariant.b,
|
|
||||||
Theme.getContentBackgroundAlpha() * 0.4)
|
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
|
|
||||||
Theme.outline.b, 0.08)
|
|
||||||
border.width: root.expanded ? 1 : 0
|
|
||||||
opacity: root.expanded ? 1 : 0
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
Row {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: SessionService.hibernateSupported ? Theme.spacingS : Theme.spacingL
|
|
||||||
visible: root.expanded
|
|
||||||
|
|
||||||
PowerButton {
|
|
||||||
width: SessionService.hibernateSupported ? 85 : 100
|
|
||||||
iconName: "logout"
|
|
||||||
text: "Logout"
|
|
||||||
onPressed: root.powerActionRequested("logout", "Logout", "Are you sure you want to logout?")
|
|
||||||
}
|
|
||||||
|
|
||||||
PowerButton {
|
|
||||||
width: SessionService.hibernateSupported ? 85 : 100
|
|
||||||
iconName: "restart_alt"
|
|
||||||
text: "Restart"
|
|
||||||
onPressed: root.powerActionRequested("reboot", "Restart", "Are you sure you want to restart?")
|
|
||||||
}
|
|
||||||
|
|
||||||
PowerButton {
|
|
||||||
width: SessionService.hibernateSupported ? 85 : 100
|
|
||||||
iconName: "bedtime"
|
|
||||||
text: "Suspend"
|
|
||||||
onPressed: root.powerActionRequested("suspend", "Suspend", "Are you sure you want to suspend?")
|
|
||||||
}
|
|
||||||
|
|
||||||
PowerButton {
|
|
||||||
width: SessionService.hibernateSupported ? 85 : 100
|
|
||||||
iconName: "ac_unit"
|
|
||||||
text: "Hibernate"
|
|
||||||
visible: SessionService.hibernateSupported
|
|
||||||
onPressed: root.powerActionRequested("hibernate", "Hibernate", "Are you sure you want to hibernate?")
|
|
||||||
}
|
|
||||||
|
|
||||||
PowerButton {
|
|
||||||
width: SessionService.hibernateSupported ? 85 : 100
|
|
||||||
iconName: "power_settings_new"
|
|
||||||
text: "Shutdown"
|
|
||||||
onPressed: root.powerActionRequested("poweroff", "Shutdown", "Are you sure you want to shutdown?")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
52
Modules/ControlCenter/Components/SizeControls.qml
Normal file
52
Modules/ControlCenter/Components/SizeControls.qml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import qs.Common
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property int currentSize: 50
|
||||||
|
property bool isSlider: false
|
||||||
|
property int widgetIndex: -1
|
||||||
|
|
||||||
|
signal sizeChanged(int newSize)
|
||||||
|
|
||||||
|
readonly property var availableSizes: isSlider ? [50, 100] : [25, 50, 75, 100]
|
||||||
|
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: root.availableSizes
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 16
|
||||||
|
height: 16
|
||||||
|
radius: 3
|
||||||
|
color: modelData === root.currentSize ? Theme.primary : Theme.surfaceContainer
|
||||||
|
border.color: modelData === root.currentSize ? Theme.primary : Theme.outline
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: modelData.toString()
|
||||||
|
font.pixelSize: 8
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: modelData === root.currentSize ? Theme.primaryText : Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
root.currentSize = modelData
|
||||||
|
root.sizeChanged(modelData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation { duration: Theme.shortDuration }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,734 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import qs.Common
|
|
||||||
import qs.Services
|
|
||||||
import qs.Modules.ControlCenter.Widgets
|
|
||||||
import qs.Modules.ControlCenter.Components
|
|
||||||
import "../utils/layout.js" as LayoutUtils
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property bool editMode: false
|
|
||||||
property string expandedSection: ""
|
|
||||||
property int expandedWidgetIndex: -1
|
|
||||||
property var model: null
|
|
||||||
property var expandedWidgetData: null
|
|
||||||
|
|
||||||
signal expandClicked(var widgetData, int globalIndex)
|
|
||||||
signal removeWidget(int index)
|
|
||||||
signal moveWidget(int fromIndex, int toIndex)
|
|
||||||
signal toggleWidgetSize(int index)
|
|
||||||
|
|
||||||
spacing: editMode ? Theme.spacingL : Theme.spacingS
|
|
||||||
|
|
||||||
property var currentRowWidgets: []
|
|
||||||
property real currentRowWidth: 0
|
|
||||||
property int expandedRowIndex: -1
|
|
||||||
|
|
||||||
function calculateRowsAndWidgets() {
|
|
||||||
return LayoutUtils.calculateRowsAndWidgets(root, expandedSection, expandedWidgetIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
property var layoutResult: {
|
|
||||||
const dummy = [expandedSection, expandedWidgetIndex, model?.controlCenterWidgets]
|
|
||||||
return calculateRowsAndWidgets()
|
|
||||||
}
|
|
||||||
|
|
||||||
onLayoutResultChanged: {
|
|
||||||
expandedRowIndex = layoutResult.expandedRowIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: root.layoutResult.rows
|
|
||||||
|
|
||||||
Column {
|
|
||||||
width: root.width
|
|
||||||
spacing: 0
|
|
||||||
property int rowIndex: index
|
|
||||||
property var rowWidgets: modelData
|
|
||||||
property bool isSliderOnlyRow: {
|
|
||||||
const widgets = rowWidgets || []
|
|
||||||
if (widgets.length === 0) return false
|
|
||||||
return widgets.every(w => w.id === "volumeSlider" || w.id === "brightnessSlider" || w.id === "inputVolumeSlider")
|
|
||||||
}
|
|
||||||
topPadding: isSliderOnlyRow ? (root.editMode ? 4 : -12) : 0
|
|
||||||
bottomPadding: isSliderOnlyRow ? (root.editMode ? 4 : -12) : 0
|
|
||||||
|
|
||||||
Flow {
|
|
||||||
width: parent.width
|
|
||||||
spacing: Theme.spacingS
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: rowWidgets || []
|
|
||||||
|
|
||||||
Item {
|
|
||||||
property var widgetData: modelData
|
|
||||||
property int globalWidgetIndex: {
|
|
||||||
const widgets = SettingsData.controlCenterWidgets || []
|
|
||||||
for (var i = 0; i < widgets.length; i++) {
|
|
||||||
if (widgets[i].id === modelData.id) {
|
|
||||||
if (modelData.id === "diskUsage") {
|
|
||||||
if (widgets[i].instanceId === modelData.instanceId) {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
property int widgetWidth: modelData.width || 50
|
|
||||||
width: {
|
|
||||||
const baseWidth = root.width
|
|
||||||
const spacing = Theme.spacingS
|
|
||||||
if (widgetWidth <= 25) {
|
|
||||||
return (baseWidth - spacing * 3) / 4
|
|
||||||
} else if (widgetWidth <= 50) {
|
|
||||||
return (baseWidth - spacing) / 2
|
|
||||||
} else if (widgetWidth <= 75) {
|
|
||||||
return (baseWidth - spacing * 2) * 0.75
|
|
||||||
} else {
|
|
||||||
return baseWidth
|
|
||||||
}
|
|
||||||
}
|
|
||||||
height: 60
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
id: widgetLoader
|
|
||||||
anchors.fill: parent
|
|
||||||
property var widgetData: parent.widgetData
|
|
||||||
property int widgetIndex: parent.globalWidgetIndex
|
|
||||||
property int globalWidgetIndex: parent.globalWidgetIndex
|
|
||||||
property int widgetWidth: parent.widgetWidth
|
|
||||||
|
|
||||||
sourceComponent: {
|
|
||||||
const id = modelData.id || ""
|
|
||||||
if (id === "wifi" || id === "bluetooth" || id === "audioOutput" || id === "audioInput") {
|
|
||||||
return compoundPillComponent
|
|
||||||
} else if (id === "volumeSlider") {
|
|
||||||
return audioSliderComponent
|
|
||||||
} else if (id === "brightnessSlider") {
|
|
||||||
return brightnessSliderComponent
|
|
||||||
} else if (id === "inputVolumeSlider") {
|
|
||||||
return inputAudioSliderComponent
|
|
||||||
} else if (id === "battery") {
|
|
||||||
return widgetWidth <= 25 ? smallBatteryComponent : batteryPillComponent
|
|
||||||
} else if (id === "diskUsage") {
|
|
||||||
return diskUsagePillComponent
|
|
||||||
} else {
|
|
||||||
return widgetWidth <= 25 ? smallToggleComponent : toggleButtonComponent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DetailHost {
|
|
||||||
width: parent.width
|
|
||||||
height: active ? (250 + Theme.spacingS) : 0
|
|
||||||
property bool active: {
|
|
||||||
if (root.expandedSection === "") return false
|
|
||||||
|
|
||||||
if (root.expandedSection.startsWith("diskUsage_") && root.expandedWidgetData) {
|
|
||||||
const expandedInstanceId = root.expandedWidgetData.instanceId
|
|
||||||
return rowWidgets.some(w => w.id === "diskUsage" && w.instanceId === expandedInstanceId)
|
|
||||||
}
|
|
||||||
|
|
||||||
return rowIndex === root.expandedRowIndex
|
|
||||||
}
|
|
||||||
visible: active
|
|
||||||
expandedSection: root.expandedSection
|
|
||||||
expandedWidgetData: root.expandedWidgetData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: compoundPillComponent
|
|
||||||
CompoundPill {
|
|
||||||
property var widgetData: parent.widgetData || {}
|
|
||||||
property int widgetIndex: parent.widgetIndex || 0
|
|
||||||
property var widgetDef: root.model?.getWidgetForId(widgetData.id || "")
|
|
||||||
width: parent.width
|
|
||||||
height: 60
|
|
||||||
iconName: {
|
|
||||||
switch (widgetData.id || "") {
|
|
||||||
case "wifi": {
|
|
||||||
if (NetworkService.wifiToggling) {
|
|
||||||
return "sync"
|
|
||||||
}
|
|
||||||
if (NetworkService.networkStatus === "ethernet") {
|
|
||||||
return "settings_ethernet"
|
|
||||||
}
|
|
||||||
if (NetworkService.networkStatus === "wifi") {
|
|
||||||
return NetworkService.wifiSignalIcon
|
|
||||||
}
|
|
||||||
if (NetworkService.wifiEnabled) {
|
|
||||||
return "wifi_off"
|
|
||||||
}
|
|
||||||
return "wifi_off"
|
|
||||||
}
|
|
||||||
case "bluetooth": {
|
|
||||||
if (!BluetoothService.available) {
|
|
||||||
return "bluetooth_disabled"
|
|
||||||
}
|
|
||||||
if (!BluetoothService.adapter || !BluetoothService.adapter.enabled) {
|
|
||||||
return "bluetooth_disabled"
|
|
||||||
}
|
|
||||||
const primaryDevice = (() => {
|
|
||||||
if (!BluetoothService.adapter || !BluetoothService.adapter.devices) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
let devices = [...BluetoothService.adapter.devices.values.filter(dev => dev && (dev.paired || dev.trusted))]
|
|
||||||
for (let device of devices) {
|
|
||||||
if (device && device.connected) {
|
|
||||||
return device
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
})()
|
|
||||||
if (primaryDevice) {
|
|
||||||
return BluetoothService.getDeviceIcon(primaryDevice)
|
|
||||||
}
|
|
||||||
return "bluetooth"
|
|
||||||
}
|
|
||||||
case "audioOutput": {
|
|
||||||
if (!AudioService.sink) return "volume_off"
|
|
||||||
let volume = AudioService.sink.audio.volume
|
|
||||||
let muted = AudioService.sink.audio.muted
|
|
||||||
if (muted || volume === 0.0) return "volume_off"
|
|
||||||
if (volume <= 0.33) return "volume_down"
|
|
||||||
if (volume <= 0.66) return "volume_up"
|
|
||||||
return "volume_up"
|
|
||||||
}
|
|
||||||
case "audioInput": {
|
|
||||||
if (!AudioService.source) return "mic_off"
|
|
||||||
let muted = AudioService.source.audio.muted
|
|
||||||
return muted ? "mic_off" : "mic"
|
|
||||||
}
|
|
||||||
default: return widgetDef?.icon || "help"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
primaryText: {
|
|
||||||
switch (widgetData.id || "") {
|
|
||||||
case "wifi": {
|
|
||||||
if (NetworkService.wifiToggling) {
|
|
||||||
return NetworkService.wifiEnabled ? "Disabling WiFi..." : "Enabling WiFi..."
|
|
||||||
}
|
|
||||||
if (NetworkService.networkStatus === "ethernet") {
|
|
||||||
return "Ethernet"
|
|
||||||
}
|
|
||||||
if (NetworkService.networkStatus === "wifi" && NetworkService.currentWifiSSID) {
|
|
||||||
return NetworkService.currentWifiSSID
|
|
||||||
}
|
|
||||||
if (NetworkService.wifiEnabled) {
|
|
||||||
return "Not connected"
|
|
||||||
}
|
|
||||||
return "WiFi off"
|
|
||||||
}
|
|
||||||
case "bluetooth": {
|
|
||||||
if (!BluetoothService.available) {
|
|
||||||
return "Bluetooth"
|
|
||||||
}
|
|
||||||
if (!BluetoothService.adapter) {
|
|
||||||
return "No adapter"
|
|
||||||
}
|
|
||||||
if (!BluetoothService.adapter.enabled) {
|
|
||||||
return "Disabled"
|
|
||||||
}
|
|
||||||
return "Enabled"
|
|
||||||
}
|
|
||||||
case "audioOutput": return AudioService.sink?.description || "No output device"
|
|
||||||
case "audioInput": return AudioService.source?.description || "No input device"
|
|
||||||
default: return widgetDef?.text || "Unknown"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
secondaryText: {
|
|
||||||
switch (widgetData.id || "") {
|
|
||||||
case "wifi": {
|
|
||||||
if (NetworkService.wifiToggling) {
|
|
||||||
return "Please wait..."
|
|
||||||
}
|
|
||||||
if (NetworkService.networkStatus === "ethernet") {
|
|
||||||
return "Connected"
|
|
||||||
}
|
|
||||||
if (NetworkService.networkStatus === "wifi") {
|
|
||||||
return NetworkService.wifiSignalStrength > 0 ? NetworkService.wifiSignalStrength + "%" : "Connected"
|
|
||||||
}
|
|
||||||
if (NetworkService.wifiEnabled) {
|
|
||||||
return "Select network"
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
case "bluetooth": {
|
|
||||||
if (!BluetoothService.available) {
|
|
||||||
return "No adapters"
|
|
||||||
}
|
|
||||||
if (!BluetoothService.adapter || !BluetoothService.adapter.enabled) {
|
|
||||||
return "Off"
|
|
||||||
}
|
|
||||||
const primaryDevice = (() => {
|
|
||||||
if (!BluetoothService.adapter || !BluetoothService.adapter.devices) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
let devices = [...BluetoothService.adapter.devices.values.filter(dev => dev && (dev.paired || dev.trusted))]
|
|
||||||
for (let device of devices) {
|
|
||||||
if (device && device.connected) {
|
|
||||||
return device
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
})()
|
|
||||||
if (primaryDevice) {
|
|
||||||
return primaryDevice.name || primaryDevice.alias || primaryDevice.deviceName || "Connected Device"
|
|
||||||
}
|
|
||||||
return "No devices"
|
|
||||||
}
|
|
||||||
case "audioOutput": {
|
|
||||||
if (!AudioService.sink) {
|
|
||||||
return "Select device"
|
|
||||||
}
|
|
||||||
if (AudioService.sink.audio.muted) {
|
|
||||||
return "Muted"
|
|
||||||
}
|
|
||||||
return Math.round(AudioService.sink.audio.volume * 100) + "%"
|
|
||||||
}
|
|
||||||
case "audioInput": {
|
|
||||||
if (!AudioService.source) {
|
|
||||||
return "Select device"
|
|
||||||
}
|
|
||||||
if (AudioService.source.audio.muted) {
|
|
||||||
return "Muted"
|
|
||||||
}
|
|
||||||
return Math.round(AudioService.source.audio.volume * 100) + "%"
|
|
||||||
}
|
|
||||||
default: return widgetDef?.description || ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
isActive: {
|
|
||||||
switch (widgetData.id || "") {
|
|
||||||
case "wifi": {
|
|
||||||
if (NetworkService.wifiToggling) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (NetworkService.networkStatus === "ethernet") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (NetworkService.networkStatus === "wifi") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return NetworkService.wifiEnabled
|
|
||||||
}
|
|
||||||
case "bluetooth": return !!(BluetoothService.available && BluetoothService.adapter && BluetoothService.adapter.enabled)
|
|
||||||
case "audioOutput": return !!(AudioService.sink && !AudioService.sink.audio.muted)
|
|
||||||
case "audioInput": return !!(AudioService.source && !AudioService.source.audio.muted)
|
|
||||||
default: return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
enabled: (widgetDef?.enabled ?? true)
|
|
||||||
onToggled: {
|
|
||||||
if (root.editMode) return
|
|
||||||
switch (widgetData.id || "") {
|
|
||||||
case "wifi": {
|
|
||||||
if (NetworkService.networkStatus !== "ethernet" && !NetworkService.wifiToggling) {
|
|
||||||
NetworkService.toggleWifiRadio()
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case "bluetooth": {
|
|
||||||
if (BluetoothService.available && BluetoothService.adapter) {
|
|
||||||
BluetoothService.adapter.enabled = !BluetoothService.adapter.enabled
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case "audioOutput": {
|
|
||||||
if (AudioService.sink && AudioService.sink.audio) {
|
|
||||||
AudioService.sink.audio.muted = !AudioService.sink.audio.muted
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case "audioInput": {
|
|
||||||
if (AudioService.source && AudioService.source.audio) {
|
|
||||||
AudioService.source.audio.muted = !AudioService.source.audio.muted
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onExpandClicked: {
|
|
||||||
if (root.editMode) return
|
|
||||||
root.expandClicked(widgetData, widgetIndex)
|
|
||||||
}
|
|
||||||
onWheelEvent: function (wheelEvent) {
|
|
||||||
const id = widgetData.id || ""
|
|
||||||
if (id === "audioOutput") {
|
|
||||||
if (!AudioService.sink || !AudioService.sink.audio) return
|
|
||||||
let delta = wheelEvent.angleDelta.y
|
|
||||||
let currentVolume = AudioService.sink.audio.volume * 100
|
|
||||||
let newVolume
|
|
||||||
if (delta > 0)
|
|
||||||
newVolume = Math.min(100, currentVolume + 5)
|
|
||||||
else
|
|
||||||
newVolume = Math.max(0, currentVolume - 5)
|
|
||||||
AudioService.sink.audio.muted = false
|
|
||||||
AudioService.sink.audio.volume = newVolume / 100
|
|
||||||
wheelEvent.accepted = true
|
|
||||||
} else if (id === "audioInput") {
|
|
||||||
if (!AudioService.source || !AudioService.source.audio) return
|
|
||||||
let delta = wheelEvent.angleDelta.y
|
|
||||||
let currentVolume = AudioService.source.audio.volume * 100
|
|
||||||
let newVolume
|
|
||||||
if (delta > 0)
|
|
||||||
newVolume = Math.min(100, currentVolume + 5)
|
|
||||||
else
|
|
||||||
newVolume = Math.max(0, currentVolume - 5)
|
|
||||||
AudioService.source.audio.muted = false
|
|
||||||
AudioService.source.audio.volume = newVolume / 100
|
|
||||||
wheelEvent.accepted = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditModeOverlay {
|
|
||||||
anchors.fill: parent
|
|
||||||
editMode: root.editMode
|
|
||||||
widgetData: parent.widgetData
|
|
||||||
widgetIndex: parent.widgetIndex
|
|
||||||
showSizeControls: true
|
|
||||||
isSlider: false
|
|
||||||
onRemoveWidget: (index) => root.removeWidget(index)
|
|
||||||
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
|
|
||||||
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: audioSliderComponent
|
|
||||||
Item {
|
|
||||||
property var widgetData: parent.widgetData || {}
|
|
||||||
property int widgetIndex: parent.widgetIndex || 0
|
|
||||||
property var widgetDef: root.model?.getWidgetForId(widgetData.id || "")
|
|
||||||
width: parent.width
|
|
||||||
height: 16
|
|
||||||
|
|
||||||
AudioSliderRow {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: parent.width
|
|
||||||
height: 14
|
|
||||||
property color sliderTrackColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.60)
|
|
||||||
}
|
|
||||||
|
|
||||||
EditModeOverlay {
|
|
||||||
anchors.fill: parent
|
|
||||||
editMode: root.editMode
|
|
||||||
widgetData: parent.widgetData
|
|
||||||
widgetIndex: parent.widgetIndex
|
|
||||||
showSizeControls: true
|
|
||||||
isSlider: true
|
|
||||||
onRemoveWidget: (index) => root.removeWidget(index)
|
|
||||||
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
|
|
||||||
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: brightnessSliderComponent
|
|
||||||
Item {
|
|
||||||
property var widgetData: parent.widgetData || {}
|
|
||||||
property int widgetIndex: parent.widgetIndex || 0
|
|
||||||
width: parent.width
|
|
||||||
height: 16
|
|
||||||
|
|
||||||
BrightnessSliderRow {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: parent.width
|
|
||||||
height: 14
|
|
||||||
property color sliderTrackColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.60)
|
|
||||||
}
|
|
||||||
|
|
||||||
EditModeOverlay {
|
|
||||||
anchors.fill: parent
|
|
||||||
editMode: root.editMode
|
|
||||||
widgetData: parent.widgetData
|
|
||||||
widgetIndex: parent.widgetIndex
|
|
||||||
showSizeControls: true
|
|
||||||
isSlider: true
|
|
||||||
onRemoveWidget: (index) => root.removeWidget(index)
|
|
||||||
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
|
|
||||||
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: inputAudioSliderComponent
|
|
||||||
Item {
|
|
||||||
property var widgetData: parent.widgetData || {}
|
|
||||||
property int widgetIndex: parent.widgetIndex || 0
|
|
||||||
width: parent.width
|
|
||||||
height: 16
|
|
||||||
|
|
||||||
InputAudioSliderRow {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: parent.width
|
|
||||||
height: 14
|
|
||||||
property color sliderTrackColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.60)
|
|
||||||
}
|
|
||||||
|
|
||||||
EditModeOverlay {
|
|
||||||
anchors.fill: parent
|
|
||||||
editMode: root.editMode
|
|
||||||
widgetData: parent.widgetData
|
|
||||||
widgetIndex: parent.widgetIndex
|
|
||||||
showSizeControls: true
|
|
||||||
isSlider: true
|
|
||||||
onRemoveWidget: (index) => root.removeWidget(index)
|
|
||||||
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
|
|
||||||
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: batteryPillComponent
|
|
||||||
BatteryPill {
|
|
||||||
property var widgetData: parent.widgetData || {}
|
|
||||||
property int widgetIndex: parent.widgetIndex || 0
|
|
||||||
width: parent.width
|
|
||||||
height: 60
|
|
||||||
|
|
||||||
onExpandClicked: {
|
|
||||||
if (!root.editMode) {
|
|
||||||
root.expandClicked(widgetData, widgetIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditModeOverlay {
|
|
||||||
anchors.fill: parent
|
|
||||||
editMode: root.editMode
|
|
||||||
widgetData: parent.widgetData
|
|
||||||
widgetIndex: parent.widgetIndex
|
|
||||||
showSizeControls: true
|
|
||||||
isSlider: false
|
|
||||||
onRemoveWidget: (index) => root.removeWidget(index)
|
|
||||||
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
|
|
||||||
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: smallBatteryComponent
|
|
||||||
SmallBatteryButton {
|
|
||||||
property var widgetData: parent.widgetData || {}
|
|
||||||
property int widgetIndex: parent.widgetIndex || 0
|
|
||||||
width: parent.width
|
|
||||||
height: 48
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
if (!root.editMode) {
|
|
||||||
root.expandClicked(widgetData, widgetIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditModeOverlay {
|
|
||||||
anchors.fill: parent
|
|
||||||
editMode: root.editMode
|
|
||||||
widgetData: parent.widgetData
|
|
||||||
widgetIndex: parent.widgetIndex
|
|
||||||
showSizeControls: true
|
|
||||||
isSlider: false
|
|
||||||
onRemoveWidget: (index) => root.removeWidget(index)
|
|
||||||
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
|
|
||||||
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: toggleButtonComponent
|
|
||||||
ToggleButton {
|
|
||||||
property var widgetData: parent.widgetData || {}
|
|
||||||
property int widgetIndex: parent.widgetIndex || 0
|
|
||||||
property var widgetDef: root.model?.getWidgetForId(widgetData.id || "")
|
|
||||||
width: parent.width
|
|
||||||
height: 60
|
|
||||||
|
|
||||||
iconName: {
|
|
||||||
switch (widgetData.id || "") {
|
|
||||||
case "nightMode": return DisplayService.nightModeEnabled ? "nightlight" : "dark_mode"
|
|
||||||
case "darkMode": return "contrast"
|
|
||||||
case "doNotDisturb": return SessionData.doNotDisturb ? "do_not_disturb_on" : "do_not_disturb_off"
|
|
||||||
case "idleInhibitor": return SessionService.idleInhibited ? "motion_sensor_active" : "motion_sensor_idle"
|
|
||||||
default: return widgetDef?.icon || "help"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
text: {
|
|
||||||
switch (widgetData.id || "") {
|
|
||||||
case "nightMode": return "Night Mode"
|
|
||||||
case "darkMode": return SessionData.isLightMode ? "Light Mode" : "Dark Mode"
|
|
||||||
case "doNotDisturb": return "Do Not Disturb"
|
|
||||||
case "idleInhibitor": return SessionService.idleInhibited ? "Keeping Awake" : "Keep Awake"
|
|
||||||
default: return widgetDef?.text || "Unknown"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
secondaryText: ""
|
|
||||||
|
|
||||||
iconRotation: widgetData.id === "darkMode" && SessionData.isLightMode ? 180 : 0
|
|
||||||
|
|
||||||
isActive: {
|
|
||||||
switch (widgetData.id || "") {
|
|
||||||
case "nightMode": return DisplayService.nightModeEnabled || false
|
|
||||||
case "darkMode": return !SessionData.isLightMode
|
|
||||||
case "doNotDisturb": return SessionData.doNotDisturb || false
|
|
||||||
case "idleInhibitor": return SessionService.idleInhibited || false
|
|
||||||
default: return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enabled: (widgetDef?.enabled ?? true) && !root.editMode
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
switch (widgetData.id || "") {
|
|
||||||
case "nightMode": {
|
|
||||||
if (DisplayService.automationAvailable) {
|
|
||||||
DisplayService.toggleNightMode()
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case "darkMode": {
|
|
||||||
Theme.toggleLightMode()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case "doNotDisturb": {
|
|
||||||
SessionData.setDoNotDisturb(!SessionData.doNotDisturb)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case "idleInhibitor": {
|
|
||||||
SessionService.toggleIdleInhibit()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditModeOverlay {
|
|
||||||
anchors.fill: parent
|
|
||||||
editMode: root.editMode
|
|
||||||
widgetData: parent.widgetData
|
|
||||||
widgetIndex: parent.widgetIndex
|
|
||||||
showSizeControls: true
|
|
||||||
isSlider: false
|
|
||||||
onRemoveWidget: (index) => root.removeWidget(index)
|
|
||||||
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
|
|
||||||
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: smallToggleComponent
|
|
||||||
SmallToggleButton {
|
|
||||||
property var widgetData: parent.widgetData || {}
|
|
||||||
property int widgetIndex: parent.widgetIndex || 0
|
|
||||||
property var widgetDef: root.model?.getWidgetForId(widgetData.id || "")
|
|
||||||
width: parent.width
|
|
||||||
height: 48
|
|
||||||
|
|
||||||
iconName: {
|
|
||||||
switch (widgetData.id || "") {
|
|
||||||
case "nightMode": return DisplayService.nightModeEnabled ? "nightlight" : "dark_mode"
|
|
||||||
case "darkMode": return "contrast"
|
|
||||||
case "doNotDisturb": return SessionData.doNotDisturb ? "do_not_disturb_on" : "do_not_disturb_off"
|
|
||||||
case "idleInhibitor": return SessionService.idleInhibited ? "motion_sensor_active" : "motion_sensor_idle"
|
|
||||||
default: return widgetDef?.icon || "help"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
iconRotation: widgetData.id === "darkMode" && SessionData.isLightMode ? 180 : 0
|
|
||||||
|
|
||||||
isActive: {
|
|
||||||
switch (widgetData.id || "") {
|
|
||||||
case "nightMode": return DisplayService.nightModeEnabled || false
|
|
||||||
case "darkMode": return !SessionData.isLightMode
|
|
||||||
case "doNotDisturb": return SessionData.doNotDisturb || false
|
|
||||||
case "idleInhibitor": return SessionService.idleInhibited || false
|
|
||||||
default: return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enabled: (widgetDef?.enabled ?? true) && !root.editMode
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
switch (widgetData.id || "") {
|
|
||||||
case "nightMode": {
|
|
||||||
if (DisplayService.automationAvailable) {
|
|
||||||
DisplayService.toggleNightMode()
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case "darkMode": {
|
|
||||||
Theme.toggleLightMode()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case "doNotDisturb": {
|
|
||||||
SessionData.setDoNotDisturb(!SessionData.doNotDisturb)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case "idleInhibitor": {
|
|
||||||
SessionService.toggleIdleInhibit()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditModeOverlay {
|
|
||||||
anchors.fill: parent
|
|
||||||
editMode: root.editMode
|
|
||||||
widgetData: parent.widgetData
|
|
||||||
widgetIndex: parent.widgetIndex
|
|
||||||
showSizeControls: true
|
|
||||||
isSlider: false
|
|
||||||
onRemoveWidget: (index) => root.removeWidget(index)
|
|
||||||
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
|
|
||||||
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: diskUsagePillComponent
|
|
||||||
DiskUsagePill {
|
|
||||||
property var widgetData: parent.widgetData || {}
|
|
||||||
property int widgetIndex: parent.widgetIndex || 0
|
|
||||||
width: parent.width
|
|
||||||
height: 60
|
|
||||||
|
|
||||||
mountPath: widgetData.mountPath || "/"
|
|
||||||
instanceId: widgetData.instanceId || ""
|
|
||||||
|
|
||||||
onExpandClicked: {
|
|
||||||
if (!root.editMode) {
|
|
||||||
root.expandClicked(widgetData, widgetIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditModeOverlay {
|
|
||||||
anchors.fill: parent
|
|
||||||
editMode: root.editMode
|
|
||||||
widgetData: parent.widgetData
|
|
||||||
widgetIndex: parent.widgetIndex
|
|
||||||
showSizeControls: true
|
|
||||||
isSlider: false
|
|
||||||
onRemoveWidget: (index) => root.removeWidget(index)
|
|
||||||
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
|
|
||||||
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,7 +10,7 @@ import qs.Common
|
|||||||
import qs.Modules.ControlCenter
|
import qs.Modules.ControlCenter
|
||||||
import qs.Modules.ControlCenter.Widgets
|
import qs.Modules.ControlCenter.Widgets
|
||||||
import qs.Modules.ControlCenter.Details
|
import qs.Modules.ControlCenter.Details
|
||||||
import qs.Modules.TopBar
|
import qs.Modules.DankBar
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
import qs.Modules.ControlCenter.Components
|
import qs.Modules.ControlCenter.Components
|
||||||
@@ -21,14 +21,11 @@ DankPopout {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string expandedSection: ""
|
property string expandedSection: ""
|
||||||
property bool powerOptionsExpanded: false
|
|
||||||
property string triggerSection: "right"
|
|
||||||
property var triggerScreen: null
|
property var triggerScreen: null
|
||||||
property bool editMode: false
|
property bool editMode: false
|
||||||
property int expandedWidgetIndex: -1
|
property int expandedWidgetIndex: -1
|
||||||
property var expandedWidgetData: null
|
property var expandedWidgetData: null
|
||||||
|
|
||||||
signal powerActionRequested(string action, string title, string message)
|
|
||||||
signal lockRequested
|
signal lockRequested
|
||||||
|
|
||||||
function collapseAll() {
|
function collapseAll() {
|
||||||
@@ -49,7 +46,7 @@ DankPopout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property color _containerBg: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.60)
|
readonly property color _containerBg: Theme.surfaceContainerHigh
|
||||||
|
|
||||||
function setTriggerPosition(x, y, width, section, screen) {
|
function setTriggerPosition(x, y, width, section, screen) {
|
||||||
StateUtils.setTriggerPosition(root, x, y, width, section, screen)
|
StateUtils.setTriggerPosition(root, x, y, width, section, screen)
|
||||||
@@ -66,9 +63,9 @@ DankPopout {
|
|||||||
popupWidth: 550
|
popupWidth: 550
|
||||||
popupHeight: Math.min((triggerScreen?.height ?? 1080) - 100, contentLoader.item && contentLoader.item.implicitHeight > 0 ? contentLoader.item.implicitHeight + 20 : 400)
|
popupHeight: Math.min((triggerScreen?.height ?? 1080) - 100, contentLoader.item && contentLoader.item.implicitHeight > 0 ? contentLoader.item.implicitHeight + 20 : 400)
|
||||||
triggerX: (triggerScreen?.width ?? 1920) - 600 - Theme.spacingL
|
triggerX: (triggerScreen?.width ?? 1920) - 600 - Theme.spacingL
|
||||||
triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.spacingXS
|
triggerY: Theme.barHeight - 4 + SettingsData.dankBarSpacing
|
||||||
triggerWidth: 80
|
triggerWidth: 80
|
||||||
positioning: "center"
|
positioning: ""
|
||||||
screen: triggerScreen
|
screen: triggerScreen
|
||||||
shouldBeVisible: false
|
shouldBeVisible: false
|
||||||
visible: shouldBeVisible
|
visible: shouldBeVisible
|
||||||
@@ -76,17 +73,21 @@ DankPopout {
|
|||||||
onShouldBeVisibleChanged: {
|
onShouldBeVisibleChanged: {
|
||||||
if (shouldBeVisible) {
|
if (shouldBeVisible) {
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
NetworkService.autoRefreshEnabled = NetworkService.wifiEnabled
|
if (NetworkService.activeService) {
|
||||||
if (UserInfoService)
|
NetworkService.activeService.autoRefreshEnabled = NetworkService.wifiEnabled
|
||||||
UserInfoService.getUptime()
|
}
|
||||||
})
|
if (UserInfoService)
|
||||||
|
UserInfoService.getUptime()
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
NetworkService.autoRefreshEnabled = false
|
if (NetworkService.activeService) {
|
||||||
if (BluetoothService.adapter && BluetoothService.adapter.discovering)
|
NetworkService.activeService.autoRefreshEnabled = false
|
||||||
BluetoothService.adapter.discovering = false
|
}
|
||||||
editMode = false
|
if (BluetoothService.adapter && BluetoothService.adapter.discovering)
|
||||||
})
|
BluetoothService.adapter.discovering = false
|
||||||
|
editMode = false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,14 +103,13 @@ DankPopout {
|
|||||||
property alias bluetoothCodecSelector: bluetoothCodecSelector
|
property alias bluetoothCodecSelector: bluetoothCodecSelector
|
||||||
|
|
||||||
color: {
|
color: {
|
||||||
const transparency = Theme.popupTransparency || 0.92
|
const transparency = Theme.popupTransparency
|
||||||
const surface = Theme.surfaceContainer || Qt.rgba(0.1, 0.1, 0.1, 1)
|
const surface = Theme.surfaceContainer || Qt.rgba(0.1, 0.1, 0.1, 1)
|
||||||
return Qt.rgba(surface.r, surface.g, surface.b, transparency)
|
return Qt.rgba(surface.r, surface.g, surface.b, transparency)
|
||||||
}
|
}
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
Theme.outline.b, 0.08)
|
border.width: 0
|
||||||
border.width: 1
|
|
||||||
antialiasing: true
|
antialiasing: true
|
||||||
smooth: true
|
smooth: true
|
||||||
|
|
||||||
@@ -123,29 +123,28 @@ DankPopout {
|
|||||||
HeaderPane {
|
HeaderPane {
|
||||||
id: headerPane
|
id: headerPane
|
||||||
width: parent.width
|
width: parent.width
|
||||||
powerOptionsExpanded: root.powerOptionsExpanded
|
|
||||||
editMode: root.editMode
|
editMode: root.editMode
|
||||||
onPowerOptionsExpandedChanged: root.powerOptionsExpanded = powerOptionsExpanded
|
|
||||||
onEditModeToggled: root.editMode = !root.editMode
|
onEditModeToggled: root.editMode = !root.editMode
|
||||||
onPowerActionRequested: (action, title, message) => root.powerActionRequested(action, title, message)
|
onPowerButtonClicked: {
|
||||||
|
if (powerMenuModalLoader) {
|
||||||
|
powerMenuModalLoader.active = true
|
||||||
|
if (powerMenuModalLoader.item) {
|
||||||
|
const popoutPos = controlContent.mapToItem(null, 0, 0)
|
||||||
|
const bounds = Qt.rect(popoutPos.x, popoutPos.y, controlContent.width, controlContent.height)
|
||||||
|
powerMenuModalLoader.item.openFromControlCenter(bounds, root.triggerScreen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
onLockRequested: {
|
onLockRequested: {
|
||||||
root.close()
|
root.close()
|
||||||
root.lockRequested()
|
root.lockRequested()
|
||||||
}
|
}
|
||||||
}
|
onSettingsButtonClicked: {
|
||||||
|
|
||||||
PowerOptionsPane {
|
|
||||||
id: powerOptionsPane
|
|
||||||
width: parent.width
|
|
||||||
expanded: root.powerOptionsExpanded
|
|
||||||
onPowerActionRequested: (action, title, message) => {
|
|
||||||
root.powerOptionsExpanded = false
|
|
||||||
root.close()
|
root.close()
|
||||||
root.powerActionRequested(action, title, message)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WidgetGrid {
|
DragDropGrid {
|
||||||
id: widgetGrid
|
id: widgetGrid
|
||||||
width: parent.width
|
width: parent.width
|
||||||
editMode: root.editMode
|
editMode: root.editMode
|
||||||
@@ -153,28 +152,37 @@ DankPopout {
|
|||||||
expandedWidgetIndex: root.expandedWidgetIndex
|
expandedWidgetIndex: root.expandedWidgetIndex
|
||||||
expandedWidgetData: root.expandedWidgetData
|
expandedWidgetData: root.expandedWidgetData
|
||||||
model: widgetModel
|
model: widgetModel
|
||||||
|
bluetoothCodecSelector: bluetoothCodecSelector
|
||||||
|
colorPickerModal: root.colorPickerModal
|
||||||
onExpandClicked: (widgetData, globalIndex) => {
|
onExpandClicked: (widgetData, globalIndex) => {
|
||||||
root.expandedWidgetIndex = globalIndex
|
root.expandedWidgetIndex = globalIndex
|
||||||
root.expandedWidgetData = widgetData
|
root.expandedWidgetData = widgetData
|
||||||
if (widgetData.id === "diskUsage") {
|
if (widgetData.id === "diskUsage") {
|
||||||
root.toggleSection("diskUsage_" + (widgetData.instanceId || "default"))
|
root.toggleSection("diskUsage_" + (widgetData.instanceId || "default"))
|
||||||
} else {
|
} else if (widgetData.id === "brightnessSlider") {
|
||||||
root.toggleSection(widgetData.id)
|
root.toggleSection("brightnessSlider_" + (widgetData.instanceId || "default"))
|
||||||
}
|
} else {
|
||||||
}
|
root.toggleSection(widgetData.id)
|
||||||
onRemoveWidget: (index) => widgetModel.removeWidget(index)
|
}
|
||||||
|
}
|
||||||
|
onRemoveWidget: index => widgetModel.removeWidget(index)
|
||||||
onMoveWidget: (fromIndex, toIndex) => widgetModel.moveWidget(fromIndex, toIndex)
|
onMoveWidget: (fromIndex, toIndex) => widgetModel.moveWidget(fromIndex, toIndex)
|
||||||
onToggleWidgetSize: (index) => widgetModel.toggleWidgetSize(index)
|
onToggleWidgetSize: index => widgetModel.toggleWidgetSize(index)
|
||||||
|
onCollapseRequested: root.collapseAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
EditControls {
|
EditControls {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
visible: editMode
|
visible: editMode
|
||||||
|
popoutContent: controlContent
|
||||||
availableWidgets: {
|
availableWidgets: {
|
||||||
|
if (!editMode)
|
||||||
|
return []
|
||||||
const existingIds = (SettingsData.controlCenterWidgets || []).map(w => w.id)
|
const existingIds = (SettingsData.controlCenterWidgets || []).map(w => w.id)
|
||||||
return widgetModel.baseWidgetDefinitions.filter(w => w.allowMultiple || !existingIds.includes(w.id))
|
const allWidgets = widgetModel.baseWidgetDefinitions.concat(widgetModel.getPluginWidgets())
|
||||||
|
return allWidgets.filter(w => w.allowMultiple || !existingIds.includes(w.id))
|
||||||
}
|
}
|
||||||
onAddWidget: (widgetId) => widgetModel.addWidget(widgetId)
|
onAddWidget: widgetId => widgetModel.addWidget(widgetId)
|
||||||
onResetToDefault: () => widgetModel.resetToDefault()
|
onResetToDefault: () => widgetModel.resetToDefault()
|
||||||
onClearAll: () => widgetModel.clearAll()
|
onClearAll: () => widgetModel.clearAll()
|
||||||
}
|
}
|
||||||
@@ -197,10 +205,10 @@ DankPopout {
|
|||||||
id: bluetoothDetailComponent
|
id: bluetoothDetailComponent
|
||||||
BluetoothDetail {
|
BluetoothDetail {
|
||||||
id: bluetoothDetail
|
id: bluetoothDetail
|
||||||
onShowCodecSelector: function(device) {
|
onShowCodecSelector: function (device) {
|
||||||
if (contentLoader.item && contentLoader.item.bluetoothCodecSelector) {
|
if (contentLoader.item && contentLoader.item.bluetoothCodecSelector) {
|
||||||
contentLoader.item.bluetoothCodecSelector.show(device)
|
contentLoader.item.bluetoothCodecSelector.show(device)
|
||||||
contentLoader.item.bluetoothCodecSelector.codecSelected.connect(function(deviceAddress, codecName) {
|
contentLoader.item.bluetoothCodecSelector.codecSelected.connect(function (deviceAddress, codecName) {
|
||||||
bluetoothDetail.updateDeviceCodecDisplay(deviceAddress, codecName)
|
bluetoothDetail.updateDeviceCodecDisplay(deviceAddress, codecName)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -222,4 +230,7 @@ DankPopout {
|
|||||||
id: batteryDetailComponent
|
id: batteryDetailComponent
|
||||||
BatteryDetail {}
|
BatteryDetail {}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
property var colorPickerModal: null
|
||||||
|
property var powerMenuModalLoader: null
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ Rectangle {
|
|||||||
|
|
||||||
implicitHeight: headerRow.height + (hasInputVolumeSliderInCC ? 0 : volumeSlider.height) + audioContent.height + Theme.spacingM
|
implicitHeight: headerRow.height + (hasInputVolumeSliderInCC ? 0 : volumeSlider.height) + audioContent.height + Theme.spacingM
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.6)
|
color: Theme.surfaceContainerHigh
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: headerRow
|
id: headerRow
|
||||||
@@ -30,7 +30,7 @@ Rectangle {
|
|||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
id: headerText
|
id: headerText
|
||||||
text: "Input Devices"
|
text: I18n.tr("Input Devices")
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -57,10 +57,6 @@ Rectangle {
|
|||||||
radius: (Theme.iconSize + Theme.spacingS * 2) / 2
|
radius: (Theme.iconSize + Theme.spacingS * 2) / 2
|
||||||
color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation { duration: Theme.shortDuration }
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: iconArea
|
id: iconArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -138,9 +134,9 @@ Rectangle {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: 50
|
height: 50
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: deviceMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, index % 2 === 0 ? 0.3 : 0.2)
|
color: deviceMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Theme.surfaceContainerHighest
|
||||||
border.color: modelData === AudioService.source ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
border.color: modelData === AudioService.source ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
border.width: modelData === AudioService.source ? 2 : 1
|
border.width: 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -198,14 +194,6 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation { duration: Theme.shortDuration }
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on border.color {
|
|
||||||
ColorAnimation { duration: Theme.shortDuration }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ Rectangle {
|
|||||||
|
|
||||||
implicitHeight: headerRow.height + (!hasVolumeSliderInCC ? volumeSlider.height : 0) + audioContent.height + Theme.spacingM
|
implicitHeight: headerRow.height + (!hasVolumeSliderInCC ? volumeSlider.height : 0) + audioContent.height + Theme.spacingM
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.6)
|
color: Theme.surfaceContainerHigh
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: headerRow
|
id: headerRow
|
||||||
@@ -30,7 +30,7 @@ Rectangle {
|
|||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
id: headerText
|
id: headerText
|
||||||
text: "Audio Devices"
|
text: I18n.tr("Audio Devices")
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -57,10 +57,6 @@ Rectangle {
|
|||||||
radius: (Theme.iconSize + Theme.spacingS * 2) / 2
|
radius: (Theme.iconSize + Theme.spacingS * 2) / 2
|
||||||
color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation { duration: Theme.shortDuration }
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: iconArea
|
id: iconArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -143,9 +139,9 @@ Rectangle {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: 50
|
height: 50
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: deviceMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, index % 2 === 0 ? 0.3 : 0.2)
|
color: deviceMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Theme.surfaceContainerHighest
|
||||||
border.color: modelData === AudioService.sink ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
border.color: modelData === AudioService.sink ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
border.width: modelData === AudioService.sink ? 2 : 1
|
border.width: 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -205,14 +201,6 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation { duration: Theme.shortDuration }
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on border.color {
|
|
||||||
ColorAnimation { duration: Theme.shortDuration }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import qs.Widgets
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
implicitHeight: contentColumn.implicitHeight + Theme.spacingL * 2
|
implicitHeight: contentColumn.implicitHeight + Theme.spacingL * 2
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.6)
|
color: Theme.surfaceContainerHigh
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: 0
|
||||||
|
|
||||||
function isActiveProfile(profile) {
|
function isActiveProfile(profile) {
|
||||||
if (typeof PowerProfiles === "undefined") {
|
if (typeof PowerProfiles === "undefined") {
|
||||||
@@ -125,16 +125,15 @@ Rectangle {
|
|||||||
width: (parent.width - Theme.spacingM) / 2
|
width: (parent.width - Theme.spacingM) / 2
|
||||||
height: 64
|
height: 64
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
|
color: Theme.surfaceContainerHighest
|
||||||
border.color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2)
|
border.width: 0
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Health"
|
text: I18n.tr("Health")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -161,16 +160,15 @@ Rectangle {
|
|||||||
width: (parent.width - Theme.spacingM) / 2
|
width: (parent.width - Theme.spacingM) / 2
|
||||||
height: 64
|
height: 64
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
|
color: Theme.surfaceContainerHighest
|
||||||
border.color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2)
|
border.width: 0
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Capacity"
|
text: I18n.tr("Capacity")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -211,7 +209,7 @@ Rectangle {
|
|||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
|
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
|
||||||
border.color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.3)
|
border.color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.3)
|
||||||
border.width: 1
|
border.width: 0
|
||||||
visible: (typeof PowerProfiles !== "undefined") && PowerProfiles.degradationReason !== PerformanceDegradationReason.None
|
visible: (typeof PowerProfiles !== "undefined") && PowerProfiles.degradationReason !== PerformanceDegradationReason.None
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -239,7 +237,7 @@ Rectangle {
|
|||||||
width: parent.width - Theme.iconSize - Theme.spacingM
|
width: parent.width - Theme.iconSize - Theme.spacingM
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Power Profile Degradation"
|
text: I18n.tr("Power Profile Degradation")
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
color: Theme.error
|
color: Theme.error
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ Item {
|
|||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Theme.surfaceContainer
|
color: Theme.surfaceContainer
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: 0
|
||||||
opacity: modalVisible ? 1 : 0
|
opacity: modalVisible ? 1 : 0
|
||||||
scale: modalVisible ? 1 : 0.9
|
scale: modalVisible ? 1 : 0.9
|
||||||
|
|
||||||
@@ -170,7 +170,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Audio Codec Selection"
|
text: I18n.tr("Audio Codec Selection")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceTextMedium
|
color: Theme.surfaceTextMedium
|
||||||
}
|
}
|
||||||
@@ -206,14 +206,14 @@ Item {
|
|||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: {
|
color: {
|
||||||
if (modelData.name === currentCodec)
|
if (modelData.name === currentCodec)
|
||||||
return Theme.surfaceContainerHigh;
|
return Theme.surfaceContainerHighest;
|
||||||
else if (codecMouseArea.containsMouse)
|
else if (codecMouseArea.containsMouse)
|
||||||
return Theme.surfaceHover;
|
return Theme.surfaceHover;
|
||||||
else
|
else
|
||||||
return "transparent";
|
return "transparent";
|
||||||
}
|
}
|
||||||
border.color: "transparent"
|
border.color: "transparent"
|
||||||
border.width: 1
|
border.width: 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -272,12 +272,6 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import qs.Widgets
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
implicitHeight: BluetoothService.adapter && BluetoothService.adapter.enabled ? headerRow.height + bluetoothContent.height + Theme.spacingM : headerRow.height
|
implicitHeight: BluetoothService.adapter && BluetoothService.adapter.enabled ? headerRow.height + bluetoothContent.height + Theme.spacingM : headerRow.height
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.6)
|
color: Theme.surfaceContainerHigh
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: 0
|
||||||
|
|
||||||
property var bluetoothCodecModalRef: null
|
property var bluetoothCodecModalRef: null
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ Rectangle {
|
|||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
id: headerText
|
id: headerText
|
||||||
text: "Bluetooth Settings"
|
text: I18n.tr("Bluetooth Settings")
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -58,11 +58,11 @@ Rectangle {
|
|||||||
radius: 18
|
radius: 18
|
||||||
color: {
|
color: {
|
||||||
if (!BluetoothService.adapter || !BluetoothService.adapter.enabled)
|
if (!BluetoothService.adapter || !BluetoothService.adapter.enabled)
|
||||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
return Theme.surfaceContainerHigh
|
||||||
return scanMouseArea.containsMouse ? Theme.surfaceContainerHigh : "transparent"
|
return scanMouseArea.containsMouse ? Theme.surfaceContainerHigh : "transparent"
|
||||||
}
|
}
|
||||||
border.color: BluetoothService.adapter && BluetoothService.adapter.enabled ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
border.color: BluetoothService.adapter && BluetoothService.adapter.enabled ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
border.width: 1
|
border.width: 0
|
||||||
visible: BluetoothService.adapter && BluetoothService.adapter.enabled
|
visible: BluetoothService.adapter && BluetoothService.adapter.enabled
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
@@ -96,13 +96,6 @@ Rectangle {
|
|||||||
BluetoothService.adapter.discovering = !BluetoothService.adapter.discovering
|
BluetoothService.adapter.discovering = !BluetoothService.adapter.discovering
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +152,7 @@ Rectangle {
|
|||||||
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12)
|
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12)
|
||||||
if (deviceMouseArea.containsMouse)
|
if (deviceMouseArea.containsMouse)
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
|
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
|
||||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, index % 2 === 0 ? 0.3 : 0.2)
|
return Theme.surfaceContainerHighest
|
||||||
}
|
}
|
||||||
border.color: {
|
border.color: {
|
||||||
if (modelData.state === BluetoothDeviceState.Connecting)
|
if (modelData.state === BluetoothDeviceState.Connecting)
|
||||||
@@ -168,7 +161,7 @@ Rectangle {
|
|||||||
return Theme.primary
|
return Theme.primary
|
||||||
return Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
return Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
}
|
}
|
||||||
border.width: (modelData.connected || modelData.state === BluetoothDeviceState.Connecting) ? 2 : 1
|
border.width: 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -284,14 +277,6 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation { duration: Theme.shortDuration }
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on border.color {
|
|
||||||
ColorAnimation { duration: Theme.shortDuration }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -347,9 +332,9 @@ Rectangle {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: 50
|
height: 50
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: availableMouseArea.containsMouse && !isBusy ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.15)
|
color: availableMouseArea.containsMouse && !isBusy ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Theme.surfaceContainerHighest
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
border.width: 1
|
border.width: 0
|
||||||
opacity: canConnect ? 1 : 0.6
|
opacity: canConnect ? 1 : 0.6
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
@@ -427,9 +412,6 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation { duration: Theme.shortDuration }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -440,7 +422,7 @@ Rectangle {
|
|||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: "No Bluetooth adapter found"
|
text: I18n.tr("No Bluetooth adapter found")
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
}
|
}
|
||||||
@@ -458,7 +440,7 @@ Rectangle {
|
|||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: Theme.popupBackground()
|
color: Theme.popupBackground()
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.width: 1
|
border.width: 0
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,7 +473,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: "Audio Codec"
|
text: I18n.tr("Audio Codec")
|
||||||
height: bluetoothContextMenu.currentDevice && BluetoothService.isAudioDevice(bluetoothContextMenu.currentDevice) && bluetoothContextMenu.currentDevice.connected ? 32 : 0
|
height: bluetoothContextMenu.currentDevice && BluetoothService.isAudioDevice(bluetoothContextMenu.currentDevice) && bluetoothContextMenu.currentDevice.connected ? 32 : 0
|
||||||
visible: bluetoothContextMenu.currentDevice && BluetoothService.isAudioDevice(bluetoothContextMenu.currentDevice) && bluetoothContextMenu.currentDevice.connected
|
visible: bluetoothContextMenu.currentDevice && BluetoothService.isAudioDevice(bluetoothContextMenu.currentDevice) && bluetoothContextMenu.currentDevice.connected
|
||||||
|
|
||||||
@@ -516,7 +498,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: "Forget Device"
|
text: I18n.tr("Forget Device")
|
||||||
height: 32
|
height: 32
|
||||||
|
|
||||||
contentItem: StyledText {
|
contentItem: StyledText {
|
||||||
|
|||||||
174
Modules/ControlCenter/Details/BrightnessDetail.qml
Normal file
174
Modules/ControlCenter/Details/BrightnessDetail.qml
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Quickshell
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string currentDeviceName: ""
|
||||||
|
property string instanceId: ""
|
||||||
|
|
||||||
|
signal deviceNameChanged(string newDeviceName)
|
||||||
|
|
||||||
|
implicitHeight: brightnessContent.height + Theme.spacingM
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.surfaceContainerHigh
|
||||||
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
|
border.width: 0
|
||||||
|
|
||||||
|
DankFlickable {
|
||||||
|
id: brightnessContent
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
anchors.topMargin: Theme.spacingM
|
||||||
|
contentHeight: brightnessColumn.height
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: brightnessColumn
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: 100
|
||||||
|
visible: !DisplayService.brightnessAvailable || !DisplayService.devices || DisplayService.devices.length === 0
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
name: DisplayService.brightnessAvailable ? "brightness_6" : "error"
|
||||||
|
size: 32
|
||||||
|
color: DisplayService.brightnessAvailable ? Theme.primary : Theme.error
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
text: DisplayService.brightnessAvailable ? "No brightness devices available" : "Brightness control not available"
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: DisplayService.devices || []
|
||||||
|
delegate: Rectangle {
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: 80
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.surfaceContainerHighest
|
||||||
|
border.color: modelData.name === currentDeviceName ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
|
border.width: modelData.name === currentDeviceName ? 2 : 0
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.leftMargin: Theme.spacingM
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: {
|
||||||
|
const deviceClass = modelData.class || ""
|
||||||
|
const deviceName = modelData.name || ""
|
||||||
|
|
||||||
|
if (deviceClass === "backlight" || deviceClass === "ddc") {
|
||||||
|
const brightness = modelData.percentage || 50
|
||||||
|
if (brightness <= 33) return "brightness_low"
|
||||||
|
if (brightness <= 66) return "brightness_medium"
|
||||||
|
return "brightness_high"
|
||||||
|
} else if (deviceName.includes("kbd")) {
|
||||||
|
return "keyboard"
|
||||||
|
} else {
|
||||||
|
return "lightbulb"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
size: Theme.iconSize
|
||||||
|
color: modelData.name === currentDeviceName ? Theme.primary : Theme.surfaceText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: (modelData.percentage || 50) + "%"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: parent.parent.width - parent.parent.anchors.leftMargin - parent.spacing - 50 - Theme.spacingM
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
const name = modelData.name || ""
|
||||||
|
const deviceClass = modelData.class || ""
|
||||||
|
if (deviceClass === "backlight") {
|
||||||
|
return name.replace("_", " ").replace(/\b\w/g, c => c.toUpperCase())
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: modelData.name === currentDeviceName ? Font.Medium : Font.Normal
|
||||||
|
elide: Text.ElideRight
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.name
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
const deviceClass = modelData.class || ""
|
||||||
|
if (deviceClass === "backlight") return "Backlight device"
|
||||||
|
if (deviceClass === "ddc") return "DDC/CI monitor"
|
||||||
|
if (deviceClass === "leds") return "LED device"
|
||||||
|
return deviceClass
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
currentDeviceName = modelData.name
|
||||||
|
deviceNameChanged(modelData.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,8 @@ import qs.Services
|
|||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
property string currentMountPath: "/"
|
property string currentMountPath: "/"
|
||||||
property string instanceId: ""
|
property string instanceId: ""
|
||||||
|
|
||||||
@@ -13,9 +15,9 @@ Rectangle {
|
|||||||
|
|
||||||
implicitHeight: diskContent.height + Theme.spacingM
|
implicitHeight: diskContent.height + Theme.spacingM
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.6)
|
color: Theme.surfaceContainerHigh
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: 0
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
DgopService.addRef(["diskmounts"])
|
DgopService.addRef(["diskmounts"])
|
||||||
@@ -76,9 +78,9 @@ Rectangle {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: 80
|
height: 80
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, index % 2 === 0 ? 0.3 : 0.2)
|
color: Theme.surfaceContainerHighest
|
||||||
border.color: modelData.mount === currentMountPath ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
border.color: modelData.mount === currentMountPath ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
border.width: modelData.mount === currentMountPath ? 2 : 1
|
border.width: modelData.mount === currentMountPath ? 2 : 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -152,16 +154,11 @@ Rectangle {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (modelData.mount !== currentMountPath) {
|
currentMountPath = modelData.mount
|
||||||
currentMountPath = modelData.mount
|
mountPathChanged(modelData.mount)
|
||||||
mountPathChanged(modelData.mount)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on border.color {
|
|
||||||
ColorAnimation { duration: Theme.shortDuration }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,30 +17,38 @@ Rectangle {
|
|||||||
return headerRow.height + wifiOffContent.height + Theme.spacingM
|
return headerRow.height + wifiOffContent.height + Theme.spacingM
|
||||||
}
|
}
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.6)
|
color: Theme.surfaceContainerHigh
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: 0
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
NetworkService.addRef()
|
NetworkService.addRef()
|
||||||
if (NetworkService.wifiEnabled) {
|
|
||||||
NetworkService.scanWifi()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onDestruction: {
|
Component.onDestruction: {
|
||||||
NetworkService.removeRef()
|
NetworkService.removeRef()
|
||||||
}
|
}
|
||||||
|
|
||||||
property var wifiPasswordModalRef: {
|
property int currentPreferenceIndex: {
|
||||||
wifiPasswordModalLoader.active = true
|
if (DMSService.apiVersion < 5) {
|
||||||
return wifiPasswordModalLoader.item
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
const pref = NetworkService.userPreference
|
||||||
|
const status = NetworkService.networkStatus
|
||||||
|
let index = 1
|
||||||
|
|
||||||
|
if (pref === "ethernet") {
|
||||||
|
index = 0
|
||||||
|
} else if (pref === "wifi") {
|
||||||
|
index = 1
|
||||||
|
} else {
|
||||||
|
index = status === "ethernet" ? 0 : 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return index
|
||||||
}
|
}
|
||||||
property var networkInfoModalRef: {
|
|
||||||
networkInfoModalLoader.active = true
|
|
||||||
return networkInfoModalLoader.item
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: headerRow
|
id: headerRow
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -50,33 +58,32 @@ Rectangle {
|
|||||||
anchors.rightMargin: Theme.spacingM
|
anchors.rightMargin: Theme.spacingM
|
||||||
anchors.topMargin: Theme.spacingS
|
anchors.topMargin: Theme.spacingS
|
||||||
height: 40
|
height: 40
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
id: headerText
|
id: headerText
|
||||||
text: "Network Settings"
|
text: I18n.tr("Network Settings")
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
width: Math.max(0, parent.width - headerText.implicitWidth - preferenceControls.width - Theme.spacingM)
|
width: Math.max(0, parent.width - headerText.implicitWidth - preferenceControls.width - Theme.spacingM)
|
||||||
height: parent.height
|
height: parent.height
|
||||||
}
|
}
|
||||||
|
|
||||||
DankButtonGroup {
|
DankButtonGroup {
|
||||||
id: preferenceControls
|
id: preferenceControls
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
visible: NetworkService.ethernetConnected && NetworkService.wifiConnected
|
visible: DMSService.apiVersion >= 5
|
||||||
|
|
||||||
property int currentPreferenceIndex: NetworkService.userPreference === "ethernet" ? 0 : 1
|
|
||||||
|
|
||||||
model: ["Ethernet", "WiFi"]
|
model: ["Ethernet", "WiFi"]
|
||||||
currentIndex: currentPreferenceIndex
|
currentIndex: currentPreferenceIndex
|
||||||
selectionMode: "single"
|
selectionMode: "single"
|
||||||
onSelectionChanged: (index, selected) => {
|
onSelectionChanged: (index, selected) => {
|
||||||
if (!selected) return
|
if (!selected) return
|
||||||
|
console.log("NetworkDetail: Setting preference to", index === 0 ? "ethernet" : "wifi")
|
||||||
NetworkService.setNetworkPreference(index === 0 ? "ethernet" : "wifi")
|
NetworkService.setNetworkPreference(index === 0 ? "ethernet" : "wifi")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,19 +96,19 @@ Rectangle {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.margins: Theme.spacingM
|
anchors.margins: Theme.spacingM
|
||||||
anchors.topMargin: Theme.spacingM
|
anchors.topMargin: Theme.spacingM
|
||||||
visible: NetworkService.wifiToggling
|
visible: currentPreferenceIndex === 1 && NetworkService.wifiToggling
|
||||||
height: visible ? 80 : 0
|
height: visible ? 80 : 0
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
name: "sync"
|
name: "sync"
|
||||||
size: 32
|
size: 32
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
|
|
||||||
RotationAnimation on rotation {
|
RotationAnimation on rotation {
|
||||||
running: NetworkService.wifiToggling
|
running: NetworkService.wifiToggling
|
||||||
loops: Animation.Infinite
|
loops: Animation.Infinite
|
||||||
@@ -110,7 +117,7 @@ Rectangle {
|
|||||||
duration: 1000
|
duration: 1000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
text: NetworkService.wifiEnabled ? "Disabling WiFi..." : "Enabling WiFi..."
|
text: NetworkService.wifiEnabled ? "Disabling WiFi..." : "Enabling WiFi..."
|
||||||
@@ -128,7 +135,7 @@ Rectangle {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.margins: Theme.spacingM
|
anchors.margins: Theme.spacingM
|
||||||
anchors.topMargin: Theme.spacingM
|
anchors.topMargin: Theme.spacingM
|
||||||
visible: !NetworkService.wifiEnabled && !NetworkService.wifiToggling
|
visible: currentPreferenceIndex === 1 && !NetworkService.wifiEnabled && !NetworkService.wifiToggling
|
||||||
height: visible ? 120 : 0
|
height: visible ? 120 : 0
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -145,7 +152,7 @@ Rectangle {
|
|||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
text: "WiFi is off"
|
text: I18n.tr("WiFi is off")
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -158,12 +165,12 @@ Rectangle {
|
|||||||
height: 36
|
height: 36
|
||||||
radius: 18
|
radius: 18
|
||||||
color: enableWifiButton.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
|
color: enableWifiButton.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
|
||||||
border.width: 1
|
border.width: 0
|
||||||
border.color: Theme.primary
|
border.color: Theme.primary
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: "Enable WiFi"
|
text: I18n.tr("Enable WiFi")
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -176,13 +183,179 @@ Rectangle {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: NetworkService.toggleWifiRadio()
|
onClicked: NetworkService.toggleWifiRadio()
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
}
|
||||||
ColorAnimation {
|
}
|
||||||
duration: Theme.shortDuration
|
}
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
DankFlickable {
|
||||||
|
id: wiredContent
|
||||||
|
anchors.top: headerRow.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
anchors.topMargin: Theme.spacingM
|
||||||
|
visible: currentPreferenceIndex === 0 && DMSService.apiVersion >= 5
|
||||||
|
contentHeight: wiredColumn.height
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: wiredColumn
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: sortedNetworks
|
||||||
|
|
||||||
|
property var sortedNetworks: {
|
||||||
|
const currentUuid = NetworkService.ethernetConnectionUuid
|
||||||
|
const networks = NetworkService.wiredConnections
|
||||||
|
let sorted = [...networks]
|
||||||
|
sorted.sort((a, b) => {
|
||||||
|
if (a.isActive && !b.isActive) return -1
|
||||||
|
if (!a.isActive && b.isActive) return 1
|
||||||
|
return a.id.localeCompare(b.id)
|
||||||
|
})
|
||||||
|
return sorted
|
||||||
}
|
}
|
||||||
|
delegate: Rectangle {
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: 50
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: wiredNetworkMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Theme.surfaceContainerHighest
|
||||||
|
border.color: Theme.primary
|
||||||
|
border.width: 0
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.leftMargin: Theme.spacingM
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "lan"
|
||||||
|
size: Theme.iconSize - 4
|
||||||
|
color: modelData.isActive ? Theme.primary : Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: 200
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.id || "Unknown Config"
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: modelData.isActive ? Theme.primary : Theme.surfaceText
|
||||||
|
font.weight: modelData.isActive ? Font.Medium : Font.Normal
|
||||||
|
elide: Text.ElideRight
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
id: wiredOptionsButton
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
iconName: "more_horiz"
|
||||||
|
buttonSize: 28
|
||||||
|
onClicked: {
|
||||||
|
if (wiredNetworkContextMenu.visible) {
|
||||||
|
wiredNetworkContextMenu.close()
|
||||||
|
} else {
|
||||||
|
wiredNetworkContextMenu.currentID = modelData.id
|
||||||
|
wiredNetworkContextMenu.currentUUID = modelData.uuid
|
||||||
|
wiredNetworkContextMenu.currentConnected = modelData.isActive
|
||||||
|
wiredNetworkContextMenu.popup(wiredOptionsButton, -wiredNetworkContextMenu.width + wiredOptionsButton.width, wiredOptionsButton.height + Theme.spacingXS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: wiredNetworkMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.rightMargin: wiredOptionsButton.width + Theme.spacingS
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: function(event) {
|
||||||
|
if (modelData.uuid !== NetworkService.ethernetConnectionUuid) {
|
||||||
|
NetworkService.connectToSpecificWiredConfig(modelData.uuid)
|
||||||
|
}
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Menu {
|
||||||
|
id: wiredNetworkContextMenu
|
||||||
|
width: 150
|
||||||
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
|
||||||
|
|
||||||
|
property string currentID: ""
|
||||||
|
property string currentUUID: ""
|
||||||
|
property bool currentConnected: false
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: Theme.popupBackground()
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
border.width: 0
|
||||||
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuItem {
|
||||||
|
text: "Activate"
|
||||||
|
height: !wiredNetworkContextMenu.currentConnected ? 32 : 0
|
||||||
|
|
||||||
|
contentItem: StyledText {
|
||||||
|
text: parent.text
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
leftPadding: Theme.spacingS
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: parent.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
||||||
|
radius: Theme.cornerRadius / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
onTriggered: {
|
||||||
|
if (!networkContextMenu.currentConnected) {
|
||||||
|
NetworkService.connectToSpecificWiredConfig(wiredNetworkContextMenu.currentUUID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuItem {
|
||||||
|
text: I18n.tr("Network Info")
|
||||||
|
height: wiredNetworkContextMenu.currentConnected ? 32 : 0
|
||||||
|
|
||||||
|
contentItem: StyledText {
|
||||||
|
text: parent.text
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
leftPadding: Theme.spacingS
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: parent.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
||||||
|
radius: Theme.cornerRadius / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
onTriggered: {
|
||||||
|
let networkData = NetworkService.getWiredNetworkInfo(wiredNetworkContextMenu.currentUUID)
|
||||||
|
networkWiredInfoModal.showNetworkInfo(wiredNetworkContextMenu.currentID, networkData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -195,10 +368,10 @@ Rectangle {
|
|||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.margins: Theme.spacingM
|
anchors.margins: Theme.spacingM
|
||||||
anchors.topMargin: Theme.spacingM
|
anchors.topMargin: Theme.spacingM
|
||||||
visible: NetworkService.wifiInterface && NetworkService.wifiEnabled && !NetworkService.wifiToggling
|
visible: currentPreferenceIndex === 1 && NetworkService.wifiEnabled && !NetworkService.wifiToggling
|
||||||
contentHeight: wifiColumn.height
|
contentHeight: wifiColumn.height
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: wifiColumn
|
id: wifiColumn
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -207,16 +380,16 @@ Rectangle {
|
|||||||
Item {
|
Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 200
|
height: 200
|
||||||
visible: NetworkService.wifiInterface && NetworkService.wifiNetworks?.length < 1 && !NetworkService.wifiToggling
|
visible: NetworkService.wifiInterface && NetworkService.wifiNetworks?.length < 1 && !NetworkService.wifiToggling && NetworkService.isScanning
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
name: "refresh"
|
name: "refresh"
|
||||||
size: 48
|
size: 48
|
||||||
color: Qt.rgba(Theme.surfaceText.r || 0.8, Theme.surfaceText.g || 0.8, Theme.surfaceText.b || 0.8, 0.3)
|
color: Qt.rgba(Theme.surfaceText.r || 0.8, Theme.surfaceText.g || 0.8, Theme.surfaceText.b || 0.8, 0.3)
|
||||||
|
|
||||||
RotationAnimation on rotation {
|
RotationAnimation on rotation {
|
||||||
running: true
|
running: NetworkService.isScanning
|
||||||
loops: Animation.Infinite
|
loops: Animation.Infinite
|
||||||
from: 0
|
from: 0
|
||||||
to: 360
|
to: 360
|
||||||
@@ -226,14 +399,18 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: {
|
model: sortedNetworks
|
||||||
let networks = [...NetworkService.wifiNetworks]
|
|
||||||
networks.sort((a, b) => {
|
property var sortedNetworks: {
|
||||||
if (a.ssid === NetworkService.currentWifiSSID) return -1
|
const ssid = NetworkService.currentWifiSSID
|
||||||
if (b.ssid === NetworkService.currentWifiSSID) return 1
|
const networks = NetworkService.wifiNetworks
|
||||||
|
let sorted = [...networks]
|
||||||
|
sorted.sort((a, b) => {
|
||||||
|
if (a.ssid === ssid) return -1
|
||||||
|
if (b.ssid === ssid) return 1
|
||||||
return b.signal - a.signal
|
return b.signal - a.signal
|
||||||
})
|
})
|
||||||
return networks
|
return sorted
|
||||||
}
|
}
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
required property var modelData
|
required property var modelData
|
||||||
@@ -242,9 +419,9 @@ Rectangle {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: 50
|
height: 50
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: networkMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, index % 2 === 0 ? 0.3 : 0.2)
|
color: networkMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Theme.surfaceContainerHighest
|
||||||
border.color: modelData.ssid === NetworkService.currentWifiSSID ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
border.color: modelData.ssid === NetworkService.currentWifiSSID ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
border.width: modelData.ssid === NetworkService.currentWifiSSID ? 2 : 1
|
border.width: 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -276,25 +453,25 @@ Rectangle {
|
|||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
width: parent.width
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: modelData.ssid === NetworkService.currentWifiSSID ? "Connected" : (modelData.secured ? "Secured" : "Open")
|
text: modelData.ssid === NetworkService.currentWifiSSID ? "Connected •" : (modelData.secured ? "Secured •" : "Open •")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: modelData.saved ? "• Saved" : ""
|
text: modelData.saved ? "Saved" : ""
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
visible: text.length > 0
|
visible: text.length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "• " + modelData.signal + "%"
|
text: (modelData.saved ? "• " : "") + modelData.signal + "%"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
}
|
}
|
||||||
@@ -332,9 +509,7 @@ Rectangle {
|
|||||||
onClicked: function(event) {
|
onClicked: function(event) {
|
||||||
if (modelData.ssid !== NetworkService.currentWifiSSID) {
|
if (modelData.ssid !== NetworkService.currentWifiSSID) {
|
||||||
if (modelData.secured && !modelData.saved) {
|
if (modelData.secured && !modelData.saved) {
|
||||||
if (wifiPasswordModalRef) {
|
wifiPasswordModal.show(modelData.ssid)
|
||||||
wifiPasswordModalRef.show(modelData.ssid)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
NetworkService.connectToWifi(modelData.ssid)
|
NetworkService.connectToWifi(modelData.ssid)
|
||||||
}
|
}
|
||||||
@@ -343,13 +518,6 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation { duration: Theme.shortDuration }
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on border.color {
|
|
||||||
ColorAnimation { duration: Theme.shortDuration }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -369,7 +537,7 @@ Rectangle {
|
|||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: Theme.popupBackground()
|
color: Theme.popupBackground()
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.width: 1
|
border.width: 0
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,9 +563,7 @@ Rectangle {
|
|||||||
NetworkService.disconnectWifi()
|
NetworkService.disconnectWifi()
|
||||||
} else {
|
} else {
|
||||||
if (networkContextMenu.currentSecured && !networkContextMenu.currentSaved) {
|
if (networkContextMenu.currentSecured && !networkContextMenu.currentSaved) {
|
||||||
if (wifiPasswordModalRef) {
|
wifiPasswordModal.show(networkContextMenu.currentSSID)
|
||||||
wifiPasswordModalRef.show(networkContextMenu.currentSSID)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
NetworkService.connectToWifi(networkContextMenu.currentSSID)
|
NetworkService.connectToWifi(networkContextMenu.currentSSID)
|
||||||
}
|
}
|
||||||
@@ -406,7 +572,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: "Network Info"
|
text: I18n.tr("Network Info")
|
||||||
height: 32
|
height: 32
|
||||||
|
|
||||||
contentItem: StyledText {
|
contentItem: StyledText {
|
||||||
@@ -423,15 +589,13 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
if (networkInfoModalRef) {
|
let networkData = NetworkService.getNetworkInfo(networkContextMenu.currentSSID)
|
||||||
let networkData = NetworkService.getNetworkInfo(networkContextMenu.currentSSID)
|
networkInfoModal.showNetworkInfo(networkContextMenu.currentSSID, networkData)
|
||||||
networkInfoModalRef.showNetworkInfo(networkContextMenu.currentSSID, networkData)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: "Forget Network"
|
text: I18n.tr("Forget Network")
|
||||||
height: networkContextMenu.currentSaved || networkContextMenu.currentConnected ? 32 : 0
|
height: networkContextMenu.currentSaved || networkContextMenu.currentConnected ? 32 : 0
|
||||||
visible: networkContextMenu.currentSaved || networkContextMenu.currentConnected
|
visible: networkContextMenu.currentSaved || networkContextMenu.currentConnected
|
||||||
|
|
||||||
@@ -454,23 +618,15 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LazyLoader {
|
WifiPasswordModal {
|
||||||
id: wifiPasswordModalLoader
|
id: wifiPasswordModal
|
||||||
active: false
|
}
|
||||||
|
|
||||||
WifiPasswordModal {
|
NetworkInfoModal {
|
||||||
id: wifiPasswordModal
|
id: networkInfoModal
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LazyLoader {
|
NetworkWiredInfoModal {
|
||||||
id: networkInfoModalLoader
|
id: networkWiredInfoModal
|
||||||
active: false
|
|
||||||
|
|
||||||
NetworkInfoModal {
|
|
||||||
id: networkInfoModal
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,38 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
|
import qs.Modules.ControlCenter.BuiltinPlugins
|
||||||
import "../utils/widgets.js" as WidgetUtils
|
import "../utils/widgets.js" as WidgetUtils
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
readonly property var baseWidgetDefinitions: [
|
property var vpnBuiltinInstance: null
|
||||||
{
|
|
||||||
|
property var vpnLoader: Loader {
|
||||||
|
active: false
|
||||||
|
sourceComponent: Component {
|
||||||
|
VpnWidget {}
|
||||||
|
}
|
||||||
|
|
||||||
|
onItemChanged: {
|
||||||
|
root.vpnBuiltinInstance = item
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: SettingsData
|
||||||
|
function onControlCenterWidgetsChanged() {
|
||||||
|
const widgets = SettingsData.controlCenterWidgets || []
|
||||||
|
const hasVpnWidget = widgets.some(w => w.id === "builtin_vpn")
|
||||||
|
if (!hasVpnWidget && vpnLoader.active) {
|
||||||
|
console.log("VpnWidget: No VPN widget in control center, deactivating loader")
|
||||||
|
vpnLoader.active = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property var coreWidgetDefinitions: [{
|
||||||
"id": "nightMode",
|
"id": "nightMode",
|
||||||
"text": "Night Mode",
|
"text": "Night Mode",
|
||||||
"description": "Blue light filter",
|
"description": "Blue light filter",
|
||||||
@@ -15,32 +40,28 @@ QtObject {
|
|||||||
"type": "toggle",
|
"type": "toggle",
|
||||||
"enabled": DisplayService.automationAvailable,
|
"enabled": DisplayService.automationAvailable,
|
||||||
"warning": !DisplayService.automationAvailable ? "Requires night mode support" : undefined
|
"warning": !DisplayService.automationAvailable ? "Requires night mode support" : undefined
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
"id": "darkMode",
|
"id": "darkMode",
|
||||||
"text": "Dark Mode",
|
"text": "Dark Mode",
|
||||||
"description": "System theme toggle",
|
"description": "System theme toggle",
|
||||||
"icon": "contrast",
|
"icon": "contrast",
|
||||||
"type": "toggle",
|
"type": "toggle",
|
||||||
"enabled": true
|
"enabled": true
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
"id": "doNotDisturb",
|
"id": "doNotDisturb",
|
||||||
"text": "Do Not Disturb",
|
"text": "Do Not Disturb",
|
||||||
"description": "Block notifications",
|
"description": "Block notifications",
|
||||||
"icon": "do_not_disturb_on",
|
"icon": "do_not_disturb_on",
|
||||||
"type": "toggle",
|
"type": "toggle",
|
||||||
"enabled": true
|
"enabled": true
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
"id": "idleInhibitor",
|
"id": "idleInhibitor",
|
||||||
"text": "Keep Awake",
|
"text": "Keep Awake",
|
||||||
"description": "Prevent screen timeout",
|
"description": "Prevent screen timeout",
|
||||||
"icon": "motion_sensor_active",
|
"icon": "motion_sensor_active",
|
||||||
"type": "toggle",
|
"type": "toggle",
|
||||||
"enabled": true
|
"enabled": true
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
"id": "wifi",
|
"id": "wifi",
|
||||||
"text": "Network",
|
"text": "Network",
|
||||||
"description": "Wi-Fi and Ethernet connection",
|
"description": "Wi-Fi and Ethernet connection",
|
||||||
@@ -48,8 +69,7 @@ QtObject {
|
|||||||
"type": "connection",
|
"type": "connection",
|
||||||
"enabled": NetworkService.wifiAvailable,
|
"enabled": NetworkService.wifiAvailable,
|
||||||
"warning": !NetworkService.wifiAvailable ? "Wi-Fi not available" : undefined
|
"warning": !NetworkService.wifiAvailable ? "Wi-Fi not available" : undefined
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
"id": "bluetooth",
|
"id": "bluetooth",
|
||||||
"text": "Bluetooth",
|
"text": "Bluetooth",
|
||||||
"description": "Device connections",
|
"description": "Device connections",
|
||||||
@@ -57,57 +77,51 @@ QtObject {
|
|||||||
"type": "connection",
|
"type": "connection",
|
||||||
"enabled": BluetoothService.available,
|
"enabled": BluetoothService.available,
|
||||||
"warning": !BluetoothService.available ? "Bluetooth not available" : undefined
|
"warning": !BluetoothService.available ? "Bluetooth not available" : undefined
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
"id": "audioOutput",
|
"id": "audioOutput",
|
||||||
"text": "Audio Output",
|
"text": "Audio Output",
|
||||||
"description": "Speaker settings",
|
"description": "Speaker settings",
|
||||||
"icon": "volume_up",
|
"icon": "volume_up",
|
||||||
"type": "connection",
|
"type": "connection",
|
||||||
"enabled": true
|
"enabled": true
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
"id": "audioInput",
|
"id": "audioInput",
|
||||||
"text": "Audio Input",
|
"text": "Audio Input",
|
||||||
"description": "Microphone settings",
|
"description": "Microphone settings",
|
||||||
"icon": "mic",
|
"icon": "mic",
|
||||||
"type": "connection",
|
"type": "connection",
|
||||||
"enabled": true
|
"enabled": true
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
"id": "volumeSlider",
|
"id": "volumeSlider",
|
||||||
"text": "Volume Slider",
|
"text": "Volume Slider",
|
||||||
"description": "Audio volume control",
|
"description": "Audio volume control",
|
||||||
"icon": "volume_up",
|
"icon": "volume_up",
|
||||||
"type": "slider",
|
"type": "slider",
|
||||||
"enabled": true
|
"enabled": true
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
"id": "brightnessSlider",
|
"id": "brightnessSlider",
|
||||||
"text": "Brightness Slider",
|
"text": "Brightness Slider",
|
||||||
"description": "Display brightness control",
|
"description": "Display brightness control",
|
||||||
"icon": "brightness_6",
|
"icon": "brightness_6",
|
||||||
"type": "slider",
|
"type": "slider",
|
||||||
"enabled": DisplayService.brightnessAvailable,
|
"enabled": DisplayService.brightnessAvailable,
|
||||||
"warning": !DisplayService.brightnessAvailable ? "Brightness control not available" : undefined
|
"warning": !DisplayService.brightnessAvailable ? "Brightness control not available" : undefined,
|
||||||
},
|
"allowMultiple": true
|
||||||
{
|
}, {
|
||||||
"id": "inputVolumeSlider",
|
"id": "inputVolumeSlider",
|
||||||
"text": "Input Volume Slider",
|
"text": "Input Volume Slider",
|
||||||
"description": "Microphone volume control",
|
"description": "Microphone volume control",
|
||||||
"icon": "mic",
|
"icon": "mic",
|
||||||
"type": "slider",
|
"type": "slider",
|
||||||
"enabled": true
|
"enabled": true
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
"id": "battery",
|
"id": "battery",
|
||||||
"text": "Battery",
|
"text": "Battery",
|
||||||
"description": "Battery and power management",
|
"description": "Battery and power management",
|
||||||
"icon": "battery_std",
|
"icon": "battery_std",
|
||||||
"type": "action",
|
"type": "action",
|
||||||
"enabled": true
|
"enabled": true
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
"id": "diskUsage",
|
"id": "diskUsage",
|
||||||
"text": "Disk Usage",
|
"text": "Disk Usage",
|
||||||
"description": "Filesystem usage monitoring",
|
"description": "Filesystem usage monitoring",
|
||||||
@@ -116,8 +130,68 @@ QtObject {
|
|||||||
"enabled": DgopService.dgopAvailable,
|
"enabled": DgopService.dgopAvailable,
|
||||||
"warning": !DgopService.dgopAvailable ? "Requires 'dgop' tool" : undefined,
|
"warning": !DgopService.dgopAvailable ? "Requires 'dgop' tool" : undefined,
|
||||||
"allowMultiple": true
|
"allowMultiple": true
|
||||||
|
}, {
|
||||||
|
"id": "colorPicker",
|
||||||
|
"text": "Color Picker",
|
||||||
|
"description": "Choose colors from palette",
|
||||||
|
"icon": "palette",
|
||||||
|
"type": "action",
|
||||||
|
"enabled": true
|
||||||
|
}, {
|
||||||
|
"id": "builtin_vpn",
|
||||||
|
"text": "VPN",
|
||||||
|
"description": "VPN connections",
|
||||||
|
"icon": "vpn_key",
|
||||||
|
"type": "builtin_plugin",
|
||||||
|
"enabled": VpnService.available,
|
||||||
|
"warning": !VpnService.available ? "VPN not available" : undefined,
|
||||||
|
"isBuiltinPlugin": true
|
||||||
|
}]
|
||||||
|
|
||||||
|
function getPluginWidgets() {
|
||||||
|
const plugins = []
|
||||||
|
const loadedPlugins = PluginService.getLoadedPlugins()
|
||||||
|
|
||||||
|
for (var i = 0; i < loadedPlugins.length; i++) {
|
||||||
|
const plugin = loadedPlugins[i]
|
||||||
|
|
||||||
|
if (plugin.type === "daemon") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const pluginComponent = PluginService.pluginWidgetComponents[plugin.id]
|
||||||
|
if (!pluginComponent) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const tempInstance = pluginComponent.createObject(null)
|
||||||
|
if (!tempInstance) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasCCWidget = tempInstance.ccWidgetIcon && tempInstance.ccWidgetIcon.length > 0
|
||||||
|
tempInstance.destroy()
|
||||||
|
|
||||||
|
if (!hasCCWidget) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins.push({
|
||||||
|
"id": "plugin_" + plugin.id,
|
||||||
|
"pluginId": plugin.id,
|
||||||
|
"text": plugin.name || "Plugin",
|
||||||
|
"description": plugin.description || "",
|
||||||
|
"icon": plugin.icon || "extension",
|
||||||
|
"type": "plugin",
|
||||||
|
"enabled": true,
|
||||||
|
"isPlugin": true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
]
|
|
||||||
|
return plugins
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property var baseWidgetDefinitions: coreWidgetDefinitions
|
||||||
|
|
||||||
function getWidgetForId(widgetId) {
|
function getWidgetForId(widgetId) {
|
||||||
return WidgetUtils.getWidgetForId(baseWidgetDefinitions, widgetId)
|
return WidgetUtils.getWidgetForId(baseWidgetDefinitions, widgetId)
|
||||||
@@ -146,4 +220,4 @@ QtObject {
|
|||||||
function clearAll() {
|
function clearAll() {
|
||||||
WidgetUtils.clearAll()
|
WidgetUtils.clearAll()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ import qs.Widgets
|
|||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
readonly property string powerOptionsText: I18n.tr("Power Options")
|
||||||
|
readonly property string logOutText: I18n.tr("Log Out")
|
||||||
|
readonly property string suspendText: I18n.tr("Suspend")
|
||||||
|
readonly property string rebootText: I18n.tr("Reboot")
|
||||||
|
readonly property string powerOffText: I18n.tr("Power Off")
|
||||||
|
|
||||||
property bool powerMenuVisible: false
|
property bool powerMenuVisible: false
|
||||||
signal powerActionRequested(string action, string title, string message)
|
signal powerActionRequested(string action, string title, string message)
|
||||||
|
|
||||||
@@ -44,7 +50,7 @@ PanelWindow {
|
|||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
|
||||||
Theme.outline.b, 0.08)
|
Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: 0
|
||||||
opacity: powerMenuVisible ? 1 : 0
|
opacity: powerMenuVisible ? 1 : 0
|
||||||
scale: powerMenuVisible ? 1 : 0.85
|
scale: powerMenuVisible ? 1 : 0.85
|
||||||
|
|
||||||
@@ -65,7 +71,7 @@ PanelWindow {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Power Options"
|
text: root.powerOptionsText
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -118,7 +124,7 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Log Out"
|
text: root.logOutText
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -168,7 +174,7 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Suspend"
|
text: root.suspendText
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -218,7 +224,7 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Reboot"
|
text: root.rebootText
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: rebootArea.containsMouse ? Theme.warning : Theme.surfaceText
|
color: rebootArea.containsMouse ? Theme.warning : Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -268,7 +274,7 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Power Off"
|
text: root.powerOffText
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: powerOffArea.containsMouse ? Theme.error : Theme.surfaceText
|
color: powerOffArea.containsMouse ? Theme.error : Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
|
|||||||
@@ -20,11 +20,7 @@ Row {
|
|||||||
height: Theme.iconSize + Theme.spacingS * 2
|
height: Theme.iconSize + Theme.spacingS * 2
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
radius: (Theme.iconSize + Theme.spacingS * 2) / 2
|
radius: (Theme.iconSize + Theme.spacingS * 2) / 2
|
||||||
color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Theme.withAlpha(Theme.primary, 0)
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation { duration: Theme.shortDuration }
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: iconArea
|
id: iconArea
|
||||||
@@ -34,7 +30,9 @@ Row {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (defaultSink) {
|
if (defaultSink) {
|
||||||
|
AudioService.suppressOSD = true
|
||||||
defaultSink.audio.muted = !defaultSink.audio.muted
|
defaultSink.audio.muted = !defaultSink.audio.muted
|
||||||
|
AudioService.suppressOSD = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,7 +68,7 @@ Row {
|
|||||||
unit: "%"
|
unit: "%"
|
||||||
valueOverride: actualVolumePercent
|
valueOverride: actualVolumePercent
|
||||||
thumbOutlineColor: Theme.surfaceContainer
|
thumbOutlineColor: Theme.surfaceContainer
|
||||||
trackColor: root.sliderTrackColor.a > 0 ? root.sliderTrackColor : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.60)
|
trackColor: root.sliderTrackColor.a > 0 ? root.sliderTrackColor : Theme.surfaceContainerHigh
|
||||||
onSliderValueChanged: function(newValue) {
|
onSliderValueChanged: function(newValue) {
|
||||||
if (defaultSink) {
|
if (defaultSink) {
|
||||||
defaultSink.audio.volume = newValue / 100.0
|
defaultSink.audio.volume = newValue / 100.0
|
||||||
|
|||||||
@@ -30,9 +30,6 @@ CompoundPill {
|
|||||||
if (!BluetoothService.adapter || !BluetoothService.adapter.enabled) {
|
if (!BluetoothService.adapter || !BluetoothService.adapter.enabled) {
|
||||||
return "bluetooth_disabled"
|
return "bluetooth_disabled"
|
||||||
}
|
}
|
||||||
if (primaryDevice) {
|
|
||||||
return BluetoothService.getDeviceIcon(primaryDevice)
|
|
||||||
}
|
|
||||||
return "bluetooth"
|
return "bluetooth"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,49 @@ import qs.Widgets
|
|||||||
Row {
|
Row {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
property string deviceName: ""
|
||||||
|
property string instanceId: ""
|
||||||
|
|
||||||
|
signal iconClicked()
|
||||||
|
|
||||||
height: 40
|
height: 40
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
|
property string targetDeviceName: {
|
||||||
|
if (!DisplayService.brightnessAvailable || !DisplayService.devices || DisplayService.devices.length === 0) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deviceName && deviceName.length > 0) {
|
||||||
|
const found = DisplayService.devices.find(dev => dev.name === deviceName)
|
||||||
|
return found ? found.name : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentDeviceName = DisplayService.currentDevice
|
||||||
|
if (currentDeviceName) {
|
||||||
|
const found = DisplayService.devices.find(dev => dev.name === currentDeviceName)
|
||||||
|
return found ? found.name : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return DisplayService.devices.length > 0 ? DisplayService.devices[0].name : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
property var targetDevice: {
|
||||||
|
if (!targetDeviceName || !DisplayService.devices) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return DisplayService.devices.find(dev => dev.name === targetDeviceName) || null
|
||||||
|
}
|
||||||
|
|
||||||
|
property real targetBrightness: {
|
||||||
|
if (!targetDeviceName) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return DisplayService.getDeviceBrightness(targetDeviceName)
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: Theme.iconSize + Theme.spacingS * 2
|
width: Theme.iconSize + Theme.spacingS * 2
|
||||||
height: Theme.iconSize + Theme.spacingS * 2
|
height: Theme.iconSize + Theme.spacingS * 2
|
||||||
@@ -18,41 +58,56 @@ Row {
|
|||||||
radius: (Theme.iconSize + Theme.spacingS * 2) / 2
|
radius: (Theme.iconSize + Theme.spacingS * 2) / 2
|
||||||
color: iconArea.containsMouse
|
color: iconArea.containsMouse
|
||||||
? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
||||||
: "transparent"
|
: Theme.withAlpha(Theme.primary, 0)
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation { duration: Theme.shortDuration }
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: iconArea
|
id: iconArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: DisplayService.devices && DisplayService.devices.length > 1 ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
|
|
||||||
onClicked: function(event) {
|
onClicked: {
|
||||||
if (DisplayService.devices.length > 1) {
|
if (DisplayService.devices && DisplayService.devices.length > 1) {
|
||||||
if (deviceMenu.visible) {
|
root.iconClicked()
|
||||||
deviceMenu.close()
|
|
||||||
} else {
|
|
||||||
deviceMenu.popup(iconArea, 0, iconArea.height + Theme.spacingXS)
|
|
||||||
}
|
|
||||||
event.accepted = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onEntered: {
|
||||||
|
tooltipLoader.active = true
|
||||||
|
if (tooltipLoader.item) {
|
||||||
|
const tooltipText = targetDevice ? "bl device: " + targetDevice.name : "Backlight Control"
|
||||||
|
const p = iconArea.mapToItem(null, iconArea.width / 2, 0)
|
||||||
|
tooltipLoader.item.show(tooltipText, p.x, p.y - 40, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onExited: {
|
||||||
|
if (tooltipLoader.item) {
|
||||||
|
tooltipLoader.item.hide()
|
||||||
|
}
|
||||||
|
tooltipLoader.active = false
|
||||||
|
}
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
name: {
|
name: {
|
||||||
if (!DisplayService.brightnessAvailable) return "brightness_low"
|
if (!DisplayService.brightnessAvailable || !targetDevice) {
|
||||||
|
return "brightness_low"
|
||||||
|
}
|
||||||
|
|
||||||
let brightness = DisplayService.brightnessLevel
|
if (targetDevice.class === "backlight" || targetDevice.class === "ddc") {
|
||||||
if (brightness <= 33) return "brightness_low"
|
const brightness = targetBrightness
|
||||||
if (brightness <= 66) return "brightness_medium"
|
if (brightness <= 33) return "brightness_low"
|
||||||
return "brightness_high"
|
if (brightness <= 66) return "brightness_medium"
|
||||||
|
return "brightness_high"
|
||||||
|
} else if (targetDevice.name.includes("kbd")) {
|
||||||
|
return "keyboard"
|
||||||
|
} else {
|
||||||
|
return "lightbulb"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
size: Theme.iconSize
|
size: Theme.iconSize
|
||||||
color: DisplayService.brightnessAvailable && DisplayService.brightnessLevel > 0 ? Theme.primary : Theme.surfaceText
|
color: DisplayService.brightnessAvailable && targetDevice && targetBrightness > 0 ? Theme.primary : Theme.surfaceText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,85 +115,22 @@ Row {
|
|||||||
DankSlider {
|
DankSlider {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
width: parent.width - (Theme.iconSize + Theme.spacingS * 2)
|
width: parent.width - (Theme.iconSize + Theme.spacingS * 2)
|
||||||
enabled: DisplayService.brightnessAvailable
|
enabled: DisplayService.brightnessAvailable && targetDeviceName.length > 0
|
||||||
minimum: 1
|
minimum: 1
|
||||||
maximum: 100
|
maximum: 100
|
||||||
value: {
|
value: targetBrightness
|
||||||
let level = DisplayService.brightnessLevel
|
|
||||||
if (level > 100) {
|
|
||||||
let deviceInfo = DisplayService.getCurrentDeviceInfo()
|
|
||||||
if (deviceInfo && deviceInfo.max > 0) {
|
|
||||||
return Math.round((level / deviceInfo.max) * 100)
|
|
||||||
}
|
|
||||||
return 50
|
|
||||||
}
|
|
||||||
return level
|
|
||||||
}
|
|
||||||
onSliderValueChanged: function(newValue) {
|
onSliderValueChanged: function(newValue) {
|
||||||
if (DisplayService.brightnessAvailable) {
|
if (DisplayService.brightnessAvailable && targetDeviceName) {
|
||||||
DisplayService.setBrightness(newValue)
|
DisplayService.setBrightness(newValue, targetDeviceName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
thumbOutlineColor: Theme.surfaceContainer
|
thumbOutlineColor: Theme.surfaceContainer
|
||||||
trackColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.60)
|
trackColor: Theme.surfaceContainerHigh
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu {
|
Loader {
|
||||||
id: deviceMenu
|
id: tooltipLoader
|
||||||
width: 200
|
active: false
|
||||||
closePolicy: Popup.CloseOnEscape
|
sourceComponent: DankTooltip {}
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: Theme.popupBackground()
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
border.width: 1
|
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
|
||||||
}
|
|
||||||
|
|
||||||
Instantiator {
|
|
||||||
model: DisplayService.devices
|
|
||||||
delegate: MenuItem {
|
|
||||||
required property var modelData
|
|
||||||
required property int index
|
|
||||||
|
|
||||||
property string deviceName: modelData.name || ""
|
|
||||||
property string deviceClass: modelData.class || ""
|
|
||||||
|
|
||||||
text: deviceName
|
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
|
||||||
height: 40
|
|
||||||
|
|
||||||
indicator: Rectangle {
|
|
||||||
visible: DisplayService.currentDevice === parent.deviceName
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
anchors.leftMargin: Theme.spacingS
|
|
||||||
width: 4
|
|
||||||
height: parent.height - Theme.spacingS * 2
|
|
||||||
radius: 2
|
|
||||||
color: Theme.primary
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: StyledText {
|
|
||||||
text: parent.text
|
|
||||||
font: parent.font
|
|
||||||
color: DisplayService.currentDevice === parent.deviceName ? Theme.primary : Theme.surfaceText
|
|
||||||
leftPadding: Theme.spacingL
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: parent.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
|
||||||
radius: Theme.cornerRadius / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
onTriggered: {
|
|
||||||
DisplayService.setCurrentDevice(deviceName, true)
|
|
||||||
deviceMenu.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onObjectAdded: (index, object) => deviceMenu.insertItem(index, object)
|
|
||||||
onObjectRemoved: (index, object) => deviceMenu.removeItem(object)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
33
Modules/ControlCenter/Widgets/ColorPickerPill.qml
Normal file
33
Modules/ControlCenter/Widgets/ColorPickerPill.qml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Quickshell
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
import qs.Modules.ControlCenter.Widgets
|
||||||
|
|
||||||
|
CompoundPill {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var colorPickerModal: null
|
||||||
|
|
||||||
|
isActive: true
|
||||||
|
iconName: "palette"
|
||||||
|
iconColor: Theme.primary
|
||||||
|
primaryText: "Color Picker"
|
||||||
|
secondaryText: "Choose a color"
|
||||||
|
|
||||||
|
onToggled: {
|
||||||
|
console.log("ColorPickerPill toggled, modal:", colorPickerModal)
|
||||||
|
if (colorPickerModal) {
|
||||||
|
colorPickerModal.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onExpandClicked: {
|
||||||
|
console.log("ColorPickerPill expandClicked, modal:", colorPickerModal)
|
||||||
|
if (colorPickerModal) {
|
||||||
|
colorPickerModal.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,9 +20,9 @@ Rectangle {
|
|||||||
width: parent ? parent.width : 200
|
width: parent ? parent.width : 200
|
||||||
height: 60
|
height: 60
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.6)
|
color: Theme.surfaceContainerHigh
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: 0
|
||||||
opacity: enabled ? 1.0 : 0.6
|
opacity: enabled ? 1.0 : 0.6
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
|
|||||||
@@ -27,20 +27,21 @@ Rectangle {
|
|||||||
return Theme.isLightMode ? Qt.darker(base, factor) : Qt.lighter(base, factor)
|
return Theme.isLightMode ? Qt.darker(base, factor) : Qt.lighter(base, factor)
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property color _containerBg:
|
readonly property color _containerBg: Theme.surfaceContainerHigh
|
||||||
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b,
|
|
||||||
Theme.getContentBackgroundAlpha() * 0.60)
|
|
||||||
|
|
||||||
color: _containerBg
|
color: {
|
||||||
|
const baseColor = bodyMouse.containsMouse ? Theme.widgetBaseHoverColor : _containerBg
|
||||||
|
return baseColor
|
||||||
|
}
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.10)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.10)
|
||||||
border.width: 1
|
border.width: 0
|
||||||
antialiasing: true
|
antialiasing: true
|
||||||
|
|
||||||
readonly property color _labelPrimary: Theme.surfaceText
|
readonly property color _labelPrimary: Theme.surfaceText
|
||||||
readonly property color _labelSecondary: Theme.surfaceVariantText
|
readonly property color _labelSecondary: Theme.surfaceVariantText
|
||||||
readonly property color _tileBgActive: Theme.primary
|
readonly property color _tileBgActive: Theme.primary
|
||||||
readonly property color _tileBgInactive: {
|
readonly property color _tileBgInactive: {
|
||||||
const transparency = Theme.popupTransparency || 0.92
|
const transparency = Theme.popupTransparency
|
||||||
const surface = Theme.surfaceContainer || Qt.rgba(0.1, 0.1, 0.1, 1)
|
const surface = Theme.surfaceContainer || Qt.rgba(0.1, 0.1, 0.1, 1)
|
||||||
return Qt.rgba(surface.r, surface.g, surface.b, transparency)
|
return Qt.rgba(surface.r, surface.g, surface.b, transparency)
|
||||||
}
|
}
|
||||||
@@ -48,7 +49,7 @@ Rectangle {
|
|||||||
Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
|
Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
|
||||||
readonly property color _tileRingInactive:
|
readonly property color _tileRingInactive:
|
||||||
Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.18)
|
Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.18)
|
||||||
readonly property color _tileIconActive: Theme.primaryContainer
|
readonly property color _tileIconActive: Theme.primaryText
|
||||||
readonly property color _tileIconInactive: Theme.primary
|
readonly property color _tileIconInactive: Theme.primary
|
||||||
|
|
||||||
property int _padH: Theme.spacingS
|
property int _padH: Theme.spacingS
|
||||||
|
|||||||
51
Modules/ControlCenter/Widgets/ErrorPill.qml
Normal file
51
Modules/ControlCenter/Widgets/ErrorPill.qml
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string primaryMessage: ""
|
||||||
|
property string secondaryMessage: ""
|
||||||
|
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.1)
|
||||||
|
border.color: Theme.warning
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "warning"
|
||||||
|
size: 16
|
||||||
|
color: Theme.warning
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width - 16 - parent.spacing
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
width: parent.width
|
||||||
|
text: root.primaryMessage
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.warning
|
||||||
|
font.weight: Font.Medium
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
width: parent.width
|
||||||
|
text: root.secondaryMessage
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.warning
|
||||||
|
visible: text.length > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,11 +20,7 @@ Row {
|
|||||||
height: Theme.iconSize + Theme.spacingS * 2
|
height: Theme.iconSize + Theme.spacingS * 2
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
radius: (Theme.iconSize + Theme.spacingS * 2) / 2
|
radius: (Theme.iconSize + Theme.spacingS * 2) / 2
|
||||||
color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Theme.withAlpha(Theme.primary, 0)
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation { duration: Theme.shortDuration }
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: iconArea
|
id: iconArea
|
||||||
@@ -34,7 +30,9 @@ Row {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (defaultSource) {
|
if (defaultSource) {
|
||||||
|
AudioService.suppressOSD = true
|
||||||
defaultSource.audio.muted = !defaultSource.audio.muted
|
defaultSource.audio.muted = !defaultSource.audio.muted
|
||||||
|
AudioService.suppressOSD = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,7 +66,10 @@ Row {
|
|||||||
unit: "%"
|
unit: "%"
|
||||||
valueOverride: actualVolumePercent
|
valueOverride: actualVolumePercent
|
||||||
thumbOutlineColor: Theme.surfaceContainer
|
thumbOutlineColor: Theme.surfaceContainer
|
||||||
trackColor: root.sliderTrackColor.a > 0 ? root.sliderTrackColor : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.60)
|
trackColor: root.sliderTrackColor.a > 0 ? root.sliderTrackColor : Theme.surfaceContainerHigh
|
||||||
|
onIsDraggingChanged: {
|
||||||
|
AudioService.suppressOSD = isDragging
|
||||||
|
}
|
||||||
onSliderValueChanged: function(newValue) {
|
onSliderValueChanged: function(newValue) {
|
||||||
if (defaultSource) {
|
if (defaultSource) {
|
||||||
defaultSource.audio.volume = newValue / 100.0
|
defaultSource.audio.volume = newValue / 100.0
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import Quickshell
|
|
||||||
import qs.Common
|
|
||||||
import qs.Services
|
|
||||||
import qs.Widgets
|
|
||||||
import qs.Modules.ControlCenter.Widgets
|
|
||||||
|
|
||||||
CompoundPill {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
isActive: {
|
|
||||||
if (NetworkService.wifiToggling) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (NetworkService.networkStatus === "ethernet") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (NetworkService.networkStatus === "wifi") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return NetworkService.wifiEnabled
|
|
||||||
}
|
|
||||||
|
|
||||||
iconName: {
|
|
||||||
if (NetworkService.wifiToggling) {
|
|
||||||
return "sync"
|
|
||||||
}
|
|
||||||
if (NetworkService.networkStatus === "ethernet") {
|
|
||||||
return "settings_ethernet"
|
|
||||||
}
|
|
||||||
if (NetworkService.networkStatus === "wifi") {
|
|
||||||
return NetworkService.wifiSignalIcon
|
|
||||||
}
|
|
||||||
if (NetworkService.wifiEnabled) {
|
|
||||||
return "wifi_off"
|
|
||||||
}
|
|
||||||
return "wifi_off"
|
|
||||||
}
|
|
||||||
|
|
||||||
primaryText: {
|
|
||||||
if (NetworkService.wifiToggling) {
|
|
||||||
return NetworkService.wifiEnabled ? "Disabling WiFi..." : "Enabling WiFi..."
|
|
||||||
}
|
|
||||||
if (NetworkService.networkStatus === "ethernet") {
|
|
||||||
return "Ethernet"
|
|
||||||
}
|
|
||||||
if (NetworkService.networkStatus === "wifi" && NetworkService.currentWifiSSID) {
|
|
||||||
return NetworkService.currentWifiSSID
|
|
||||||
}
|
|
||||||
if (NetworkService.wifiEnabled) {
|
|
||||||
return "Not connected"
|
|
||||||
}
|
|
||||||
return "WiFi off"
|
|
||||||
}
|
|
||||||
|
|
||||||
secondaryText: {
|
|
||||||
if (NetworkService.wifiToggling) {
|
|
||||||
return "Please wait..."
|
|
||||||
}
|
|
||||||
if (NetworkService.networkStatus === "ethernet") {
|
|
||||||
return "Connected"
|
|
||||||
}
|
|
||||||
if (NetworkService.networkStatus === "wifi") {
|
|
||||||
return NetworkService.wifiSignalStrength > 0 ? NetworkService.wifiSignalStrength + "%" : "Connected"
|
|
||||||
}
|
|
||||||
if (NetworkService.wifiEnabled) {
|
|
||||||
return "Select network"
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
onToggled: {
|
|
||||||
if (NetworkService.networkStatus !== "ethernet" && !NetworkService.wifiToggling) {
|
|
||||||
NetworkService.toggleWifiRadio()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -25,15 +25,17 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
readonly property color _tileBgActive: Theme.primary
|
readonly property color _tileBgActive: Theme.primary
|
||||||
readonly property color _tileBgInactive:
|
readonly property color _tileBgInactive: Theme.surfaceContainerHigh
|
||||||
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b,
|
|
||||||
Theme.getContentBackgroundAlpha() * 0.60)
|
|
||||||
readonly property color _tileRingActive:
|
readonly property color _tileRingActive:
|
||||||
Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
|
Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
|
||||||
readonly property color _tileIconActive: Theme.primaryContainer
|
readonly property color _tileIconActive: Theme.primaryText
|
||||||
readonly property color _tileIconInactive: Theme.primary
|
readonly property color _tileIconInactive: Theme.primary
|
||||||
|
|
||||||
color: isActive ? _tileBgActive : _tileBgInactive
|
color: {
|
||||||
|
if (isActive) return _tileBgActive
|
||||||
|
const baseColor = mouseArea.containsMouse ? Theme.widgetBaseHoverColor : _tileBgInactive
|
||||||
|
return baseColor
|
||||||
|
}
|
||||||
border.color: isActive ? _tileRingActive : "transparent"
|
border.color: isActive ? _tileRingActive : "transparent"
|
||||||
border.width: isActive ? 1 : 0
|
border.width: isActive ? 1 : 0
|
||||||
antialiasing: true
|
antialiasing: true
|
||||||
@@ -89,13 +91,6 @@ Rectangle {
|
|||||||
onClicked: root.clicked()
|
onClicked: root.clicked()
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on radius {
|
Behavior on radius {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ Rectangle {
|
|||||||
property real iconRotation: 0
|
property real iconRotation: 0
|
||||||
|
|
||||||
signal clicked()
|
signal clicked()
|
||||||
|
signal iconRotationCompleted()
|
||||||
|
|
||||||
width: parent ? ((parent.width - parent.spacing * 3) / 4) : 48
|
width: parent ? ((parent.width - parent.spacing * 3) / 4) : 48
|
||||||
height: 48
|
height: 48
|
||||||
@@ -26,15 +27,17 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
readonly property color _tileBgActive: Theme.primary
|
readonly property color _tileBgActive: Theme.primary
|
||||||
readonly property color _tileBgInactive:
|
readonly property color _tileBgInactive: Theme.surfaceContainerHigh
|
||||||
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b,
|
|
||||||
Theme.getContentBackgroundAlpha() * 0.60)
|
|
||||||
readonly property color _tileRingActive:
|
readonly property color _tileRingActive:
|
||||||
Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
|
Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
|
||||||
readonly property color _tileIconActive: Theme.primaryContainer
|
readonly property color _tileIconActive: Theme.primaryText
|
||||||
readonly property color _tileIconInactive: Theme.primary
|
readonly property color _tileIconInactive: Theme.primary
|
||||||
|
|
||||||
color: isActive ? _tileBgActive : _tileBgInactive
|
color: {
|
||||||
|
if (isActive) return _tileBgActive
|
||||||
|
const baseColor = mouseArea.containsMouse ? Theme.widgetBaseHoverColor : _tileBgInactive
|
||||||
|
return baseColor
|
||||||
|
}
|
||||||
border.color: isActive ? _tileRingActive : "transparent"
|
border.color: isActive ? _tileRingActive : "transparent"
|
||||||
border.width: isActive ? 1 : 0
|
border.width: isActive ? 1 : 0
|
||||||
antialiasing: true
|
antialiasing: true
|
||||||
@@ -56,6 +59,7 @@ Rectangle {
|
|||||||
size: Theme.iconSize
|
size: Theme.iconSize
|
||||||
color: isActive ? _tileIconActive : _tileIconInactive
|
color: isActive ? _tileIconActive : _tileIconInactive
|
||||||
rotation: iconRotation
|
rotation: iconRotation
|
||||||
|
onRotationCompleted: root.iconRotationCompleted()
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
@@ -67,13 +71,6 @@ Rectangle {
|
|||||||
onClicked: root.clicked()
|
onClicked: root.clicked()
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on radius {
|
Behavior on radius {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
|
|||||||
@@ -12,8 +12,9 @@ Rectangle {
|
|||||||
property bool enabled: true
|
property bool enabled: true
|
||||||
property string secondaryText: ""
|
property string secondaryText: ""
|
||||||
property real iconRotation: 0
|
property real iconRotation: 0
|
||||||
|
|
||||||
signal clicked()
|
signal clicked()
|
||||||
|
signal iconRotationCompleted()
|
||||||
|
|
||||||
width: parent ? parent.width : 200
|
width: parent ? parent.width : 200
|
||||||
height: 60
|
height: 60
|
||||||
@@ -23,15 +24,17 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
readonly property color _tileBgActive: Theme.primary
|
readonly property color _tileBgActive: Theme.primary
|
||||||
readonly property color _tileBgInactive:
|
readonly property color _tileBgInactive: Theme.surfaceContainerHigh
|
||||||
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b,
|
|
||||||
Theme.getContentBackgroundAlpha() * 0.60)
|
|
||||||
readonly property color _tileRingActive:
|
readonly property color _tileRingActive:
|
||||||
Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
|
Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
|
||||||
|
|
||||||
color: isActive ? _tileBgActive : _tileBgInactive
|
color: {
|
||||||
|
if (isActive) return _tileBgActive
|
||||||
|
const baseColor = mouseArea.containsMouse ? Theme.widgetBaseHoverColor : _tileBgInactive
|
||||||
|
return baseColor
|
||||||
|
}
|
||||||
border.color: isActive ? _tileRingActive : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: isActive ? _tileRingActive : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: isActive ? 1 : 1
|
border.width: 0
|
||||||
opacity: enabled ? 1.0 : 0.6
|
opacity: enabled ? 1.0 : 0.6
|
||||||
|
|
||||||
function hoverTint(base) {
|
function hoverTint(base) {
|
||||||
@@ -39,14 +42,12 @@ Rectangle {
|
|||||||
return Theme.isLightMode ? Qt.darker(base, factor) : Qt.lighter(base, factor)
|
return Theme.isLightMode ? Qt.darker(base, factor) : Qt.lighter(base, factor)
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property color _containerBg:
|
readonly property color _containerBg: Theme.surfaceContainerHigh
|
||||||
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b,
|
|
||||||
Theme.getContentBackgroundAlpha() * 0.60)
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: mouseArea.containsMouse ? hoverTint(_containerBg) : "transparent"
|
color: mouseArea.containsMouse ? hoverTint(_containerBg) : Theme.withAlpha(_containerBg, 0)
|
||||||
opacity: mouseArea.containsMouse ? 0.08 : 0.0
|
opacity: mouseArea.containsMouse ? 0.08 : 0.0
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
@@ -63,9 +64,10 @@ Rectangle {
|
|||||||
DankIcon {
|
DankIcon {
|
||||||
name: root.iconName
|
name: root.iconName
|
||||||
size: Theme.iconSize
|
size: Theme.iconSize
|
||||||
color: isActive ? Theme.primaryContainer : Theme.primary
|
color: isActive ? Theme.primaryText : Theme.primary
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
rotation: root.iconRotation
|
rotation: root.iconRotation
|
||||||
|
onRotationCompleted: root.iconRotationCompleted()
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
@@ -82,7 +84,7 @@ Rectangle {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
text: root.text
|
text: root.text
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: isActive ? Theme.primaryContainer : Theme.surfaceText
|
color: isActive ? Theme.primaryText : Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
wrapMode: Text.NoWrap
|
wrapMode: Text.NoWrap
|
||||||
@@ -92,7 +94,7 @@ Rectangle {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
text: root.secondaryText
|
text: root.secondaryText
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: isActive ? Theme.primaryContainer : Theme.surfaceVariantText
|
color: isActive ? Theme.primaryText : Theme.surfaceVariantText
|
||||||
visible: text.length > 0
|
visible: text.length > 0
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
wrapMode: Text.NoWrap
|
wrapMode: Text.NoWrap
|
||||||
@@ -110,13 +112,6 @@ Rectangle {
|
|||||||
onClicked: root.clicked()
|
onClicked: root.clicked()
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on radius {
|
Behavior on radius {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ function addWidget(widgetId) {
|
|||||||
widget.mountPath = "/"
|
widget.mountPath = "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (widgetId === "brightnessSlider") {
|
||||||
|
widget.instanceId = generateUniqueId()
|
||||||
|
widget.deviceName = ""
|
||||||
|
}
|
||||||
|
|
||||||
widgets.push(widget)
|
widgets.push(widget)
|
||||||
SettingsData.setControlCenterWidgets(widgets)
|
SettingsData.setControlCenterWidgets(widgets)
|
||||||
}
|
}
|
||||||
|
|||||||
179
Modules/DankBar/AutoHideManager.qml
Normal file
179
Modules/DankBar/AutoHideManager.qml
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property var barWindow
|
||||||
|
required property var axis
|
||||||
|
required property var appDrawerLoader
|
||||||
|
required property var dankDashPopoutLoader
|
||||||
|
required property var processListPopoutLoader
|
||||||
|
required property var notificationCenterLoader
|
||||||
|
required property var batteryPopoutLoader
|
||||||
|
required property var vpnPopoutLoader
|
||||||
|
required property var controlCenterLoader
|
||||||
|
required property var clipboardHistoryModalPopup
|
||||||
|
required property var systemUpdateLoader
|
||||||
|
required property var notepadInstance
|
||||||
|
|
||||||
|
property alias reveal: core.reveal
|
||||||
|
property alias autoHide: core.autoHide
|
||||||
|
property alias backgroundTransparency: core.backgroundTransparency
|
||||||
|
property alias hasActivePopout: core.hasActivePopout
|
||||||
|
property alias mouseArea: topBarMouseArea
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: inputMask
|
||||||
|
|
||||||
|
readonly property int barThickness: barWindow.px(barWindow.effectiveBarThickness + SettingsData.dankBarSpacing)
|
||||||
|
|
||||||
|
readonly property bool showing: SettingsData.dankBarVisible && (core.reveal
|
||||||
|
|| (CompositorService.isNiri && NiriService.inOverview && SettingsData.dankBarOpenOnOverview)
|
||||||
|
|| !core.autoHide)
|
||||||
|
|
||||||
|
readonly property int maskThickness: showing ? barThickness : 1
|
||||||
|
|
||||||
|
x: {
|
||||||
|
if (!axis.isVertical) {
|
||||||
|
return 0
|
||||||
|
} else {
|
||||||
|
switch (SettingsData.dankBarPosition) {
|
||||||
|
case SettingsData.Position.Left: return 0
|
||||||
|
case SettingsData.Position.Right: return parent.width - maskThickness
|
||||||
|
default: return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
y: {
|
||||||
|
if (axis.isVertical) {
|
||||||
|
return 0
|
||||||
|
} else {
|
||||||
|
switch (SettingsData.dankBarPosition) {
|
||||||
|
case SettingsData.Position.Top: return 0
|
||||||
|
case SettingsData.Position.Bottom: return parent.height - maskThickness
|
||||||
|
default: return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
width: axis.isVertical ? maskThickness : parent.width
|
||||||
|
height: axis.isVertical ? parent.height : maskThickness
|
||||||
|
}
|
||||||
|
|
||||||
|
Region {
|
||||||
|
id: mask
|
||||||
|
item: inputMask
|
||||||
|
}
|
||||||
|
|
||||||
|
property alias maskRegion: mask
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: core
|
||||||
|
|
||||||
|
property real backgroundTransparency: SettingsData.dankBarTransparency
|
||||||
|
property bool autoHide: SettingsData.dankBarAutoHide
|
||||||
|
property bool revealSticky: false
|
||||||
|
|
||||||
|
property bool notepadInstanceVisible: notepadInstance?.isVisible ?? false
|
||||||
|
|
||||||
|
readonly property bool hasActivePopout: {
|
||||||
|
const loaders = [{
|
||||||
|
"loader": appDrawerLoader,
|
||||||
|
"prop": "shouldBeVisible"
|
||||||
|
}, {
|
||||||
|
"loader": dankDashPopoutLoader,
|
||||||
|
"prop": "shouldBeVisible"
|
||||||
|
}, {
|
||||||
|
"loader": processListPopoutLoader,
|
||||||
|
"prop": "shouldBeVisible"
|
||||||
|
}, {
|
||||||
|
"loader": notificationCenterLoader,
|
||||||
|
"prop": "shouldBeVisible"
|
||||||
|
}, {
|
||||||
|
"loader": batteryPopoutLoader,
|
||||||
|
"prop": "shouldBeVisible"
|
||||||
|
}, {
|
||||||
|
"loader": vpnPopoutLoader,
|
||||||
|
"prop": "shouldBeVisible"
|
||||||
|
}, {
|
||||||
|
"loader": controlCenterLoader,
|
||||||
|
"prop": "shouldBeVisible"
|
||||||
|
}, {
|
||||||
|
"loader": clipboardHistoryModalPopup,
|
||||||
|
"prop": "visible"
|
||||||
|
}, {
|
||||||
|
"loader": systemUpdateLoader,
|
||||||
|
"prop": "shouldBeVisible"
|
||||||
|
}]
|
||||||
|
return notepadInstanceVisible || loaders.some(item => {
|
||||||
|
if (item.loader) {
|
||||||
|
return item.loader?.item?.[item.prop]
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
property bool reveal: {
|
||||||
|
if (CompositorService.isNiri && NiriService.inOverview) {
|
||||||
|
return SettingsData.dankBarOpenOnOverview
|
||||||
|
}
|
||||||
|
return SettingsData.dankBarVisible && (!autoHide || topBarMouseArea.containsMouse || hasActivePopout || revealSticky)
|
||||||
|
}
|
||||||
|
|
||||||
|
onHasActivePopoutChanged: {
|
||||||
|
if (!hasActivePopout && autoHide && !topBarMouseArea.containsMouse) {
|
||||||
|
revealSticky = true
|
||||||
|
revealHold.restart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: revealHold
|
||||||
|
interval: 250
|
||||||
|
repeat: false
|
||||||
|
onTriggered: core.revealSticky = false
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
function onDankBarTransparencyChanged() {
|
||||||
|
core.backgroundTransparency = SettingsData.dankBarTransparency
|
||||||
|
}
|
||||||
|
|
||||||
|
target: SettingsData
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: topBarMouseArea
|
||||||
|
function onContainsMouseChanged() {
|
||||||
|
if (topBarMouseArea.containsMouse) {
|
||||||
|
core.revealSticky = true
|
||||||
|
revealHold.stop()
|
||||||
|
} else {
|
||||||
|
if (core.autoHide && !core.hasActivePopout) {
|
||||||
|
revealHold.restart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: topBarMouseArea
|
||||||
|
y: !barWindow.isVertical ? (SettingsData.dankBarPosition === SettingsData.Position.Bottom ? parent.height - height : 0) : 0
|
||||||
|
x: barWindow.isVertical ? (SettingsData.dankBarPosition === SettingsData.Position.Right ? parent.width - width : 0) : 0
|
||||||
|
height: !barWindow.isVertical ? barWindow.px(barWindow.effectiveBarThickness + SettingsData.dankBarSpacing) : undefined
|
||||||
|
width: barWindow.isVertical ? barWindow.px(barWindow.effectiveBarThickness + SettingsData.dankBarSpacing) : undefined
|
||||||
|
anchors {
|
||||||
|
left: !barWindow.isVertical ? parent.left : (SettingsData.dankBarPosition === SettingsData.Position.Left ? parent.left : undefined)
|
||||||
|
right: !barWindow.isVertical ? parent.right : (SettingsData.dankBarPosition === SettingsData.Position.Right ? parent.right : undefined)
|
||||||
|
top: barWindow.isVertical ? parent.top : undefined
|
||||||
|
bottom: barWindow.isVertical ? parent.bottom : undefined
|
||||||
|
}
|
||||||
|
hoverEnabled: SettingsData.dankBarAutoHide && !core.reveal
|
||||||
|
acceptedButtons: Qt.NoButton
|
||||||
|
enabled: SettingsData.dankBarAutoHide && !core.reveal
|
||||||
|
}
|
||||||
|
}
|
||||||
57
Modules/DankBar/AxisContext.qml
Normal file
57
Modules/DankBar/AxisContext.qml
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import QtQuick
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string edge: "top"
|
||||||
|
|
||||||
|
readonly property string orientation: isVertical ? "vertical" : "horizontal"
|
||||||
|
readonly property bool isVertical: edge === "left" || edge === "right"
|
||||||
|
readonly property bool isHorizontal: !isVertical
|
||||||
|
|
||||||
|
function primarySize(item) {
|
||||||
|
return isVertical ? item.height : item.width
|
||||||
|
}
|
||||||
|
|
||||||
|
function crossSize(item) {
|
||||||
|
return isVertical ? item.width : item.height
|
||||||
|
}
|
||||||
|
|
||||||
|
function setPrimaryPos(item, value) {
|
||||||
|
if (isVertical) {
|
||||||
|
item.y = value
|
||||||
|
} else {
|
||||||
|
item.x = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPrimaryPos(item) {
|
||||||
|
return isVertical ? item.y : item.x
|
||||||
|
}
|
||||||
|
|
||||||
|
function primaryAnchor(anchors) {
|
||||||
|
return isVertical ? anchors.verticalCenter : anchors.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
function crossAnchor(anchors) {
|
||||||
|
return isVertical ? anchors.horizontalCenter : anchors.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
function outerVisualEdge() {
|
||||||
|
if (edge === "bottom") return "bottom"
|
||||||
|
if (edge === "left") return "right"
|
||||||
|
if (edge === "right") return "left"
|
||||||
|
if (edge === "top") return "top"
|
||||||
|
return "bottom"
|
||||||
|
}
|
||||||
|
|
||||||
|
signal axisEdgeChanged()
|
||||||
|
signal axisOrientationChanged()
|
||||||
|
signal changed() // Single coalesced signal
|
||||||
|
|
||||||
|
onEdgeChanged: {
|
||||||
|
axisEdgeChanged()
|
||||||
|
axisOrientationChanged()
|
||||||
|
changed()
|
||||||
|
}
|
||||||
|
}
|
||||||
372
Modules/DankBar/BarCanvas.qml
Normal file
372
Modules/DankBar/BarCanvas.qml
Normal file
@@ -0,0 +1,372 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property var barWindow
|
||||||
|
required property var axis
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.leftMargin: -(SettingsData.dankBarGothCornersEnabled && axis.isVertical && axis.edge === "right" ? barWindow._wingR : 0)
|
||||||
|
anchors.rightMargin: -(SettingsData.dankBarGothCornersEnabled && axis.isVertical && axis.edge === "left" ? barWindow._wingR : 0)
|
||||||
|
anchors.topMargin: -(SettingsData.dankBarGothCornersEnabled && !axis.isVertical && axis.edge === "bottom" ? barWindow._wingR : 0)
|
||||||
|
anchors.bottomMargin: -(SettingsData.dankBarGothCornersEnabled && !axis.isVertical && axis.edge === "top" ? barWindow._wingR : 0)
|
||||||
|
|
||||||
|
readonly property real dpr: {
|
||||||
|
if (CompositorService.isNiri && barWindow.screen) {
|
||||||
|
const niriScale = NiriService.displayScales[barWindow.screen.name]
|
||||||
|
if (niriScale !== undefined) return niriScale
|
||||||
|
}
|
||||||
|
if (CompositorService.isHyprland && barWindow.screen) {
|
||||||
|
const hyprlandMonitor = Hyprland.monitors.values.find(m => m.name === barWindow.screen.name)
|
||||||
|
if (hyprlandMonitor?.scale !== undefined) return hyprlandMonitor.scale
|
||||||
|
}
|
||||||
|
return barWindow.screen?.devicePixelRatio || 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestRepaint() {
|
||||||
|
debounceTimer.restart()
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: debounceTimer
|
||||||
|
interval: 50
|
||||||
|
repeat: false
|
||||||
|
onTriggered: {
|
||||||
|
barShape.requestPaint()
|
||||||
|
barTint.requestPaint()
|
||||||
|
barBorder.requestPaint()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Canvas {
|
||||||
|
id: barShape
|
||||||
|
anchors.fill: parent
|
||||||
|
antialiasing: true
|
||||||
|
renderTarget: Canvas.FramebufferObject
|
||||||
|
renderStrategy: Canvas.Cooperative
|
||||||
|
|
||||||
|
readonly property real correctWidth: Theme.px(root.width, dpr)
|
||||||
|
readonly property real correctHeight: Theme.px(root.height, dpr)
|
||||||
|
canvasSize: Qt.size(correctWidth, correctHeight)
|
||||||
|
|
||||||
|
property real wing: SettingsData.dankBarGothCornersEnabled ? Theme.px(barWindow._wingR, dpr) : 0
|
||||||
|
property real rt: SettingsData.dankBarSquareCorners ? 0 : Theme.px(Theme.cornerRadius, dpr)
|
||||||
|
|
||||||
|
onWingChanged: root.requestRepaint()
|
||||||
|
onRtChanged: root.requestRepaint()
|
||||||
|
onCorrectWidthChanged: root.requestRepaint()
|
||||||
|
onCorrectHeightChanged: root.requestRepaint()
|
||||||
|
onVisibleChanged: if (visible) root.requestRepaint()
|
||||||
|
Component.onCompleted: root.requestRepaint()
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: root
|
||||||
|
function onDprChanged() { root.requestRepaint() }
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: barWindow
|
||||||
|
function on_BgColorChanged() { root.requestRepaint() }
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Theme
|
||||||
|
function onIsLightModeChanged() { root.requestRepaint() }
|
||||||
|
function onSurfaceContainerChanged() { root.requestRepaint() }
|
||||||
|
}
|
||||||
|
|
||||||
|
onPaint: {
|
||||||
|
const ctx = getContext("2d")
|
||||||
|
const W = barWindow.isVertical ? correctHeight : correctWidth
|
||||||
|
const H_raw = barWindow.isVertical ? correctWidth : correctHeight
|
||||||
|
const R = wing
|
||||||
|
const RT = rt
|
||||||
|
const H = H_raw - (R > 0 ? R : 0)
|
||||||
|
const isTop = SettingsData.dankBarPosition === SettingsData.Position.Top
|
||||||
|
const isBottom = SettingsData.dankBarPosition === SettingsData.Position.Bottom
|
||||||
|
const isLeft = SettingsData.dankBarPosition === SettingsData.Position.Left
|
||||||
|
const isRight = SettingsData.dankBarPosition === SettingsData.Position.Right
|
||||||
|
|
||||||
|
function drawTopPath() {
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.moveTo(RT, 0)
|
||||||
|
ctx.lineTo(W - RT, 0)
|
||||||
|
ctx.arcTo(W, 0, W, RT, RT)
|
||||||
|
ctx.lineTo(W, H)
|
||||||
|
|
||||||
|
if (R > 0) {
|
||||||
|
ctx.lineTo(W, H + R)
|
||||||
|
ctx.arc(W - R, H + R, R, 0, -Math.PI / 2, true)
|
||||||
|
ctx.lineTo(R, H)
|
||||||
|
ctx.arc(R, H + R, R, -Math.PI / 2, -Math.PI, true)
|
||||||
|
ctx.lineTo(0, H + R)
|
||||||
|
} else {
|
||||||
|
ctx.lineTo(W, H - RT)
|
||||||
|
ctx.arcTo(W, H, W - RT, H, RT)
|
||||||
|
ctx.lineTo(RT, H)
|
||||||
|
ctx.arcTo(0, H, 0, H - RT, RT)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.lineTo(0, RT)
|
||||||
|
ctx.arcTo(0, 0, RT, 0, RT)
|
||||||
|
ctx.closePath()
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.reset()
|
||||||
|
ctx.clearRect(0, 0, W, H_raw)
|
||||||
|
|
||||||
|
ctx.save()
|
||||||
|
if (isBottom) {
|
||||||
|
ctx.translate(W, H_raw)
|
||||||
|
ctx.rotate(Math.PI)
|
||||||
|
} else if (isLeft) {
|
||||||
|
ctx.translate(0, W)
|
||||||
|
ctx.rotate(-Math.PI / 2)
|
||||||
|
} else if (isRight) {
|
||||||
|
ctx.translate(H_raw, 0)
|
||||||
|
ctx.rotate(Math.PI / 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
drawTopPath()
|
||||||
|
ctx.restore()
|
||||||
|
|
||||||
|
ctx.fillStyle = barWindow._bgColor
|
||||||
|
ctx.fill()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Canvas {
|
||||||
|
id: barTint
|
||||||
|
anchors.fill: parent
|
||||||
|
antialiasing: true
|
||||||
|
renderTarget: Canvas.FramebufferObject
|
||||||
|
renderStrategy: Canvas.Cooperative
|
||||||
|
|
||||||
|
readonly property real correctWidth: Theme.px(root.width, dpr)
|
||||||
|
readonly property real correctHeight: Theme.px(root.height, dpr)
|
||||||
|
canvasSize: Qt.size(correctWidth, correctHeight)
|
||||||
|
|
||||||
|
property real wing: SettingsData.dankBarGothCornersEnabled ? Theme.px(barWindow._wingR, dpr) : 0
|
||||||
|
property real rt: SettingsData.dankBarSquareCorners ? 0 : Theme.px(Theme.cornerRadius, dpr)
|
||||||
|
property real alphaTint: (barWindow._bgColor?.a ?? 1) < 0.99 ? (Theme.stateLayerOpacity ?? 0) : 0
|
||||||
|
|
||||||
|
onWingChanged: root.requestRepaint()
|
||||||
|
onRtChanged: root.requestRepaint()
|
||||||
|
onAlphaTintChanged: root.requestRepaint()
|
||||||
|
onCorrectWidthChanged: root.requestRepaint()
|
||||||
|
onCorrectHeightChanged: root.requestRepaint()
|
||||||
|
onVisibleChanged: if (visible) root.requestRepaint()
|
||||||
|
Component.onCompleted: root.requestRepaint()
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: root
|
||||||
|
function onDprChanged() { root.requestRepaint() }
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: barWindow
|
||||||
|
function on_BgColorChanged() { root.requestRepaint() }
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Theme
|
||||||
|
function onIsLightModeChanged() { root.requestRepaint() }
|
||||||
|
function onSurfaceChanged() { root.requestRepaint() }
|
||||||
|
}
|
||||||
|
|
||||||
|
onPaint: {
|
||||||
|
const ctx = getContext("2d")
|
||||||
|
const W = barWindow.isVertical ? correctHeight : correctWidth
|
||||||
|
const H_raw = barWindow.isVertical ? correctWidth : correctHeight
|
||||||
|
const R = wing
|
||||||
|
const RT = rt
|
||||||
|
const H = H_raw - (R > 0 ? R : 0)
|
||||||
|
const isTop = SettingsData.dankBarPosition === SettingsData.Position.Top
|
||||||
|
const isBottom = SettingsData.dankBarPosition === SettingsData.Position.Bottom
|
||||||
|
const isLeft = SettingsData.dankBarPosition === SettingsData.Position.Left
|
||||||
|
const isRight = SettingsData.dankBarPosition === SettingsData.Position.Right
|
||||||
|
|
||||||
|
function drawTopPath() {
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.moveTo(RT, 0)
|
||||||
|
ctx.lineTo(W - RT, 0)
|
||||||
|
ctx.arcTo(W, 0, W, RT, RT)
|
||||||
|
ctx.lineTo(W, H)
|
||||||
|
|
||||||
|
if (R > 0) {
|
||||||
|
ctx.lineTo(W, H + R)
|
||||||
|
ctx.arc(W - R, H + R, R, 0, -Math.PI / 2, true)
|
||||||
|
ctx.lineTo(R, H)
|
||||||
|
ctx.arc(R, H + R, R, -Math.PI / 2, -Math.PI, true)
|
||||||
|
ctx.lineTo(0, H + R)
|
||||||
|
} else {
|
||||||
|
ctx.lineTo(W, H - RT)
|
||||||
|
ctx.arcTo(W, H, W - RT, H, RT)
|
||||||
|
ctx.lineTo(RT, H)
|
||||||
|
ctx.arcTo(0, H, 0, H - RT, RT)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.lineTo(0, RT)
|
||||||
|
ctx.arcTo(0, 0, RT, 0, RT)
|
||||||
|
ctx.closePath()
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.reset()
|
||||||
|
ctx.clearRect(0, 0, W, H_raw)
|
||||||
|
|
||||||
|
ctx.save()
|
||||||
|
if (isBottom) {
|
||||||
|
ctx.translate(W, H_raw)
|
||||||
|
ctx.rotate(Math.PI)
|
||||||
|
} else if (isLeft) {
|
||||||
|
ctx.translate(0, W)
|
||||||
|
ctx.rotate(-Math.PI / 2)
|
||||||
|
} else if (isRight) {
|
||||||
|
ctx.translate(H_raw, 0)
|
||||||
|
ctx.rotate(Math.PI / 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
drawTopPath()
|
||||||
|
ctx.restore()
|
||||||
|
|
||||||
|
ctx.fillStyle = Qt.rgba(Theme.surface.r, Theme.surface.g, Theme.surface.b, alphaTint)
|
||||||
|
ctx.fill()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Canvas {
|
||||||
|
id: barBorder
|
||||||
|
anchors.fill: parent
|
||||||
|
antialiasing: false
|
||||||
|
visible: SettingsData.dankBarBorderEnabled
|
||||||
|
renderTarget: Canvas.FramebufferObject
|
||||||
|
renderStrategy: Canvas.Cooperative
|
||||||
|
|
||||||
|
readonly property real correctWidth: Theme.px(root.width, dpr)
|
||||||
|
readonly property real correctHeight: Theme.px(root.height, dpr)
|
||||||
|
canvasSize: Qt.size(correctWidth, correctHeight)
|
||||||
|
|
||||||
|
property real wing: SettingsData.dankBarGothCornersEnabled ? Theme.px(barWindow._wingR, dpr) : 0
|
||||||
|
property real rt: SettingsData.dankBarSquareCorners ? 0 : Theme.px(Theme.cornerRadius, dpr)
|
||||||
|
property bool borderEnabled: SettingsData.dankBarBorderEnabled
|
||||||
|
|
||||||
|
onWingChanged: root.requestRepaint()
|
||||||
|
onRtChanged: root.requestRepaint()
|
||||||
|
onBorderEnabledChanged: root.requestRepaint()
|
||||||
|
onCorrectWidthChanged: root.requestRepaint()
|
||||||
|
onCorrectHeightChanged: root.requestRepaint()
|
||||||
|
onVisibleChanged: if (visible) root.requestRepaint()
|
||||||
|
Component.onCompleted: root.requestRepaint()
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: root
|
||||||
|
function onDprChanged() { root.requestRepaint() }
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Theme
|
||||||
|
function onIsLightModeChanged() { root.requestRepaint() }
|
||||||
|
function onSurfaceTextChanged() { root.requestRepaint() }
|
||||||
|
function onPrimaryChanged() { root.requestRepaint() }
|
||||||
|
function onSecondaryChanged() { root.requestRepaint() }
|
||||||
|
function onOutlineChanged() { root.requestRepaint() }
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: SettingsData
|
||||||
|
function onDankBarBorderColorChanged() { root.requestRepaint() }
|
||||||
|
function onDankBarBorderOpacityChanged() { root.requestRepaint() }
|
||||||
|
function onDankBarBorderThicknessChanged() { root.requestRepaint() }
|
||||||
|
function onDankBarSpacingChanged() { root.requestRepaint() }
|
||||||
|
function onDankBarSquareCornersChanged() { root.requestRepaint() }
|
||||||
|
function onDankBarTransparencyChanged() { root.requestRepaint() }
|
||||||
|
}
|
||||||
|
|
||||||
|
onPaint: {
|
||||||
|
if (!borderEnabled) return
|
||||||
|
|
||||||
|
const ctx = getContext("2d")
|
||||||
|
const W = barWindow.isVertical ? correctHeight : correctWidth
|
||||||
|
const H_raw = barWindow.isVertical ? correctWidth : correctHeight
|
||||||
|
const R = wing
|
||||||
|
const RT = rt
|
||||||
|
const H = H_raw - (R > 0 ? R : 0)
|
||||||
|
const isTop = SettingsData.dankBarPosition === SettingsData.Position.Top
|
||||||
|
const isBottom = SettingsData.dankBarPosition === SettingsData.Position.Bottom
|
||||||
|
const isLeft = SettingsData.dankBarPosition === SettingsData.Position.Left
|
||||||
|
const isRight = SettingsData.dankBarPosition === SettingsData.Position.Right
|
||||||
|
|
||||||
|
const spacing = SettingsData.dankBarSpacing
|
||||||
|
const hasEdgeGap = spacing > 0 || RT > 0
|
||||||
|
|
||||||
|
function drawTopBorder() {
|
||||||
|
ctx.beginPath()
|
||||||
|
|
||||||
|
if (!hasEdgeGap) {
|
||||||
|
ctx.moveTo(0, H)
|
||||||
|
ctx.lineTo(W, H)
|
||||||
|
} else {
|
||||||
|
ctx.moveTo(RT, 0)
|
||||||
|
ctx.lineTo(W - RT, 0)
|
||||||
|
ctx.arcTo(W, 0, W, RT, RT)
|
||||||
|
ctx.lineTo(W, H)
|
||||||
|
|
||||||
|
if (R > 0) {
|
||||||
|
ctx.lineTo(W, H + R)
|
||||||
|
ctx.arc(W - R, H + R, R, 0, -Math.PI / 2, true)
|
||||||
|
ctx.lineTo(R, H)
|
||||||
|
ctx.arc(R, H + R, R, -Math.PI / 2, -Math.PI, true)
|
||||||
|
ctx.lineTo(0, H + R)
|
||||||
|
} else {
|
||||||
|
ctx.lineTo(W, H - RT)
|
||||||
|
ctx.arcTo(W, H, W - RT, H, RT)
|
||||||
|
ctx.lineTo(RT, H)
|
||||||
|
ctx.arcTo(0, H, 0, H - RT, RT)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.lineTo(0, RT)
|
||||||
|
ctx.arcTo(0, 0, RT, 0, RT)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.closePath()
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.reset()
|
||||||
|
ctx.clearRect(0, 0, W, H_raw)
|
||||||
|
|
||||||
|
ctx.save()
|
||||||
|
if (isBottom) {
|
||||||
|
ctx.translate(W, H_raw)
|
||||||
|
ctx.rotate(Math.PI)
|
||||||
|
} else if (isLeft) {
|
||||||
|
ctx.translate(0, W)
|
||||||
|
ctx.rotate(-Math.PI / 2)
|
||||||
|
} else if (isRight) {
|
||||||
|
ctx.translate(H_raw, 0)
|
||||||
|
ctx.rotate(Math.PI / 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
drawTopBorder()
|
||||||
|
ctx.restore()
|
||||||
|
|
||||||
|
const key = SettingsData.dankBarBorderColor || "surfaceText"
|
||||||
|
const base = (key === "surfaceText") ? Theme.surfaceText
|
||||||
|
: (key === "primary") ? Theme.primary
|
||||||
|
: Theme.secondary
|
||||||
|
const color = Theme.withAlpha(base, SettingsData.dankBarBorderOpacity ?? 1.0)
|
||||||
|
const thickness = Math.max(1, SettingsData.dankBarBorderThickness ?? 1)
|
||||||
|
|
||||||
|
ctx.globalCompositeOperation = "source-over"
|
||||||
|
ctx.lineWidth = thickness
|
||||||
|
ctx.strokeStyle = color
|
||||||
|
ctx.stroke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
460
Modules/DankBar/CenterSection.qml
Normal file
460
Modules/DankBar/CenterSection.qml
Normal file
@@ -0,0 +1,460 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var widgetsModel: null
|
||||||
|
property var components: null
|
||||||
|
property bool noBackground: false
|
||||||
|
required property var axis
|
||||||
|
property string section: "center"
|
||||||
|
property var parentScreen: null
|
||||||
|
property real widgetThickness: 30
|
||||||
|
property real barThickness: 48
|
||||||
|
|
||||||
|
readonly property bool isVertical: axis?.isVertical ?? false
|
||||||
|
readonly property real spacing: noBackground ? 2 : Theme.spacingXS
|
||||||
|
|
||||||
|
property var centerWidgets: []
|
||||||
|
property int totalWidgets: 0
|
||||||
|
property real totalSize: 0
|
||||||
|
|
||||||
|
function updateLayout() {
|
||||||
|
const containerSize = isVertical ? height : width
|
||||||
|
if (containerSize <= 0 || !visible) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
centerWidgets = []
|
||||||
|
totalWidgets = 0
|
||||||
|
totalSize = 0
|
||||||
|
|
||||||
|
let configuredWidgets = 0
|
||||||
|
for (var i = 0; i < centerRepeater.count; i++) {
|
||||||
|
const item = centerRepeater.itemAt(i)
|
||||||
|
if (item && getWidgetVisible(item.widgetId)) {
|
||||||
|
configuredWidgets++
|
||||||
|
if (item.active && item.item) {
|
||||||
|
centerWidgets.push(item.item)
|
||||||
|
totalWidgets++
|
||||||
|
totalSize += isVertical ? item.item.height : item.item.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalWidgets > 1) {
|
||||||
|
totalSize += spacing * (totalWidgets - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
positionWidgets(configuredWidgets)
|
||||||
|
}
|
||||||
|
|
||||||
|
function positionWidgets(configuredWidgets) {
|
||||||
|
if (totalWidgets === 0 || (isVertical ? height : width) <= 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const parentCenter = (isVertical ? height : width) / 2
|
||||||
|
const isOdd = configuredWidgets % 2 === 1
|
||||||
|
|
||||||
|
centerWidgets.forEach(widget => {
|
||||||
|
if (isVertical) {
|
||||||
|
widget.anchors.verticalCenter = undefined
|
||||||
|
} else {
|
||||||
|
widget.anchors.horizontalCenter = undefined
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (isOdd) {
|
||||||
|
const middleIndex = Math.floor(configuredWidgets / 2)
|
||||||
|
let currentActiveIndex = 0
|
||||||
|
let middleWidget = null
|
||||||
|
|
||||||
|
for (var i = 0; i < centerRepeater.count; i++) {
|
||||||
|
const item = centerRepeater.itemAt(i)
|
||||||
|
if (item && getWidgetVisible(item.widgetId)) {
|
||||||
|
if (currentActiveIndex === middleIndex && item.active && item.item) {
|
||||||
|
middleWidget = item.item
|
||||||
|
break
|
||||||
|
}
|
||||||
|
currentActiveIndex++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (middleWidget) {
|
||||||
|
const middleSize = isVertical ? middleWidget.height : middleWidget.width
|
||||||
|
if (isVertical) {
|
||||||
|
middleWidget.y = parentCenter - (middleSize / 2)
|
||||||
|
} else {
|
||||||
|
middleWidget.x = parentCenter - (middleSize / 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
let leftWidgets = []
|
||||||
|
let rightWidgets = []
|
||||||
|
let foundMiddle = false
|
||||||
|
|
||||||
|
for (var i = 0; i < centerWidgets.length; i++) {
|
||||||
|
if (centerWidgets[i] === middleWidget) {
|
||||||
|
foundMiddle = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (!foundMiddle) {
|
||||||
|
leftWidgets.push(centerWidgets[i])
|
||||||
|
} else {
|
||||||
|
rightWidgets.push(centerWidgets[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentPos = isVertical ? middleWidget.y : middleWidget.x
|
||||||
|
for (var i = leftWidgets.length - 1; i >= 0; i--) {
|
||||||
|
const size = isVertical ? leftWidgets[i].height : leftWidgets[i].width
|
||||||
|
currentPos -= (spacing + size)
|
||||||
|
if (isVertical) {
|
||||||
|
leftWidgets[i].y = currentPos
|
||||||
|
} else {
|
||||||
|
leftWidgets[i].x = currentPos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPos = (isVertical ? middleWidget.y : middleWidget.x) + middleSize
|
||||||
|
for (var i = 0; i < rightWidgets.length; i++) {
|
||||||
|
currentPos += spacing
|
||||||
|
if (isVertical) {
|
||||||
|
rightWidgets[i].y = currentPos
|
||||||
|
} else {
|
||||||
|
rightWidgets[i].x = currentPos
|
||||||
|
}
|
||||||
|
currentPos += isVertical ? rightWidgets[i].height : rightWidgets[i].width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let configuredLeftIndex = (configuredWidgets / 2) - 1
|
||||||
|
let configuredRightIndex = configuredWidgets / 2
|
||||||
|
const halfSpacing = spacing / 2
|
||||||
|
|
||||||
|
let leftWidget = null
|
||||||
|
let rightWidget = null
|
||||||
|
let leftWidgets = []
|
||||||
|
let rightWidgets = []
|
||||||
|
|
||||||
|
let currentConfigIndex = 0
|
||||||
|
for (var i = 0; i < centerRepeater.count; i++) {
|
||||||
|
const item = centerRepeater.itemAt(i)
|
||||||
|
if (item && getWidgetVisible(item.widgetId)) {
|
||||||
|
if (item.active && item.item) {
|
||||||
|
if (currentConfigIndex < configuredLeftIndex) {
|
||||||
|
leftWidgets.push(item.item)
|
||||||
|
} else if (currentConfigIndex === configuredLeftIndex) {
|
||||||
|
leftWidget = item.item
|
||||||
|
} else if (currentConfigIndex === configuredRightIndex) {
|
||||||
|
rightWidget = item.item
|
||||||
|
} else {
|
||||||
|
rightWidgets.push(item.item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentConfigIndex++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leftWidget && rightWidget) {
|
||||||
|
const leftSize = isVertical ? leftWidget.height : leftWidget.width
|
||||||
|
if (isVertical) {
|
||||||
|
leftWidget.y = parentCenter - halfSpacing - leftSize
|
||||||
|
rightWidget.y = parentCenter + halfSpacing
|
||||||
|
} else {
|
||||||
|
leftWidget.x = parentCenter - halfSpacing - leftSize
|
||||||
|
rightWidget.x = parentCenter + halfSpacing
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentPos = isVertical ? leftWidget.y : leftWidget.x
|
||||||
|
for (var i = leftWidgets.length - 1; i >= 0; i--) {
|
||||||
|
const size = isVertical ? leftWidgets[i].height : leftWidgets[i].width
|
||||||
|
currentPos -= (spacing + size)
|
||||||
|
if (isVertical) {
|
||||||
|
leftWidgets[i].y = currentPos
|
||||||
|
} else {
|
||||||
|
leftWidgets[i].x = currentPos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPos = (isVertical ? rightWidget.y + rightWidget.height : rightWidget.x + rightWidget.width)
|
||||||
|
for (var i = 0; i < rightWidgets.length; i++) {
|
||||||
|
currentPos += spacing
|
||||||
|
if (isVertical) {
|
||||||
|
rightWidgets[i].y = currentPos
|
||||||
|
} else {
|
||||||
|
rightWidgets[i].x = currentPos
|
||||||
|
}
|
||||||
|
currentPos += isVertical ? rightWidgets[i].height : rightWidgets[i].width
|
||||||
|
}
|
||||||
|
} else if (leftWidget && !rightWidget) {
|
||||||
|
const leftSize = isVertical ? leftWidget.height : leftWidget.width
|
||||||
|
if (isVertical) {
|
||||||
|
leftWidget.y = parentCenter - halfSpacing - leftSize
|
||||||
|
} else {
|
||||||
|
leftWidget.x = parentCenter - halfSpacing - leftSize
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentPos = isVertical ? leftWidget.y : leftWidget.x
|
||||||
|
for (var i = leftWidgets.length - 1; i >= 0; i--) {
|
||||||
|
const size = isVertical ? leftWidgets[i].height : leftWidgets[i].width
|
||||||
|
currentPos -= (spacing + size)
|
||||||
|
if (isVertical) {
|
||||||
|
leftWidgets[i].y = currentPos
|
||||||
|
} else {
|
||||||
|
leftWidgets[i].x = currentPos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPos = (isVertical ? leftWidget.y + leftWidget.height : leftWidget.x + leftWidget.width) + spacing
|
||||||
|
for (var i = 0; i < rightWidgets.length; i++) {
|
||||||
|
currentPos += spacing
|
||||||
|
if (isVertical) {
|
||||||
|
rightWidgets[i].y = currentPos
|
||||||
|
} else {
|
||||||
|
rightWidgets[i].x = currentPos
|
||||||
|
}
|
||||||
|
currentPos += isVertical ? rightWidgets[i].height : rightWidgets[i].width
|
||||||
|
}
|
||||||
|
} else if (!leftWidget && rightWidget) {
|
||||||
|
if (isVertical) {
|
||||||
|
rightWidget.y = parentCenter + halfSpacing
|
||||||
|
} else {
|
||||||
|
rightWidget.x = parentCenter + halfSpacing
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentPos = (isVertical ? rightWidget.y : rightWidget.x) - spacing
|
||||||
|
for (var i = leftWidgets.length - 1; i >= 0; i--) {
|
||||||
|
const size = isVertical ? leftWidgets[i].height : leftWidgets[i].width
|
||||||
|
currentPos -= size
|
||||||
|
if (isVertical) {
|
||||||
|
leftWidgets[i].y = currentPos
|
||||||
|
} else {
|
||||||
|
leftWidgets[i].x = currentPos
|
||||||
|
}
|
||||||
|
currentPos -= spacing
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPos = (isVertical ? rightWidget.y + rightWidget.height : rightWidget.x + rightWidget.width)
|
||||||
|
for (var i = 0; i < rightWidgets.length; i++) {
|
||||||
|
currentPos += spacing
|
||||||
|
if (isVertical) {
|
||||||
|
rightWidgets[i].y = currentPos
|
||||||
|
} else {
|
||||||
|
rightWidgets[i].x = currentPos
|
||||||
|
}
|
||||||
|
currentPos += isVertical ? rightWidgets[i].height : rightWidgets[i].width
|
||||||
|
}
|
||||||
|
} else if (totalWidgets === 1 && centerWidgets[0]) {
|
||||||
|
const size = isVertical ? centerWidgets[0].height : centerWidgets[0].width
|
||||||
|
if (isVertical) {
|
||||||
|
centerWidgets[0].y = parentCenter - (size / 2)
|
||||||
|
} else {
|
||||||
|
centerWidgets[0].x = parentCenter - (size / 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWidgetVisible(widgetId) {
|
||||||
|
const widgetVisibility = {
|
||||||
|
"cpuUsage": DgopService.dgopAvailable,
|
||||||
|
"memUsage": DgopService.dgopAvailable,
|
||||||
|
"cpuTemp": DgopService.dgopAvailable,
|
||||||
|
"gpuTemp": DgopService.dgopAvailable,
|
||||||
|
"network_speed_monitor": DgopService.dgopAvailable
|
||||||
|
}
|
||||||
|
return widgetVisibility[widgetId] ?? true
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWidgetComponent(widgetId) {
|
||||||
|
// Build dynamic component map including plugins
|
||||||
|
let baseMap = {
|
||||||
|
"launcherButton": "launcherButtonComponent",
|
||||||
|
"workspaceSwitcher": "workspaceSwitcherComponent",
|
||||||
|
"focusedWindow": "focusedWindowComponent",
|
||||||
|
"runningApps": "runningAppsComponent",
|
||||||
|
"clock": "clockComponent",
|
||||||
|
"music": "mediaComponent",
|
||||||
|
"weather": "weatherComponent",
|
||||||
|
"systemTray": "systemTrayComponent",
|
||||||
|
"privacyIndicator": "privacyIndicatorComponent",
|
||||||
|
"clipboard": "clipboardComponent",
|
||||||
|
"cpuUsage": "cpuUsageComponent",
|
||||||
|
"memUsage": "memUsageComponent",
|
||||||
|
"diskUsage": "diskUsageComponent",
|
||||||
|
"cpuTemp": "cpuTempComponent",
|
||||||
|
"gpuTemp": "gpuTempComponent",
|
||||||
|
"notificationButton": "notificationButtonComponent",
|
||||||
|
"battery": "batteryComponent",
|
||||||
|
"controlCenterButton": "controlCenterButtonComponent",
|
||||||
|
"idleInhibitor": "idleInhibitorComponent",
|
||||||
|
"spacer": "spacerComponent",
|
||||||
|
"separator": "separatorComponent",
|
||||||
|
"network_speed_monitor": "networkComponent",
|
||||||
|
"keyboard_layout_name": "keyboardLayoutNameComponent",
|
||||||
|
"vpn": "vpnComponent",
|
||||||
|
"notepadButton": "notepadButtonComponent",
|
||||||
|
"colorPicker": "colorPickerComponent",
|
||||||
|
"systemUpdate": "systemUpdateComponent"
|
||||||
|
}
|
||||||
|
|
||||||
|
// For built-in components, get from components property
|
||||||
|
const componentKey = baseMap[widgetId]
|
||||||
|
if (componentKey && root.components[componentKey]) {
|
||||||
|
return root.components[componentKey]
|
||||||
|
}
|
||||||
|
|
||||||
|
// For plugin components, get from PluginService
|
||||||
|
var parts = widgetId.split(":")
|
||||||
|
var pluginId = parts[0]
|
||||||
|
let pluginComponents = PluginService.getWidgetComponents()
|
||||||
|
return pluginComponents[pluginId] || null
|
||||||
|
}
|
||||||
|
|
||||||
|
height: parent.height
|
||||||
|
width: parent.width
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: layoutTimer
|
||||||
|
interval: 0
|
||||||
|
repeat: false
|
||||||
|
onTriggered: root.updateLayout()
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
layoutTimer.restart()
|
||||||
|
}
|
||||||
|
|
||||||
|
onWidthChanged: {
|
||||||
|
if (width > 0) {
|
||||||
|
layoutTimer.restart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onHeightChanged: {
|
||||||
|
if (height > 0) {
|
||||||
|
layoutTimer.restart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onVisibleChanged: {
|
||||||
|
if (visible && (isVertical ? height : width) > 0) {
|
||||||
|
layoutTimer.restart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: centerRepeater
|
||||||
|
model: root.widgetsModel
|
||||||
|
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
property string widgetId: model.widgetId
|
||||||
|
property var widgetData: model
|
||||||
|
property int spacerSize: model.size || 20
|
||||||
|
|
||||||
|
anchors.verticalCenter: !root.isVertical ? parent.verticalCenter : undefined
|
||||||
|
anchors.horizontalCenter: root.isVertical ? parent.horizontalCenter : undefined
|
||||||
|
active: root.getWidgetVisible(model.widgetId) && (model.widgetId !== "music" || MprisController.activePlayer !== null)
|
||||||
|
sourceComponent: root.getWidgetComponent(model.widgetId)
|
||||||
|
opacity: (model.enabled !== false) ? 1 : 0
|
||||||
|
asynchronous: false
|
||||||
|
|
||||||
|
onLoaded: {
|
||||||
|
if (!item) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
item.widthChanged.connect(() => layoutTimer.restart())
|
||||||
|
item.heightChanged.connect(() => layoutTimer.restart())
|
||||||
|
if (model.widgetId === "spacer") {
|
||||||
|
item.spacerSize = Qt.binding(() => model.size || 20)
|
||||||
|
}
|
||||||
|
if (root.axis && "axis" in item) {
|
||||||
|
item.axis = Qt.binding(() => root.axis)
|
||||||
|
}
|
||||||
|
if (root.axis && "isVertical" in item) {
|
||||||
|
try {
|
||||||
|
item.isVertical = Qt.binding(() => root.axis.isVertical)
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inject properties for plugin widgets
|
||||||
|
if ("section" in item) {
|
||||||
|
item.section = root.section
|
||||||
|
}
|
||||||
|
if ("parentScreen" in item) {
|
||||||
|
item.parentScreen = Qt.binding(() => root.parentScreen)
|
||||||
|
}
|
||||||
|
if ("widgetThickness" in item) {
|
||||||
|
item.widgetThickness = Qt.binding(() => root.widgetThickness)
|
||||||
|
}
|
||||||
|
if ("barThickness" in item) {
|
||||||
|
item.barThickness = Qt.binding(() => root.barThickness)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inject PluginService for plugin widgets
|
||||||
|
if (item.pluginService !== undefined) {
|
||||||
|
var parts = model.widgetId.split(":")
|
||||||
|
var pluginId = parts[0]
|
||||||
|
var variantId = parts.length > 1 ? parts[1] : null
|
||||||
|
|
||||||
|
if (item.pluginId !== undefined) {
|
||||||
|
item.pluginId = pluginId
|
||||||
|
}
|
||||||
|
if (item.variantId !== undefined) {
|
||||||
|
item.variantId = variantId
|
||||||
|
}
|
||||||
|
if (item.variantData !== undefined && variantId) {
|
||||||
|
item.variantData = PluginService.getPluginVariantData(pluginId, variantId)
|
||||||
|
}
|
||||||
|
item.pluginService = PluginService
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.popoutService !== undefined) {
|
||||||
|
item.popoutService = PopoutService
|
||||||
|
}
|
||||||
|
|
||||||
|
layoutTimer.restart()
|
||||||
|
}
|
||||||
|
|
||||||
|
onActiveChanged: {
|
||||||
|
layoutTimer.restart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: widgetsModel
|
||||||
|
function onCountChanged() {
|
||||||
|
layoutTimer.restart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for plugin changes and refresh components
|
||||||
|
Connections {
|
||||||
|
target: PluginService
|
||||||
|
function onPluginLoaded(pluginId) {
|
||||||
|
// Force refresh of component lookups
|
||||||
|
for (var i = 0; i < centerRepeater.count; i++) {
|
||||||
|
var item = centerRepeater.itemAt(i)
|
||||||
|
if (item && item.widgetId.startsWith(pluginId)) {
|
||||||
|
item.sourceComponent = root.getWidgetComponent(item.widgetId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function onPluginUnloaded(pluginId) {
|
||||||
|
// Force refresh of component lookups
|
||||||
|
for (var i = 0; i < centerRepeater.count; i++) {
|
||||||
|
var item = centerRepeater.itemAt(i)
|
||||||
|
if (item && item.widgetId.startsWith(pluginId)) {
|
||||||
|
item.sourceComponent = root.getWidgetComponent(item.widgetId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1208
Modules/DankBar/DankBar.qml
Normal file
1208
Modules/DankBar/DankBar.qml
Normal file
File diff suppressed because it is too large
Load Diff
82
Modules/DankBar/LeftSection.qml
Normal file
82
Modules/DankBar/LeftSection.qml
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var widgetsModel: null
|
||||||
|
property var components: null
|
||||||
|
property bool noBackground: false
|
||||||
|
required property var axis
|
||||||
|
property var parentScreen: null
|
||||||
|
property real widgetThickness: 30
|
||||||
|
property real barThickness: 48
|
||||||
|
|
||||||
|
readonly property bool isVertical: axis?.isVertical ?? false
|
||||||
|
|
||||||
|
implicitHeight: layoutLoader.item ? (layoutLoader.item.implicitHeight || layoutLoader.item.height) : 0
|
||||||
|
implicitWidth: layoutLoader.item ? (layoutLoader.item.implicitWidth || layoutLoader.item.width) : 0
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: layoutLoader
|
||||||
|
anchors.fill: parent
|
||||||
|
sourceComponent: root.isVertical ? columnComp : rowComp
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: rowComp
|
||||||
|
Row {
|
||||||
|
spacing: noBackground ? 2 : Theme.spacingXS
|
||||||
|
Repeater {
|
||||||
|
model: root.widgetsModel
|
||||||
|
Item {
|
||||||
|
width: widgetLoader.item ? widgetLoader.item.width : 0
|
||||||
|
height: widgetLoader.item ? widgetLoader.item.height : 0
|
||||||
|
WidgetHost {
|
||||||
|
id: widgetLoader
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
widgetId: model.widgetId
|
||||||
|
widgetData: model
|
||||||
|
spacerSize: model.size || 20
|
||||||
|
components: root.components
|
||||||
|
isInColumn: false
|
||||||
|
axis: root.axis
|
||||||
|
section: "left"
|
||||||
|
parentScreen: root.parentScreen
|
||||||
|
widgetThickness: root.widgetThickness
|
||||||
|
barThickness: root.barThickness
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: columnComp
|
||||||
|
Column {
|
||||||
|
width: Math.max(parent.width, 200)
|
||||||
|
spacing: noBackground ? 2 : Theme.spacingXS
|
||||||
|
Repeater {
|
||||||
|
model: root.widgetsModel
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: widgetLoader.item ? widgetLoader.item.height : 0
|
||||||
|
WidgetHost {
|
||||||
|
id: widgetLoader
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
widgetId: model.widgetId
|
||||||
|
widgetData: model
|
||||||
|
spacerSize: model.size || 20
|
||||||
|
components: root.components
|
||||||
|
isInColumn: true
|
||||||
|
axis: root.axis
|
||||||
|
section: "left"
|
||||||
|
parentScreen: root.parentScreen
|
||||||
|
widgetThickness: root.widgetThickness
|
||||||
|
barThickness: root.barThickness
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,6 @@ import qs.Widgets
|
|||||||
DankPopout {
|
DankPopout {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string triggerSection: "right"
|
|
||||||
property var triggerScreen: null
|
property var triggerScreen: null
|
||||||
|
|
||||||
function setTriggerPosition(x, y, width, section, screen) {
|
function setTriggerPosition(x, y, width, section, screen) {
|
||||||
@@ -45,9 +44,9 @@ DankPopout {
|
|||||||
popupWidth: 400
|
popupWidth: 400
|
||||||
popupHeight: contentLoader.item ? contentLoader.item.implicitHeight : 400
|
popupHeight: contentLoader.item ? contentLoader.item.implicitHeight : 400
|
||||||
triggerX: Screen.width - 380 - Theme.spacingL
|
triggerX: Screen.width - 380 - Theme.spacingL
|
||||||
triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.spacingS
|
triggerY: Theme.barHeight - 4 + SettingsData.dankBarSpacing
|
||||||
triggerWidth: 70
|
triggerWidth: 70
|
||||||
positioning: "center"
|
positioning: ""
|
||||||
screen: triggerScreen
|
screen: triggerScreen
|
||||||
shouldBeVisible: false
|
shouldBeVisible: false
|
||||||
visible: shouldBeVisible
|
visible: shouldBeVisible
|
||||||
@@ -60,7 +59,7 @@ DankPopout {
|
|||||||
color: Theme.popupBackground()
|
color: Theme.popupBackground()
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.color: Theme.outlineMedium
|
border.color: Theme.outlineMedium
|
||||||
border.width: 1
|
border.width: 0
|
||||||
antialiasing: true
|
antialiasing: true
|
||||||
smooth: true
|
smooth: true
|
||||||
focus: true
|
focus: true
|
||||||
@@ -96,7 +95,7 @@ DankPopout {
|
|||||||
color: "transparent"
|
color: "transparent"
|
||||||
radius: parent.radius + 3
|
radius: parent.radius + 3
|
||||||
border.color: Qt.rgba(0, 0, 0, 0.05)
|
border.color: Qt.rgba(0, 0, 0, 0.05)
|
||||||
border.width: 1
|
border.width: 0
|
||||||
z: -3
|
z: -3
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +105,7 @@ DankPopout {
|
|||||||
color: "transparent"
|
color: "transparent"
|
||||||
radius: parent.radius + 2
|
radius: parent.radius + 2
|
||||||
border.color: Theme.shadowMedium
|
border.color: Theme.shadowMedium
|
||||||
border.width: 1
|
border.width: 0
|
||||||
z: -2
|
z: -2
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +113,7 @@ DankPopout {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
border.color: Theme.outlineStrong
|
border.color: Theme.outlineStrong
|
||||||
border.width: 1
|
border.width: 0
|
||||||
radius: parent.radius
|
radius: parent.radius
|
||||||
z: -1
|
z: -1
|
||||||
}
|
}
|
||||||
@@ -274,7 +273,7 @@ DankPopout {
|
|||||||
height: 32
|
height: 32
|
||||||
radius: 16
|
radius: 16
|
||||||
color: closeBatteryArea.containsMouse ? Theme.errorHover : "transparent"
|
color: closeBatteryArea.containsMouse ? Theme.errorHover : "transparent"
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.top: parent.top
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -304,16 +303,15 @@ DankPopout {
|
|||||||
width: (parent.width - Theme.spacingM) / 2
|
width: (parent.width - Theme.spacingM) / 2
|
||||||
height: 64
|
height: 64
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
|
color: Theme.surfaceContainerHigh
|
||||||
border.color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2)
|
border.width: 0
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Health"
|
text: I18n.tr("Health")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -340,16 +338,15 @@ DankPopout {
|
|||||||
width: (parent.width - Theme.spacingM) / 2
|
width: (parent.width - Theme.spacingM) / 2
|
||||||
height: 64
|
height: 64
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
|
color: Theme.surfaceContainerHigh
|
||||||
border.color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2)
|
border.width: 0
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Capacity"
|
text: I18n.tr("Capacity")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -367,6 +364,210 @@ DankPopout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Individual battery details for multiple batteries
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
visible: !BatteryService.usePreferred && BatteryService.batteries.length > 1
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Individual Batteries")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceTextMedium
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: BatteryService.batteries
|
||||||
|
|
||||||
|
delegate: StyledRect {
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: batteryColumn.implicitHeight + Theme.spacingM * 2
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.surfaceContainer
|
||||||
|
border.width: 0
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: batteryColumn
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
// Top row: name and percentage
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Column {
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: parent.width - percentText.width - chargingIcon.width - Theme.spacingM * 2
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.model || `Battery ${index + 1}`
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
elide: Text.ElideRight
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.nativePath
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceTextMedium
|
||||||
|
elide: Text.ElideMiddle
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 1
|
||||||
|
height: parent.height
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: percentText
|
||||||
|
text: `${Math.round(100 * modelData.percentage)}%`
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Bold
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
id: chargingIcon
|
||||||
|
name: modelData.state === UPowerDeviceState.Charging ? "bolt" : ""
|
||||||
|
size: Theme.iconSizeSmall
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: modelData.state === UPowerDeviceState.Charging
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bottom row: Health, Capacity and Time
|
||||||
|
Flow {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
width: (parent.width - Theme.spacingS * 2) / 3
|
||||||
|
height: 48
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.surfaceContainerHigh
|
||||||
|
border.width: 0
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Health")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceTextMedium
|
||||||
|
font.weight: Font.Medium
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
if (!modelData.healthSupported || modelData.healthPercentage <= 0)
|
||||||
|
return "N/A"
|
||||||
|
return `${Math.round(modelData.healthPercentage)}%`
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: {
|
||||||
|
if (!modelData.healthSupported || modelData.healthPercentage <= 0)
|
||||||
|
return Theme.surfaceText
|
||||||
|
return modelData.healthPercentage < 80 ? Theme.error : Theme.surfaceText
|
||||||
|
}
|
||||||
|
font.weight: Font.Bold
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
width: (parent.width - Theme.spacingS * 2) / 3
|
||||||
|
height: 48
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.surfaceContainerHigh
|
||||||
|
border.width: 0
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Capacity")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceTextMedium
|
||||||
|
font.weight: Font.Medium
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.energyCapacity > 0 ? `${modelData.energyCapacity.toFixed(1)}` : "N/A"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Bold
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
width: (parent.width - Theme.spacingS * 2) / 3
|
||||||
|
height: 48
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.surfaceContainerHigh
|
||||||
|
border.width: 0
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.state === UPowerDeviceState.Charging
|
||||||
|
? I18n.tr("To Full")
|
||||||
|
: modelData.state === UPowerDeviceState.Discharging
|
||||||
|
? I18n.tr("Left") : ""
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceTextMedium
|
||||||
|
font.weight: Font.Medium
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
const time = modelData.state === UPowerDeviceState.Charging
|
||||||
|
? modelData.timeToFull
|
||||||
|
: modelData.state === UPowerDeviceState.Discharging && BatteryService.changeRate > 0
|
||||||
|
? (3600 * modelData.energy) / BatteryService.changeRate : 0
|
||||||
|
|
||||||
|
if (!time || time <= 0 || time > 86400)
|
||||||
|
return "N/A"
|
||||||
|
|
||||||
|
const hours = Math.floor(time / 3600)
|
||||||
|
const minutes = Math.floor((time % 3600) / 60)
|
||||||
|
return hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Bold
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DankButtonGroup {
|
DankButtonGroup {
|
||||||
property var profileModel: (typeof PowerProfiles !== "undefined") ? [PowerProfile.PowerSaver, PowerProfile.Balanced].concat(PowerProfiles.hasPerformanceProfile ? [PowerProfile.Performance] : []) : [PowerProfile.PowerSaver, PowerProfile.Balanced, PowerProfile.Performance]
|
property var profileModel: (typeof PowerProfiles !== "undefined") ? [PowerProfile.PowerSaver, PowerProfile.Balanced].concat(PowerProfiles.hasPerformanceProfile ? [PowerProfile.Performance] : []) : [PowerProfile.PowerSaver, PowerProfile.Balanced, PowerProfile.Performance]
|
||||||
property int currentProfileIndex: {
|
property int currentProfileIndex: {
|
||||||
@@ -390,7 +591,7 @@ DankPopout {
|
|||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
|
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
|
||||||
border.color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.3)
|
border.color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.3)
|
||||||
border.width: 1
|
border.width: 0
|
||||||
visible: (typeof PowerProfiles !== "undefined") && PowerProfiles.degradationReason !== PerformanceDegradationReason.None
|
visible: (typeof PowerProfiles !== "undefined") && PowerProfiles.degradationReason !== PerformanceDegradationReason.None
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -418,7 +619,7 @@ DankPopout {
|
|||||||
width: parent.width - Theme.iconSize - Theme.spacingM
|
width: parent.width - Theme.iconSize - Theme.spacingM
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Power Profile Degradation"
|
text: I18n.tr("Power Profile Degradation")
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
color: Theme.error
|
color: Theme.error
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -13,7 +13,10 @@ import qs.Widgets
|
|||||||
DankPopout {
|
DankPopout {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string triggerSection: "right"
|
Ref {
|
||||||
|
service: VpnService
|
||||||
|
}
|
||||||
|
|
||||||
property var triggerScreen: null
|
property var triggerScreen: null
|
||||||
|
|
||||||
function setTriggerPosition(x, y, width, section, screen) {
|
function setTriggerPosition(x, y, width, section, screen) {
|
||||||
@@ -27,9 +30,9 @@ DankPopout {
|
|||||||
popupWidth: 360
|
popupWidth: 360
|
||||||
popupHeight: Math.min(Screen.height - 100, contentLoader.item ? contentLoader.item.implicitHeight : 260)
|
popupHeight: Math.min(Screen.height - 100, contentLoader.item ? contentLoader.item.implicitHeight : 260)
|
||||||
triggerX: Screen.width - 380 - Theme.spacingL
|
triggerX: Screen.width - 380 - Theme.spacingL
|
||||||
triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.spacingS
|
triggerY: Theme.barHeight - 4 + SettingsData.dankBarSpacing
|
||||||
triggerWidth: 70
|
triggerWidth: 70
|
||||||
positioning: "center"
|
positioning: ""
|
||||||
screen: triggerScreen
|
screen: triggerScreen
|
||||||
shouldBeVisible: false
|
shouldBeVisible: false
|
||||||
visible: shouldBeVisible
|
visible: shouldBeVisible
|
||||||
@@ -42,7 +45,7 @@ DankPopout {
|
|||||||
color: Theme.popupBackground()
|
color: Theme.popupBackground()
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.color: Theme.outlineMedium
|
border.color: Theme.outlineMedium
|
||||||
border.width: 1
|
border.width: 0
|
||||||
antialiasing: true
|
antialiasing: true
|
||||||
smooth: true
|
smooth: true
|
||||||
focus: true
|
focus: true
|
||||||
@@ -60,7 +63,7 @@ DankPopout {
|
|||||||
color: "transparent"
|
color: "transparent"
|
||||||
radius: parent.radius + 3
|
radius: parent.radius + 3
|
||||||
border.color: Qt.rgba(0, 0, 0, 0.05)
|
border.color: Qt.rgba(0, 0, 0, 0.05)
|
||||||
border.width: 1
|
border.width: 0
|
||||||
z: -3
|
z: -3
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +73,7 @@ DankPopout {
|
|||||||
color: "transparent"
|
color: "transparent"
|
||||||
radius: parent.radius + 2
|
radius: parent.radius + 2
|
||||||
border.color: Theme.shadowMedium
|
border.color: Theme.shadowMedium
|
||||||
border.width: 1
|
border.width: 0
|
||||||
z: -2
|
z: -2
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +81,7 @@ DankPopout {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
border.color: Theme.outlineStrong
|
border.color: Theme.outlineStrong
|
||||||
border.width: 1
|
border.width: 0
|
||||||
radius: parent.radius
|
radius: parent.radius
|
||||||
z: -1
|
z: -1
|
||||||
}
|
}
|
||||||
@@ -97,7 +100,7 @@ DankPopout {
|
|||||||
height: 32
|
height: 32
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "VPN Connections"
|
text: I18n.tr("VPN Connections")
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -142,7 +145,7 @@ DankPopout {
|
|||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.surfaceContainerHigh.r, Theme.surfaceContainerHigh.g, Theme.surfaceContainerHigh.b, Theme.getContentBackgroundAlpha() * 0.6)
|
color: Qt.rgba(Theme.surfaceContainerHigh.r, Theme.surfaceContainerHigh.g, Theme.surfaceContainerHigh.b, Theme.getContentBackgroundAlpha() * 0.6)
|
||||||
border.color: Theme.outlineStrong
|
border.color: Theme.outlineStrong
|
||||||
border.width: 1
|
border.width: 0
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -193,7 +196,7 @@ DankPopout {
|
|||||||
visible: VpnService.connected
|
visible: VpnService.connected
|
||||||
width: 130
|
width: 130
|
||||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||||
border.width: 1
|
border.width: 0
|
||||||
border.color: Theme.outlineLight
|
border.color: Theme.outlineLight
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
@@ -207,7 +210,7 @@ DankPopout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Disconnect"
|
text: I18n.tr("Disconnect")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -263,14 +266,14 @@ DankPopout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "No VPN profiles found"
|
text: I18n.tr("No VPN profiles found")
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: "Add a VPN in NetworkManager"
|
text: I18n.tr("Add a VPN in NetworkManager")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
84
Modules/DankBar/RightSection.qml
Normal file
84
Modules/DankBar/RightSection.qml
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var widgetsModel: null
|
||||||
|
property var components: null
|
||||||
|
property bool noBackground: false
|
||||||
|
required property var axis
|
||||||
|
property var parentScreen: null
|
||||||
|
property real widgetThickness: 30
|
||||||
|
property real barThickness: 48
|
||||||
|
|
||||||
|
readonly property bool isVertical: axis?.isVertical ?? false
|
||||||
|
|
||||||
|
implicitHeight: layoutLoader.item ? layoutLoader.item.implicitHeight : 0
|
||||||
|
implicitWidth: layoutLoader.item ? layoutLoader.item.implicitWidth : 0
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: layoutLoader
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
sourceComponent: root.isVertical ? columnComp : rowComp
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: rowComp
|
||||||
|
Row {
|
||||||
|
spacing: noBackground ? 2 : Theme.spacingXS
|
||||||
|
anchors.right: parent ? parent.right : undefined
|
||||||
|
Repeater {
|
||||||
|
model: root.widgetsModel
|
||||||
|
Item {
|
||||||
|
width: widgetLoader.item ? widgetLoader.item.width : 0
|
||||||
|
height: widgetLoader.item ? widgetLoader.item.height : 0
|
||||||
|
WidgetHost {
|
||||||
|
id: widgetLoader
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
widgetId: model.widgetId
|
||||||
|
widgetData: model
|
||||||
|
spacerSize: model.size || 20
|
||||||
|
components: root.components
|
||||||
|
isInColumn: false
|
||||||
|
axis: root.axis
|
||||||
|
section: "right"
|
||||||
|
parentScreen: root.parentScreen
|
||||||
|
widgetThickness: root.widgetThickness
|
||||||
|
barThickness: root.barThickness
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: columnComp
|
||||||
|
Column {
|
||||||
|
width: parent ? parent.width : 0
|
||||||
|
spacing: noBackground ? 2 : Theme.spacingXS
|
||||||
|
Repeater {
|
||||||
|
model: root.widgetsModel
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: widgetLoader.item ? widgetLoader.item.height : 0
|
||||||
|
WidgetHost {
|
||||||
|
id: widgetLoader
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
widgetId: model.widgetId
|
||||||
|
widgetData: model
|
||||||
|
spacerSize: model.size || 20
|
||||||
|
components: root.components
|
||||||
|
isInColumn: true
|
||||||
|
axis: root.axis
|
||||||
|
section: "right"
|
||||||
|
parentScreen: root.parentScreen
|
||||||
|
widgetThickness: root.widgetThickness
|
||||||
|
barThickness: root.barThickness
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
166
Modules/DankBar/WidgetHost.qml
Normal file
166
Modules/DankBar/WidgetHost.qml
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string widgetId: ""
|
||||||
|
property var widgetData: null
|
||||||
|
property int spacerSize: 20
|
||||||
|
property var components: null
|
||||||
|
property bool isInColumn: false
|
||||||
|
property var axis: null
|
||||||
|
property string section: "center"
|
||||||
|
property var parentScreen: null
|
||||||
|
property real widgetThickness: 30
|
||||||
|
property real barThickness: 48
|
||||||
|
|
||||||
|
asynchronous: false
|
||||||
|
|
||||||
|
active: getWidgetVisible(widgetId, DgopService.dgopAvailable) &&
|
||||||
|
(widgetId !== "music" || MprisController.activePlayer !== null)
|
||||||
|
sourceComponent: getWidgetComponent(widgetId, components)
|
||||||
|
opacity: getWidgetEnabled(widgetData?.enabled) ? 1 : 0
|
||||||
|
|
||||||
|
signal contentItemReady(var item)
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: root.item
|
||||||
|
when: root.item && "parentScreen" in root.item
|
||||||
|
property: "parentScreen"
|
||||||
|
value: root.parentScreen
|
||||||
|
restoreMode: Binding.RestoreNone
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: root.item
|
||||||
|
when: root.item && "section" in root.item
|
||||||
|
property: "section"
|
||||||
|
value: root.section
|
||||||
|
restoreMode: Binding.RestoreNone
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: root.item
|
||||||
|
when: root.item && "widgetThickness" in root.item
|
||||||
|
property: "widgetThickness"
|
||||||
|
value: root.widgetThickness
|
||||||
|
restoreMode: Binding.RestoreNone
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: root.item
|
||||||
|
when: root.item && "barThickness" in root.item
|
||||||
|
property: "barThickness"
|
||||||
|
value: root.barThickness
|
||||||
|
restoreMode: Binding.RestoreNone
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: root.item
|
||||||
|
when: root.item && "axis" in root.item
|
||||||
|
property: "axis"
|
||||||
|
value: root.axis
|
||||||
|
restoreMode: Binding.RestoreNone
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: root.item
|
||||||
|
when: root.item && "widgetData" in root.item
|
||||||
|
property: "widgetData"
|
||||||
|
value: root.widgetData
|
||||||
|
restoreMode: Binding.RestoreNone
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoaded: {
|
||||||
|
if (item) {
|
||||||
|
contentItemReady(item)
|
||||||
|
if (axis && "isVertical" in item) {
|
||||||
|
try {
|
||||||
|
item.isVertical = axis.isVertical
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.pluginService !== undefined) {
|
||||||
|
var parts = widgetId.split(":")
|
||||||
|
var pluginId = parts[0]
|
||||||
|
var variantId = parts.length > 1 ? parts[1] : null
|
||||||
|
|
||||||
|
if (item.pluginId !== undefined) {
|
||||||
|
item.pluginId = pluginId
|
||||||
|
}
|
||||||
|
if (item.variantId !== undefined) {
|
||||||
|
item.variantId = variantId
|
||||||
|
}
|
||||||
|
if (item.variantData !== undefined && variantId) {
|
||||||
|
item.variantData = PluginService.getPluginVariantData(pluginId, variantId)
|
||||||
|
}
|
||||||
|
item.pluginService = PluginService
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.popoutService !== undefined) {
|
||||||
|
item.popoutService = PopoutService
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWidgetComponent(widgetId, components) {
|
||||||
|
const componentMap = {
|
||||||
|
"launcherButton": components.launcherButtonComponent,
|
||||||
|
"workspaceSwitcher": components.workspaceSwitcherComponent,
|
||||||
|
"focusedWindow": components.focusedWindowComponent,
|
||||||
|
"runningApps": components.runningAppsComponent,
|
||||||
|
"clock": components.clockComponent,
|
||||||
|
"music": components.mediaComponent,
|
||||||
|
"weather": components.weatherComponent,
|
||||||
|
"systemTray": components.systemTrayComponent,
|
||||||
|
"privacyIndicator": components.privacyIndicatorComponent,
|
||||||
|
"clipboard": components.clipboardComponent,
|
||||||
|
"cpuUsage": components.cpuUsageComponent,
|
||||||
|
"memUsage": components.memUsageComponent,
|
||||||
|
"diskUsage": components.diskUsageComponent,
|
||||||
|
"cpuTemp": components.cpuTempComponent,
|
||||||
|
"gpuTemp": components.gpuTempComponent,
|
||||||
|
"notificationButton": components.notificationButtonComponent,
|
||||||
|
"battery": components.batteryComponent,
|
||||||
|
"controlCenterButton": components.controlCenterButtonComponent,
|
||||||
|
"idleInhibitor": components.idleInhibitorComponent,
|
||||||
|
"spacer": components.spacerComponent,
|
||||||
|
"separator": components.separatorComponent,
|
||||||
|
"network_speed_monitor": components.networkComponent,
|
||||||
|
"keyboard_layout_name": components.keyboardLayoutNameComponent,
|
||||||
|
"vpn": components.vpnComponent,
|
||||||
|
"notepadButton": components.notepadButtonComponent,
|
||||||
|
"colorPicker": components.colorPickerComponent,
|
||||||
|
"systemUpdate": components.systemUpdateComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
if (componentMap[widgetId]) {
|
||||||
|
return componentMap[widgetId]
|
||||||
|
}
|
||||||
|
|
||||||
|
var parts = widgetId.split(":")
|
||||||
|
var pluginId = parts[0]
|
||||||
|
|
||||||
|
let pluginMap = PluginService.getWidgetComponents()
|
||||||
|
return pluginMap[pluginId] || null
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWidgetVisible(widgetId, dgopAvailable) {
|
||||||
|
const widgetVisibility = {
|
||||||
|
"cpuUsage": dgopAvailable,
|
||||||
|
"memUsage": dgopAvailable,
|
||||||
|
"cpuTemp": dgopAvailable,
|
||||||
|
"gpuTemp": dgopAvailable,
|
||||||
|
"network_speed_monitor": dgopAvailable
|
||||||
|
}
|
||||||
|
|
||||||
|
return widgetVisibility[widgetId] ?? true
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWidgetEnabled(enabled) {
|
||||||
|
return enabled !== false
|
||||||
|
}
|
||||||
|
}
|
||||||
126
Modules/DankBar/Widgets/Battery.qml
Normal file
126
Modules/DankBar/Widgets/Battery.qml
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell.Services.UPower
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: battery
|
||||||
|
|
||||||
|
property bool isVertical: axis?.isVertical ?? false
|
||||||
|
property var axis: null
|
||||||
|
property bool batteryPopupVisible: false
|
||||||
|
property string section: "right"
|
||||||
|
property var popupTarget: null
|
||||||
|
property var parentScreen: null
|
||||||
|
property real widgetThickness: 30
|
||||||
|
property real barThickness: 48
|
||||||
|
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
||||||
|
|
||||||
|
signal toggleBatteryPopup()
|
||||||
|
|
||||||
|
width: isVertical ? widgetThickness : (batteryContent.implicitWidth + horizontalPadding * 2)
|
||||||
|
height: isVertical ? (batteryColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
|
||||||
|
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
||||||
|
color: {
|
||||||
|
if (SettingsData.dankBarNoBackground) {
|
||||||
|
return "transparent";
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseColor = batteryArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
||||||
|
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
||||||
|
}
|
||||||
|
visible: true
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: batteryColumn
|
||||||
|
visible: battery.isVertical
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 1
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: BatteryService.getBatteryIcon()
|
||||||
|
size: Theme.barIconSize(barThickness)
|
||||||
|
color: {
|
||||||
|
if (!BatteryService.batteryAvailable) {
|
||||||
|
return Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
|
||||||
|
return Theme.error
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BatteryService.isCharging || BatteryService.isPluggedIn) {
|
||||||
|
return Theme.primary
|
||||||
|
}
|
||||||
|
|
||||||
|
return Theme.surfaceText
|
||||||
|
}
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: BatteryService.batteryLevel.toString()
|
||||||
|
font.pixelSize: Theme.barTextSize(barThickness)
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
visible: BatteryService.batteryAvailable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: batteryContent
|
||||||
|
visible: !battery.isVertical
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: SettingsData.dankBarNoBackground ? 1 : 2
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: BatteryService.getBatteryIcon()
|
||||||
|
size: Theme.barIconSize(barThickness, -4)
|
||||||
|
color: {
|
||||||
|
if (!BatteryService.batteryAvailable) {
|
||||||
|
return Theme.surfaceText;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
|
||||||
|
return Theme.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BatteryService.isCharging || BatteryService.isPluggedIn) {
|
||||||
|
return Theme.primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Theme.surfaceText;
|
||||||
|
}
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: `${BatteryService.batteryLevel}%`
|
||||||
|
font.pixelSize: Theme.barTextSize(barThickness)
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: BatteryService.batteryAvailable
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: batteryArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onPressed: {
|
||||||
|
if (popupTarget && popupTarget.setTriggerPosition) {
|
||||||
|
const globalPos = mapToGlobal(0, 0)
|
||||||
|
const currentScreen = parentScreen || Screen
|
||||||
|
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
|
||||||
|
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
||||||
|
}
|
||||||
|
toggleBatteryPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user