mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-06 05:25:41 -05:00
Compare commits
441 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1548286083 | ||
|
|
c018d953b8 | ||
|
|
cf66d28774 | ||
|
|
9cec6fd212 | ||
|
|
92926331b5 | ||
|
|
f9932ea222 | ||
|
|
a65d6b7630 | ||
|
|
7252d1e4d7 | ||
|
|
3b5a951431 | ||
|
|
0b1c331705 | ||
|
|
3c354b71f5 | ||
|
|
1eb5f381ae | ||
|
|
c5efd28781 | ||
|
|
53ae8ac917 | ||
|
|
505b6368e6 | ||
|
|
3c20e9e203 | ||
|
|
43427461f5 | ||
|
|
d7740ff6d2 | ||
|
|
1fb4eb33aa | ||
|
|
b27f362b44 | ||
|
|
325e3bc19b | ||
|
|
9215985335 | ||
|
|
293179daa6 | ||
|
|
4fe79dbe85 | ||
|
|
55d738e917 | ||
|
|
986b07f4a9 | ||
|
|
450c2e91ed | ||
|
|
4d06333624 | ||
|
|
fbe4122404 | ||
|
|
baf9b5e6f3 | ||
|
|
c88fc20701 | ||
|
|
b1078d6c73 | ||
|
|
5033d10246 | ||
|
|
986993a890 | ||
|
|
19b13a1e81 | ||
|
|
76637fab33 | ||
|
|
0a79d9a187 | ||
|
|
36b3b3c7ae | ||
|
|
8caeca0c08 | ||
|
|
1c323f54ee | ||
|
|
7ed0b752a8 | ||
|
|
0569906f7c | ||
|
|
2a7cf187ad | ||
|
|
cc5b98a5d2 | ||
|
|
1478c92f49 | ||
|
|
e1785a1738 | ||
|
|
44ebd2918c | ||
|
|
c87fa0de5e | ||
|
|
7b26692c8e | ||
|
|
b294e391e7 | ||
|
|
85f8e362e6 | ||
|
|
d68a6a1056 | ||
|
|
3dae9c0639 | ||
|
|
aede6b064a | ||
|
|
76b168020c | ||
|
|
5e36b1454a | ||
|
|
bd35fbac4d | ||
|
|
e081ec19cc | ||
|
|
d870d8bad6 | ||
|
|
20fd13c836 | ||
|
|
59f98b151d | ||
|
|
4ac1990c12 | ||
|
|
0a5105cc62 | ||
|
|
a9f8b835ee | ||
|
|
0109bd5bda | ||
|
|
01dad64c6d | ||
|
|
ee38f57f6d | ||
|
|
6b163dcb5f | ||
|
|
baadbbc65a | ||
|
|
13a2813db9 | ||
|
|
cfa7d12dd3 | ||
|
|
8bf23d820f | ||
|
|
3c7e903ace | ||
|
|
ee0e3aece9 | ||
|
|
d7efd1b285 | ||
|
|
34f7a7ab18 | ||
|
|
695eb0a401 | ||
|
|
0d44b95a40 | ||
|
|
116c421492 | ||
|
|
53507ef56b | ||
|
|
3c049e031f | ||
|
|
b6688adb35 | ||
|
|
b46fe28c05 | ||
|
|
e7debdcf46 | ||
|
|
2c2930e876 | ||
|
|
ca294fc049 | ||
|
|
86d1a40299 | ||
|
|
7a3884a633 | ||
|
|
7e5c6581c9 | ||
|
|
f17bbbd689 | ||
|
|
24b046e9d7 | ||
|
|
48a7d24c11 | ||
|
|
033f96a4b0 | ||
|
|
f0a1cb6525 | ||
|
|
db5782783b | ||
|
|
29022e260d | ||
|
|
1e1f58d3ed | ||
|
|
12389e2856 | ||
|
|
cde7427449 | ||
|
|
42e7cb7b5f | ||
|
|
d7992bc1f7 | ||
|
|
61c8549401 | ||
|
|
a284dcf61d | ||
|
|
2e462b0899 | ||
|
|
b79c66d59a | ||
|
|
2f2020e7e2 | ||
|
|
b7e99c0d2b | ||
|
|
2648848898 | ||
|
|
79b23ca829 | ||
|
|
0ac5b7bc87 | ||
|
|
1d211e8474 | ||
|
|
1981a83e82 | ||
|
|
cac071e7af | ||
|
|
c6efccd61c | ||
|
|
a90b00e5fe | ||
|
|
7863d03282 | ||
|
|
968606d781 | ||
|
|
f7e8de2556 | ||
|
|
17a8edc1ae | ||
|
|
30dc63c801 | ||
|
|
8db7b8419a | ||
|
|
8c626b20e1 | ||
|
|
a8929c8046 | ||
|
|
f8e4b5e958 | ||
|
|
58cae24157 | ||
|
|
bb4f5f37cc | ||
|
|
237333941a | ||
|
|
e1406875aa | ||
|
|
6ab96898a3 | ||
|
|
7973b2f3da | ||
|
|
90284625af | ||
|
|
6b3442512a | ||
|
|
f9208136af | ||
|
|
9895fbde0d | ||
|
|
30370e4985 | ||
|
|
7a45f370b5 | ||
|
|
38068eeaac | ||
|
|
62df30ed6c | ||
|
|
7e75c9e510 | ||
|
|
42f9edf566 | ||
|
|
c72b6144a5 | ||
|
|
032777e32e | ||
|
|
607b5320fd | ||
|
|
959766b265 | ||
|
|
9774991b56 | ||
|
|
e1587995d0 | ||
|
|
08e6e22046 | ||
|
|
454d8bdc88 | ||
|
|
d0ae7431eb | ||
|
|
d88dc17b21 | ||
|
|
705f569571 | ||
|
|
abc7badfa9 | ||
|
|
e8c2469227 | ||
|
|
1e5e8cd246 | ||
|
|
adc81cfb95 | ||
|
|
e175fa64cb | ||
|
|
f9994d0e42 | ||
|
|
3e5d1c514a | ||
|
|
6310394034 | ||
|
|
f32596053b | ||
|
|
cf4a6969d3 | ||
|
|
0918412916 | ||
|
|
41b1718587 | ||
|
|
ca2acbc704 | ||
|
|
1abd3ef8b1 | ||
|
|
cedba3770c | ||
|
|
f733be1fd1 | ||
|
|
01e02232d7 | ||
|
|
771920b38b | ||
|
|
0f55bbc148 | ||
|
|
ab4e9646ad | ||
|
|
884b73599a | ||
|
|
492c0e7ef7 | ||
|
|
0865ae000b | ||
|
|
049c9b44e4 | ||
|
|
199edd3771 | ||
|
|
8806217d25 | ||
|
|
cc1588debd | ||
|
|
d2ba4b32fe | ||
|
|
b3d5054966 | ||
|
|
57a921425c | ||
|
|
061aaeb933 | ||
|
|
0c7af9c740 | ||
|
|
d5c4b990dc | ||
|
|
a650a79dfc | ||
|
|
7ac6e94348 | ||
|
|
b4abdf3d51 | ||
|
|
b59b87d84e | ||
|
|
799ae1a20e | ||
|
|
1e58e69c59 | ||
|
|
c667bab5ca | ||
|
|
2a744fb174 | ||
|
|
a9744a0cad | ||
|
|
0aab22f242 | ||
|
|
b0f65225a9 | ||
|
|
1311da7258 | ||
|
|
61d68b1f76 | ||
|
|
5dd1769536 | ||
|
|
a45a9bda9e | ||
|
|
4e43c797e2 | ||
|
|
beab1a7b01 | ||
|
|
85c00a9c4e | ||
|
|
b2e5565110 | ||
|
|
95785afec9 | ||
|
|
d9d16eccfe | ||
|
|
26900c9b62 | ||
|
|
8113ddc809 | ||
|
|
3cd6a1a558 | ||
|
|
9128141be0 | ||
|
|
0b0af20a84 | ||
|
|
225144cb46 | ||
|
|
bbe802037e | ||
|
|
1db4e92779 | ||
|
|
072883dcd4 | ||
|
|
a25e929200 | ||
|
|
6c4d27be8a | ||
|
|
8825382502 | ||
|
|
9ce3c5bd73 | ||
|
|
771346c8fa | ||
|
|
a56b2d6a9f | ||
|
|
342f980bad | ||
|
|
dbc1bdeb3b | ||
|
|
1cb10e879e | ||
|
|
d90dd5288b | ||
|
|
e55a517dae | ||
|
|
378def1fa3 | ||
|
|
ea7676c98e | ||
|
|
b9b15568b4 | ||
|
|
701d8cbd8a | ||
|
|
bd525763de | ||
|
|
479868718e | ||
|
|
951136bc4c | ||
|
|
8ab25ef8e4 | ||
|
|
a11cd9b0df | ||
|
|
1e72733e81 | ||
|
|
967b7d05de | ||
|
|
90bc890190 | ||
|
|
647c358b72 | ||
|
|
2a89885437 | ||
|
|
47cc43185d | ||
|
|
aa6e09ed3e | ||
|
|
c7bc3d6f3b | ||
|
|
0b68bf7c07 | ||
|
|
3274ef5e3e | ||
|
|
f93a00c8d4 | ||
|
|
92bcb83b16 | ||
|
|
4e0c813db7 | ||
|
|
d4509c80b7 | ||
|
|
c9313df3a4 | ||
|
|
9b6fb29d46 | ||
|
|
50ce5cf257 | ||
|
|
b19e5b3b40 | ||
|
|
c07ba3f737 | ||
|
|
eff5f60264 | ||
|
|
355b2e16b4 | ||
|
|
c389101a10 | ||
|
|
4aa0b3d0fc | ||
|
|
322f1415f6 | ||
|
|
0d57691e38 | ||
|
|
507b516f89 | ||
|
|
7bf73ab14d | ||
|
|
9a305355c2 | ||
|
|
6ac382a25f | ||
|
|
e02b255442 | ||
|
|
d978740d66 | ||
|
|
62b7492e9f | ||
|
|
c2f42f3f69 | ||
|
|
2c4a40e778 | ||
|
|
635fcad416 | ||
|
|
2c92f830d1 | ||
|
|
4924f3e55a | ||
|
|
53306165e1 | ||
|
|
1ebcdaaf62 | ||
|
|
078ef203b6 | ||
|
|
59669d8b7f | ||
|
|
d38b98459a | ||
|
|
f54e53b8a0 | ||
|
|
851d47213c | ||
|
|
ad778b5d81 | ||
|
|
0cb081a6d0 | ||
|
|
daf3525e80 | ||
|
|
b35198c710 | ||
|
|
1feb77aadb | ||
|
|
d6b690ae2f | ||
|
|
b1ae246c86 | ||
|
|
4ceb5f13e5 | ||
|
|
64960e4dcd | ||
|
|
e1c180a13f | ||
|
|
86a0fd409a | ||
|
|
5a32398446 | ||
|
|
bcb22ec265 | ||
|
|
8719dcf98f | ||
|
|
9b4b2f75c1 | ||
|
|
8acde3a347 | ||
|
|
7c1e247ef8 | ||
|
|
f4cd27d316 | ||
|
|
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 |
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -24,6 +24,8 @@ assignees: ""
|
|||||||
|
|
||||||
- [ ] niri
|
- [ ] niri
|
||||||
- [ ] Hyprland
|
- [ ] Hyprland
|
||||||
|
- [ ] dwl (MangoWC)
|
||||||
|
- [ ] sway
|
||||||
- [ ] Other (specify)
|
- [ ] Other (specify)
|
||||||
|
|
||||||
## Distribution
|
## Distribution
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -21,6 +21,8 @@ Is this feature specific to one compositor?
|
|||||||
- [ ] All compositors
|
- [ ] All compositors
|
||||||
- [ ] niri
|
- [ ] niri
|
||||||
- [ ] Hyprland
|
- [ ] Hyprland
|
||||||
|
- [ ] dwl (MangoWC)
|
||||||
|
- [ ] sway
|
||||||
|
|
||||||
## Proposed Solution
|
## Proposed Solution
|
||||||
|
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/support_request.md
vendored
2
.github/ISSUE_TEMPLATE/support_request.md
vendored
@@ -10,6 +10,8 @@ assignees: ""
|
|||||||
|
|
||||||
- [ ] niri
|
- [ ] niri
|
||||||
- [ ] Hyprland
|
- [ ] Hyprland
|
||||||
|
- [ ] dwl (MangoWC)
|
||||||
|
- [ ] sway
|
||||||
- [ ] other
|
- [ ] other
|
||||||
|
|
||||||
## Distribution
|
## Distribution
|
||||||
|
|||||||
322
.github/workflows/copr-release.yml
vendored
Normal file
322
.github/workflows/copr-release.yml
vendored
Normal file
@@ -0,0 +1,322 @@
|
|||||||
|
name: DMS Copr Stable Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_run:
|
||||||
|
workflows: ["Create Release from DMS"]
|
||||||
|
types: [completed]
|
||||||
|
branches: [master]
|
||||||
|
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
|
||||||
|
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}
|
||||||
|
|
||||||
|
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 }}" = "workflow_run" ]; then
|
||||||
|
VERSION=$(curl -s https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r '.tag_name' | sed 's/^v//')
|
||||||
|
echo "Using latest release version from workflow_run: $VERSION"
|
||||||
|
else
|
||||||
|
VERSION=$(curl -s https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r '.tag_name' | sed 's/^v//')
|
||||||
|
echo "Using latest release version: $VERSION"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
echo "✅ Building DMS stable version: $VERSION"
|
||||||
|
|
||||||
|
- 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: accountsservice
|
||||||
|
Requires: dms-cli
|
||||||
|
Requires: dgop
|
||||||
|
|
||||||
|
Recommends: brightnessctl
|
||||||
|
Recommends: cava
|
||||||
|
Recommends: cliphist
|
||||||
|
Recommends: hyprpicker
|
||||||
|
Recommends: matugen
|
||||||
|
Recommends: wl-clipboard
|
||||||
|
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}%{_datadir}/quickshell/dms
|
||||||
|
cp -r %{_builddir}/dms-qml/* %{buildroot}%{_datadir}/quickshell/dms/
|
||||||
|
|
||||||
|
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.git*
|
||||||
|
rm -f %{buildroot}%{_datadir}/quickshell/dms/.gitignore
|
||||||
|
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.github
|
||||||
|
rm -f %{buildroot}%{_datadir}/quickshell/dms/*.spec
|
||||||
|
|
||||||
|
%posttrans
|
||||||
|
# Clean up old installation path from previous versions (only if empty)
|
||||||
|
if [ -d "%{_sysconfdir}/xdg/quickshell/dms" ]; then
|
||||||
|
# Remove directories only if empty (preserves any user-added files)
|
||||||
|
rmdir "%{_sysconfdir}/xdg/quickshell/dms" 2>/dev/null || true
|
||||||
|
rmdir "%{_sysconfdir}/xdg/quickshell" 2>/dev/null || true
|
||||||
|
rmdir "%{_sysconfdir}/xdg" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Restart DMS for active users after upgrade
|
||||||
|
if [ "$1" -ge 2 ]; then
|
||||||
|
# Find all quickshell DMS processes (PID and username)
|
||||||
|
while read pid cmd; do
|
||||||
|
username=$(ps -o user= -p "$pid" 2>/dev/null)
|
||||||
|
|
||||||
|
[ "$username" = "root" ] && continue
|
||||||
|
[ -z "$username" ] && continue
|
||||||
|
|
||||||
|
# Get user's UID and validate session
|
||||||
|
user_uid=$(id -u "$username" 2>/dev/null)
|
||||||
|
[ -z "$user_uid" ] && continue
|
||||||
|
[ ! -d "/run/user/$user_uid" ] && continue
|
||||||
|
|
||||||
|
wayland_display=$(tr '\0' '\n' < /proc/$pid/environ 2>/dev/null | grep '^WAYLAND_DISPLAY=' | cut -d= -f2)
|
||||||
|
[ -z "$wayland_display" ] && continue
|
||||||
|
|
||||||
|
echo "Restarting DMS for user: $username"
|
||||||
|
|
||||||
|
# Run as user with full Wayland session environment
|
||||||
|
runuser -u "$username" -- /bin/sh -c "
|
||||||
|
export XDG_RUNTIME_DIR=/run/user/$user_uid
|
||||||
|
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$user_uid/bus
|
||||||
|
export WAYLAND_DISPLAY=$wayland_display
|
||||||
|
export PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:\$PATH
|
||||||
|
dms restart >/dev/null 2>&1
|
||||||
|
" 2>/dev/null || true
|
||||||
|
|
||||||
|
break
|
||||||
|
done < <(pgrep -a -f 'quickshell.*dms' 2>/dev/null)
|
||||||
|
fi
|
||||||
|
|
||||||
|
%files
|
||||||
|
%license LICENSE
|
||||||
|
%doc README.md CONTRIBUTING.md
|
||||||
|
%{_datadir}/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
|
||||||
192
.github/workflows/poeditor-export.yml
vendored
Normal file
192
.github/workflows/poeditor-export.yml
vendored
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
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: Setup Python
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.x'
|
||||||
|
|
||||||
|
- name: Install jq
|
||||||
|
run: sudo apt-get update && sudo apt-get install -y jq
|
||||||
|
|
||||||
|
- name: Extract source strings from codebase
|
||||||
|
env:
|
||||||
|
API_TOKEN: ${{ secrets.POEDITOR_API_TOKEN }}
|
||||||
|
PROJECT_ID: ${{ secrets.POEDITOR_PROJECT_ID }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "::group::Extracting strings from QML files"
|
||||||
|
python3 translations/extract_translations.py
|
||||||
|
echo "::endgroup::"
|
||||||
|
|
||||||
|
echo "::group::Checking for changes in en.json"
|
||||||
|
if [[ -f "translations/en.json" ]]; then
|
||||||
|
jq -S . "translations/en.json" > /tmp/en_new.json
|
||||||
|
if [[ -f "translations/en.json.orig" ]]; then
|
||||||
|
jq -S . "translations/en.json.orig" > /tmp/en_old.json
|
||||||
|
else
|
||||||
|
git show HEAD:translations/en.json > /tmp/en_old.json 2>/dev/null || echo "[]" > /tmp/en_old.json
|
||||||
|
jq -S . /tmp/en_old.json > /tmp/en_old.json.tmp && mv /tmp/en_old.json.tmp /tmp/en_old.json
|
||||||
|
fi
|
||||||
|
|
||||||
|
if diff -q /tmp/en_new.json /tmp/en_old.json >/dev/null 2>&1; then
|
||||||
|
echo "No changes in source strings"
|
||||||
|
echo "source_changed=false" >> "$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
echo "Detected changes in source strings"
|
||||||
|
echo "source_changed=true" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
echo "::group::Uploading source strings to POEditor"
|
||||||
|
RESP=$(curl -sS -X POST https://api.poeditor.com/v2/projects/upload \
|
||||||
|
-F api_token="$API_TOKEN" \
|
||||||
|
-F id="$PROJECT_ID" \
|
||||||
|
-F updating="terms" \
|
||||||
|
-F file=@"translations/en.json")
|
||||||
|
|
||||||
|
STATUS=$(echo "$RESP" | jq -r '.response.status')
|
||||||
|
if [[ "$STATUS" != "success" ]]; then
|
||||||
|
echo "::warning::POEditor upload failed: $RESP"
|
||||||
|
else
|
||||||
|
TERMS_ADDED=$(echo "$RESP" | jq -r '.result.terms.added // 0')
|
||||||
|
TERMS_UPDATED=$(echo "$RESP" | jq -r '.result.terms.updated // 0')
|
||||||
|
TERMS_DELETED=$(echo "$RESP" | jq -r '.result.terms.deleted // 0')
|
||||||
|
echo "Terms added: $TERMS_ADDED, updated: $TERMS_UPDATED, deleted: $TERMS_DELETED"
|
||||||
|
fi
|
||||||
|
echo "::endgroup::"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "::warning::translations/en.json not found"
|
||||||
|
echo "source_changed=false" >> "$GITHUB_OUTPUT"
|
||||||
|
fi
|
||||||
|
echo "::endgroup::"
|
||||||
|
id: extract
|
||||||
|
|
||||||
|
- name: Commit and push source strings
|
||||||
|
if: steps.extract.outputs.source_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/en.json translations/template.json
|
||||||
|
git commit -m "i18n: update source strings from codebase"
|
||||||
|
|
||||||
|
for attempt in 1 2 3; do
|
||||||
|
if git push; then
|
||||||
|
echo "Successfully pushed source string 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
|
||||||
|
|
||||||
|
- 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"
|
||||||
|
"zh-Hant:translations/poexports/zh_TW.json"
|
||||||
|
"pt-br:translations/poexports/pt.json"
|
||||||
|
"tr:translations/poexports/tr.json"
|
||||||
|
"it:translations/poexports/it.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
|
||||||
54
.github/workflows/release.yml
vendored
54
.github/workflows/release.yml
vendored
@@ -7,6 +7,7 @@ on:
|
|||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
actions: write
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: release-${{ github.event.client_payload.tag }}
|
group: release-${{ github.event.client_payload.tag }}
|
||||||
@@ -29,22 +30,18 @@ jobs:
|
|||||||
git config user.name "github-actions[bot]"
|
git config user.name "github-actions[bot]"
|
||||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
|
||||||
# create/update VERSION file to match incoming tag
|
|
||||||
echo "${TAG}" > VERSION
|
echo "${TAG}" > VERSION
|
||||||
if ! git diff --quiet -- VERSION; then
|
|
||||||
git add VERSION
|
git add -A VERSION
|
||||||
git commit -m "Add VERSION file for ${TAG} (from DMS)"
|
|
||||||
|
if ! git diff --cached --quiet; then
|
||||||
|
git commit -m "Update VERSION to ${TAG} (from DMS)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# If tag doesn't exist (or differs), (re)create it
|
git tag -f "${TAG}"
|
||||||
if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then
|
|
||||||
echo "Tag ${TAG} already exists"
|
|
||||||
else
|
|
||||||
git tag "${TAG}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Push commit (if any) and tag
|
git push origin HEAD
|
||||||
git push --follow-tags origin HEAD
|
git push -f origin "${TAG}"
|
||||||
|
|
||||||
- name: Generate Changelog
|
- name: Generate Changelog
|
||||||
id: changelog
|
id: changelog
|
||||||
@@ -52,21 +49,23 @@ jobs:
|
|||||||
set -e
|
set -e
|
||||||
PREVIOUS_TAG=$(git describe --tags --abbrev=0 "${TAG}^" 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
|
||||||
CHANGELOG=$(git log --oneline --pretty=format:"- %s (%h)" | head -50)
|
CHANGELOG=$(git log --oneline --pretty=format:"%an|%s (%h)" | grep -v "^github-actions\[bot\]|" | sed 's/^[^|]*|/- /' | head -50)
|
||||||
else
|
else
|
||||||
CHANGELOG=$(git log --oneline --pretty=format:"- %s (%h)" "${PREVIOUS_TAG}..${TAG}")
|
CHANGELOG=$(git log --oneline --pretty=format:"%an|%s (%h)" "${PREVIOUS_TAG}..${TAG}" | grep -v "^github-actions\[bot\]|" | sed 's/^[^|]*|/- /')
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cat > RELEASE_BODY.md << 'EOF'
|
cat > RELEASE_BODY.md << 'EOF'
|
||||||
## Assets
|
## Assets
|
||||||
|
|
||||||
### Complete Packages
|
### Complete Packages
|
||||||
- **`dms-full-amd64.tar.gz`** - Complete package for x86_64 systems (CLI binary + QML source + installation guide)
|
- **`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 binary + QML source + installation guide)
|
- **`dms-full-arm64.tar.gz`** - Complete package for ARM64 systems (CLI binaries + QML source + installation guide)
|
||||||
|
|
||||||
### Individual Components
|
### Individual Components
|
||||||
- **`dms-cli-amd64.gz`** - DMS CLI binary for x86_64 systems
|
- **`dms-cli-amd64.gz`** - DMS CLI binary for x86_64 systems
|
||||||
- **`dms-cli-arm64.gz`** - DMS CLI binary for ARM64 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
|
- **`dms-qml.tar.gz`** - QML source code only
|
||||||
|
|
||||||
### Checksums
|
### Checksums
|
||||||
@@ -112,14 +111,16 @@ jobs:
|
|||||||
# Download DMS CLI binaries from the danklinux repo
|
# Download DMS CLI binaries from the danklinux repo
|
||||||
gh release download "${TAG}" -R "${DMS_REPO}" --dir ./_dms_assets
|
gh release download "${TAG}" -R "${DMS_REPO}" --dir ./_dms_assets
|
||||||
|
|
||||||
# Rename CLI binaries to dms-cli-* format
|
# Rename CLI binaries to dms-cli-* format and copy distropkg binaries
|
||||||
for file in _dms_assets/dms-*.gz*; do
|
for file in _dms_assets/dms-*.gz*; do
|
||||||
if [ -f "$file" ]; then
|
if [ -f "$file" ]; then
|
||||||
basename=$(basename "$file")
|
basename=$(basename "$file")
|
||||||
# dms-amd64.gz -> dms-cli-amd64.gz
|
if [[ "$basename" == dms-distropkg-* ]]; then
|
||||||
# dms-amd64.gz.sha256 -> dms-cli-amd64.gz.sha256
|
cp "$file" "_release_assets/$basename"
|
||||||
newname=$(echo "$basename" | sed 's/^dms-/dms-cli-/')
|
else
|
||||||
cp "$file" "_release_assets/$newname"
|
newname=$(echo "$basename" | sed 's/^dms-/dms-cli-/')
|
||||||
|
cp "$file" "_release_assets/$newname"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -148,6 +149,12 @@ jobs:
|
|||||||
chmod +x _temp_full/bin/dms
|
chmod +x _temp_full/bin/dms
|
||||||
fi
|
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
|
# Create INSTALL.md
|
||||||
cat > _temp_full/INSTALL.md << 'EOF'
|
cat > _temp_full/INSTALL.md << 'EOF'
|
||||||
# DankMaterialShell Installation
|
# DankMaterialShell Installation
|
||||||
@@ -166,7 +173,7 @@ jobs:
|
|||||||
cp -r dms ~/.config/quickshell/
|
cp -r dms ~/.config/quickshell/
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Install the DMS CLI binary:**
|
2. **Install the DMS CLI binaries:**
|
||||||
```bash
|
```bash
|
||||||
sudo install -m 755 bin/dms /usr/local/bin/dms
|
sudo install -m 755 bin/dms /usr/local/bin/dms
|
||||||
# or install to a local directory:
|
# or install to a local directory:
|
||||||
@@ -210,4 +217,5 @@ jobs:
|
|||||||
tag_name: ${{ env.TAG }}
|
tag_name: ${{ env.TAG }}
|
||||||
files: _release_assets/**
|
files: _release_assets/**
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
|||||||
22
CLAUDE.md
22
CLAUDE.md
@@ -191,9 +191,9 @@ shell.qml # Main entry point (minimal orchestration)
|
|||||||
|
|
||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property type value: defaultValue
|
property type value: defaultValue
|
||||||
|
|
||||||
function performAction() { /* implementation */ }
|
function performAction() { /* implementation */ }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -251,23 +251,23 @@ shell.qml # Main entry point (minimal orchestration)
|
|||||||
// For regular components
|
// For regular components
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property type name: value
|
property type name: value
|
||||||
|
|
||||||
signal customSignal(type param)
|
signal customSignal(type param)
|
||||||
|
|
||||||
onSignal: { /* handler */ }
|
onSignal: { /* handler */ }
|
||||||
|
|
||||||
Component { /* children */ }
|
Component { /* children */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
// For services (singletons)
|
// For services (singletons)
|
||||||
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) { /* implementation */ }
|
function performAction(param) { /* implementation */ }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -305,7 +305,7 @@ shell.qml # Main entry point (minimal orchestration)
|
|||||||
```qml
|
```qml
|
||||||
// In services - detect capabilities
|
// In services - detect capabilities
|
||||||
property bool brightnessAvailable: false
|
property bool brightnessAvailable: false
|
||||||
|
|
||||||
// In modules - adapt UI accordingly
|
// In modules - adapt UI accordingly
|
||||||
DankSlider {
|
DankSlider {
|
||||||
visible: DisplayService.brightnessAvailable
|
visible: DisplayService.brightnessAvailable
|
||||||
@@ -335,7 +335,7 @@ shell.qml # Main entry point (minimal orchestration)
|
|||||||
console.log("Info message") // General info
|
console.log("Info message") // General info
|
||||||
console.warn("Warning message") // Warnings
|
console.warn("Warning message") // Warnings
|
||||||
console.error("Error message") // Errors
|
console.error("Error message") // Errors
|
||||||
|
|
||||||
// Include context in service operations
|
// Include context in service operations
|
||||||
onExited: (exitCode) => {
|
onExited: (exitCode) => {
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
|
|||||||
206
Common/CacheData.qml
Normal file
206
Common/CacheData.qml
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
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: ""
|
||||||
|
|
||||||
|
property var fileBrowserSettings: ({
|
||||||
|
"wallpaper": {
|
||||||
|
"lastPath": "",
|
||||||
|
"viewMode": "grid",
|
||||||
|
"sortBy": "name",
|
||||||
|
"sortAscending": true,
|
||||||
|
"iconSizeIndex": 1,
|
||||||
|
"showSidebar": true
|
||||||
|
},
|
||||||
|
"profile": {
|
||||||
|
"lastPath": "",
|
||||||
|
"viewMode": "grid",
|
||||||
|
"sortBy": "name",
|
||||||
|
"sortAscending": true,
|
||||||
|
"iconSizeIndex": 1,
|
||||||
|
"showSidebar": true
|
||||||
|
},
|
||||||
|
"notepad_save": {
|
||||||
|
"lastPath": "",
|
||||||
|
"viewMode": "list",
|
||||||
|
"sortBy": "name",
|
||||||
|
"sortAscending": true,
|
||||||
|
"iconSizeIndex": 1,
|
||||||
|
"showSidebar": true
|
||||||
|
},
|
||||||
|
"notepad_load": {
|
||||||
|
"lastPath": "",
|
||||||
|
"viewMode": "list",
|
||||||
|
"sortBy": "name",
|
||||||
|
"sortAscending": true,
|
||||||
|
"iconSizeIndex": 1,
|
||||||
|
"showSidebar": true
|
||||||
|
},
|
||||||
|
"generic": {
|
||||||
|
"lastPath": "",
|
||||||
|
"viewMode": "list",
|
||||||
|
"sortBy": "name",
|
||||||
|
"sortAscending": true,
|
||||||
|
"iconSizeIndex": 1,
|
||||||
|
"showSidebar": true
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"lastPath": "",
|
||||||
|
"viewMode": "list",
|
||||||
|
"sortBy": "name",
|
||||||
|
"sortAscending": true,
|
||||||
|
"iconSizeIndex": 1,
|
||||||
|
"showSidebar": true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
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.fileBrowserSettings !== undefined) {
|
||||||
|
fileBrowserSettings = cache.fileBrowserSettings
|
||||||
|
} else if (cache.fileBrowserViewMode !== undefined) {
|
||||||
|
fileBrowserSettings = {
|
||||||
|
"wallpaper": {
|
||||||
|
"lastPath": cache.wallpaperLastPath || "",
|
||||||
|
"viewMode": cache.fileBrowserViewMode || "grid",
|
||||||
|
"sortBy": cache.fileBrowserSortBy || "name",
|
||||||
|
"sortAscending": cache.fileBrowserSortAscending !== undefined ? cache.fileBrowserSortAscending : true,
|
||||||
|
"iconSizeIndex": cache.fileBrowserIconSizeIndex !== undefined ? cache.fileBrowserIconSizeIndex : 1,
|
||||||
|
"showSidebar": cache.fileBrowserShowSidebar !== undefined ? cache.fileBrowserShowSidebar : true
|
||||||
|
},
|
||||||
|
"profile": {
|
||||||
|
"lastPath": cache.profileLastPath || "",
|
||||||
|
"viewMode": cache.fileBrowserViewMode || "grid",
|
||||||
|
"sortBy": cache.fileBrowserSortBy || "name",
|
||||||
|
"sortAscending": cache.fileBrowserSortAscending !== undefined ? cache.fileBrowserSortAscending : true,
|
||||||
|
"iconSizeIndex": cache.fileBrowserIconSizeIndex !== undefined ? cache.fileBrowserIconSizeIndex : 1,
|
||||||
|
"showSidebar": cache.fileBrowserShowSidebar !== undefined ? cache.fileBrowserShowSidebar : true
|
||||||
|
},
|
||||||
|
"file": {
|
||||||
|
"lastPath": "",
|
||||||
|
"viewMode": "list",
|
||||||
|
"sortBy": "name",
|
||||||
|
"sortAscending": true,
|
||||||
|
"iconSizeIndex": 1,
|
||||||
|
"showSidebar": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
"fileBrowserSettings": fileBrowserSettings,
|
||||||
|
"configVersion": cacheConfigVersion
|
||||||
|
}, null, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
function migrateFromUndefinedToV1(cache) {
|
||||||
|
console.info("CacheData: Migrating configuration from undefined to version 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanupUnusedKeys() {
|
||||||
|
const validKeys = [
|
||||||
|
"wallpaperLastPath",
|
||||||
|
"profileLastPath",
|
||||||
|
"fileBrowserSettings",
|
||||||
|
"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.info("CacheData: No cache file found, starting fresh")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@ Singleton {
|
|||||||
return [fullUnderscore, fullHyphen, _lang].filter(c => c && c !== "en");
|
return [fullUnderscore, fullHyphen, _lang].filter(c => c && c !== "en");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
readonly property url translationsFolder: Qt.resolvedUrl("../translations/poexports")
|
readonly property url translationsFolder: Qt.resolvedUrl("../translations/poexports")
|
||||||
|
|
||||||
property string currentLocale: "en"
|
property string currentLocale: "en"
|
||||||
@@ -43,7 +43,7 @@ Singleton {
|
|||||||
try {
|
try {
|
||||||
root.translations = JSON.parse(text())
|
root.translations = JSON.parse(text())
|
||||||
root.translationsLoaded = true
|
root.translationsLoaded = true
|
||||||
console.log(`I18n: Loaded translations for '${root.currentLocale}' ` +
|
console.info(`I18n: Loaded translations for '${root.currentLocale}' ` +
|
||||||
`(${Object.keys(root.translations).length} contexts)`)
|
`(${Object.keys(root.translations).length} contexts)`)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(`I18n: Error parsing '${root.currentLocale}':`, e,
|
console.warn(`I18n: Error parsing '${root.currentLocale}':`, e,
|
||||||
@@ -84,7 +84,7 @@ Singleton {
|
|||||||
_selectedPath = fileUrl
|
_selectedPath = fileUrl
|
||||||
translationsLoaded = false
|
translationsLoaded = false
|
||||||
translations = ({})
|
translations = ({})
|
||||||
console.log(`I18n: Using locale '${localeTag}' from ${fileUrl}`)
|
console.info(`I18n: Using locale '${localeTag}' from ${fileUrl}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
function _fallbackToEnglish() {
|
function _fallbackToEnglish() {
|
||||||
|
|||||||
110
Common/Proc.qml
Normal file
110
Common/Proc.qml
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property int defaultDebounceMs: 50
|
||||||
|
property int defaultTimeoutMs: 10000
|
||||||
|
property var _procDebouncers: ({})
|
||||||
|
|
||||||
|
function runCommand(id, command, callback, debounceMs, timeoutMs) {
|
||||||
|
const wait = (typeof debounceMs === "number" && debounceMs >= 0) ? debounceMs : defaultDebounceMs
|
||||||
|
const timeout = (typeof timeoutMs === "number" && timeoutMs > 0) ? timeoutMs : defaultTimeoutMs
|
||||||
|
let procId = id ? id : Math.random()
|
||||||
|
const isRandomId = !id
|
||||||
|
|
||||||
|
if (!_procDebouncers[procId]) {
|
||||||
|
const t = Qt.createQmlObject('import QtQuick; Timer { repeat: false }', root)
|
||||||
|
t.triggered.connect(function() { _launchProc(procId, isRandomId) })
|
||||||
|
_procDebouncers[procId] = { timer: t, command: command, callback: callback, waitMs: wait, timeoutMs: timeout, isRandomId: isRandomId }
|
||||||
|
} else {
|
||||||
|
_procDebouncers[procId].command = command
|
||||||
|
_procDebouncers[procId].callback = callback
|
||||||
|
_procDebouncers[procId].waitMs = wait
|
||||||
|
_procDebouncers[procId].timeoutMs = timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
const entry = _procDebouncers[procId]
|
||||||
|
entry.timer.interval = entry.waitMs
|
||||||
|
entry.timer.restart()
|
||||||
|
}
|
||||||
|
|
||||||
|
function _launchProc(id, isRandomId) {
|
||||||
|
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)
|
||||||
|
const timeoutTimer = Qt.createQmlObject('import QtQuick; Timer { repeat: false }', root)
|
||||||
|
|
||||||
|
proc.stdout = out
|
||||||
|
proc.stderr = err
|
||||||
|
proc.command = entry.command
|
||||||
|
|
||||||
|
let capturedOut = ""
|
||||||
|
let capturedErr = ""
|
||||||
|
let exitSeen = false
|
||||||
|
let exitCodeValue = -1
|
||||||
|
let outSeen = false
|
||||||
|
let errSeen = false
|
||||||
|
let timedOut = false
|
||||||
|
|
||||||
|
timeoutTimer.interval = entry.timeoutMs
|
||||||
|
timeoutTimer.triggered.connect(function() {
|
||||||
|
if (!exitSeen) {
|
||||||
|
timedOut = true
|
||||||
|
proc.running = false
|
||||||
|
exitSeen = true
|
||||||
|
exitCodeValue = 124
|
||||||
|
maybeComplete()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
out.streamFinished.connect(function() {
|
||||||
|
capturedOut = out.text || ""
|
||||||
|
outSeen = true
|
||||||
|
maybeComplete()
|
||||||
|
})
|
||||||
|
|
||||||
|
err.streamFinished.connect(function() {
|
||||||
|
capturedErr = err.text || ""
|
||||||
|
errSeen = true
|
||||||
|
maybeComplete()
|
||||||
|
})
|
||||||
|
|
||||||
|
proc.exited.connect(function(code) {
|
||||||
|
timeoutTimer.stop()
|
||||||
|
exitSeen = true
|
||||||
|
exitCodeValue = code
|
||||||
|
maybeComplete()
|
||||||
|
})
|
||||||
|
|
||||||
|
function maybeComplete() {
|
||||||
|
if (!exitSeen || !outSeen || !errSeen) return
|
||||||
|
timeoutTimer.stop()
|
||||||
|
if (typeof entry.callback === "function") {
|
||||||
|
try { entry.callback(capturedOut, exitCodeValue) } catch (e) { console.warn("runCommand callback error:", e) }
|
||||||
|
}
|
||||||
|
try { proc.destroy() } catch (_) {}
|
||||||
|
try { timeoutTimer.destroy() } catch (_) {}
|
||||||
|
|
||||||
|
if (isRandomId || entry.isRandomId) {
|
||||||
|
Qt.callLater(function() {
|
||||||
|
if (_procDebouncers[id]) {
|
||||||
|
try { _procDebouncers[id].timer.destroy() } catch (_) {}
|
||||||
|
delete _procDebouncers[id]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proc.running = true
|
||||||
|
timeoutTimer.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
|
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtCore
|
import QtCore
|
||||||
@@ -9,15 +10,19 @@ 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"
|
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 perModeWallpaper: false
|
property bool perModeWallpaper: false
|
||||||
@@ -25,55 +30,39 @@ Singleton {
|
|||||||
property string wallpaperPathDark: ""
|
property string wallpaperPathDark: ""
|
||||||
property var monitorWallpapersLight: ({})
|
property var monitorWallpapersLight: ({})
|
||||||
property var monitorWallpapersDark: ({})
|
property var monitorWallpapersDark: ({})
|
||||||
property bool doNotDisturb: false
|
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
|
||||||
property int nightModeEndMinute: 0
|
property int nightModeEndMinute: 0
|
||||||
property real latitude: 0.0
|
property real latitude: 0.0
|
||||||
property real longitude: 0.0
|
property real longitude: 0.0
|
||||||
|
property bool nightModeUseIPLocation: false
|
||||||
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
|
|
||||||
property bool loginctlLockIntegration: true
|
|
||||||
property var recentColors: []
|
|
||||||
property bool showThirdPartyPlugins: false
|
|
||||||
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (!isGreeterMode) {
|
if (!isGreeterMode) {
|
||||||
@@ -95,8 +84,6 @@ 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
|
perModeWallpaper = settings.perModeWallpaper !== undefined ? settings.perModeWallpaper : false
|
||||||
@@ -109,7 +96,6 @@ Singleton {
|
|||||||
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
|
||||||
@@ -128,6 +114,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
latitude = settings.latitude !== undefined ? settings.latitude : 0.0
|
latitude = settings.latitude !== undefined ? settings.latitude : 0.0
|
||||||
longitude = settings.longitude !== undefined ? settings.longitude : 0.0
|
longitude = settings.longitude !== undefined ? settings.longitude : 0.0
|
||||||
|
nightModeUseIPLocation = settings.nightModeUseIPLocation !== undefined ? settings.nightModeUseIPLocation : false
|
||||||
nightModeLocationProvider = settings.nightModeLocationProvider !== undefined ? settings.nightModeLocationProvider : ""
|
nightModeLocationProvider = settings.nightModeLocationProvider !== undefined ? settings.nightModeLocationProvider : ""
|
||||||
pinnedApps = settings.pinnedApps !== undefined ? settings.pinnedApps : []
|
pinnedApps = settings.pinnedApps !== undefined ? settings.pinnedApps : []
|
||||||
selectedGpuIndex = settings.selectedGpuIndex !== undefined ? settings.selectedGpuIndex : 0
|
selectedGpuIndex = settings.selectedGpuIndex !== undefined ? settings.selectedGpuIndex : 0
|
||||||
@@ -143,25 +130,25 @@ 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")
|
||||||
|
|
||||||
acMonitorTimeout = settings.acMonitorTimeout !== undefined ? settings.acMonitorTimeout : 0
|
|
||||||
acLockTimeout = settings.acLockTimeout !== undefined ? settings.acLockTimeout : 0
|
|
||||||
acSuspendTimeout = settings.acSuspendTimeout !== undefined ? settings.acSuspendTimeout : 0
|
|
||||||
acHibernateTimeout = settings.acHibernateTimeout !== undefined ? settings.acHibernateTimeout : 0
|
|
||||||
batteryMonitorTimeout = settings.batteryMonitorTimeout !== undefined ? settings.batteryMonitorTimeout : 0
|
|
||||||
batteryLockTimeout = settings.batteryLockTimeout !== undefined ? settings.batteryLockTimeout : 0
|
|
||||||
batterySuspendTimeout = settings.batterySuspendTimeout !== undefined ? settings.batterySuspendTimeout : 0
|
|
||||||
batteryHibernateTimeout = settings.batteryHibernateTimeout !== undefined ? settings.batteryHibernateTimeout : 0
|
|
||||||
lockBeforeSuspend = settings.lockBeforeSuspend !== undefined ? settings.lockBeforeSuspend : false
|
|
||||||
loginctlLockIntegration = settings.loginctlLockIntegration !== undefined ? settings.loginctlLockIntegration : true
|
|
||||||
recentColors = settings.recentColors !== undefined ? settings.recentColors : []
|
recentColors = settings.recentColors !== undefined ? settings.recentColors : []
|
||||||
showThirdPartyPlugins = settings.showThirdPartyPlugins !== undefined ? settings.showThirdPartyPlugins : false
|
showThirdPartyPlugins = settings.showThirdPartyPlugins !== undefined ? settings.showThirdPartyPlugins : false
|
||||||
|
|
||||||
|
if (settings.configVersion === undefined) {
|
||||||
|
migrateFromUndefinedToV1(settings)
|
||||||
|
saveSettings()
|
||||||
|
} else if (settings.configVersion === sessionConfigVersion) {
|
||||||
|
cleanupUnusedKeys()
|
||||||
|
}
|
||||||
|
|
||||||
if (!isGreeterMode) {
|
if (!isGreeterMode) {
|
||||||
if (typeof Theme !== "undefined") {
|
if (typeof Theme !== "undefined") {
|
||||||
Theme.generateSystemThemesFromCurrentTheme()
|
Theme.generateSystemThemesFromCurrentTheme()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof WallpaperCyclingService !== "undefined") {
|
||||||
|
WallpaperCyclingService.updateCyclingState()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
||||||
@@ -169,12 +156,11 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function saveSettings() {
|
function saveSettings() {
|
||||||
if (isGreeterMode) return
|
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,
|
"perModeWallpaper": perModeWallpaper,
|
||||||
@@ -193,6 +179,7 @@ Singleton {
|
|||||||
"nightModeEndMinute": nightModeEndMinute,
|
"nightModeEndMinute": nightModeEndMinute,
|
||||||
"latitude": latitude,
|
"latitude": latitude,
|
||||||
"longitude": longitude,
|
"longitude": longitude,
|
||||||
|
"nightModeUseIPLocation": nightModeUseIPLocation,
|
||||||
"nightModeLocationProvider": nightModeLocationProvider,
|
"nightModeLocationProvider": nightModeLocationProvider,
|
||||||
"pinnedApps": pinnedApps,
|
"pinnedApps": pinnedApps,
|
||||||
"selectedGpuIndex": selectedGpuIndex,
|
"selectedGpuIndex": selectedGpuIndex,
|
||||||
@@ -208,101 +195,98 @@ Singleton {
|
|||||||
"launchPrefix": launchPrefix,
|
"launchPrefix": launchPrefix,
|
||||||
"wallpaperTransition": wallpaperTransition,
|
"wallpaperTransition": wallpaperTransition,
|
||||||
"includedTransitions": includedTransitions,
|
"includedTransitions": includedTransitions,
|
||||||
"acMonitorTimeout": acMonitorTimeout,
|
|
||||||
"acLockTimeout": acLockTimeout,
|
|
||||||
"acSuspendTimeout": acSuspendTimeout,
|
|
||||||
"acHibernateTimeout": acHibernateTimeout,
|
|
||||||
"batteryMonitorTimeout": batteryMonitorTimeout,
|
|
||||||
"batteryLockTimeout": batteryLockTimeout,
|
|
||||||
"batterySuspendTimeout": batterySuspendTimeout,
|
|
||||||
"batteryHibernateTimeout": batteryHibernateTimeout,
|
|
||||||
"lockBeforeSuspend": lockBeforeSuspend,
|
|
||||||
"loginctlLockIntegration": loginctlLockIntegration,
|
|
||||||
"recentColors": recentColors,
|
"recentColors": recentColors,
|
||||||
"showThirdPartyPlugins": showThirdPartyPlugins
|
"showThirdPartyPlugins": showThirdPartyPlugins,
|
||||||
|
"configVersion": sessionConfigVersion
|
||||||
}, null, 2))
|
}, null, 2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function migrateFromUndefinedToV1(settings) {
|
||||||
|
console.info("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", "nightModeUseIPLocation", "nightModeLocationProvider", "pinnedApps", "selectedGpuIndex", "nvidiaGpuTempEnabled", "nonNvidiaGpuTempEnabled", "enabledGpuPciIds", "wallpaperCyclingEnabled", "wallpaperCyclingMode", "wallpaperCyclingInterval", "wallpaperCyclingTime", "monitorCyclingSettings", "lastBrightnessDevice", "launchPrefix", "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()
|
syncWallpaperForCurrentMode()
|
||||||
saveSettings()
|
saveSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
function syncWallpaperForCurrentMode() {
|
|
||||||
if (!perModeWallpaper) return
|
|
||||||
|
|
||||||
if (perMonitorWallpaper) {
|
|
||||||
monitorWallpapers = isLightMode ? Object.assign({}, monitorWallpapersLight) : Object.assign({}, monitorWallpapersDark)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
wallpaperPath = isLightMode ? wallpaperPathLight : wallpaperPathDark
|
|
||||||
}
|
|
||||||
|
|
||||||
function setDoNotDisturb(enabled) {
|
function setDoNotDisturb(enabled) {
|
||||||
doNotDisturb = enabled
|
doNotDisturb = enabled
|
||||||
saveSettings()
|
saveSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
function setNightModeEnabled(enabled) {
|
|
||||||
nightModeEnabled = enabled
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setNightModeTemperature(temperature) {
|
|
||||||
nightModeTemperature = temperature
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setNightModeAutoEnabled(enabled) {
|
|
||||||
console.log("SessionData: Setting nightModeAutoEnabled to", enabled)
|
|
||||||
nightModeAutoEnabled = enabled
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setNightModeAutoMode(mode) {
|
|
||||||
nightModeAutoMode = mode
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setNightModeStartHour(hour) {
|
|
||||||
nightModeStartHour = hour
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setNightModeStartMinute(minute) {
|
|
||||||
nightModeStartMinute = minute
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setNightModeEndHour(hour) {
|
|
||||||
nightModeEndHour = hour
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setNightModeEndMinute(minute) {
|
|
||||||
nightModeEndMinute = minute
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setLatitude(lat) {
|
|
||||||
console.log("SessionData: Setting latitude to", lat)
|
|
||||||
latitude = lat
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setLongitude(lng) {
|
|
||||||
console.log("SessionData: Setting longitude to", lng)
|
|
||||||
longitude = lng
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setNightModeLocationProvider(provider) {
|
|
||||||
nightModeLocationProvider = provider
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setWallpaperPath(path) {
|
function setWallpaperPath(path) {
|
||||||
wallpaperPath = path
|
wallpaperPath = path
|
||||||
saveSettings()
|
saveSettings()
|
||||||
@@ -353,141 +337,6 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setWallpaperLastPath(path) {
|
|
||||||
wallpaperLastPath = path
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setProfileLastPath(path) {
|
|
||||||
profileLastPath = path
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setPinnedApps(apps) {
|
|
||||||
pinnedApps = apps
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
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 addPinnedApp(appId) {
|
|
||||||
if (!appId)
|
|
||||||
return
|
|
||||||
var currentPinned = [...pinnedApps]
|
|
||||||
if (currentPinned.indexOf(appId) === -1) {
|
|
||||||
currentPinned.push(appId)
|
|
||||||
setPinnedApps(currentPinned)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removePinnedApp(appId) {
|
|
||||||
if (!appId)
|
|
||||||
return
|
|
||||||
var currentPinned = pinnedApps.filter(id => id !== appId)
|
|
||||||
setPinnedApps(currentPinned)
|
|
||||||
}
|
|
||||||
|
|
||||||
function isPinnedApp(appId) {
|
|
||||||
return appId && pinnedApps.indexOf(appId) !== -1
|
|
||||||
}
|
|
||||||
|
|
||||||
function setSelectedGpuIndex(index) {
|
|
||||||
selectedGpuIndex = index
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setNvidiaGpuTempEnabled(enabled) {
|
|
||||||
nvidiaGpuTempEnabled = enabled
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setNonNvidiaGpuTempEnabled(enabled) {
|
|
||||||
nonNvidiaGpuTempEnabled = enabled
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setEnabledGpuPciIds(pciIds) {
|
|
||||||
enabledGpuPciIds = pciIds
|
|
||||||
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 getMonitorCyclingSettings(screenName) {
|
|
||||||
return monitorCyclingSettings[screenName] || {
|
|
||||||
enabled: false,
|
|
||||||
mode: "interval",
|
|
||||||
interval: 300,
|
|
||||||
time: "06:00"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
function setPerMonitorWallpaper(enabled) {
|
||||||
perMonitorWallpaper = enabled
|
perMonitorWallpaper = enabled
|
||||||
if (enabled && perModeWallpaper) {
|
if (enabled && perModeWallpaper) {
|
||||||
@@ -571,14 +420,249 @@ Singleton {
|
|||||||
|
|
||||||
saveSettings()
|
saveSettings()
|
||||||
|
|
||||||
if (typeof Theme !== "undefined" && typeof Quickshell !== "undefined") {
|
if (typeof Theme !== "undefined" && typeof Quickshell !== "undefined" && typeof SettingsData !== "undefined") {
|
||||||
var screens = Quickshell.screens
|
var screens = Quickshell.screens
|
||||||
if (screens.length > 0 && screenName === screens[0].name) {
|
if (screens.length > 0) {
|
||||||
Theme.generateSystemThemesFromCurrentTheme()
|
var targetMonitor = (SettingsData.matugenTargetMonitor && SettingsData.matugenTargetMonitor !== "") ? SettingsData.matugenTargetMonitor : screens[0].name
|
||||||
|
if (screenName === targetMonitor) {
|
||||||
|
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) {
|
||||||
|
nightModeEnabled = enabled
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNightModeTemperature(temperature) {
|
||||||
|
nightModeTemperature = temperature
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNightModeAutoEnabled(enabled) {
|
||||||
|
console.log("SessionData: Setting nightModeAutoEnabled to", enabled)
|
||||||
|
nightModeAutoEnabled = enabled
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNightModeAutoMode(mode) {
|
||||||
|
nightModeAutoMode = mode
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNightModeStartHour(hour) {
|
||||||
|
nightModeStartHour = hour
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNightModeStartMinute(minute) {
|
||||||
|
nightModeStartMinute = minute
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNightModeEndHour(hour) {
|
||||||
|
nightModeEndHour = hour
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNightModeEndMinute(minute) {
|
||||||
|
nightModeEndMinute = minute
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNightModeUseIPLocation(use) {
|
||||||
|
nightModeUseIPLocation = use
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLatitude(lat) {
|
||||||
|
console.log("SessionData: Setting latitude to", lat)
|
||||||
|
latitude = lat
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLongitude(lng) {
|
||||||
|
console.log("SessionData: Setting longitude to", lng)
|
||||||
|
longitude = lng
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNightModeLocationProvider(provider) {
|
||||||
|
nightModeLocationProvider = provider
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setPinnedApps(apps) {
|
||||||
|
pinnedApps = apps
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function addPinnedApp(appId) {
|
||||||
|
if (!appId)
|
||||||
|
return
|
||||||
|
var currentPinned = [...pinnedApps]
|
||||||
|
if (currentPinned.indexOf(appId) === -1) {
|
||||||
|
currentPinned.push(appId)
|
||||||
|
setPinnedApps(currentPinned)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removePinnedApp(appId) {
|
||||||
|
if (!appId)
|
||||||
|
return
|
||||||
|
var currentPinned = pinnedApps.filter(id => id !== appId)
|
||||||
|
setPinnedApps(currentPinned)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPinnedApp(appId) {
|
||||||
|
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) {
|
||||||
|
selectedGpuIndex = index
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNvidiaGpuTempEnabled(enabled) {
|
||||||
|
nvidiaGpuTempEnabled = enabled
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNonNvidiaGpuTempEnabled(enabled) {
|
||||||
|
nonNvidiaGpuTempEnabled = enabled
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setEnabledGpuPciIds(pciIds) {
|
||||||
|
enabledGpuPciIds = pciIds
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncWallpaperForCurrentMode() {
|
||||||
|
if (!perModeWallpaper)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (perMonitorWallpaper) {
|
||||||
|
monitorWallpapers = isLightMode ? Object.assign({}, monitorWallpapersLight) : Object.assign({}, monitorWallpapersDark)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
wallpaperPath = isLightMode ? wallpaperPathLight : wallpaperPathDark
|
||||||
|
}
|
||||||
|
|
||||||
function getMonitorWallpaper(screenName) {
|
function getMonitorWallpaper(screenName) {
|
||||||
if (!perMonitorWallpaper) {
|
if (!perMonitorWallpaper) {
|
||||||
return wallpaperPath
|
return wallpaperPath
|
||||||
@@ -586,74 +670,13 @@ Singleton {
|
|||||||
return monitorWallpapers[screenName] || wallpaperPath
|
return monitorWallpapers[screenName] || wallpaperPath
|
||||||
}
|
}
|
||||||
|
|
||||||
function setLastBrightnessDevice(device) {
|
function getMonitorCyclingSettings(screenName) {
|
||||||
lastBrightnessDevice = device
|
return monitorCyclingSettings[screenName] || {
|
||||||
saveSettings()
|
"enabled": false,
|
||||||
}
|
"mode": "interval",
|
||||||
|
"interval": 300,
|
||||||
function setLaunchPrefix(prefix) {
|
"time": "06:00"
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setLoginctlLockIntegration(enabled) {
|
|
||||||
loginctlLockIntegration = enabled
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setShowThirdPartyPlugins(enabled) {
|
|
||||||
showThirdPartyPlugins = enabled
|
|
||||||
saveSettings()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FileView {
|
FileView {
|
||||||
@@ -704,7 +727,7 @@ Singleton {
|
|||||||
running: false
|
running: false
|
||||||
onExited: exitCode => {
|
onExited: exitCode => {
|
||||||
if (exitCode === 0) {
|
if (exitCode === 0) {
|
||||||
console.log("Copied default-session.json to session.json")
|
console.info("Copied default-session.json to session.json")
|
||||||
settingsFile.reload()
|
settingsFile.reload()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
560
Common/Theme.qml
560
Common/Theme.qml
@@ -14,25 +14,30 @@ 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: {
|
readonly property real popupDistance: {
|
||||||
if (typeof SettingsData === "undefined") return 4
|
if (typeof SettingsData === "undefined")
|
||||||
|
return 4
|
||||||
return SettingsData.popupGapsAuto ? Math.max(4, SettingsData.dankBarSpacing) : SettingsData.popupGapsManual
|
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: typeof SessionData !== "undefined" ? SessionData.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"
|
||||||
|
|
||||||
readonly property string homeDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.HomeLocation))
|
readonly property string homeDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.HomeLocation))
|
||||||
readonly property string configDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation))
|
readonly property string configDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation))
|
||||||
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) {
|
||||||
var screens = Quickshell.screens
|
var screens = Quickshell.screens
|
||||||
@@ -57,14 +62,28 @@ Singleton {
|
|||||||
return wallpaperPath
|
return wallpaperPath
|
||||||
}
|
}
|
||||||
readonly property string rawWallpaperPath: {
|
readonly property string rawWallpaperPath: {
|
||||||
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 targetMonitor = (typeof SettingsData !== "undefined" && SettingsData.matugenTargetMonitor && SettingsData.matugenTargetMonitor !== "") ? SettingsData.matugenTargetMonitor : screens[0].name
|
||||||
return firstMonitorWallpaper || SessionData.wallpaperPath
|
|
||||||
|
var targetMonitorExists = false
|
||||||
|
for (var i = 0; i < screens.length; i++) {
|
||||||
|
if (screens[i].name === targetMonitor) {
|
||||||
|
targetMonitorExists = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetMonitorExists) {
|
||||||
|
targetMonitor = screens[0].name
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetMonitorWallpaper = SessionData.getMonitorWallpaper(targetMonitor)
|
||||||
|
return targetMonitorWallpaper || SessionData.wallpaperPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,11 +97,61 @@ Singleton {
|
|||||||
property var matugenColors: ({})
|
property var matugenColors: ({})
|
||||||
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) => {
|
||||||
|
matugenAvailable = (code === 0) && !envDisableMatugen
|
||||||
|
const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode)
|
||||||
|
|
||||||
|
if (!matugenAvailable || isGreeterMode) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colorsFileLoadFailed && currentTheme === dynamic && rawWallpaperPath) {
|
||||||
|
console.info("Theme: Matugen now available, regenerating colors for dynamic theme")
|
||||||
|
const isLight = (typeof SessionData !== "undefined" && SessionData.isLightMode)
|
||||||
|
const iconTheme = (typeof SettingsData !== "undefined" && SettingsData.iconTheme) ? SettingsData.iconTheme : "System Default"
|
||||||
|
const selectedMatugenType = (typeof SettingsData !== "undefined" && SettingsData.matugenScheme) ? SettingsData.matugenScheme : "scheme-tonal-spot"
|
||||||
|
const effectivePath = rawWallpaperPath.startsWith("we:") ? (stateDir + "/we_screenshots/" + rawWallpaperPath.substring(3) + ".jpg") : rawWallpaperPath
|
||||||
|
if (effectivePath.startsWith("#")) {
|
||||||
|
setDesiredTheme("hex", effectivePath, isLight, iconTheme, selectedMatugenType)
|
||||||
|
} else {
|
||||||
|
setDesiredTheme("image", effectivePath, 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 (rawWallpaperPath) {
|
||||||
|
const selectedMatugenType = (typeof SettingsData !== "undefined" && SettingsData.matugenScheme) ? SettingsData.matugenScheme : "scheme-tonal-spot"
|
||||||
|
const effectivePath = rawWallpaperPath.startsWith("we:") ? (stateDir + "/we_screenshots/" + rawWallpaperPath.substring(3) + ".jpg") : rawWallpaperPath
|
||||||
|
if (effectivePath.startsWith("#")) {
|
||||||
|
setDesiredTheme("hex", effectivePath, isLight, iconTheme, selectedMatugenType)
|
||||||
|
} else {
|
||||||
|
setDesiredTheme("image", effectivePath, 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) {
|
||||||
|
setDesiredTheme("hex", primaryColor, isLight, iconTheme, matugenType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 0)
|
||||||
if (typeof SessionData !== "undefined") {
|
if (typeof SessionData !== "undefined") {
|
||||||
SessionData.isLightModeChanged.connect(root.onLightModeChanged)
|
SessionData.isLightModeChanged.connect(root.onLightModeChanged)
|
||||||
}
|
}
|
||||||
@@ -140,20 +209,51 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property var availableMatugenSchemes: [
|
readonly property var availableMatugenSchemes: [({
|
||||||
({ "value": "scheme-tonal-spot", "label": "Tonal Spot", "description": "Balanced palette with focused accents (default)." }),
|
"value": "scheme-tonal-spot",
|
||||||
({ "value": "scheme-content", "label": "Content", "description": "Derives colors that closely match the underlying image." }),
|
"label": "Tonal Spot",
|
||||||
({ "value": "scheme-expressive", "label": "Expressive", "description": "Vibrant palette with playful saturation." }),
|
"description": I18n.tr("Balanced palette with focused accents (default).")
|
||||||
({ "value": "scheme-fidelity", "label": "Fidelity", "description": "High-fidelity palette that preserves source hues." }),
|
}), ({
|
||||||
({ "value": "scheme-fruit-salad", "label": "Fruit Salad", "description": "Colorful mix of bright contrasting accents." }),
|
"value": "scheme-vibrant-spot",
|
||||||
({ "value": "scheme-monochrome", "label": "Monochrome", "description": "Minimal palette built around a single hue." }),
|
"label": "Vibrant Spot",
|
||||||
({ "value": "scheme-neutral", "label": "Neutral", "description": "Muted palette with subdued, calming tones." }),
|
"description": I18n.tr("Lively palette with saturated accents.")
|
||||||
({ "value": "scheme-rainbow", "label": "Rainbow", "description": "Diverse palette spanning the full spectrum." })
|
}), ({
|
||||||
]
|
"value": "scheme-dynamic-contrast",
|
||||||
|
"label": "Dynamic Contrast",
|
||||||
|
"description": I18n.tr("High-contrast palette for strong visual distinction.")
|
||||||
|
}), ({
|
||||||
|
"value": "scheme-content",
|
||||||
|
"label": "Content",
|
||||||
|
"description": I18n.tr("Derives colors that closely match the underlying image.")
|
||||||
|
}), ({
|
||||||
|
"value": "scheme-expressive",
|
||||||
|
"label": "Expressive",
|
||||||
|
"description": I18n.tr("Vibrant palette with playful saturation.")
|
||||||
|
}), ({
|
||||||
|
"value": "scheme-fidelity",
|
||||||
|
"label": "Fidelity",
|
||||||
|
"description": I18n.tr("High-fidelity palette that preserves source hues.")
|
||||||
|
}), ({
|
||||||
|
"value": "scheme-fruit-salad",
|
||||||
|
"label": "Fruit Salad",
|
||||||
|
"description": I18n.tr("Colorful mix of bright contrasting accents.")
|
||||||
|
}), ({
|
||||||
|
"value": "scheme-monochrome",
|
||||||
|
"label": "Monochrome",
|
||||||
|
"description": I18n.tr("Minimal palette built around a single hue.")
|
||||||
|
}), ({
|
||||||
|
"value": "scheme-neutral",
|
||||||
|
"label": "Neutral",
|
||||||
|
"description": I18n.tr("Muted palette with subdued, calming tones.")
|
||||||
|
}), ({
|
||||||
|
"value": "scheme-rainbow",
|
||||||
|
"label": "Rainbow",
|
||||||
|
"description": I18n.tr("Diverse palette spanning the full spectrum.")
|
||||||
|
})]
|
||||||
|
|
||||||
function getMatugenScheme(value) {
|
function getMatugenScheme(value) {
|
||||||
const schemes = availableMatugenSchemes
|
const schemes = availableMatugenSchemes
|
||||||
for (let i = 0; i < schemes.length; i++) {
|
for (var i = 0; i < schemes.length; i++) {
|
||||||
if (schemes[i].value === value)
|
if (schemes[i].value === value)
|
||||||
return schemes[i]
|
return schemes[i]
|
||||||
}
|
}
|
||||||
@@ -240,13 +340,37 @@ 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)
|
||||||
|
|
||||||
readonly property var animationDurations: [
|
readonly property var animationDurations: [{
|
||||||
{ shorter: 0, short: 0, medium: 0, long: 0, extraLong: 0 },
|
"shorter": 0,
|
||||||
{ shorter: 50, short: 75, medium: 150, long: 250, extraLong: 500 },
|
"short": 0,
|
||||||
{ shorter: 100, short: 150, medium: 300, long: 500, extraLong: 1000 },
|
"medium": 0,
|
||||||
{ shorter: 150, short: 225, medium: 450, long: 750, extraLong: 1500 },
|
"long": 0,
|
||||||
{ shorter: 200, short: 300, medium: 600, long: 1000, extraLong: 2000 }
|
"extraLong": 0
|
||||||
]
|
}, {
|
||||||
|
"shorter": 50,
|
||||||
|
"short": 75,
|
||||||
|
"medium": 150,
|
||||||
|
"long": 250,
|
||||||
|
"extraLong": 500
|
||||||
|
}, {
|
||||||
|
"shorter": 100,
|
||||||
|
"short": 150,
|
||||||
|
"medium": 300,
|
||||||
|
"long": 500,
|
||||||
|
"extraLong": 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 int currentAnimationSpeed: typeof SettingsData !== "undefined" ? SettingsData.animationSpeed : SettingsData.AnimationSpeed.Short
|
||||||
readonly property var currentDurations: animationDurations[currentAnimationSpeed] || animationDurations[SettingsData.AnimationSpeed.Short]
|
readonly property var currentDurations: animationDurations[currentAnimationSpeed] || animationDurations[SettingsData.AnimationSpeed.Short]
|
||||||
@@ -259,6 +383,62 @@ Singleton {
|
|||||||
property int standardEasing: Easing.OutCubic
|
property int standardEasing: Easing.OutCubic
|
||||||
property int emphasizedEasing: Easing.OutQuart
|
property int emphasizedEasing: Easing.OutQuart
|
||||||
|
|
||||||
|
readonly property var expressiveCurves: {
|
||||||
|
"emphasized": [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1],
|
||||||
|
"emphasizedAccel": [0.3, 0, 0.8, 0.15, 1, 1],
|
||||||
|
"emphasizedDecel": [0.05, 0.7, 0.1, 1, 1, 1],
|
||||||
|
"standard": [0.2, 0, 0, 1, 1, 1],
|
||||||
|
"standardAccel": [0.3, 0, 1, 1, 1, 1],
|
||||||
|
"standardDecel": [0, 0, 0, 1, 1, 1],
|
||||||
|
"expressiveFastSpatial": [0.42, 1.67, 0.21, 0.9, 1, 1],
|
||||||
|
"expressiveDefaultSpatial": [0.38, 1.21, 0.22, 1, 1, 1],
|
||||||
|
"expressiveEffects": [0.34, 0.8, 0.34, 1, 1, 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property var animationPresetDurations: {
|
||||||
|
"none": 0,
|
||||||
|
"short": 250,
|
||||||
|
"medium": 500,
|
||||||
|
"long": 750
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property int currentAnimationBaseDuration: {
|
||||||
|
if (typeof SettingsData === "undefined")
|
||||||
|
return 500
|
||||||
|
|
||||||
|
if (SettingsData.animationSpeed === SettingsData.AnimationSpeed.Custom) {
|
||||||
|
return SettingsData.customAnimationDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
const presetMap = [0, 250, 500, 750]
|
||||||
|
return presetMap[SettingsData.animationSpeed] !== undefined ? presetMap[SettingsData.animationSpeed] : 500
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property var expressiveDurations: {
|
||||||
|
if (typeof SettingsData === "undefined") {
|
||||||
|
return {
|
||||||
|
"fast": 200,
|
||||||
|
"normal": 400,
|
||||||
|
"large": 600,
|
||||||
|
"extraLarge": 1000,
|
||||||
|
"expressiveFastSpatial": 350,
|
||||||
|
"expressiveDefaultSpatial": 500,
|
||||||
|
"expressiveEffects": 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseDuration = currentAnimationBaseDuration
|
||||||
|
return {
|
||||||
|
"fast": baseDuration * 0.4,
|
||||||
|
"normal": baseDuration * 0.8,
|
||||||
|
"large": baseDuration * 1.2,
|
||||||
|
"extraLarge": baseDuration * 2.0,
|
||||||
|
"expressiveFastSpatial": baseDuration * 0.7,
|
||||||
|
"expressiveDefaultSpatial": baseDuration,
|
||||||
|
"expressiveEffects": baseDuration * 0.4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
property real cornerRadius: typeof SettingsData !== "undefined" ? SettingsData.cornerRadius : 12
|
property real cornerRadius: typeof SettingsData !== "undefined" ? SettingsData.cornerRadius : 12
|
||||||
property real spacingXS: 4
|
property real spacingXS: 4
|
||||||
property real spacingS: 8
|
property real spacingS: 8
|
||||||
@@ -327,11 +507,13 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode)
|
const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode)
|
||||||
isLightMode = light
|
|
||||||
if (savePrefs && typeof SessionData !== "undefined" && !isGreeterMode)
|
if (savePrefs && typeof SessionData !== "undefined" && !isGreeterMode)
|
||||||
SessionData.setLightMode(isLightMode)
|
SessionData.setLightMode(light)
|
||||||
if (!isGreeterMode) {
|
if (!isGreeterMode) {
|
||||||
PortalService.setLightMode(isLightMode)
|
// Skip with matugen becuase, our script runner will do it.
|
||||||
|
if (!matugenAvailable) {
|
||||||
|
PortalService.setLightMode(light)
|
||||||
|
}
|
||||||
generateSystemThemesFromCurrentTheme()
|
generateSystemThemesFromCurrentTheme()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -372,20 +554,40 @@ Singleton {
|
|||||||
|
|
||||||
function getCatppuccinColor(variantName) {
|
function getCatppuccinColor(variantName) {
|
||||||
const catColors = {
|
const catColors = {
|
||||||
"cat-rosewater": "#f5e0dc", "cat-flamingo": "#f2cdcd", "cat-pink": "#f5c2e7", "cat-mauve": "#cba6f7",
|
"cat-rosewater": "#f5e0dc",
|
||||||
"cat-red": "#f38ba8", "cat-maroon": "#eba0ac", "cat-peach": "#fab387", "cat-yellow": "#f9e2af",
|
"cat-flamingo": "#f2cdcd",
|
||||||
"cat-green": "#a6e3a1", "cat-teal": "#94e2d5", "cat-sky": "#89dceb", "cat-sapphire": "#74c7ec",
|
"cat-pink": "#f5c2e7",
|
||||||
"cat-blue": "#89b4fa", "cat-lavender": "#b4befe"
|
"cat-mauve": "#cba6f7",
|
||||||
|
"cat-red": "#f38ba8",
|
||||||
|
"cat-maroon": "#eba0ac",
|
||||||
|
"cat-peach": "#fab387",
|
||||||
|
"cat-yellow": "#f9e2af",
|
||||||
|
"cat-green": "#a6e3a1",
|
||||||
|
"cat-teal": "#94e2d5",
|
||||||
|
"cat-sky": "#89dceb",
|
||||||
|
"cat-sapphire": "#74c7ec",
|
||||||
|
"cat-blue": "#89b4fa",
|
||||||
|
"cat-lavender": "#b4befe"
|
||||||
}
|
}
|
||||||
return catColors[variantName] || "#cba6f7"
|
return catColors[variantName] || "#cba6f7"
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCatppuccinVariantName(variantName) {
|
function getCatppuccinVariantName(variantName) {
|
||||||
const catNames = {
|
const catNames = {
|
||||||
"cat-rosewater": "Rosewater", "cat-flamingo": "Flamingo", "cat-pink": "Pink", "cat-mauve": "Mauve",
|
"cat-rosewater": "Rosewater",
|
||||||
"cat-red": "Red", "cat-maroon": "Maroon", "cat-peach": "Peach", "cat-yellow": "Yellow",
|
"cat-flamingo": "Flamingo",
|
||||||
"cat-green": "Green", "cat-teal": "Teal", "cat-sky": "Sky", "cat-sapphire": "Sapphire",
|
"cat-pink": "Pink",
|
||||||
"cat-blue": "Blue", "cat-lavender": "Lavender"
|
"cat-mauve": "Mauve",
|
||||||
|
"cat-red": "Red",
|
||||||
|
"cat-maroon": "Maroon",
|
||||||
|
"cat-peach": "Peach",
|
||||||
|
"cat-yellow": "Yellow",
|
||||||
|
"cat-green": "Green",
|
||||||
|
"cat-teal": "Teal",
|
||||||
|
"cat-sky": "Sky",
|
||||||
|
"cat-sapphire": "Sapphire",
|
||||||
|
"cat-blue": "Blue",
|
||||||
|
"cat-lavender": "Lavender"
|
||||||
}
|
}
|
||||||
return catNames[variantName] || "Unknown"
|
return catNames[variantName] || "Unknown"
|
||||||
}
|
}
|
||||||
@@ -428,14 +630,14 @@ Singleton {
|
|||||||
const colorMode = typeof SettingsData !== "undefined" ? SettingsData.widgetBackgroundColor : "sch"
|
const colorMode = typeof SettingsData !== "undefined" ? SettingsData.widgetBackgroundColor : "sch"
|
||||||
switch (colorMode) {
|
switch (colorMode) {
|
||||||
case "s":
|
case "s":
|
||||||
return surface
|
return surface
|
||||||
case "sc":
|
case "sc":
|
||||||
return surfaceContainer
|
return surfaceContainer
|
||||||
case "sch":
|
case "sch":
|
||||||
return surfaceContainerHigh
|
return surfaceContainerHigh
|
||||||
case "sth":
|
case "sth":
|
||||||
default:
|
default:
|
||||||
return surfaceTextHover
|
return surfaceTextHover
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -449,14 +651,14 @@ Singleton {
|
|||||||
const colorMode = typeof SettingsData !== "undefined" ? SettingsData.widgetBackgroundColor : "sch"
|
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)
|
||||||
case "sc":
|
case "sc":
|
||||||
return Qt.rgba(surfaceContainer.r, surfaceContainer.g, surfaceContainer.b, widgetTransparency)
|
return Qt.rgba(surfaceContainer.r, surfaceContainer.g, surfaceContainer.b, widgetTransparency)
|
||||||
case "sch":
|
case "sch":
|
||||||
return Qt.rgba(surfaceContainerHigh.r, surfaceContainerHigh.g, surfaceContainerHigh.b, widgetTransparency)
|
return Qt.rgba(surfaceContainerHigh.r, surfaceContainerHigh.g, surfaceContainerHigh.b, widgetTransparency)
|
||||||
case "sth":
|
case "sth":
|
||||||
default:
|
default:
|
||||||
return Qt.rgba(surfaceContainer.r, surfaceContainer.g, surfaceContainer.b, widgetTransparency)
|
return Qt.rgba(surfaceContainer.r, surfaceContainer.g, surfaceContainer.b, widgetTransparency)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -480,8 +682,10 @@ Singleton {
|
|||||||
function barTextSize(barThickness) {
|
function barTextSize(barThickness) {
|
||||||
const scale = barThickness / 48
|
const scale = barThickness / 48
|
||||||
const dankBarScale = (typeof SettingsData !== "undefined" ? SettingsData.dankBarFontScale : 1.0)
|
const dankBarScale = (typeof SettingsData !== "undefined" ? SettingsData.dankBarFontScale : 1.0)
|
||||||
if (scale <= 0.75) return fontSizeSmall * 0.9 * dankBarScale
|
if (scale <= 0.75)
|
||||||
if (scale >= 1.25) return fontSizeMedium * dankBarScale
|
return fontSizeSmall * 0.9 * dankBarScale
|
||||||
|
if (scale >= 1.25)
|
||||||
|
return fontSizeMedium * dankBarScale
|
||||||
return fontSizeSmall * dankBarScale
|
return fontSizeSmall * dankBarScale
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -575,7 +779,6 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function onLightModeChanged() {
|
function onLightModeChanged() {
|
||||||
if (currentTheme === "custom" && customThemeFileView.path) {
|
if (currentTheme === "custom" && customThemeFileView.path) {
|
||||||
customThemeFileView.reload()
|
customThemeFileView.reload()
|
||||||
@@ -584,10 +787,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.info("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()
|
||||||
}
|
}
|
||||||
@@ -606,15 +811,15 @@ Singleton {
|
|||||||
const desiredPath = stateDir + "/matugen.desired.json"
|
const desiredPath = stateDir + "/matugen.desired.json"
|
||||||
|
|
||||||
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`])
|
||||||
|
Quickshell.execDetached(["rm", "-f", stateDir + "/matugen.key"])
|
||||||
workerRunning = true
|
workerRunning = true
|
||||||
|
const syncModeWithPortal = (typeof SettingsData !== "undefined" && SettingsData.syncModeWithPortal) ? "true" : "false"
|
||||||
if (rawWallpaperPath.startsWith("we:")) {
|
if (rawWallpaperPath.startsWith("we:")) {
|
||||||
console.log("calling matugen worker")
|
console.log("Theme: Starting matugen worker (WE wallpaper, waiting for screenshot)")
|
||||||
systemThemeGenerator.command = [
|
systemThemeGenerator.command = ["sh", "-c", `sleep 3 && ${shellDir}/scripts/matugen-worker.sh '${stateDir}' '${shellDir}' '${configDir}' '${syncModeWithPortal}' --run`]
|
||||||
"sh", "-c",
|
|
||||||
`sleep 1 && ${shellDir}/scripts/matugen-worker.sh '${stateDir}' '${shellDir}' '${configDir}' --run`
|
|
||||||
]
|
|
||||||
} else {
|
} else {
|
||||||
systemThemeGenerator.command = [shellDir + "/scripts/matugen-worker.sh", stateDir, shellDir, configDir, "--run"]
|
console.log("Theme: Starting matugen worker")
|
||||||
|
systemThemeGenerator.command = [shellDir + "/scripts/matugen-worker.sh", stateDir, shellDir, configDir, syncModeWithPortal, "--run"]
|
||||||
}
|
}
|
||||||
systemThemeGenerator.running = true
|
systemThemeGenerator.running = true
|
||||||
}
|
}
|
||||||
@@ -628,14 +833,15 @@ Singleton {
|
|||||||
const iconTheme = (typeof SettingsData !== "undefined" && SettingsData.iconTheme) ? SettingsData.iconTheme : "System Default"
|
const iconTheme = (typeof SettingsData !== "undefined" && SettingsData.iconTheme) ? SettingsData.iconTheme : "System Default"
|
||||||
|
|
||||||
if (currentTheme === dynamic) {
|
if (currentTheme === dynamic) {
|
||||||
if (!wallpaperPath) {
|
if (!rawWallpaperPath) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const selectedMatugenType = (typeof SettingsData !== "undefined" && SettingsData.matugenScheme) ? SettingsData.matugenScheme : "scheme-tonal-spot"
|
const selectedMatugenType = (typeof SettingsData !== "undefined" && SettingsData.matugenScheme) ? SettingsData.matugenScheme : "scheme-tonal-spot"
|
||||||
if (wallpaperPath.startsWith("#")) {
|
const effectivePath = rawWallpaperPath.startsWith("we:") ? (stateDir + "/we_screenshots/" + rawWallpaperPath.substring(3) + ".jpg") : rawWallpaperPath
|
||||||
setDesiredTheme("hex", wallpaperPath, isLight, iconTheme, selectedMatugenType)
|
if (effectivePath.startsWith("#")) {
|
||||||
|
setDesiredTheme("hex", effectivePath, isLight, iconTheme, selectedMatugenType)
|
||||||
} else {
|
} else {
|
||||||
setDesiredTheme("image", wallpaperPath, isLight, iconTheme, selectedMatugenType)
|
setDesiredTheme("image", effectivePath, isLight, iconTheme, selectedMatugenType)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let primaryColor
|
let primaryColor
|
||||||
@@ -669,8 +875,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() {
|
||||||
@@ -681,11 +896,45 @@ 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")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (typeof ToastService !== "undefined") {
|
||||||
|
ToastService.showError("Failed to apply Qt colors")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function withAlpha(c, a) { return Qt.rgba(c.r, c.g, c.b, a); }
|
function withAlpha(c, a) {
|
||||||
|
return Qt.rgba(c.r, c.g, c.b, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFillMode(modeName) {
|
||||||
|
switch (modeName) {
|
||||||
|
case "Stretch":
|
||||||
|
return Image.Stretch
|
||||||
|
case "Fit":
|
||||||
|
case "PreserveAspectFit":
|
||||||
|
return Image.PreserveAspectFit
|
||||||
|
case "Fill":
|
||||||
|
case "PreserveAspectCrop":
|
||||||
|
return Image.PreserveAspectCrop
|
||||||
|
case "Tile":
|
||||||
|
return Image.Tile
|
||||||
|
case "TileVertically":
|
||||||
|
return Image.TileVertically
|
||||||
|
case "TileHorizontally":
|
||||||
|
return Image.TileHorizontally
|
||||||
|
case "Pad":
|
||||||
|
return Image.Pad
|
||||||
|
default:
|
||||||
|
return Image.PreserveAspectCrop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function snap(value, dpr) {
|
function snap(value, dpr) {
|
||||||
const s = dpr || 1
|
const s = dpr || 1
|
||||||
@@ -702,40 +951,48 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function invertHex(hex) {
|
function invertHex(hex) {
|
||||||
hex = hex.replace('#', '');
|
hex = hex.replace('#', '')
|
||||||
|
|
||||||
if (!/^[0-9A-Fa-f]{6}$/.test(hex)) {
|
if (!/^[0-9A-Fa-f]{6}$/.test(hex)) {
|
||||||
return hex;
|
return hex
|
||||||
}
|
}
|
||||||
|
|
||||||
const r = parseInt(hex.substr(0, 2), 16);
|
const r = parseInt(hex.substr(0, 2), 16)
|
||||||
const g = parseInt(hex.substr(2, 2), 16);
|
const g = parseInt(hex.substr(2, 2), 16)
|
||||||
const b = parseInt(hex.substr(4, 2), 16);
|
const b = parseInt(hex.substr(4, 2), 16)
|
||||||
|
|
||||||
const invR = (255 - r).toString(16).padStart(2, '0');
|
const invR = (255 - r).toString(16).padStart(2, '0')
|
||||||
const invG = (255 - g).toString(16).padStart(2, '0');
|
const invG = (255 - g).toString(16).padStart(2, '0')
|
||||||
const invB = (255 - b).toString(16).padStart(2, '0');
|
const invB = (255 - b).toString(16).padStart(2, '0')
|
||||||
|
|
||||||
return `#${invR}${invG}${invB}`;
|
return `#${invR}${invG}${invB}`
|
||||||
}
|
}
|
||||||
|
|
||||||
property string baseLogoColor: {
|
property string baseLogoColor: {
|
||||||
if (typeof SettingsData === "undefined") return ""
|
if (typeof SettingsData === "undefined")
|
||||||
|
return ""
|
||||||
const colorOverride = SettingsData.launcherLogoColorOverride
|
const colorOverride = SettingsData.launcherLogoColorOverride
|
||||||
if (!colorOverride || colorOverride === "") return ""
|
if (!colorOverride || colorOverride === "")
|
||||||
if (colorOverride === "primary") return primary
|
return ""
|
||||||
if (colorOverride === "surface") return surfaceText
|
if (colorOverride === "primary")
|
||||||
|
return primary
|
||||||
|
if (colorOverride === "surface")
|
||||||
|
return surfaceText
|
||||||
return colorOverride
|
return colorOverride
|
||||||
}
|
}
|
||||||
|
|
||||||
property string effectiveLogoColor: {
|
property string effectiveLogoColor: {
|
||||||
if (typeof SettingsData === "undefined") return ""
|
if (typeof SettingsData === "undefined")
|
||||||
|
return ""
|
||||||
|
|
||||||
const colorOverride = SettingsData.launcherLogoColorOverride
|
const colorOverride = SettingsData.launcherLogoColorOverride
|
||||||
if (!colorOverride || colorOverride === "") return ""
|
if (!colorOverride || colorOverride === "")
|
||||||
|
return ""
|
||||||
|
|
||||||
if (colorOverride === "primary") return primary
|
if (colorOverride === "primary")
|
||||||
if (colorOverride === "surface") return surfaceText
|
return primary
|
||||||
|
if (colorOverride === "surface")
|
||||||
|
return surfaceText
|
||||||
|
|
||||||
if (!SettingsData.launcherLogoColorInvertOnMode) {
|
if (!SettingsData.launcherLogoColorInvertOnMode) {
|
||||||
return colorOverride
|
return colorOverride
|
||||||
@@ -748,59 +1005,6 @@ Singleton {
|
|||||||
return colorOverride
|
return colorOverride
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: matugenCheck
|
|
||||||
command: ["which", "matugen"]
|
|
||||||
onExited: code => {
|
|
||||||
matugenAvailable = (code === 0) && !envDisableMatugen
|
|
||||||
const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode)
|
|
||||||
|
|
||||||
if (!matugenAvailable || isGreeterMode) {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: ensureStateDir
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: systemThemeGenerator
|
id: systemThemeGenerator
|
||||||
running: false
|
running: false
|
||||||
@@ -808,61 +1012,19 @@ Singleton {
|
|||||||
onExited: exitCode => {
|
onExited: exitCode => {
|
||||||
workerRunning = false
|
workerRunning = false
|
||||||
|
|
||||||
if (exitCode !== 0 && exitCode !== 2) {
|
if (exitCode === 0) {
|
||||||
|
console.info("Theme: Matugen worker completed successfully")
|
||||||
|
if (currentTheme === dynamic) {
|
||||||
|
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -899,9 +1061,7 @@ Singleton {
|
|||||||
id: dynamicColorsFileView
|
id: dynamicColorsFileView
|
||||||
path: {
|
path: {
|
||||||
const greetCfgDir = Quickshell.env("DMS_GREET_CFG_DIR") || "/etc/greetd/.dms"
|
const greetCfgDir = Quickshell.env("DMS_GREET_CFG_DIR") || "/etc/greetd/.dms"
|
||||||
const colorsPath = SessionData.isGreeterMode
|
const colorsPath = SessionData.isGreeterMode ? greetCfgDir + "/colors.json" : stateDir + "/dms-colors.json"
|
||||||
? greetCfgDir + "/colors.json"
|
|
||||||
: stateDir + "/dms-colors.json"
|
|
||||||
return colorsPath
|
return colorsPath
|
||||||
}
|
}
|
||||||
watchChanges: currentTheme === dynamic && !SessionData.isGreeterMode
|
watchChanges: currentTheme === dynamic && !SessionData.isGreeterMode
|
||||||
@@ -926,6 +1086,8 @@ Singleton {
|
|||||||
|
|
||||||
onLoaded: {
|
onLoaded: {
|
||||||
if (currentTheme === dynamic) {
|
if (currentTheme === dynamic) {
|
||||||
|
console.info("Theme: Dynamic colors file loaded successfully")
|
||||||
|
colorsFileLoadFailed = false
|
||||||
parseAndLoadColors()
|
parseAndLoadColors()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -937,10 +1099,20 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onLoadFailed: function (error) {
|
onLoadFailed: function (error) {
|
||||||
if (currentTheme === dynamic && typeof ToastService !== "undefined") {
|
if (currentTheme === dynamic) {
|
||||||
ToastService.showError("Failed to read dynamic colors: " + error)
|
console.warn("Theme: Dynamic colors file load failed, marking for regeneration")
|
||||||
|
colorsFileLoadFailed = true
|
||||||
|
const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode)
|
||||||
|
if (!isGreeterMode && matugenAvailable && rawWallpaperPath) {
|
||||||
|
console.log("Theme: Matugen available, triggering immediate regeneration")
|
||||||
|
generateSystemThemesFromCurrentTheme()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onPathChanged: {
|
||||||
|
colorsFileLoadFailed = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
|
|||||||
@@ -1,29 +1,11 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Wayland
|
|
||||||
import Quickshell.Services.Greetd
|
import Quickshell.Services.Greetd
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modules.Greetd
|
import qs.Modules.Greetd
|
||||||
|
|
||||||
Item {
|
Scope {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
WlSessionLock {
|
GreeterSurface {}
|
||||||
id: sessionLock
|
|
||||||
locked: false
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
Qt.callLater(() => { locked = true })
|
|
||||||
}
|
|
||||||
|
|
||||||
onLockedChanged: {
|
|
||||||
if (!locked) {
|
|
||||||
console.log("Greetd session unlocked, exiting")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GreeterSurface {
|
|
||||||
lock: sessionLock
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
1014
DMSShell.qml
1014
DMSShell.qml
File diff suppressed because it is too large
Load Diff
149
DMSShellIPC.qml
149
DMSShellIPC.qml
@@ -13,6 +13,9 @@ Item {
|
|||||||
required property var controlCenterLoader
|
required property var controlCenterLoader
|
||||||
required property var dankDashPopoutLoader
|
required property var dankDashPopoutLoader
|
||||||
required property var notepadSlideoutVariants
|
required property var notepadSlideoutVariants
|
||||||
|
required property var hyprKeybindsModalLoader
|
||||||
|
required property var dankBarLoader
|
||||||
|
required property var hyprlandOverviewLoader
|
||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
function open() {
|
function open() {
|
||||||
@@ -75,9 +78,8 @@ Item {
|
|||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
function open(): string {
|
function open(): string {
|
||||||
root.controlCenterLoader.active = true
|
if (root.dankBarLoader.item) {
|
||||||
if (root.controlCenterLoader.item) {
|
root.dankBarLoader.item.triggerControlCenterOnFocusedScreen()
|
||||||
root.controlCenterLoader.item.open()
|
|
||||||
return "CONTROL_CENTER_OPEN_SUCCESS"
|
return "CONTROL_CENTER_OPEN_SUCCESS"
|
||||||
}
|
}
|
||||||
return "CONTROL_CENTER_OPEN_FAILED"
|
return "CONTROL_CENTER_OPEN_FAILED"
|
||||||
@@ -92,9 +94,8 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function toggle(): string {
|
function toggle(): string {
|
||||||
root.controlCenterLoader.active = true
|
if (root.dankBarLoader.item) {
|
||||||
if (root.controlCenterLoader.item) {
|
root.dankBarLoader.item.triggerControlCenterOnFocusedScreen()
|
||||||
root.controlCenterLoader.item.toggle()
|
|
||||||
return "CONTROL_CENTER_TOGGLE_SUCCESS"
|
return "CONTROL_CENTER_TOGGLE_SUCCESS"
|
||||||
}
|
}
|
||||||
return "CONTROL_CENTER_TOGGLE_FAILED"
|
return "CONTROL_CENTER_TOGGLE_FAILED"
|
||||||
@@ -134,24 +135,22 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function toggle(tab: string): string {
|
function toggle(tab: string): string {
|
||||||
root.dankDashPopoutLoader.active = true
|
if (root.dankBarLoader.item && root.dankBarLoader.item.triggerWallpaperBrowserOnFocusedScreen()) {
|
||||||
if (root.dankDashPopoutLoader.item) {
|
if (root.dankDashPopoutLoader.item) {
|
||||||
if (root.dankDashPopoutLoader.item.dashVisible) {
|
|
||||||
root.dankDashPopoutLoader.item.dashVisible = false
|
|
||||||
} else {
|
|
||||||
switch (tab.toLowerCase()) {
|
switch (tab.toLowerCase()) {
|
||||||
case "media":
|
case "media":
|
||||||
root.dankDashPopoutLoader.item.currentTabIndex = 1
|
root.dankDashPopoutLoader.item.currentTabIndex = 1
|
||||||
break
|
break
|
||||||
|
case "wallpaper":
|
||||||
|
root.dankDashPopoutLoader.item.currentTabIndex = 2
|
||||||
|
break
|
||||||
case "weather":
|
case "weather":
|
||||||
root.dankDashPopoutLoader.item.currentTabIndex = SettingsData.weatherEnabled ? 2 : 0
|
root.dankDashPopoutLoader.item.currentTabIndex = SettingsData.weatherEnabled ? 3 : 0
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
root.dankDashPopoutLoader.item.currentTabIndex = 0
|
root.dankDashPopoutLoader.item.currentTabIndex = 0
|
||||||
break
|
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_SUCCESS"
|
||||||
}
|
}
|
||||||
@@ -262,4 +261,126 @@ Item {
|
|||||||
|
|
||||||
target: "inhibit"
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleOverview(): string {
|
||||||
|
if (!CompositorService.isHyprland || !root.hyprlandOverviewLoader.item) {
|
||||||
|
return "HYPR_NOT_AVAILABLE"
|
||||||
|
}
|
||||||
|
root.hyprlandOverviewLoader.item.overviewOpen = !root.hyprlandOverviewLoader.item.overviewOpen
|
||||||
|
return root.hyprlandOverviewLoader.item.overviewOpen ? "OVERVIEW_OPEN_SUCCESS" : "OVERVIEW_CLOSE_SUCCESS"
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeOverview(): string {
|
||||||
|
if (!CompositorService.isHyprland || !root.hyprlandOverviewLoader.item) {
|
||||||
|
return "HYPR_NOT_AVAILABLE"
|
||||||
|
}
|
||||||
|
root.hyprlandOverviewLoader.item.overviewOpen = false
|
||||||
|
return "OVERVIEW_CLOSE_SUCCESS"
|
||||||
|
}
|
||||||
|
|
||||||
|
function openOverview(): string {
|
||||||
|
if (!CompositorService.isHyprland || !root.hyprlandOverviewLoader.item) {
|
||||||
|
return "HYPR_NOT_AVAILABLE"
|
||||||
|
}
|
||||||
|
root.hyprlandOverviewLoader.item.overviewOpen = true
|
||||||
|
return "OVERVIEW_OPEN_SUCCESS"
|
||||||
|
}
|
||||||
|
|
||||||
|
target: "hypr"
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
function wallpaper(): string {
|
||||||
|
if (root.dankBarLoader.item && root.dankBarLoader.item.triggerWallpaperBrowserOnFocusedScreen()) {
|
||||||
|
return "SUCCESS: Toggled wallpaper browser"
|
||||||
|
}
|
||||||
|
return "ERROR: Failed to toggle wallpaper browser"
|
||||||
|
}
|
||||||
|
|
||||||
|
target: "dankdash"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
362
Modals/BluetoothPairingModal.qml
Normal file
362
Modals/BluetoothPairingModal.qml
Normal file
@@ -0,0 +1,362 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
import qs.Modals.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
DankModal {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string deviceName: ""
|
||||||
|
property string deviceAddress: ""
|
||||||
|
property string requestType: ""
|
||||||
|
property string token: ""
|
||||||
|
property int passkey: 0
|
||||||
|
property string pinInput: ""
|
||||||
|
property string passkeyInput: ""
|
||||||
|
|
||||||
|
function show(pairingData) {
|
||||||
|
token = pairingData.token || ""
|
||||||
|
deviceName = pairingData.deviceName || ""
|
||||||
|
deviceAddress = pairingData.deviceAddr || ""
|
||||||
|
requestType = pairingData.requestType || ""
|
||||||
|
passkey = pairingData.passkey || 0
|
||||||
|
pinInput = ""
|
||||||
|
passkeyInput = ""
|
||||||
|
|
||||||
|
open()
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (contentLoader.item) {
|
||||||
|
if (requestType === "pin" && contentLoader.item.pinInputField) {
|
||||||
|
contentLoader.item.pinInputField.forceActiveFocus()
|
||||||
|
} else if (requestType === "passkey" && contentLoader.item.passkeyInputField) {
|
||||||
|
contentLoader.item.passkeyInputField.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldBeVisible: false
|
||||||
|
width: 420
|
||||||
|
height: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 240
|
||||||
|
|
||||||
|
onShouldBeVisibleChanged: () => {
|
||||||
|
if (!shouldBeVisible) {
|
||||||
|
pinInput = ""
|
||||||
|
passkeyInput = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onOpened: {
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (contentLoader.item) {
|
||||||
|
if (requestType === "pin" && contentLoader.item.pinInputField) {
|
||||||
|
contentLoader.item.pinInputField.forceActiveFocus()
|
||||||
|
} else if (requestType === "passkey" && contentLoader.item.passkeyInputField) {
|
||||||
|
contentLoader.item.passkeyInputField.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onBackgroundClicked: () => {
|
||||||
|
DMSService.bluetoothCancelPairing(token)
|
||||||
|
close()
|
||||||
|
pinInput = ""
|
||||||
|
passkeyInput = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
content: Component {
|
||||||
|
FocusScope {
|
||||||
|
id: pairingContent
|
||||||
|
|
||||||
|
property alias pinInputField: pinInputField
|
||||||
|
property alias passkeyInputField: passkeyInputField
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
focus: true
|
||||||
|
implicitHeight: mainColumn.implicitHeight
|
||||||
|
|
||||||
|
Keys.onEscapePressed: event => {
|
||||||
|
DMSService.bluetoothCancelPairing(token)
|
||||||
|
close()
|
||||||
|
pinInput = ""
|
||||||
|
passkeyInput = ""
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: mainColumn
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.leftMargin: Theme.spacingM
|
||||||
|
anchors.rightMargin: Theme.spacingM
|
||||||
|
anchors.topMargin: Theme.spacingM
|
||||||
|
spacing: requestType === "pin" || requestType === "passkey" ? Theme.spacingM : Theme.spacingS
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Pair Bluetooth Device")
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
if (requestType === "confirm")
|
||||||
|
return I18n.tr("Confirm passkey for ") + deviceName
|
||||||
|
if (requestType === "authorize")
|
||||||
|
return I18n.tr("Authorize pairing with ") + deviceName
|
||||||
|
if (requestType.startsWith("authorize-service"))
|
||||||
|
return I18n.tr("Authorize service for ") + deviceName
|
||||||
|
if (requestType === "pin")
|
||||||
|
return I18n.tr("Enter PIN for ") + deviceName
|
||||||
|
if (requestType === "passkey")
|
||||||
|
return I18n.tr("Enter passkey for ") + deviceName
|
||||||
|
return deviceName
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceTextMedium
|
||||||
|
width: parent.width - 40
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 50
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.surfaceHover
|
||||||
|
border.color: pinInputField.activeFocus ? Theme.primary : Theme.outlineStrong
|
||||||
|
border.width: pinInputField.activeFocus ? 2 : 1
|
||||||
|
visible: requestType === "pin"
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: () => {
|
||||||
|
pinInputField.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankTextField {
|
||||||
|
id: pinInputField
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
textColor: Theme.surfaceText
|
||||||
|
text: pinInput
|
||||||
|
placeholderText: I18n.tr("Enter PIN")
|
||||||
|
backgroundColor: "transparent"
|
||||||
|
enabled: root.shouldBeVisible
|
||||||
|
onTextEdited: () => {
|
||||||
|
pinInput = text
|
||||||
|
}
|
||||||
|
onAccepted: () => {
|
||||||
|
submitPairing()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 50
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.surfaceHover
|
||||||
|
border.color: passkeyInputField.activeFocus ? Theme.primary : Theme.outlineStrong
|
||||||
|
border.width: passkeyInputField.activeFocus ? 2 : 1
|
||||||
|
visible: requestType === "passkey"
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: () => {
|
||||||
|
passkeyInputField.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankTextField {
|
||||||
|
id: passkeyInputField
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
textColor: Theme.surfaceText
|
||||||
|
text: passkeyInput
|
||||||
|
placeholderText: I18n.tr("Enter 6-digit passkey")
|
||||||
|
backgroundColor: "transparent"
|
||||||
|
enabled: root.shouldBeVisible
|
||||||
|
onTextEdited: () => {
|
||||||
|
passkeyInput = text
|
||||||
|
}
|
||||||
|
onAccepted: () => {
|
||||||
|
submitPairing()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 56
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.surfaceContainerHighest
|
||||||
|
visible: requestType === "confirm"
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Passkey:")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceTextMedium
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: String(passkey).padStart(6, "0")
|
||||||
|
font.pixelSize: Theme.fontSizeXLarge
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Bold
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: 36
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: Math.max(70, cancelText.contentWidth + Theme.spacingM * 2)
|
||||||
|
height: 36
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: cancelArea.containsMouse ? Theme.surfaceTextHover : "transparent"
|
||||||
|
border.color: Theme.surfaceVariantAlpha
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: cancelText
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: I18n.tr("Cancel")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: cancelArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: () => {
|
||||||
|
DMSService.bluetoothCancelPairing(token)
|
||||||
|
close()
|
||||||
|
pinInput = ""
|
||||||
|
passkeyInput = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: Math.max(80, pairText.contentWidth + Theme.spacingM * 2)
|
||||||
|
height: 36
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: pairArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
|
||||||
|
enabled: {
|
||||||
|
if (requestType === "pin")
|
||||||
|
return pinInput.length > 0
|
||||||
|
if (requestType === "passkey")
|
||||||
|
return passkeyInput.length === 6
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
opacity: enabled ? 1 : 0.5
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: pairText
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: {
|
||||||
|
if (requestType === "confirm")
|
||||||
|
return I18n.tr("Confirm")
|
||||||
|
if (requestType === "authorize" || requestType.startsWith("authorize-service"))
|
||||||
|
return I18n.tr("Authorize")
|
||||||
|
return I18n.tr("Pair")
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.background
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: pairArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
enabled: parent.enabled
|
||||||
|
onClicked: () => {
|
||||||
|
submitPairing()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.topMargin: Theme.spacingM
|
||||||
|
anchors.rightMargin: Theme.spacingM
|
||||||
|
iconName: "close"
|
||||||
|
iconSize: Theme.iconSize - 4
|
||||||
|
iconColor: Theme.surfaceText
|
||||||
|
onClicked: () => {
|
||||||
|
DMSService.bluetoothCancelPairing(token)
|
||||||
|
close()
|
||||||
|
pinInput = ""
|
||||||
|
passkeyInput = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitPairing() {
|
||||||
|
const secrets = {}
|
||||||
|
|
||||||
|
if (requestType === "pin") {
|
||||||
|
secrets["pin"] = pinInput
|
||||||
|
} else if (requestType === "passkey") {
|
||||||
|
secrets["passkey"] = passkeyInput
|
||||||
|
} else if (requestType === "confirm" || requestType === "authorize" || requestType.startsWith("authorize-service")) {
|
||||||
|
secrets["decision"] = "yes"
|
||||||
|
}
|
||||||
|
|
||||||
|
DMSService.bluetoothSubmitPairing(token, secrets, true, response => {
|
||||||
|
if (response.error) {
|
||||||
|
ToastService.showError(I18n.tr("Pairing failed"), response.error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
close()
|
||||||
|
pinInput = ""
|
||||||
|
passkeyInput = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -84,7 +84,7 @@ Item {
|
|||||||
id: clipboardListView
|
id: clipboardListView
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
model: filteredModel
|
model: filteredModel
|
||||||
|
|
||||||
currentIndex: clipboardContent.modal ? clipboardContent.modal.selectedIndex : 0
|
currentIndex: clipboardContent.modal ? clipboardContent.modal.selectedIndex : 0
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
interactive: true
|
interactive: true
|
||||||
@@ -94,7 +94,7 @@ Item {
|
|||||||
boundsMovement: Flickable.FollowBoundsBehavior
|
boundsMovement: Flickable.FollowBoundsBehavior
|
||||||
pressDelay: 0
|
pressDelay: 0
|
||||||
flickableDirection: Flickable.VerticalFlick
|
flickableDirection: Flickable.VerticalFlick
|
||||||
|
|
||||||
function ensureVisible(index) {
|
function ensureVisible(index) {
|
||||||
if (index < 0 || index >= count) {
|
if (index < 0 || index >= count) {
|
||||||
return
|
return
|
||||||
@@ -108,13 +108,13 @@ Item {
|
|||||||
contentY = itemBottom - height
|
contentY = itemBottom - height
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onCurrentIndexChanged: {
|
onCurrentIndexChanged: {
|
||||||
if (clipboardContent.modal && clipboardContent.modal.keyboardNavigationActive && currentIndex >= 0) {
|
if (clipboardContent.modal && clipboardContent.modal.keyboardNavigationActive && currentIndex >= 0) {
|
||||||
ensureVisible(currentIndex)
|
ensureVisible(currentIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: I18n.tr("No clipboard entries found")
|
text: I18n.tr("No clipboard entries found")
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -122,11 +122,11 @@ Item {
|
|||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
visible: filteredModel.count === 0
|
visible: filteredModel.count === 0
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: ClipboardEntry {
|
delegate: ClipboardEntry {
|
||||||
required property int index
|
required property int index
|
||||||
required property var model
|
required property var model
|
||||||
|
|
||||||
width: clipboardListView.width
|
width: clipboardListView.width
|
||||||
height: ClipboardConstants.itemHeight
|
height: ClipboardConstants.itemHeight
|
||||||
entryData: model.entry
|
entryData: model.entry
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ DankModal {
|
|||||||
shouldBeVisible: false
|
shouldBeVisible: false
|
||||||
allowStacking: true
|
allowStacking: true
|
||||||
width: 350
|
width: 350
|
||||||
height: 160
|
height: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 160
|
||||||
enableShadow: true
|
enableShadow: true
|
||||||
shouldHaveFocus: true
|
shouldHaveFocus: true
|
||||||
onBackgroundClicked: {
|
onBackgroundClicked: {
|
||||||
@@ -158,11 +158,17 @@ DankModal {
|
|||||||
content: Component {
|
content: Component {
|
||||||
Item {
|
Item {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
implicitHeight: mainColumn.implicitHeight
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.centerIn: parent
|
id: mainColumn
|
||||||
width: parent.width - Theme.spacingM * 2
|
anchors.left: parent.left
|
||||||
spacing: Theme.spacingM
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.leftMargin: Theme.spacingL
|
||||||
|
anchors.rightMargin: Theme.spacingL
|
||||||
|
anchors.topMargin: Theme.spacingL
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: confirmTitle
|
text: confirmTitle
|
||||||
@@ -173,6 +179,11 @@ DankModal {
|
|||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 1
|
||||||
|
height: Theme.spacingL
|
||||||
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: confirmMessage
|
text: confirmMessage
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
@@ -183,7 +194,8 @@ DankModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
height: Theme.spacingS
|
width: 1
|
||||||
|
height: Theme.spacingL * 1.5
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
@@ -265,6 +277,11 @@ DankModal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 1
|
||||||
|
height: Theme.spacingL
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,21 +12,12 @@ 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: {
|
readonly property real dpr: CompositorService.getScreenScale(screen)
|
||||||
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"
|
||||||
@@ -34,8 +25,11 @@ 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.shortDuration
|
property int animationDuration: Theme.expressiveDurations.expressiveDefaultSpatial
|
||||||
property var animationEasing: Theme.emphasizedEasing
|
property real animationScaleCollapsed: 0.96
|
||||||
|
property real animationOffset: Theme.spacingL
|
||||||
|
property list<real> animationEnterCurve: Theme.expressiveCurves.expressiveDefaultSpatial
|
||||||
|
property list<real> animationExitCurve: Theme.expressiveCurves.emphasized
|
||||||
property color backgroundColor: Theme.surfaceContainer
|
property color backgroundColor: Theme.surfaceContainer
|
||||||
property color borderColor: Theme.outlineMedium
|
property color borderColor: Theme.outlineMedium
|
||||||
property real borderWidth: 1
|
property real borderWidth: 1
|
||||||
@@ -103,7 +97,7 @@ PanelWindow {
|
|||||||
Timer {
|
Timer {
|
||||||
id: closeTimer
|
id: closeTimer
|
||||||
|
|
||||||
interval: animationDuration + 100
|
interval: animationDuration + 120
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
visible = false
|
visible = false
|
||||||
}
|
}
|
||||||
@@ -138,7 +132,8 @@ PanelWindow {
|
|||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: root.animationDuration
|
duration: root.animationDuration
|
||||||
easing.type: root.animationEasing
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,23 +170,69 @@ PanelWindow {
|
|||||||
border.width: root.borderWidth
|
border.width: root.borderWidth
|
||||||
clip: false
|
clip: false
|
||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
|
layer.smooth: true
|
||||||
|
layer.textureSize: Qt.size(width * Math.max(2, root.screen?.devicePixelRatio || 1), height * Math.max(2, root.screen?.devicePixelRatio || 1))
|
||||||
|
layer.samples: 4
|
||||||
opacity: root.shouldBeVisible ? 1 : 0
|
opacity: root.shouldBeVisible ? 1 : 0
|
||||||
transform: root.animationType === "slide" ? slideTransform : null
|
transform: [scaleTransform, motionTransform]
|
||||||
|
|
||||||
|
Scale {
|
||||||
|
id: scaleTransform
|
||||||
|
|
||||||
|
origin.x: contentContainer.width / 2
|
||||||
|
origin.y: contentContainer.height / 2
|
||||||
|
xScale: root.shouldBeVisible ? 1 : root.animationScaleCollapsed
|
||||||
|
yScale: root.shouldBeVisible ? 1 : root.animationScaleCollapsed
|
||||||
|
|
||||||
|
Behavior on xScale {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: root.animationDuration
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on yScale {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: root.animationDuration
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Translate {
|
Translate {
|
||||||
id: slideTransform
|
id: motionTransform
|
||||||
|
|
||||||
readonly property real rawX: root.shouldBeVisible ? 0 : 15
|
readonly property bool slide: root.animationType === "slide"
|
||||||
readonly property real rawY: root.shouldBeVisible ? 0 : -30
|
readonly property real hiddenX: slide ? 15 : 0
|
||||||
|
readonly property real hiddenY: slide ? -30 : root.animationOffset
|
||||||
|
|
||||||
x: Theme.snap(rawX, root.dpr)
|
x: Theme.snap(root.shouldBeVisible ? 0 : hiddenX, root.dpr)
|
||||||
y: Theme.snap(rawY, root.dpr)
|
y: Theme.snap(root.shouldBeVisible ? 0 : hiddenY, root.dpr)
|
||||||
|
|
||||||
|
Behavior on x {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: root.animationDuration
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on y {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: root.animationDuration
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: animationDuration
|
duration: animationDuration
|
||||||
easing.type: animationEasing
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,14 +241,44 @@ PanelWindow {
|
|||||||
focus: root.shouldBeVisible
|
focus: root.shouldBeVisible
|
||||||
clip: false
|
clip: false
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: directContentWrapper
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: contentLoader
|
id: contentLoader
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: root.keepContentLoaded || root.shouldBeVisible || root.visible
|
active: root.directContent === null && (root.keepContentLoaded || root.shouldBeVisible || root.visible)
|
||||||
asynchronous: false
|
asynchronous: false
|
||||||
focus: true
|
focus: true
|
||||||
clip: false
|
clip: false
|
||||||
|
visible: root.directContent === null
|
||||||
|
|
||||||
onLoaded: {
|
onLoaded: {
|
||||||
if (item) {
|
if (item) {
|
||||||
|
|||||||
@@ -2,17 +2,16 @@ import QtQuick
|
|||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import Quickshell.Wayland
|
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Modals.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
PanelWindow {
|
DankModal {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string pickerTitle: "Choose Color"
|
property string pickerTitle: "Choose Color"
|
||||||
property color selectedColor: Theme.primary
|
property color selectedColor: SessionData.recentColors.length > 0 ? SessionData.recentColors[0] : Theme.primary
|
||||||
property bool shouldBeVisible: false
|
|
||||||
property var onColorSelectedCallback: null
|
property var onColorSelectedCallback: null
|
||||||
|
|
||||||
signal colorSelected(color selectedColor)
|
signal colorSelected(color selectedColor)
|
||||||
@@ -25,26 +24,33 @@ PanelWindow {
|
|||||||
property real gradientX: 0
|
property real gradientX: 0
|
||||||
property real gradientY: 0
|
property real gradientY: 0
|
||||||
|
|
||||||
function open() {
|
readonly property var standardColors: [
|
||||||
currentColor = selectedColor
|
"#f44336", "#e91e63", "#9c27b0", "#673ab7", "#3f51b5", "#2196f3", "#03a9f4", "#00bcd4",
|
||||||
updateFromColor(currentColor)
|
"#009688", "#4caf50", "#8bc34a", "#cddc39", "#ffeb3b", "#ffc107", "#ff9800", "#ff5722",
|
||||||
shouldBeVisible = true
|
"#d32f2f", "#c2185b", "#7b1fa2", "#512da8", "#303f9f", "#1976d2", "#0288d1", "#0097a7",
|
||||||
Qt.callLater(() => colorContent.forceActiveFocus())
|
"#00796b", "#388e3c", "#689f38", "#afb42b", "#fbc02d", "#ffa000", "#f57c00", "#e64a19",
|
||||||
}
|
"#c62828", "#ad1457", "#6a1b9a", "#4527a0", "#283593", "#1565c0", "#0277bd", "#00838f",
|
||||||
|
"#00695c", "#2e7d32", "#558b2f", "#9e9d24", "#f9a825", "#ff8f00", "#ef6c00", "#d84315",
|
||||||
function close() {
|
"#ffffff", "#9e9e9e", "#212121"
|
||||||
shouldBeVisible = false
|
]
|
||||||
onColorSelectedCallback = null
|
|
||||||
}
|
|
||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
|
currentColor = selectedColor
|
||||||
|
updateFromColor(currentColor)
|
||||||
open()
|
open()
|
||||||
}
|
}
|
||||||
|
|
||||||
function hide() {
|
function hide() {
|
||||||
|
onColorSelectedCallback = null
|
||||||
close()
|
close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hideInstant() {
|
||||||
|
onColorSelectedCallback = null
|
||||||
|
shouldBeVisible = false
|
||||||
|
visible = false
|
||||||
|
}
|
||||||
|
|
||||||
onColorSelected: (color) => {
|
onColorSelected: (color) => {
|
||||||
if (onColorSelectedCallback) {
|
if (onColorSelectedCallback) {
|
||||||
onColorSelectedCallback(color)
|
onColorSelectedCallback(color)
|
||||||
@@ -74,96 +80,50 @@ PanelWindow {
|
|||||||
saturation = Math.max(0, Math.min(1, x))
|
saturation = Math.max(0, Math.min(1, x))
|
||||||
value = Math.max(0, Math.min(1, 1 - y))
|
value = Math.max(0, Math.min(1, 1 - y))
|
||||||
updateColor()
|
updateColor()
|
||||||
|
selectedColor = currentColor
|
||||||
}
|
}
|
||||||
|
|
||||||
function pickColorFromScreen() {
|
function pickColorFromScreen() {
|
||||||
close()
|
hideInstant()
|
||||||
hyprpickerProcess.running = true
|
Proc.runCommand("hyprpicker", ["hyprpicker", "--format=hex"], (output, errorCode) => {
|
||||||
}
|
if (errorCode !== 0) {
|
||||||
|
console.warn("hyprpicker exited with code:", errorCode)
|
||||||
Process {
|
root.show()
|
||||||
id: hyprpickerProcess
|
return
|
||||||
running: false
|
|
||||||
command: ["hyprpicker", "--format=hex"]
|
|
||||||
|
|
||||||
stdout: SplitParser {
|
|
||||||
onRead: data => {
|
|
||||||
const colorStr = data.trim()
|
|
||||||
if (colorStr.length >= 7 && colorStr.startsWith('#')) {
|
|
||||||
root.currentColor = colorStr
|
|
||||||
root.updateFromColor(root.currentColor)
|
|
||||||
hexInput.text = root.currentColor.toString()
|
|
||||||
copyColorToClipboard(colorStr)
|
|
||||||
root.open()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
const colorStr = output.trim()
|
||||||
|
if (colorStr.length >= 7 && colorStr.startsWith('#')) {
|
||||||
onExited: (exitCode, exitStatus) => {
|
const pickedColor = Qt.color(colorStr)
|
||||||
if (exitCode !== 0) {
|
root.selectedColor = pickedColor
|
||||||
console.warn("hyprpicker exited with code:", exitCode)
|
root.currentColor = pickedColor
|
||||||
|
root.updateFromColor(pickedColor)
|
||||||
|
copyColorToClipboard(colorStr)
|
||||||
|
root.show()
|
||||||
}
|
}
|
||||||
root.open()
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property var standardColors: [
|
width: 680
|
||||||
"#f44336", "#e91e63", "#9c27b0", "#673ab7", "#3f51b5", "#2196f3", "#03a9f4", "#00bcd4",
|
height: 680
|
||||||
"#009688", "#4caf50", "#8bc34a", "#cddc39", "#ffeb3b", "#ffc107", "#ff9800", "#ff5722",
|
backgroundColor: Theme.surfaceContainer
|
||||||
"#d32f2f", "#c2185b", "#7b1fa2", "#512da8", "#303f9f", "#1976d2", "#0288d1", "#0097a7",
|
cornerRadius: Theme.cornerRadius
|
||||||
"#00796b", "#388e3c", "#689f38", "#afb42b", "#fbc02d", "#ffa000", "#f57c00", "#e64a19",
|
borderColor: Theme.outlineMedium
|
||||||
"#c62828", "#ad1457", "#6a1b9a", "#4527a0", "#283593", "#1565c0", "#0277bd", "#00838f",
|
borderWidth: 1
|
||||||
"#00695c", "#2e7d32", "#558b2f", "#9e9d24", "#f9a825", "#ff8f00", "#ef6c00", "#d84315",
|
keepContentLoaded: true
|
||||||
"#ffffff", "#9e9e9e", "#212121"
|
|
||||||
]
|
|
||||||
|
|
||||||
visible: shouldBeVisible
|
onBackgroundClicked: hide()
|
||||||
|
|
||||||
WlrLayershell.namespace: "quickshell:color-picker"
|
|
||||||
WlrLayershell.layer: WlrLayer.Overlay
|
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
|
||||||
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: true
|
|
||||||
left: true
|
|
||||||
right: true
|
|
||||||
bottom: true
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: root.close()
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
color: "#80000000"
|
|
||||||
anchors.fill: parent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: 680
|
|
||||||
height: 680
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: Theme.surfaceContainer
|
|
||||||
border.color: Theme.outlineMedium
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: {} // Prevent clicks from propagating to background
|
|
||||||
}
|
|
||||||
|
|
||||||
|
content: Component {
|
||||||
FocusScope {
|
FocusScope {
|
||||||
id: colorContent
|
id: colorContent
|
||||||
|
|
||||||
|
property alias hexInput: hexInput
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
focus: root.shouldBeVisible
|
focus: true
|
||||||
|
|
||||||
Keys.onEscapePressed: event => {
|
Keys.onEscapePressed: event => {
|
||||||
root.close()
|
root.hide()
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +159,7 @@ PanelWindow {
|
|||||||
iconSize: Theme.iconSize - 4
|
iconSize: Theme.iconSize - 4
|
||||||
iconColor: Theme.surfaceText
|
iconColor: Theme.surfaceText
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
pickColorFromScreen()
|
root.pickColorFromScreen()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,7 +168,7 @@ PanelWindow {
|
|||||||
iconSize: Theme.iconSize - 4
|
iconSize: Theme.iconSize - 4
|
||||||
iconColor: Theme.surfaceText
|
iconColor: Theme.surfaceText
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
root.close()
|
root.hide()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -329,12 +289,14 @@ PanelWindow {
|
|||||||
const h = Math.max(0, Math.min(1, mouse.y / height))
|
const h = Math.max(0, Math.min(1, mouse.y / height))
|
||||||
root.hue = h
|
root.hue = h
|
||||||
root.updateColor()
|
root.updateColor()
|
||||||
|
root.selectedColor = root.currentColor
|
||||||
}
|
}
|
||||||
onPositionChanged: mouse => {
|
onPositionChanged: mouse => {
|
||||||
if (pressed) {
|
if (pressed) {
|
||||||
const h = Math.max(0, Math.min(1, mouse.y / height))
|
const h = Math.max(0, Math.min(1, mouse.y / height))
|
||||||
root.hue = h
|
root.hue = h
|
||||||
root.updateColor()
|
root.updateColor()
|
||||||
|
root.selectedColor = root.currentColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -373,8 +335,10 @@ PanelWindow {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
root.currentColor = modelData
|
const pickedColor = Qt.color(modelData)
|
||||||
root.updateFromColor(root.currentColor)
|
root.selectedColor = pickedColor
|
||||||
|
root.currentColor = pickedColor
|
||||||
|
root.updateFromColor(pickedColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -429,8 +393,10 @@ PanelWindow {
|
|||||||
enabled: index < SessionData.recentColors.length
|
enabled: index < SessionData.recentColors.length
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
if (index < SessionData.recentColors.length) {
|
if (index < SessionData.recentColors.length) {
|
||||||
root.currentColor = SessionData.recentColors[index]
|
const pickedColor = SessionData.recentColors[index]
|
||||||
root.updateFromColor(root.currentColor)
|
root.selectedColor = pickedColor
|
||||||
|
root.currentColor = pickedColor
|
||||||
|
root.updateFromColor(pickedColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -459,6 +425,7 @@ PanelWindow {
|
|||||||
onSliderValueChanged: (newValue) => {
|
onSliderValueChanged: (newValue) => {
|
||||||
root.alpha = newValue / 100
|
root.alpha = newValue / 100
|
||||||
root.updateColor()
|
root.updateColor()
|
||||||
|
root.selectedColor = root.currentColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -509,6 +476,7 @@ PanelWindow {
|
|||||||
if (!hexPattern.test(text)) return
|
if (!hexPattern.test(text)) return
|
||||||
const color = Qt.color(text)
|
const color = Qt.color(text)
|
||||||
if (color) {
|
if (color) {
|
||||||
|
root.selectedColor = color
|
||||||
root.currentColor = color
|
root.currentColor = color
|
||||||
root.updateFromColor(color)
|
root.updateFromColor(color)
|
||||||
}
|
}
|
||||||
@@ -530,9 +498,9 @@ PanelWindow {
|
|||||||
root.currentColor = color
|
root.currentColor = color
|
||||||
root.updateFromColor(color)
|
root.updateFromColor(color)
|
||||||
root.selectedColor = root.currentColor
|
root.selectedColor = root.currentColor
|
||||||
colorSelected(root.currentColor)
|
root.colorSelected(root.currentColor)
|
||||||
SessionData.addRecentColor(root.currentColor)
|
SessionData.addRecentColor(root.currentColor)
|
||||||
root.close()
|
root.hide()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -549,8 +517,8 @@ PanelWindow {
|
|||||||
backgroundColor: "transparent"
|
backgroundColor: "transparent"
|
||||||
textColor: Theme.surfaceText
|
textColor: Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
onClicked: root.close()
|
onClicked: root.hide()
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
@@ -570,7 +538,7 @@ PanelWindow {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
onClicked: {
|
onClicked: {
|
||||||
const colorString = root.currentColor.toString()
|
const colorString = root.currentColor.toString()
|
||||||
copyColorToClipboard(colorString)
|
root.copyColorToClipboard(colorString)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
204
Modals/FileBrowser/FileBrowserGridDelegate.qml
Normal file
204
Modals/FileBrowser/FileBrowserGridDelegate.qml
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
import qs.Common
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
id: delegateRoot
|
||||||
|
|
||||||
|
required property bool fileIsDir
|
||||||
|
required property string filePath
|
||||||
|
required property string fileName
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
property bool weMode: false
|
||||||
|
property var iconSizes: [80, 120, 160, 200]
|
||||||
|
property int iconSizeIndex: 1
|
||||||
|
property int selectedIndex: -1
|
||||||
|
property bool keyboardNavigationActive: false
|
||||||
|
|
||||||
|
signal itemClicked(int index, string path, string name, bool isDir)
|
||||||
|
signal itemSelected(int index, string path, string name, bool isDir)
|
||||||
|
|
||||||
|
function getFileExtension(fileName) {
|
||||||
|
const parts = fileName.split('.')
|
||||||
|
if (parts.length > 1) {
|
||||||
|
return parts[parts.length - 1].toLowerCase()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function determineFileType(fileName) {
|
||||||
|
const ext = getFileExtension(fileName)
|
||||||
|
|
||||||
|
const imageExts = ["png", "jpg", "jpeg", "gif", "bmp", "webp", "svg", "ico"]
|
||||||
|
if (imageExts.includes(ext)) {
|
||||||
|
return "image"
|
||||||
|
}
|
||||||
|
|
||||||
|
const videoExts = ["mp4", "mkv", "avi", "mov", "webm", "flv", "wmv", "m4v"]
|
||||||
|
if (videoExts.includes(ext)) {
|
||||||
|
return "video"
|
||||||
|
}
|
||||||
|
|
||||||
|
const audioExts = ["mp3", "wav", "flac", "ogg", "m4a", "aac", "wma"]
|
||||||
|
if (audioExts.includes(ext)) {
|
||||||
|
return "audio"
|
||||||
|
}
|
||||||
|
|
||||||
|
const codeExts = ["js", "ts", "jsx", "tsx", "py", "go", "rs", "c", "cpp", "h", "java", "kt", "swift", "rb", "php", "html", "css", "scss", "json", "xml", "yaml", "yml", "toml", "sh", "bash", "zsh", "fish", "qml", "vue", "svelte"]
|
||||||
|
if (codeExts.includes(ext)) {
|
||||||
|
return "code"
|
||||||
|
}
|
||||||
|
|
||||||
|
const docExts = ["txt", "md", "pdf", "doc", "docx", "odt", "rtf"]
|
||||||
|
if (docExts.includes(ext)) {
|
||||||
|
return "document"
|
||||||
|
}
|
||||||
|
|
||||||
|
const archiveExts = ["zip", "tar", "gz", "bz2", "xz", "7z", "rar"]
|
||||||
|
if (archiveExts.includes(ext)) {
|
||||||
|
return "archive"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ext || fileName.indexOf('.') === -1) {
|
||||||
|
return "binary"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "file"
|
||||||
|
}
|
||||||
|
|
||||||
|
function isImageFile(fileName) {
|
||||||
|
if (!fileName) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return determineFileType(fileName) === "image"
|
||||||
|
}
|
||||||
|
|
||||||
|
function getIconForFile(fileName) {
|
||||||
|
const lowerName = fileName.toLowerCase()
|
||||||
|
if (lowerName.startsWith("dockerfile")) {
|
||||||
|
return "docker"
|
||||||
|
}
|
||||||
|
const ext = fileName.split('.').pop()
|
||||||
|
return ext || ""
|
||||||
|
}
|
||||||
|
|
||||||
|
width: weMode ? 245 : iconSizes[iconSizeIndex] + 16
|
||||||
|
height: weMode ? 205 : iconSizes[iconSizeIndex] + 48
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: {
|
||||||
|
if (keyboardNavigationActive && delegateRoot.index === selectedIndex)
|
||||||
|
return Theme.surfacePressed
|
||||||
|
|
||||||
|
return mouseArea.containsMouse ? Theme.surfaceContainerHigh : "transparent"
|
||||||
|
}
|
||||||
|
border.color: keyboardNavigationActive && delegateRoot.index === selectedIndex ? Theme.primary : "transparent"
|
||||||
|
border.width: (keyboardNavigationActive && delegateRoot.index === selectedIndex) ? 2 : 0
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (keyboardNavigationActive && delegateRoot.index === selectedIndex)
|
||||||
|
itemSelected(delegateRoot.index, delegateRoot.filePath, delegateRoot.fileName, delegateRoot.fileIsDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelectedIndexChanged: {
|
||||||
|
if (keyboardNavigationActive && selectedIndex === delegateRoot.index)
|
||||||
|
itemSelected(delegateRoot.index, delegateRoot.filePath, delegateRoot.fileName, delegateRoot.fileIsDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: weMode ? 225 : (iconSizes[iconSizeIndex] - 8)
|
||||||
|
height: weMode ? 165 : (iconSizes[iconSizeIndex] - 8)
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
CachingImage {
|
||||||
|
id: gridPreviewImage
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 2
|
||||||
|
property var weExtensions: [".jpg", ".jpeg", ".png", ".webp", ".gif", ".bmp", ".tga"]
|
||||||
|
property int weExtIndex: 0
|
||||||
|
source: {
|
||||||
|
if (weMode && delegateRoot.fileIsDir) {
|
||||||
|
return "file://" + delegateRoot.filePath + "/preview" + weExtensions[weExtIndex]
|
||||||
|
}
|
||||||
|
return (!delegateRoot.fileIsDir && isImageFile(delegateRoot.fileName)) ? ("file://" + delegateRoot.filePath) : ""
|
||||||
|
}
|
||||||
|
onStatusChanged: {
|
||||||
|
if (weMode && delegateRoot.fileIsDir && status === Image.Error) {
|
||||||
|
if (weExtIndex < weExtensions.length - 1) {
|
||||||
|
weExtIndex++
|
||||||
|
source = "file://" + delegateRoot.filePath + "/preview" + weExtensions[weExtIndex]
|
||||||
|
} else {
|
||||||
|
source = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
maxCacheSize: weMode ? 225 : iconSizes[iconSizeIndex]
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiEffect {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 2
|
||||||
|
source: gridPreviewImage
|
||||||
|
maskEnabled: true
|
||||||
|
maskSource: gridImageMask
|
||||||
|
visible: gridPreviewImage.status === Image.Ready && ((!delegateRoot.fileIsDir && isImageFile(delegateRoot.fileName)) || (weMode && delegateRoot.fileIsDir))
|
||||||
|
maskThresholdMin: 0.5
|
||||||
|
maskSpreadAtMin: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: gridImageMask
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 2
|
||||||
|
layer.enabled: true
|
||||||
|
layer.smooth: true
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: "black"
|
||||||
|
antialiasing: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankNFIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: delegateRoot.fileIsDir ? "folder" : getIconForFile(delegateRoot.fileName)
|
||||||
|
size: iconSizes[iconSizeIndex] * 0.45
|
||||||
|
color: delegateRoot.fileIsDir ? Theme.primary : Theme.surfaceText
|
||||||
|
visible: (!delegateRoot.fileIsDir && !isImageFile(delegateRoot.fileName)) || (delegateRoot.fileIsDir && !weMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: delegateRoot.fileName || ""
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
width: delegateRoot.width - Theme.spacingM
|
||||||
|
elide: Text.ElideRight
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
maximumLineCount: 2
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
itemClicked(delegateRoot.index, delegateRoot.filePath, delegateRoot.fileName, delegateRoot.fileIsDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
209
Modals/FileBrowser/FileBrowserListDelegate.qml
Normal file
209
Modals/FileBrowser/FileBrowserListDelegate.qml
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
import qs.Common
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
id: listDelegateRoot
|
||||||
|
|
||||||
|
required property bool fileIsDir
|
||||||
|
required property string filePath
|
||||||
|
required property string fileName
|
||||||
|
required property int index
|
||||||
|
required property var fileModified
|
||||||
|
required property int fileSize
|
||||||
|
|
||||||
|
property int selectedIndex: -1
|
||||||
|
property bool keyboardNavigationActive: false
|
||||||
|
|
||||||
|
signal itemClicked(int index, string path, string name, bool isDir)
|
||||||
|
signal itemSelected(int index, string path, string name, bool isDir)
|
||||||
|
|
||||||
|
function getFileExtension(fileName) {
|
||||||
|
const parts = fileName.split('.')
|
||||||
|
if (parts.length > 1) {
|
||||||
|
return parts[parts.length - 1].toLowerCase()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function determineFileType(fileName) {
|
||||||
|
const ext = getFileExtension(fileName)
|
||||||
|
|
||||||
|
const imageExts = ["png", "jpg", "jpeg", "gif", "bmp", "webp", "svg", "ico"]
|
||||||
|
if (imageExts.includes(ext)) {
|
||||||
|
return "image"
|
||||||
|
}
|
||||||
|
|
||||||
|
const videoExts = ["mp4", "mkv", "avi", "mov", "webm", "flv", "wmv", "m4v"]
|
||||||
|
if (videoExts.includes(ext)) {
|
||||||
|
return "video"
|
||||||
|
}
|
||||||
|
|
||||||
|
const audioExts = ["mp3", "wav", "flac", "ogg", "m4a", "aac", "wma"]
|
||||||
|
if (audioExts.includes(ext)) {
|
||||||
|
return "audio"
|
||||||
|
}
|
||||||
|
|
||||||
|
const codeExts = ["js", "ts", "jsx", "tsx", "py", "go", "rs", "c", "cpp", "h", "java", "kt", "swift", "rb", "php", "html", "css", "scss", "json", "xml", "yaml", "yml", "toml", "sh", "bash", "zsh", "fish", "qml", "vue", "svelte"]
|
||||||
|
if (codeExts.includes(ext)) {
|
||||||
|
return "code"
|
||||||
|
}
|
||||||
|
|
||||||
|
const docExts = ["txt", "md", "pdf", "doc", "docx", "odt", "rtf"]
|
||||||
|
if (docExts.includes(ext)) {
|
||||||
|
return "document"
|
||||||
|
}
|
||||||
|
|
||||||
|
const archiveExts = ["zip", "tar", "gz", "bz2", "xz", "7z", "rar"]
|
||||||
|
if (archiveExts.includes(ext)) {
|
||||||
|
return "archive"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ext || fileName.indexOf('.') === -1) {
|
||||||
|
return "binary"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "file"
|
||||||
|
}
|
||||||
|
|
||||||
|
function isImageFile(fileName) {
|
||||||
|
if (!fileName) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return determineFileType(fileName) === "image"
|
||||||
|
}
|
||||||
|
|
||||||
|
function getIconForFile(fileName) {
|
||||||
|
const lowerName = fileName.toLowerCase()
|
||||||
|
if (lowerName.startsWith("dockerfile")) {
|
||||||
|
return "docker"
|
||||||
|
}
|
||||||
|
const ext = fileName.split('.').pop()
|
||||||
|
return ext || ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatFileSize(size) {
|
||||||
|
if (size < 1024)
|
||||||
|
return size + " B"
|
||||||
|
if (size < 1024 * 1024)
|
||||||
|
return (size / 1024).toFixed(1) + " KB"
|
||||||
|
if (size < 1024 * 1024 * 1024)
|
||||||
|
return (size / (1024 * 1024)).toFixed(1) + " MB"
|
||||||
|
return (size / (1024 * 1024 * 1024)).toFixed(1) + " GB"
|
||||||
|
}
|
||||||
|
|
||||||
|
height: 44
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: {
|
||||||
|
if (keyboardNavigationActive && listDelegateRoot.index === selectedIndex)
|
||||||
|
return Theme.surfacePressed
|
||||||
|
return listMouseArea.containsMouse ? Theme.surfaceContainerHigh : "transparent"
|
||||||
|
}
|
||||||
|
border.color: keyboardNavigationActive && listDelegateRoot.index === selectedIndex ? Theme.primary : "transparent"
|
||||||
|
border.width: (keyboardNavigationActive && listDelegateRoot.index === selectedIndex) ? 2 : 0
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (keyboardNavigationActive && listDelegateRoot.index === selectedIndex)
|
||||||
|
itemSelected(listDelegateRoot.index, listDelegateRoot.filePath, listDelegateRoot.fileName, listDelegateRoot.fileIsDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelectedIndexChanged: {
|
||||||
|
if (keyboardNavigationActive && selectedIndex === listDelegateRoot.index)
|
||||||
|
itemSelected(listDelegateRoot.index, listDelegateRoot.filePath, listDelegateRoot.fileName, listDelegateRoot.fileIsDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 28
|
||||||
|
height: 28
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
CachingImage {
|
||||||
|
id: listPreviewImage
|
||||||
|
anchors.fill: parent
|
||||||
|
source: (!listDelegateRoot.fileIsDir && isImageFile(listDelegateRoot.fileName)) ? ("file://" + listDelegateRoot.filePath) : ""
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
maxCacheSize: 32
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiEffect {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: listPreviewImage
|
||||||
|
maskEnabled: true
|
||||||
|
maskSource: listImageMask
|
||||||
|
visible: listPreviewImage.status === Image.Ready && !listDelegateRoot.fileIsDir && isImageFile(listDelegateRoot.fileName)
|
||||||
|
maskThresholdMin: 0.5
|
||||||
|
maskSpreadAtMin: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: listImageMask
|
||||||
|
anchors.fill: parent
|
||||||
|
layer.enabled: true
|
||||||
|
layer.smooth: true
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: "black"
|
||||||
|
antialiasing: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankNFIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: listDelegateRoot.fileIsDir ? "folder" : getIconForFile(listDelegateRoot.fileName)
|
||||||
|
size: Theme.iconSize - 2
|
||||||
|
color: listDelegateRoot.fileIsDir ? Theme.primary : Theme.surfaceText
|
||||||
|
visible: listDelegateRoot.fileIsDir || !isImageFile(listDelegateRoot.fileName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: listDelegateRoot.fileName || ""
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
width: parent.width - 280
|
||||||
|
elide: Text.ElideRight
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
maximumLineCount: 1
|
||||||
|
clip: true
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: listDelegateRoot.fileIsDir ? "" : formatFileSize(listDelegateRoot.fileSize)
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceTextMedium
|
||||||
|
width: 70
|
||||||
|
horizontalAlignment: Text.AlignRight
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: Qt.formatDateTime(listDelegateRoot.fileModified, "MMM d, yyyy h:mm AP")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceTextMedium
|
||||||
|
width: 140
|
||||||
|
horizontalAlignment: Text.AlignRight
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: listMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
itemClicked(listDelegateRoot.index, listDelegateRoot.filePath, listDelegateRoot.fileName, listDelegateRoot.fileIsDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
130
Modals/FileBrowser/FileBrowserNavigation.qml
Normal file
130
Modals/FileBrowser/FileBrowserNavigation.qml
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: navigation
|
||||||
|
|
||||||
|
property string currentPath: ""
|
||||||
|
property string homeDir: ""
|
||||||
|
property bool backButtonFocused: false
|
||||||
|
property bool keyboardNavigationActive: false
|
||||||
|
property bool showSidebar: true
|
||||||
|
property bool pathEditMode: false
|
||||||
|
property bool pathInputHasFocus: false
|
||||||
|
|
||||||
|
signal navigateUp()
|
||||||
|
signal navigateTo(string path)
|
||||||
|
signal pathInputFocusChanged(bool hasFocus)
|
||||||
|
|
||||||
|
height: 40
|
||||||
|
leftPadding: Theme.spacingM
|
||||||
|
rightPadding: Theme.spacingM
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
width: 32
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: (backButtonMouseArea.containsMouse || (backButtonFocused && keyboardNavigationActive)) && currentPath !== homeDir ? Theme.surfaceVariant : "transparent"
|
||||||
|
opacity: currentPath !== homeDir ? 1 : 0
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: "arrow_back"
|
||||||
|
size: Theme.iconSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: backButtonMouseArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: currentPath !== homeDir
|
||||||
|
cursorShape: currentPath !== homeDir ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
|
enabled: currentPath !== homeDir
|
||||||
|
onClicked: navigation.navigateUp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: Math.max(0, (parent?.width ?? 0) - 40 - Theme.spacingS - (showSidebar ? 0 : 80))
|
||||||
|
height: 32
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: pathEditMode ? Theme.surfaceContainer : "transparent"
|
||||||
|
border.color: pathEditMode ? Theme.primary : "transparent"
|
||||||
|
border.width: pathEditMode ? 1 : 0
|
||||||
|
visible: !pathEditMode
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: pathDisplay
|
||||||
|
text: currentPath.replace("file://", "")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
elide: Text.ElideMiddle
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
maximumLineCount: 1
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.IBeamCursor
|
||||||
|
onClicked: {
|
||||||
|
pathEditMode = true
|
||||||
|
pathInput.text = currentPath.replace("file://", "")
|
||||||
|
Qt.callLater(() => pathInput.forceActiveFocus())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankTextField {
|
||||||
|
id: pathInput
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: pathEditMode
|
||||||
|
topPadding: Theme.spacingXS
|
||||||
|
bottomPadding: Theme.spacingXS
|
||||||
|
onAccepted: {
|
||||||
|
const newPath = text.trim()
|
||||||
|
if (newPath !== "") {
|
||||||
|
navigation.navigateTo(newPath)
|
||||||
|
}
|
||||||
|
pathEditMode = false
|
||||||
|
}
|
||||||
|
Keys.onEscapePressed: {
|
||||||
|
pathEditMode = false
|
||||||
|
}
|
||||||
|
Keys.onDownPressed: {
|
||||||
|
pathEditMode = false
|
||||||
|
}
|
||||||
|
onActiveFocusChanged: {
|
||||||
|
navigation.pathInputFocusChanged(activeFocus)
|
||||||
|
if (!activeFocus && pathEditMode) {
|
||||||
|
pathEditMode = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
visible: !showSidebar
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
circular: false
|
||||||
|
iconName: "sort"
|
||||||
|
iconSize: Theme.iconSize - 6
|
||||||
|
iconColor: Theme.surfaceText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
127
Modals/FileBrowser/FileBrowserOverwriteDialog.qml
Normal file
127
Modals/FileBrowser/FileBrowserOverwriteDialog.qml
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: overwriteDialog
|
||||||
|
|
||||||
|
property bool showDialog: false
|
||||||
|
property string pendingFilePath: ""
|
||||||
|
|
||||||
|
signal confirmed(string filePath)
|
||||||
|
signal cancelled()
|
||||||
|
|
||||||
|
visible: showDialog
|
||||||
|
focus: showDialog
|
||||||
|
|
||||||
|
Keys.onEscapePressed: {
|
||||||
|
cancelled()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onReturnPressed: {
|
||||||
|
confirmed(pendingFilePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: Theme.shadowStrong
|
||||||
|
opacity: 0.8
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
cancelled()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: 400
|
||||||
|
height: 160
|
||||||
|
color: Theme.surfaceContainer
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
border.color: Theme.outlineMedium
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width - Theme.spacingL * 2
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("File Already Exists")
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("A file with this name already exists. Do you want to overwrite it?")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceTextMedium
|
||||||
|
width: parent.width
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
width: 80
|
||||||
|
height: 36
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: cancelArea.containsMouse ? Theme.surfaceVariantHover : Theme.surfaceVariant
|
||||||
|
border.color: Theme.outline
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: I18n.tr("Cancel")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: cancelArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
cancelled()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
width: 90
|
||||||
|
height: 36
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: overwriteArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: I18n.tr("Overwrite")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.background
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: overwriteArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
confirmed(pendingFilePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
74
Modals/FileBrowser/FileBrowserSaveRow.qml
Normal file
74
Modals/FileBrowser/FileBrowserSaveRow.qml
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: saveRow
|
||||||
|
|
||||||
|
property bool saveMode: false
|
||||||
|
property string defaultFileName: ""
|
||||||
|
property string currentPath: ""
|
||||||
|
|
||||||
|
signal saveRequested(string filePath)
|
||||||
|
|
||||||
|
height: saveMode ? 40 : 0
|
||||||
|
visible: saveMode
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
DankTextField {
|
||||||
|
id: fileNameInput
|
||||||
|
|
||||||
|
width: parent.width - saveButton.width - Theme.spacingM
|
||||||
|
height: 40
|
||||||
|
text: defaultFileName
|
||||||
|
placeholderText: I18n.tr("Enter filename...")
|
||||||
|
ignoreLeftRightKeys: false
|
||||||
|
focus: saveMode
|
||||||
|
topPadding: Theme.spacingS
|
||||||
|
bottomPadding: Theme.spacingS
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (saveMode)
|
||||||
|
Qt.callLater(() => {
|
||||||
|
forceActiveFocus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onAccepted: {
|
||||||
|
if (text.trim() !== "") {
|
||||||
|
var basePath = currentPath.replace(/^file:\/\//, '')
|
||||||
|
var fullPath = basePath + "/" + text.trim()
|
||||||
|
fullPath = fullPath.replace(/\/+/g, '/')
|
||||||
|
saveRequested(fullPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
id: saveButton
|
||||||
|
|
||||||
|
width: 80
|
||||||
|
height: 40
|
||||||
|
color: fileNameInput.text.trim() !== "" ? Theme.primary : Theme.surfaceVariant
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: I18n.tr("Save")
|
||||||
|
color: fileNameInput.text.trim() !== "" ? Theme.primaryText : Theme.surfaceVariantText
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
}
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
stateColor: Theme.primary
|
||||||
|
cornerRadius: Theme.cornerRadius
|
||||||
|
enabled: fileNameInput.text.trim() !== ""
|
||||||
|
onClicked: {
|
||||||
|
if (fileNameInput.text.trim() !== "") {
|
||||||
|
var basePath = currentPath.replace(/^file:\/\//, '')
|
||||||
|
var fullPath = basePath + "/" + fileNameInput.text.trim()
|
||||||
|
fullPath = fullPath.replace(/\/+/g, '/')
|
||||||
|
saveRequested(fullPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
70
Modals/FileBrowser/FileBrowserSidebar.qml
Normal file
70
Modals/FileBrowser/FileBrowserSidebar.qml
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
id: sidebar
|
||||||
|
|
||||||
|
property var quickAccessLocations: []
|
||||||
|
property string currentPath: ""
|
||||||
|
signal locationSelected(string path)
|
||||||
|
|
||||||
|
width: 200
|
||||||
|
color: Theme.surface
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingS
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "Quick Access"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceTextMedium
|
||||||
|
font.weight: Font.Medium
|
||||||
|
leftPadding: Theme.spacingS
|
||||||
|
bottomPadding: Theme.spacingXS
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: quickAccessLocations
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
width: parent?.width ?? 0
|
||||||
|
height: 38
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: quickAccessMouseArea.containsMouse ? Theme.surfaceContainerHigh : (currentPath === modelData?.path ? Theme.surfacePressed : "transparent")
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Theme.spacingM
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: modelData?.icon ?? ""
|
||||||
|
size: Theme.iconSize - 2
|
||||||
|
color: currentPath === modelData?.path ? Theme.primary : Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData?.name ?? ""
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: currentPath === modelData?.path ? Theme.primary : Theme.surfaceText
|
||||||
|
font.weight: currentPath === modelData?.path ? Font.Medium : Font.Normal
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: quickAccessMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: locationSelected(modelData?.path ?? "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
183
Modals/FileBrowser/FileBrowserSortMenu.qml
Normal file
183
Modals/FileBrowser/FileBrowserSortMenu.qml
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
id: sortMenu
|
||||||
|
|
||||||
|
property string sortBy: "name"
|
||||||
|
property bool sortAscending: true
|
||||||
|
|
||||||
|
signal sortBySelected(string value)
|
||||||
|
signal sortOrderSelected(bool ascending)
|
||||||
|
|
||||||
|
width: 200
|
||||||
|
height: sortColumn.height + Theme.spacingM * 2
|
||||||
|
color: Theme.surfaceContainer
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
border.color: Theme.outlineMedium
|
||||||
|
border.width: 1
|
||||||
|
visible: false
|
||||||
|
z: 100
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: sortColumn
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "Sort By"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceTextMedium
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: [{
|
||||||
|
"name": "Name",
|
||||||
|
"value": "name"
|
||||||
|
}, {
|
||||||
|
"name": "Size",
|
||||||
|
"value": "size"
|
||||||
|
}, {
|
||||||
|
"name": "Modified",
|
||||||
|
"value": "modified"
|
||||||
|
}, {
|
||||||
|
"name": "Type",
|
||||||
|
"value": "type"
|
||||||
|
}]
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
width: sortColumn?.width ?? 0
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: sortMouseArea.containsMouse ? Theme.surfaceVariant : (sortBy === modelData?.value ? Theme.surfacePressed : "transparent")
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: sortBy === modelData?.value ? "check" : ""
|
||||||
|
size: Theme.iconSizeSmall
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: sortBy === modelData?.value
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData?.name ?? ""
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: sortBy === modelData?.value ? Theme.primary : Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: sortMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
sortMenu.sortBySelected(modelData?.value ?? "name")
|
||||||
|
sortMenu.visible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
width: sortColumn.width
|
||||||
|
height: 1
|
||||||
|
color: Theme.outline
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "Order"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceTextMedium
|
||||||
|
font.weight: Font.Medium
|
||||||
|
topPadding: Theme.spacingXS
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
width: sortColumn?.width ?? 0
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: ascMouseArea.containsMouse ? Theme.surfaceVariant : (sortAscending ? Theme.surfacePressed : "transparent")
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "arrow_upward"
|
||||||
|
size: Theme.iconSizeSmall
|
||||||
|
color: sortAscending ? Theme.primary : Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "Ascending"
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: sortAscending ? Theme.primary : Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: ascMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
sortMenu.sortOrderSelected(true)
|
||||||
|
sortMenu.visible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
width: sortColumn?.width ?? 0
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: descMouseArea.containsMouse ? Theme.surfaceVariant : (!sortAscending ? Theme.surfacePressed : "transparent")
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "arrow_downward"
|
||||||
|
size: Theme.iconSizeSmall
|
||||||
|
color: !sortAscending ? Theme.primary : Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "Descending"
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: !sortAscending ? Theme.primary : Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: descMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
sortMenu.sortOrderSelected(false)
|
||||||
|
sortMenu.visible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ DankModal {
|
|||||||
if (modalKeyboardController && notificationListRef) {
|
if (modalKeyboardController && notificationListRef) {
|
||||||
modalKeyboardController.listView = notificationListRef
|
modalKeyboardController.listView = notificationListRef
|
||||||
modalKeyboardController.rebuildFlatNavigation()
|
modalKeyboardController.rebuildFlatNavigation()
|
||||||
|
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
modalKeyboardController.keyboardNavigationActive = true
|
modalKeyboardController.keyboardNavigationActive = true
|
||||||
modalKeyboardController.selectedFlatIndex = 0
|
modalKeyboardController.selectedFlatIndex = 0
|
||||||
|
|||||||
356
Modals/PolkitAuthModal.qml
Normal file
356
Modals/PolkitAuthModal.qml
Normal file
@@ -0,0 +1,356 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
import qs.Modals.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
DankModal {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string passwordInput: ""
|
||||||
|
property var currentFlow: PolkitService.agent?.flow
|
||||||
|
property bool isLoading: false
|
||||||
|
property real minHeight: 240
|
||||||
|
|
||||||
|
function show() {
|
||||||
|
passwordInput = ""
|
||||||
|
isLoading = false
|
||||||
|
open()
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (contentLoader.item && contentLoader.item.passwordField) {
|
||||||
|
contentLoader.item.passwordField.forceActiveFocus()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldBeVisible: false
|
||||||
|
width: 420
|
||||||
|
height: Math.max(minHeight, contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 240)
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: contentLoader.item
|
||||||
|
function onImplicitHeightChanged() {
|
||||||
|
if (shouldBeVisible && contentLoader.item) {
|
||||||
|
const newHeight = contentLoader.item.implicitHeight + Theme.spacingM * 2
|
||||||
|
if (newHeight > minHeight) {
|
||||||
|
minHeight = newHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onOpened: {
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (contentLoader.item && contentLoader.item.passwordField) {
|
||||||
|
contentLoader.item.passwordField.forceActiveFocus()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onClosed: {
|
||||||
|
passwordInput = ""
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
|
||||||
|
onBackgroundClicked: () => {
|
||||||
|
if (currentFlow && !isLoading) {
|
||||||
|
currentFlow.cancelAuthenticationRequest()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: PolkitService.agent
|
||||||
|
enabled: PolkitService.polkitAvailable
|
||||||
|
|
||||||
|
function onAuthenticationRequestStarted() {
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
|
||||||
|
function onIsActiveChanged() {
|
||||||
|
if (!(PolkitService.agent?.isActive ?? false)) {
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: currentFlow
|
||||||
|
enabled: currentFlow !== null
|
||||||
|
|
||||||
|
function onIsResponseRequiredChanged() {
|
||||||
|
if (currentFlow.isResponseRequired) {
|
||||||
|
isLoading = false
|
||||||
|
passwordInput = ""
|
||||||
|
if (contentLoader.item && contentLoader.item.passwordField) {
|
||||||
|
contentLoader.item.passwordField.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onAuthenticationSucceeded() {
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
|
||||||
|
function onAuthenticationFailed() {
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function onAuthenticationRequestCancelled() {
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
content: Component {
|
||||||
|
FocusScope {
|
||||||
|
id: authContent
|
||||||
|
|
||||||
|
property alias passwordField: passwordField
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
focus: true
|
||||||
|
implicitHeight: headerRow.implicitHeight + mainColumn.implicitHeight + Theme.spacingM
|
||||||
|
|
||||||
|
Keys.onEscapePressed: event => {
|
||||||
|
if (currentFlow && !isLoading) {
|
||||||
|
currentFlow.cancelAuthenticationRequest()
|
||||||
|
}
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: headerRow
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.leftMargin: Theme.spacingM
|
||||||
|
anchors.rightMargin: Theme.spacingM
|
||||||
|
anchors.topMargin: Theme.spacingM
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width - 40
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Authentication Required")
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: currentFlow?.message ?? ""
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceTextMedium
|
||||||
|
width: parent.width
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
visible: (currentFlow?.supplementaryMessage ?? "") !== ""
|
||||||
|
text: currentFlow?.supplementaryMessage ?? ""
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: (currentFlow?.supplementaryIsError ?? false) ? Theme.error : Theme.surfaceTextMedium
|
||||||
|
width: parent.width
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
opacity: (currentFlow?.supplementaryIsError ?? false) ? 1 : 0.8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
iconName: "close"
|
||||||
|
iconSize: Theme.iconSize - 4
|
||||||
|
iconColor: Theme.surfaceText
|
||||||
|
enabled: !isLoading
|
||||||
|
opacity: enabled ? 1 : 0.5
|
||||||
|
onClicked: () => {
|
||||||
|
if (currentFlow) {
|
||||||
|
currentFlow.cancelAuthenticationRequest()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: mainColumn
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.leftMargin: Theme.spacingM
|
||||||
|
anchors.rightMargin: Theme.spacingM
|
||||||
|
anchors.bottomMargin: Theme.spacingM
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: currentFlow?.inputPrompt ?? ""
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
width: parent.width
|
||||||
|
visible: (currentFlow?.inputPrompt ?? "") !== ""
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 50
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.surfaceHover
|
||||||
|
border.color: passwordField.activeFocus ? Theme.primary : Theme.outlineStrong
|
||||||
|
border.width: passwordField.activeFocus ? 2 : 1
|
||||||
|
opacity: isLoading ? 0.5 : 1
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
enabled: !isLoading
|
||||||
|
onClicked: () => {
|
||||||
|
passwordField.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankTextField {
|
||||||
|
id: passwordField
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
textColor: Theme.surfaceText
|
||||||
|
text: passwordInput
|
||||||
|
echoMode: (currentFlow?.responseVisible ?? false) ? TextInput.Normal : TextInput.Password
|
||||||
|
placeholderText: ""
|
||||||
|
backgroundColor: "transparent"
|
||||||
|
enabled: !isLoading
|
||||||
|
onTextEdited: () => {
|
||||||
|
passwordInput = text
|
||||||
|
}
|
||||||
|
onAccepted: () => {
|
||||||
|
if (passwordInput.length > 0 && currentFlow && !isLoading) {
|
||||||
|
isLoading = true
|
||||||
|
currentFlow.submit(passwordInput)
|
||||||
|
passwordInput = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: (currentFlow?.failed ?? false) ? failedText.implicitHeight : 0
|
||||||
|
visible: height > 0
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: failedText
|
||||||
|
text: I18n.tr("Authentication failed, please try again")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.error
|
||||||
|
width: parent.width
|
||||||
|
opacity: (currentFlow?.failed ?? false) ? 1 : 0
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: 40
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: Math.max(70, cancelText.contentWidth + Theme.spacingM * 2)
|
||||||
|
height: 36
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: cancelArea.containsMouse ? Theme.surfaceTextHover : "transparent"
|
||||||
|
border.color: Theme.surfaceVariantAlpha
|
||||||
|
border.width: 1
|
||||||
|
enabled: !isLoading
|
||||||
|
opacity: enabled ? 1 : 0.5
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: cancelText
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: I18n.tr("Cancel")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: cancelArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
enabled: parent.enabled
|
||||||
|
onClicked: () => {
|
||||||
|
if (currentFlow) {
|
||||||
|
currentFlow.cancelAuthenticationRequest()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: Math.max(80, authText.contentWidth + Theme.spacingM * 2)
|
||||||
|
height: 36
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: authArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
|
||||||
|
enabled: !isLoading && (passwordInput.length > 0 || !(currentFlow?.isResponseRequired ?? true))
|
||||||
|
opacity: enabled ? 1 : 0.5
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: authText
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: I18n.tr("Authenticate")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.background
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: authArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
enabled: parent.enabled
|
||||||
|
onClicked: () => {
|
||||||
|
if (currentFlow && !isLoading) {
|
||||||
|
isLoading = true
|
||||||
|
currentFlow.submit(passwordInput)
|
||||||
|
passwordInput = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -62,7 +62,7 @@ Item {
|
|||||||
DankToggle {
|
DankToggle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
text: I18n.tr("Show Power Actions")
|
text: I18n.tr("Show Power Actions")
|
||||||
description: "Show power, restart, and logout buttons on the lock screen"
|
description: I18n.tr("Show power, restart, and logout buttons on the lock screen")
|
||||||
checked: SettingsData.lockScreenShowPowerActions
|
checked: SettingsData.lockScreenShowPowerActions
|
||||||
onToggled: checked => SettingsData.setLockScreenShowPowerActions(checked)
|
onToggled: checked => SettingsData.setLockScreenShowPowerActions(checked)
|
||||||
}
|
}
|
||||||
@@ -79,12 +79,12 @@ Item {
|
|||||||
DankToggle {
|
DankToggle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
text: I18n.tr("Enable loginctl lock integration")
|
text: I18n.tr("Enable loginctl lock integration")
|
||||||
description: "Bind lock screen to dbus signals from loginctl. Disable if using an external lock screen."
|
description: I18n.tr("Bind lock screen to dbus signals from loginctl. Disable if using an external lock screen")
|
||||||
checked: SessionService.loginctlAvailable && SessionData.loginctlLockIntegration
|
checked: SessionService.loginctlAvailable && SettingsData.loginctlLockIntegration
|
||||||
enabled: SessionService.loginctlAvailable
|
enabled: SessionService.loginctlAvailable
|
||||||
onToggled: checked => {
|
onToggled: checked => {
|
||||||
if (SessionService.loginctlAvailable) {
|
if (SessionService.loginctlAvailable) {
|
||||||
SessionData.setLoginctlLockIntegration(checked)
|
SettingsData.setLoginctlLockIntegration(checked)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,10 +92,19 @@ Item {
|
|||||||
DankToggle {
|
DankToggle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
text: I18n.tr("Lock before suspend")
|
text: I18n.tr("Lock before suspend")
|
||||||
description: "Automatically lock the screen when the system prepares to suspend"
|
description: I18n.tr("Automatically lock the screen when the system prepares to suspend")
|
||||||
checked: SessionData.lockBeforeSuspend
|
checked: SettingsData.lockBeforeSuspend
|
||||||
visible: SessionService.loginctlAvailable && SessionData.loginctlLockIntegration
|
visible: SessionService.loginctlAvailable && SettingsData.loginctlLockIntegration
|
||||||
onToggled: checked => SessionData.setLockBeforeSuspend(checked)
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,14 +169,14 @@ Item {
|
|||||||
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"
|
||||||
}
|
}
|
||||||
@@ -177,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,14 +205,14 @@ Item {
|
|||||||
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"
|
||||||
}
|
}
|
||||||
@@ -213,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,14 +241,14 @@ Item {
|
|||||||
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"
|
||||||
}
|
}
|
||||||
@@ -249,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -269,14 +278,14 @@ Item {
|
|||||||
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"
|
||||||
}
|
}
|
||||||
@@ -286,9 +295,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.setAcHibernateTimeout(timeout)
|
SettingsData.setAcHibernateTimeout(timeout)
|
||||||
} else {
|
} else {
|
||||||
SessionData.setBatteryHibernateTimeout(timeout)
|
SettingsData.setBatteryHibernateTimeout(timeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -304,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,12 +2,11 @@ import QtQuick
|
|||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modules.Settings
|
import qs.Modules.Settings
|
||||||
|
|
||||||
FocusScope {
|
Item {
|
||||||
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
|
||||||
@@ -23,7 +22,6 @@ FocusScope {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: root.currentIndex === 0
|
active: root.currentIndex === 0
|
||||||
visible: active
|
visible: active
|
||||||
asynchronous: true
|
|
||||||
|
|
||||||
sourceComponent: Component {
|
sourceComponent: Component {
|
||||||
PersonalizationTab {
|
PersonalizationTab {
|
||||||
@@ -40,7 +38,6 @@ FocusScope {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: root.currentIndex === 1
|
active: root.currentIndex === 1
|
||||||
visible: active
|
visible: active
|
||||||
asynchronous: true
|
|
||||||
|
|
||||||
sourceComponent: TimeWeatherTab {
|
sourceComponent: TimeWeatherTab {
|
||||||
}
|
}
|
||||||
@@ -53,7 +50,6 @@ FocusScope {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: root.currentIndex === 2
|
active: root.currentIndex === 2
|
||||||
visible: active
|
visible: active
|
||||||
asynchronous: true
|
|
||||||
|
|
||||||
sourceComponent: DankBarTab {
|
sourceComponent: DankBarTab {
|
||||||
parentModal: root.parentModal
|
parentModal: root.parentModal
|
||||||
@@ -67,7 +63,6 @@ FocusScope {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: root.currentIndex === 3
|
active: root.currentIndex === 3
|
||||||
visible: active
|
visible: active
|
||||||
asynchronous: true
|
|
||||||
|
|
||||||
sourceComponent: WidgetTweaksTab {
|
sourceComponent: WidgetTweaksTab {
|
||||||
}
|
}
|
||||||
@@ -80,7 +75,6 @@ FocusScope {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: root.currentIndex === 4
|
active: root.currentIndex === 4
|
||||||
visible: active
|
visible: active
|
||||||
asynchronous: true
|
|
||||||
|
|
||||||
sourceComponent: Component {
|
sourceComponent: Component {
|
||||||
DockTab {
|
DockTab {
|
||||||
@@ -96,7 +90,6 @@ FocusScope {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: root.currentIndex === 5
|
active: root.currentIndex === 5
|
||||||
visible: active
|
visible: active
|
||||||
asynchronous: true
|
|
||||||
|
|
||||||
sourceComponent: DisplaysTab {
|
sourceComponent: DisplaysTab {
|
||||||
}
|
}
|
||||||
@@ -109,7 +102,6 @@ FocusScope {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: root.currentIndex === 6
|
active: root.currentIndex === 6
|
||||||
visible: active
|
visible: active
|
||||||
asynchronous: true
|
|
||||||
|
|
||||||
sourceComponent: LauncherTab {
|
sourceComponent: LauncherTab {
|
||||||
}
|
}
|
||||||
@@ -122,7 +114,6 @@ FocusScope {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: root.currentIndex === 7
|
active: root.currentIndex === 7
|
||||||
visible: active
|
visible: active
|
||||||
asynchronous: true
|
|
||||||
|
|
||||||
sourceComponent: ThemeColorsTab {
|
sourceComponent: ThemeColorsTab {
|
||||||
}
|
}
|
||||||
@@ -135,7 +126,6 @@ FocusScope {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: root.currentIndex === 8
|
active: root.currentIndex === 8
|
||||||
visible: active
|
visible: active
|
||||||
asynchronous: true
|
|
||||||
|
|
||||||
sourceComponent: PowerSettings {
|
sourceComponent: PowerSettings {
|
||||||
}
|
}
|
||||||
@@ -148,7 +138,6 @@ FocusScope {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: root.currentIndex === 9
|
active: root.currentIndex === 9
|
||||||
visible: active
|
visible: active
|
||||||
asynchronous: true
|
|
||||||
|
|
||||||
sourceComponent: PluginsTab {
|
sourceComponent: PluginsTab {
|
||||||
parentModal: root.parentModal
|
parentModal: root.parentModal
|
||||||
@@ -162,7 +151,6 @@ FocusScope {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: root.currentIndex === 10
|
active: root.currentIndex === 10
|
||||||
visible: active
|
visible: active
|
||||||
asynchronous: true
|
|
||||||
|
|
||||||
sourceComponent: AboutTab {
|
sourceComponent: AboutTab {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ DankModal {
|
|||||||
|
|
||||||
property Component settingsContent
|
property Component settingsContent
|
||||||
property alias profileBrowser: profileBrowser
|
property alias profileBrowser: profileBrowser
|
||||||
|
property int currentTabIndex: 0
|
||||||
|
|
||||||
signal closingModal()
|
signal closingModal()
|
||||||
|
|
||||||
@@ -33,13 +34,32 @@ DankModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
objectName: "settingsModal"
|
objectName: "settingsModal"
|
||||||
width: 800
|
width: Math.min(800, screenWidth * 0.9)
|
||||||
height: 800
|
height: Math.min(800, screenHeight * 0.85)
|
||||||
visible: false
|
visible: false
|
||||||
onBackgroundClicked: () => {
|
onBackgroundClicked: () => {
|
||||||
return hide();
|
return hide();
|
||||||
}
|
}
|
||||||
content: settingsContent
|
content: settingsContent
|
||||||
|
onOpened: () => {
|
||||||
|
Qt.callLater(() => modalFocusScope.forceActiveFocus())
|
||||||
|
}
|
||||||
|
modalFocusScope.Keys.onPressed: event => {
|
||||||
|
const tabCount = 11
|
||||||
|
if (event.key === Qt.Key_Down) {
|
||||||
|
currentTabIndex = (currentTabIndex + 1) % tabCount
|
||||||
|
event.accepted = true
|
||||||
|
} else if (event.key === Qt.Key_Up) {
|
||||||
|
currentTabIndex = (currentTabIndex - 1 + tabCount) % tabCount
|
||||||
|
event.accepted = true
|
||||||
|
} else if (event.key === Qt.Key_Tab && !event.modifiers) {
|
||||||
|
currentTabIndex = (currentTabIndex + 1) % tabCount
|
||||||
|
event.accepted = true
|
||||||
|
} else if (event.key === Qt.Key_Backtab || (event.key === Qt.Key_Tab && event.modifiers & Qt.ShiftModifier)) {
|
||||||
|
currentTabIndex = (currentTabIndex - 1 + tabCount) % tabCount
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
function open(): string {
|
function open(): string {
|
||||||
@@ -82,6 +102,7 @@ DankModal {
|
|||||||
browserTitle: "Select Profile Image"
|
browserTitle: "Select Profile Image"
|
||||||
browserIcon: "person"
|
browserIcon: "person"
|
||||||
browserType: "profile"
|
browserType: "profile"
|
||||||
|
showHiddenFiles: true
|
||||||
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
|
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
|
||||||
onFileSelected: (path) => {
|
onFileSelected: (path) => {
|
||||||
PortalService.setProfileImage(path);
|
PortalService.setProfileImage(path);
|
||||||
@@ -100,6 +121,7 @@ DankModal {
|
|||||||
browserTitle: "Select Wallpaper"
|
browserTitle: "Select Wallpaper"
|
||||||
browserIcon: "wallpaper"
|
browserIcon: "wallpaper"
|
||||||
browserType: "wallpaper"
|
browserType: "wallpaper"
|
||||||
|
showHiddenFiles: true
|
||||||
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
|
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
|
||||||
onFileSelected: (path) => {
|
onFileSelected: (path) => {
|
||||||
SessionData.setWallpaper(path);
|
SessionData.setWallpaper(path);
|
||||||
@@ -111,9 +133,9 @@ DankModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
settingsContent: Component {
|
settingsContent: Component {
|
||||||
FocusScope {
|
Item {
|
||||||
|
id: rootScope
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
focus: true
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -172,7 +194,10 @@ DankModal {
|
|||||||
id: sidebar
|
id: sidebar
|
||||||
|
|
||||||
parentModal: settingsModal
|
parentModal: settingsModal
|
||||||
onCurrentIndexChanged: content.currentIndex = currentIndex
|
currentIndex: settingsModal.currentTabIndex
|
||||||
|
onCurrentIndexChanged: {
|
||||||
|
settingsModal.currentTabIndex = currentIndex
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsContent {
|
SettingsContent {
|
||||||
@@ -181,7 +206,7 @@ DankModal {
|
|||||||
width: parent.width - sidebar.width
|
width: parent.width - sidebar.width
|
||||||
height: parent.height
|
height: parent.height
|
||||||
parentModal: settingsModal
|
parentModal: settingsModal
|
||||||
currentIndex: sidebar.currentIndex
|
currentIndex: settingsModal.currentTabIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Settings
|
import qs.Modals.Settings
|
||||||
@@ -33,8 +35,8 @@ Rectangle {
|
|||||||
"text": I18n.tr("Theme & Colors"),
|
"text": I18n.tr("Theme & Colors"),
|
||||||
"icon": "palette"
|
"icon": "palette"
|
||||||
}, {
|
}, {
|
||||||
"text": I18n.tr("Idle & Lock Screen"),
|
"text": I18n.tr("Power & Security"),
|
||||||
"icon": "lock"
|
"icon": "power"
|
||||||
}, {
|
}, {
|
||||||
"text": I18n.tr("Plugins"),
|
"text": I18n.tr("Plugins"),
|
||||||
"icon": "extension"
|
"icon": "extension"
|
||||||
@@ -43,86 +45,106 @@ Rectangle {
|
|||||||
"icon": "info"
|
"icon": "info"
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
function navigateNext() {
|
||||||
|
currentIndex = (currentIndex + 1) % sidebarItems.length
|
||||||
|
}
|
||||||
|
|
||||||
|
function navigatePrevious() {
|
||||||
|
currentIndex = (currentIndex - 1 + sidebarItems.length) % sidebarItems.length
|
||||||
|
}
|
||||||
|
|
||||||
width: 270
|
width: 270
|
||||||
height: parent.height
|
height: parent.height
|
||||||
color: Theme.surfaceContainer
|
color: Theme.surfaceContainer
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
|
|
||||||
Column {
|
DankFlickable {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.leftMargin: Theme.spacingS
|
clip: true
|
||||||
anchors.rightMargin: Theme.spacingS
|
contentHeight: sidebarColumn.implicitHeight
|
||||||
anchors.bottomMargin: Theme.spacingS
|
|
||||||
anchors.topMargin: Theme.spacingM + 2
|
|
||||||
spacing: Theme.spacingXS
|
|
||||||
|
|
||||||
ProfileSection {
|
Column {
|
||||||
parentModal: sidebarContainer.parentModal
|
id: sidebarColumn
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: parent.width - Theme.spacingS * 2
|
|
||||||
height: 1
|
|
||||||
color: Theme.outline
|
|
||||||
opacity: 0.2
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: Theme.spacingL
|
anchors.leftMargin: Theme.spacingS
|
||||||
}
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
anchors.bottomMargin: Theme.spacingS
|
||||||
|
anchors.topMargin: Theme.spacingM + 2
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
Repeater {
|
ProfileSection {
|
||||||
id: sidebarRepeater
|
parentModal: sidebarContainer.parentModal
|
||||||
|
}
|
||||||
model: sidebarContainer.sidebarItems
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
property bool isActive: sidebarContainer.currentIndex === index
|
|
||||||
|
|
||||||
width: parent.width - Theme.spacingS * 2
|
width: parent.width - Theme.spacingS * 2
|
||||||
height: 44
|
height: 1
|
||||||
radius: Theme.cornerRadius
|
color: Theme.outline
|
||||||
color: isActive ? Theme.primary : tabMouseArea.containsMouse ? Theme.surfaceHover : "transparent"
|
opacity: 0.2
|
||||||
|
}
|
||||||
|
|
||||||
Row {
|
Item {
|
||||||
anchors.left: parent.left
|
width: parent.width
|
||||||
anchors.leftMargin: Theme.spacingM
|
height: Theme.spacingL
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
}
|
||||||
spacing: Theme.spacingM
|
|
||||||
|
|
||||||
DankIcon {
|
Repeater {
|
||||||
name: modelData.icon || ""
|
id: sidebarRepeater
|
||||||
size: Theme.iconSize - 2
|
|
||||||
color: parent.parent.isActive ? Theme.primaryText : Theme.surfaceText
|
model: sidebarContainer.sidebarItems
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
required property int index
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
property bool isActive: sidebarContainer.currentIndex === index
|
||||||
|
|
||||||
|
width: sidebarColumn.width - Theme.spacingS * 2
|
||||||
|
height: 44
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: isActive ? Theme.primary : tabMouseArea.containsMouse ? Theme.surfaceHover : "transparent"
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingM
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: modelData.icon || ""
|
||||||
|
size: Theme.iconSize - 2
|
||||||
|
color: parent.parent.isActive ? Theme.primaryText : Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.text || ""
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: parent.parent.isActive ? Theme.primaryText : Theme.surfaceText
|
||||||
|
font.weight: parent.parent.isActive ? Font.Medium : Font.Normal
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
MouseArea {
|
||||||
text: modelData.text || ""
|
id: tabMouseArea
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
|
||||||
color: parent.parent.isActive ? Theme.primaryText : Theme.surfaceText
|
anchors.fill: parent
|
||||||
font.weight: parent.parent.isActive ? Font.Medium : Font.Normal
|
hoverEnabled: true
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: () => {
|
||||||
|
sidebarContainer.currentIndex = index;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: tabMouseArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: () => {
|
|
||||||
sidebarContainer.currentIndex = index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
237
Modals/Spotlight/FileSearchController.qml
Normal file
237
Modals/Spotlight/FileSearchController.qml
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell.Io
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: controller
|
||||||
|
|
||||||
|
property string searchQuery: ""
|
||||||
|
property alias model: fileModel
|
||||||
|
property int selectedIndex: 0
|
||||||
|
property bool keyboardNavigationActive: false
|
||||||
|
property bool isSearching: false
|
||||||
|
property int totalResults: 0
|
||||||
|
property string searchField: "filename"
|
||||||
|
|
||||||
|
signal searchCompleted
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: fileModel
|
||||||
|
}
|
||||||
|
|
||||||
|
function performSearch() {
|
||||||
|
if (!DSearchService.dsearchAvailable) {
|
||||||
|
model.clear()
|
||||||
|
totalResults = 0
|
||||||
|
isSearching = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchQuery.length === 0) {
|
||||||
|
model.clear()
|
||||||
|
totalResults = 0
|
||||||
|
isSearching = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isSearching = true
|
||||||
|
const params = {
|
||||||
|
"limit": 50,
|
||||||
|
"fuzzy": true,
|
||||||
|
"sort": "score",
|
||||||
|
"desc": true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchField && searchField !== "all") {
|
||||||
|
params.field = searchField
|
||||||
|
}
|
||||||
|
|
||||||
|
DSearchService.search(searchQuery, params, response => {
|
||||||
|
if (response.error) {
|
||||||
|
model.clear()
|
||||||
|
totalResults = 0
|
||||||
|
isSearching = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.result) {
|
||||||
|
updateModel(response.result)
|
||||||
|
}
|
||||||
|
|
||||||
|
isSearching = false
|
||||||
|
searchCompleted()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateModel(result) {
|
||||||
|
model.clear()
|
||||||
|
totalResults = result.total_hits || 0
|
||||||
|
selectedIndex = 0
|
||||||
|
keyboardNavigationActive = true
|
||||||
|
|
||||||
|
if (!result.hits || result.hits.length === 0) {
|
||||||
|
selectedIndex = -1
|
||||||
|
keyboardNavigationActive = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < result.hits.length; i++) {
|
||||||
|
const hit = result.hits[i]
|
||||||
|
const filePath = hit.id || ""
|
||||||
|
const fileName = getFileName(filePath)
|
||||||
|
const fileExt = getFileExtension(fileName)
|
||||||
|
const fileType = determineFileType(fileName, filePath)
|
||||||
|
const dirPath = getDirPath(filePath)
|
||||||
|
|
||||||
|
model.append({
|
||||||
|
"filePath": filePath,
|
||||||
|
"fileName": fileName,
|
||||||
|
"fileExtension": fileExt,
|
||||||
|
"fileType": fileType,
|
||||||
|
"dirPath": dirPath,
|
||||||
|
"score": hit.score || 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFileName(path) {
|
||||||
|
const parts = path.split('/')
|
||||||
|
return parts[parts.length - 1] || path
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFileExtension(fileName) {
|
||||||
|
const parts = fileName.split('.')
|
||||||
|
if (parts.length > 1) {
|
||||||
|
return parts[parts.length - 1].toLowerCase()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDirPath(path) {
|
||||||
|
const lastSlash = path.lastIndexOf('/')
|
||||||
|
if (lastSlash > 0) {
|
||||||
|
return path.substring(0, lastSlash)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function determineFileType(fileName, filePath) {
|
||||||
|
const ext = getFileExtension(fileName)
|
||||||
|
|
||||||
|
const imageExts = ["png", "jpg", "jpeg", "gif", "bmp", "webp", "svg", "ico"]
|
||||||
|
if (imageExts.includes(ext)) {
|
||||||
|
return "image"
|
||||||
|
}
|
||||||
|
|
||||||
|
const videoExts = ["mp4", "mkv", "avi", "mov", "webm", "flv", "wmv", "m4v"]
|
||||||
|
if (videoExts.includes(ext)) {
|
||||||
|
return "video"
|
||||||
|
}
|
||||||
|
|
||||||
|
const audioExts = ["mp3", "wav", "flac", "ogg", "m4a", "aac", "wma"]
|
||||||
|
if (audioExts.includes(ext)) {
|
||||||
|
return "audio"
|
||||||
|
}
|
||||||
|
|
||||||
|
const codeExts = ["js", "ts", "jsx", "tsx", "py", "go", "rs", "c", "cpp", "h", "java", "kt", "swift", "rb", "php", "html", "css", "scss", "json", "xml", "yaml", "yml", "toml", "sh", "bash", "zsh", "fish", "qml", "vue", "svelte"]
|
||||||
|
if (codeExts.includes(ext)) {
|
||||||
|
return "code"
|
||||||
|
}
|
||||||
|
|
||||||
|
const docExts = ["txt", "md", "pdf", "doc", "docx", "odt", "rtf"]
|
||||||
|
if (docExts.includes(ext)) {
|
||||||
|
return "document"
|
||||||
|
}
|
||||||
|
|
||||||
|
const archiveExts = ["zip", "tar", "gz", "bz2", "xz", "7z", "rar"]
|
||||||
|
if (archiveExts.includes(ext)) {
|
||||||
|
return "archive"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ext || fileName.indexOf('.') === -1) {
|
||||||
|
return "binary"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "file"
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectNext() {
|
||||||
|
if (model.count === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
keyboardNavigationActive = true
|
||||||
|
selectedIndex = Math.min(selectedIndex + 1, model.count - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectPrevious() {
|
||||||
|
if (model.count === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
keyboardNavigationActive = true
|
||||||
|
selectedIndex = Math.max(selectedIndex - 1, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
signal fileOpened
|
||||||
|
|
||||||
|
function openFile(filePath) {
|
||||||
|
if (!filePath || filePath.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let url = filePath
|
||||||
|
if (!url.startsWith("file://")) {
|
||||||
|
url = "file://" + filePath
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt.openUrlExternally(url)
|
||||||
|
fileOpened()
|
||||||
|
}
|
||||||
|
|
||||||
|
function openFolder(filePath) {
|
||||||
|
if (!filePath || filePath.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastSlash = filePath.lastIndexOf('/')
|
||||||
|
if (lastSlash <= 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const dirPath = filePath.substring(0, lastSlash)
|
||||||
|
let url = dirPath
|
||||||
|
if (!url.startsWith("file://")) {
|
||||||
|
url = "file://" + dirPath
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt.openUrlExternally(url)
|
||||||
|
fileOpened()
|
||||||
|
}
|
||||||
|
|
||||||
|
function openSelected() {
|
||||||
|
if (model.count === 0 || selectedIndex < 0 || selectedIndex >= model.count) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = model.get(selectedIndex)
|
||||||
|
if (item && item.filePath) {
|
||||||
|
openFile(item.filePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
searchQuery = ""
|
||||||
|
model.clear()
|
||||||
|
selectedIndex = -1
|
||||||
|
keyboardNavigationActive = false
|
||||||
|
isSearching = false
|
||||||
|
totalResults = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
onSearchQueryChanged: {
|
||||||
|
performSearch()
|
||||||
|
}
|
||||||
|
|
||||||
|
onSearchFieldChanged: {
|
||||||
|
performSearch()
|
||||||
|
}
|
||||||
|
}
|
||||||
155
Modals/Spotlight/FileSearchEntry.qml
Normal file
155
Modals/Spotlight/FileSearchEntry.qml
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
import Quickshell
|
||||||
|
import qs.Common
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: entry
|
||||||
|
|
||||||
|
required property string filePath
|
||||||
|
required property string fileName
|
||||||
|
required property string fileExtension
|
||||||
|
required property string fileType
|
||||||
|
required property string dirPath
|
||||||
|
required property bool isSelected
|
||||||
|
required property int itemIndex
|
||||||
|
|
||||||
|
signal clicked()
|
||||||
|
|
||||||
|
readonly property int iconSize: 40
|
||||||
|
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: isSelected ? Theme.primaryPressed : mouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceContainerHigh
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: iconSize
|
||||||
|
height: iconSize
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: imagePreview
|
||||||
|
anchors.fill: parent
|
||||||
|
source: fileType === "image" ? `file://${filePath}` : ""
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
smooth: true
|
||||||
|
cache: true
|
||||||
|
asynchronous: true
|
||||||
|
visible: fileType === "image" && status === Image.Ready
|
||||||
|
sourceSize.width: 128
|
||||||
|
sourceSize.height: 128
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiEffect {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: imagePreview
|
||||||
|
maskEnabled: true
|
||||||
|
maskSource: imageMask
|
||||||
|
visible: fileType === "image" && imagePreview.status === Image.Ready
|
||||||
|
maskThresholdMin: 0.5
|
||||||
|
maskSpreadAtMin: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: imageMask
|
||||||
|
width: iconSize
|
||||||
|
height: iconSize
|
||||||
|
layer.enabled: true
|
||||||
|
layer.smooth: true
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: width / 2
|
||||||
|
color: "black"
|
||||||
|
antialiasing: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: width / 2
|
||||||
|
color: getFileTypeColor()
|
||||||
|
visible: fileType !== "image" || imagePreview.status !== Image.Ready
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: getFileIconText()
|
||||||
|
font.pixelSize: fileExtension.length > 0 ? (fileExtension.length > 3 ? Theme.fontSizeSmall - 2 : Theme.fontSizeSmall) : Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Bold
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: parent.width - iconSize - Theme.spacingL
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
width: parent.width
|
||||||
|
text: fileName
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
elide: Text.ElideMiddle
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
maximumLineCount: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
width: parent.width
|
||||||
|
text: dirPath
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
elide: Text.ElideMiddle
|
||||||
|
maximumLineCount: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: entry.clicked()
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFileTypeColor() {
|
||||||
|
switch (fileType) {
|
||||||
|
case "code":
|
||||||
|
return Theme.codeFileColor || Theme.primarySelected
|
||||||
|
case "document":
|
||||||
|
return Theme.docFileColor || Theme.secondarySelected
|
||||||
|
case "video":
|
||||||
|
return Theme.videoFileColor || Theme.tertiarySelected
|
||||||
|
case "audio":
|
||||||
|
return Theme.audioFileColor || Theme.errorSelected
|
||||||
|
case "archive":
|
||||||
|
return Theme.archiveFileColor || Theme.warningSelected
|
||||||
|
case "binary":
|
||||||
|
return Theme.binaryFileColor || Theme.surfaceDim
|
||||||
|
default:
|
||||||
|
return Theme.surfaceLight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFileIconText() {
|
||||||
|
if (fileType === "binary") {
|
||||||
|
return "bin"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileExtension.length > 0) {
|
||||||
|
return fileExtension
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileName.charAt(0).toUpperCase()
|
||||||
|
}
|
||||||
|
}
|
||||||
246
Modals/Spotlight/FileSearchResults.qml
Normal file
246
Modals/Spotlight/FileSearchResults.qml
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: resultsContainer
|
||||||
|
|
||||||
|
property var fileSearchController: null
|
||||||
|
|
||||||
|
function resetScroll() {
|
||||||
|
filesList.contentY = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
color: "transparent"
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
DankListView {
|
||||||
|
id: filesList
|
||||||
|
|
||||||
|
property int itemHeight: 60
|
||||||
|
property int itemSpacing: Theme.spacingS
|
||||||
|
property bool hoverUpdatesSelection: false
|
||||||
|
property bool keyboardNavigationActive: fileSearchController ? fileSearchController.keyboardNavigationActive : false
|
||||||
|
|
||||||
|
signal keyboardNavigationReset
|
||||||
|
signal itemClicked(int index)
|
||||||
|
signal itemRightClicked(int index)
|
||||||
|
|
||||||
|
function ensureVisible(index) {
|
||||||
|
if (index < 0 || index >= count)
|
||||||
|
return
|
||||||
|
|
||||||
|
const itemY = index * (itemHeight + itemSpacing)
|
||||||
|
const itemBottom = itemY + itemHeight
|
||||||
|
if (itemY < contentY)
|
||||||
|
contentY = itemY
|
||||||
|
else if (itemBottom > contentY + height)
|
||||||
|
contentY = itemBottom - height
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingS
|
||||||
|
model: fileSearchController ? fileSearchController.model : null
|
||||||
|
currentIndex: fileSearchController ? fileSearchController.selectedIndex : -1
|
||||||
|
clip: true
|
||||||
|
spacing: itemSpacing
|
||||||
|
focus: true
|
||||||
|
interactive: true
|
||||||
|
cacheBuffer: Math.max(0, Math.min(height * 2, 1000))
|
||||||
|
reuseItems: true
|
||||||
|
|
||||||
|
onCurrentIndexChanged: {
|
||||||
|
if (keyboardNavigationActive)
|
||||||
|
ensureVisible(currentIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
onItemClicked: function (index) {
|
||||||
|
if (fileSearchController) {
|
||||||
|
const item = fileSearchController.model.get(index)
|
||||||
|
fileSearchController.openFile(item.filePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onItemRightClicked: function (index) {
|
||||||
|
if (fileSearchController) {
|
||||||
|
const item = fileSearchController.model.get(index)
|
||||||
|
fileSearchController.openFolder(item.filePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyboardNavigationReset: {
|
||||||
|
if (fileSearchController)
|
||||||
|
fileSearchController.keyboardNavigationActive = false
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
required property int index
|
||||||
|
required property string filePath
|
||||||
|
required property string fileName
|
||||||
|
required property string fileExtension
|
||||||
|
required property string fileType
|
||||||
|
required property string dirPath
|
||||||
|
|
||||||
|
width: ListView.view.width
|
||||||
|
height: filesList.itemHeight
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: ListView.isCurrentItem ? Theme.primaryPressed : fileMouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceContainerHigh
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 40
|
||||||
|
height: 40
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: iconBackground
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: width / 2
|
||||||
|
color: Theme.surfaceLight
|
||||||
|
visible: fileType !== "image"
|
||||||
|
|
||||||
|
DankNFIcon {
|
||||||
|
id: nerdIcon
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: {
|
||||||
|
const lowerName = fileName.toLowerCase()
|
||||||
|
if (lowerName.startsWith("dockerfile"))
|
||||||
|
return "docker"
|
||||||
|
if (lowerName.startsWith("makefile"))
|
||||||
|
return "makefile"
|
||||||
|
if (lowerName.startsWith("license"))
|
||||||
|
return "license"
|
||||||
|
if (lowerName.startsWith("readme"))
|
||||||
|
return "readme"
|
||||||
|
return fileExtension.toLowerCase()
|
||||||
|
}
|
||||||
|
size: Theme.fontSizeXLarge
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: fileExtension ? (fileExtension.length > 4 ? fileExtension.substring(0, 4) : fileExtension) : "?"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Bold
|
||||||
|
visible: !nerdIcon.visible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
anchors.fill: parent
|
||||||
|
active: fileType === "image"
|
||||||
|
sourceComponent: Image {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: "file://" + filePath
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
asynchronous: true
|
||||||
|
cache: false
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
maskEnabled: true
|
||||||
|
maskThresholdMin: 0.5
|
||||||
|
maskSpreadAtMin: 1.0
|
||||||
|
maskSource: ShaderEffectSource {
|
||||||
|
sourceItem: Rectangle {
|
||||||
|
width: 40
|
||||||
|
height: 40
|
||||||
|
radius: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: parent.width - 40 - Theme.spacingL
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
width: parent.width
|
||||||
|
text: fileName || ""
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
elide: Text.ElideMiddle
|
||||||
|
maximumLineCount: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
width: parent.width
|
||||||
|
text: dirPath || ""
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
elide: Text.ElideMiddle
|
||||||
|
maximumLineCount: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: fileMouseArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
z: 10
|
||||||
|
onEntered: {
|
||||||
|
if (filesList.hoverUpdatesSelection && !filesList.keyboardNavigationActive)
|
||||||
|
filesList.currentIndex = index
|
||||||
|
}
|
||||||
|
onPositionChanged: {
|
||||||
|
filesList.keyboardNavigationReset()
|
||||||
|
}
|
||||||
|
onClicked: mouse => {
|
||||||
|
if (mouse.button === Qt.LeftButton) {
|
||||||
|
filesList.itemClicked(index)
|
||||||
|
} else if (mouse.button === Qt.RightButton) {
|
||||||
|
filesList.itemRightClicked(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: !fileSearchController || !fileSearchController.model || fileSearchController.model.count === 0
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
property string displayText: {
|
||||||
|
if (!fileSearchController) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if (!DSearchService.dsearchAvailable) {
|
||||||
|
return I18n.tr("DankSearch not available")
|
||||||
|
}
|
||||||
|
if (fileSearchController.isSearching) {
|
||||||
|
return I18n.tr("Searching...")
|
||||||
|
}
|
||||||
|
if (fileSearchController.searchQuery.length === 0) {
|
||||||
|
return I18n.tr("Enter a search query")
|
||||||
|
}
|
||||||
|
if (!fileSearchController.model || fileSearchController.model.count === 0) {
|
||||||
|
return I18n.tr("No files found")
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
text: displayText
|
||||||
|
anchors.centerIn: parent
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
visible: displayText.length > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,10 +11,40 @@ Item {
|
|||||||
|
|
||||||
property alias appLauncher: appLauncher
|
property alias appLauncher: appLauncher
|
||||||
property alias searchField: searchField
|
property alias searchField: searchField
|
||||||
|
property alias fileSearchController: fileSearchController
|
||||||
property var parentModal: null
|
property var parentModal: null
|
||||||
|
property string searchMode: "apps"
|
||||||
|
|
||||||
function resetScroll() {
|
function resetScroll() {
|
||||||
resultsView.resetScroll()
|
if (searchMode === "apps") {
|
||||||
|
resultsView.resetScroll()
|
||||||
|
} else {
|
||||||
|
fileSearchResults.resetScroll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSearchMode() {
|
||||||
|
if (searchField.text.startsWith("/")) {
|
||||||
|
if (searchMode !== "files") {
|
||||||
|
searchMode = "files"
|
||||||
|
}
|
||||||
|
const query = searchField.text.substring(1)
|
||||||
|
fileSearchController.searchQuery = query
|
||||||
|
} else {
|
||||||
|
if (searchMode !== "apps") {
|
||||||
|
searchMode = "apps"
|
||||||
|
fileSearchController.reset()
|
||||||
|
appLauncher.searchQuery = searchField.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onSearchModeChanged: {
|
||||||
|
if (searchMode === "files") {
|
||||||
|
appLauncher.keyboardNavigationActive = false
|
||||||
|
} else {
|
||||||
|
fileSearchController.keyboardNavigationActive = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -27,59 +57,95 @@ Item {
|
|||||||
|
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
} else if (event.key === Qt.Key_Down) {
|
} else if (event.key === Qt.Key_Down) {
|
||||||
appLauncher.selectNext()
|
if (searchMode === "apps") {
|
||||||
|
appLauncher.selectNext()
|
||||||
|
} else {
|
||||||
|
fileSearchController.selectNext()
|
||||||
|
}
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
} else if (event.key === Qt.Key_Up) {
|
} else if (event.key === Qt.Key_Up) {
|
||||||
appLauncher.selectPrevious()
|
if (searchMode === "apps") {
|
||||||
|
appLauncher.selectPrevious()
|
||||||
|
} else {
|
||||||
|
fileSearchController.selectPrevious()
|
||||||
|
}
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
} else if (event.key === Qt.Key_Right && appLauncher.viewMode === "grid") {
|
} else if (event.key === Qt.Key_Right && searchMode === "apps" && appLauncher.viewMode === "grid") {
|
||||||
appLauncher.selectNextInRow()
|
appLauncher.selectNextInRow()
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
} else if (event.key === Qt.Key_Left && appLauncher.viewMode === "grid") {
|
} else if (event.key === Qt.Key_Left && searchMode === "apps" && appLauncher.viewMode === "grid") {
|
||||||
appLauncher.selectPreviousInRow()
|
appLauncher.selectPreviousInRow()
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
} else if (event.key == Qt.Key_J && event.modifiers & Qt.ControlModifier) {
|
} else if (event.key == Qt.Key_J && event.modifiers & Qt.ControlModifier) {
|
||||||
appLauncher.selectNext()
|
if (searchMode === "apps") {
|
||||||
|
appLauncher.selectNext()
|
||||||
|
} else {
|
||||||
|
fileSearchController.selectNext()
|
||||||
|
}
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
} else if (event.key == Qt.Key_K && event.modifiers & Qt.ControlModifier) {
|
} else if (event.key == Qt.Key_K && event.modifiers & Qt.ControlModifier) {
|
||||||
appLauncher.selectPrevious()
|
if (searchMode === "apps") {
|
||||||
|
appLauncher.selectPrevious()
|
||||||
|
} else {
|
||||||
|
fileSearchController.selectPrevious()
|
||||||
|
}
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
} else if (event.key == Qt.Key_L && event.modifiers & Qt.ControlModifier && appLauncher.viewMode === "grid") {
|
} else if (event.key == Qt.Key_L && event.modifiers & Qt.ControlModifier && searchMode === "apps" && appLauncher.viewMode === "grid") {
|
||||||
appLauncher.selectNextInRow()
|
appLauncher.selectNextInRow()
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
} else if (event.key == Qt.Key_H && event.modifiers & Qt.ControlModifier && appLauncher.viewMode === "grid") {
|
} else if (event.key == Qt.Key_H && event.modifiers & Qt.ControlModifier && searchMode === "apps" && appLauncher.viewMode === "grid") {
|
||||||
appLauncher.selectPreviousInRow()
|
appLauncher.selectPreviousInRow()
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
} else if (event.key === Qt.Key_Tab) {
|
} else if (event.key === Qt.Key_Tab) {
|
||||||
if (appLauncher.viewMode === "grid") {
|
if (searchMode === "apps") {
|
||||||
appLauncher.selectNextInRow()
|
if (appLauncher.viewMode === "grid") {
|
||||||
|
appLauncher.selectNextInRow()
|
||||||
|
} else {
|
||||||
|
appLauncher.selectNext()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
appLauncher.selectNext()
|
fileSearchController.selectNext()
|
||||||
}
|
}
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
} else if (event.key === Qt.Key_Backtab) {
|
} else if (event.key === Qt.Key_Backtab) {
|
||||||
if (appLauncher.viewMode === "grid") {
|
if (searchMode === "apps") {
|
||||||
appLauncher.selectPreviousInRow()
|
if (appLauncher.viewMode === "grid") {
|
||||||
|
appLauncher.selectPreviousInRow()
|
||||||
|
} else {
|
||||||
|
appLauncher.selectPrevious()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
appLauncher.selectPrevious()
|
fileSearchController.selectPrevious()
|
||||||
}
|
}
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
} else if (event.key === Qt.Key_N && event.modifiers & Qt.ControlModifier) {
|
} else if (event.key === Qt.Key_N && event.modifiers & Qt.ControlModifier) {
|
||||||
if (appLauncher.viewMode === "grid") {
|
if (searchMode === "apps") {
|
||||||
appLauncher.selectNextInRow()
|
if (appLauncher.viewMode === "grid") {
|
||||||
|
appLauncher.selectNextInRow()
|
||||||
|
} else {
|
||||||
|
appLauncher.selectNext()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
appLauncher.selectNext()
|
fileSearchController.selectNext()
|
||||||
}
|
}
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
} else if (event.key === Qt.Key_P && event.modifiers & Qt.ControlModifier) {
|
} else if (event.key === Qt.Key_P && event.modifiers & Qt.ControlModifier) {
|
||||||
if (appLauncher.viewMode === "grid") {
|
if (searchMode === "apps") {
|
||||||
appLauncher.selectPreviousInRow()
|
if (appLauncher.viewMode === "grid") {
|
||||||
|
appLauncher.selectPreviousInRow()
|
||||||
|
} else {
|
||||||
|
appLauncher.selectPrevious()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
appLauncher.selectPrevious()
|
fileSearchController.selectPrevious()
|
||||||
}
|
}
|
||||||
event.accepted = true
|
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()
|
if (searchMode === "apps") {
|
||||||
|
appLauncher.launchSelected()
|
||||||
|
} else if (searchMode === "files") {
|
||||||
|
fileSearchController.openSelected()
|
||||||
|
}
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,6 +164,15 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FileSearchController {
|
||||||
|
id: fileSearchController
|
||||||
|
|
||||||
|
onFileOpened: () => {
|
||||||
|
if (parentModal)
|
||||||
|
parentModal.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingM
|
anchors.margins: Theme.spacingM
|
||||||
@@ -118,7 +193,7 @@ Item {
|
|||||||
backgroundColor: Theme.surfaceContainerHigh
|
backgroundColor: Theme.surfaceContainerHigh
|
||||||
normalBorderColor: Theme.outlineMedium
|
normalBorderColor: Theme.outlineMedium
|
||||||
focusedBorderColor: Theme.primary
|
focusedBorderColor: Theme.primary
|
||||||
leftIconName: "search"
|
leftIconName: searchMode === "files" ? "folder" : "search"
|
||||||
leftIconSize: Theme.iconSize
|
leftIconSize: Theme.iconSize
|
||||||
leftIconColor: Theme.surfaceVariantText
|
leftIconColor: Theme.surfaceVariantText
|
||||||
leftIconFocusedColor: Theme.primary
|
leftIconFocusedColor: Theme.primary
|
||||||
@@ -126,14 +201,18 @@ Item {
|
|||||||
textColor: Theme.surfaceText
|
textColor: Theme.surfaceText
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
enabled: parentModal ? parentModal.spotlightOpen : true
|
enabled: parentModal ? parentModal.spotlightOpen : true
|
||||||
placeholderText: ""
|
placeholderText: searchMode === "files" ? "Search files..." : "Search apps..."
|
||||||
ignoreLeftRightKeys: appLauncher.viewMode !== "list"
|
ignoreLeftRightKeys: appLauncher.viewMode !== "list"
|
||||||
ignoreTabKeys: true
|
ignoreTabKeys: true
|
||||||
keyForwardTargets: [spotlightKeyHandler]
|
keyForwardTargets: [spotlightKeyHandler]
|
||||||
text: appLauncher.searchQuery
|
onTextChanged: {
|
||||||
onTextEdited: () => {
|
if (searchMode === "apps") {
|
||||||
appLauncher.searchQuery = text
|
appLauncher.searchQuery = text
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
onTextEdited: {
|
||||||
|
updateSearchMode()
|
||||||
|
}
|
||||||
Keys.onPressed: event => {
|
Keys.onPressed: event => {
|
||||||
if (event.key === Qt.Key_Escape) {
|
if (event.key === Qt.Key_Escape) {
|
||||||
if (parentModal)
|
if (parentModal)
|
||||||
@@ -141,12 +220,18 @@ Item {
|
|||||||
|
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
} else if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && text.length > 0) {
|
} else if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && text.length > 0) {
|
||||||
if (appLauncher.keyboardNavigationActive && appLauncher.model.count > 0)
|
if (searchMode === "apps") {
|
||||||
appLauncher.launchSelected()
|
if (appLauncher.keyboardNavigationActive && appLauncher.model.count > 0)
|
||||||
else if (appLauncher.model.count > 0)
|
appLauncher.launchSelected()
|
||||||
appLauncher.launchApp(appLauncher.model.get(0))
|
else if (appLauncher.model.count > 0)
|
||||||
|
appLauncher.launchApp(appLauncher.model.get(0))
|
||||||
|
} else if (searchMode === "files") {
|
||||||
|
if (fileSearchController.model.count > 0)
|
||||||
|
fileSearchController.openSelected()
|
||||||
|
}
|
||||||
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_Tab || event.key === Qt.Key_Backtab || ((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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,7 +239,7 @@ Item {
|
|||||||
|
|
||||||
Row {
|
Row {
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
visible: appLauncher.model.count > 0
|
visible: searchMode === "apps" && appLauncher.model.count > 0
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -207,12 +292,116 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
visible: searchMode === "files"
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: filenameFilterButton
|
||||||
|
|
||||||
|
width: 36
|
||||||
|
height: 36
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: fileSearchController.searchField === "filename" ? Theme.primaryHover : filenameFilterArea.containsMouse ? Theme.surfaceHover : "transparent"
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: "title"
|
||||||
|
size: 18
|
||||||
|
color: fileSearchController.searchField === "filename" ? Theme.primary : Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: filenameFilterArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: () => {
|
||||||
|
fileSearchController.searchField = "filename"
|
||||||
|
}
|
||||||
|
onEntered: {
|
||||||
|
filenameTooltipLoader.active = true
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (filenameTooltipLoader.item) {
|
||||||
|
const p = mapToItem(null, width / 2, height + Theme.spacingXS)
|
||||||
|
filenameTooltipLoader.item.show(I18n.tr("Search filenames"), p.x, p.y, null)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onExited: {
|
||||||
|
if (filenameTooltipLoader.item)
|
||||||
|
filenameTooltipLoader.item.hide()
|
||||||
|
|
||||||
|
filenameTooltipLoader.active = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: contentFilterButton
|
||||||
|
|
||||||
|
width: 36
|
||||||
|
height: 36
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: fileSearchController.searchField === "body" ? Theme.primaryHover : contentFilterArea.containsMouse ? Theme.surfaceHover : "transparent"
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: "description"
|
||||||
|
size: 18
|
||||||
|
color: fileSearchController.searchField === "body" ? Theme.primary : Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: contentFilterArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: () => {
|
||||||
|
fileSearchController.searchField = "body"
|
||||||
|
}
|
||||||
|
onEntered: {
|
||||||
|
contentTooltipLoader.active = true
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (contentTooltipLoader.item) {
|
||||||
|
const p = mapToItem(null, width / 2, height + Theme.spacingXS)
|
||||||
|
contentTooltipLoader.item.show(I18n.tr("Search file contents"), p.x, p.y, null)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onExited: {
|
||||||
|
if (contentTooltipLoader.item)
|
||||||
|
contentTooltipLoader.item.hide()
|
||||||
|
|
||||||
|
contentTooltipLoader.active = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SpotlightResults {
|
Item {
|
||||||
id: resultsView
|
width: parent.width
|
||||||
appLauncher: spotlightKeyHandler.appLauncher
|
height: parent.height - y
|
||||||
contextMenu: contextMenu
|
|
||||||
|
SpotlightResults {
|
||||||
|
id: resultsView
|
||||||
|
anchors.fill: parent
|
||||||
|
appLauncher: spotlightKeyHandler.appLauncher
|
||||||
|
contextMenu: contextMenu
|
||||||
|
visible: searchMode === "apps"
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSearchResults {
|
||||||
|
id: fileSearchResults
|
||||||
|
anchors.fill: parent
|
||||||
|
fileSearchController: spotlightKeyHandler.fileSearchController
|
||||||
|
visible: searchMode === "files"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,7 +422,6 @@ Item {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
|
||||||
// Prevent closing when clicking on the menu itself
|
|
||||||
x: contextMenu.x
|
x: contextMenu.x
|
||||||
y: contextMenu.y
|
y: contextMenu.y
|
||||||
width: contextMenu.width
|
width: contextMenu.width
|
||||||
@@ -241,4 +429,18 @@ Item {
|
|||||||
onClicked: () => {}
|
onClicked: () => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: filenameTooltipLoader
|
||||||
|
|
||||||
|
active: false
|
||||||
|
sourceComponent: DankTooltip {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: contentTooltipLoader
|
||||||
|
|
||||||
|
active: false
|
||||||
|
sourceComponent: DankTooltip {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ Popup {
|
|||||||
property var currentApp: null
|
property var currentApp: null
|
||||||
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
|
||||||
@@ -77,8 +78,7 @@ Popup {
|
|||||||
spacing: 1
|
spacing: 1
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
implicitWidth: pinRow.implicitWidth + Theme.spacingS * 2
|
width: parent.width
|
||||||
width: implicitWidth
|
|
||||||
height: 32
|
height: 32
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
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"
|
||||||
@@ -92,10 +92,10 @@ Popup {
|
|||||||
|
|
||||||
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
|
||||||
@@ -106,10 +106,10 @@ Popup {
|
|||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: {
|
text: {
|
||||||
if (!contextMenu.currentApp || !contextMenu.currentApp.desktopEntry)
|
if (!desktopEntry)
|
||||||
return I18n.tr("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) ? I18n.tr("Unpin from Dock") : I18n.tr("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
|
||||||
@@ -126,10 +126,10 @@ Popup {
|
|||||||
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
|
||||||
@@ -154,11 +154,10 @@ Popup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: contextMenu.currentApp && contextMenu.currentApp.desktopEntry && contextMenu.currentApp.desktopEntry.actions ? contextMenu.currentApp.desktopEntry.actions : []
|
model: desktopEntry && desktopEntry.actions ? desktopEntry.actions : []
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
implicitWidth: actionRow.implicitWidth + Theme.spacingS * 2
|
width: parent.width
|
||||||
width: implicitWidth
|
|
||||||
height: 32
|
height: 32
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: actionMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
color: actionMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
@@ -200,9 +199,9 @@ Popup {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (modelData && contextMenu.currentApp && contextMenu.currentApp.desktopEntry) {
|
if (modelData && desktopEntry) {
|
||||||
SessionService.launchDesktopAction(contextMenu.currentApp.desktopEntry, modelData)
|
SessionService.launchDesktopAction(desktopEntry, modelData)
|
||||||
if (appLauncher) {
|
if (appLauncher && contextMenu.currentApp) {
|
||||||
appLauncher.appLaunched(contextMenu.currentApp)
|
appLauncher.appLaunched(contextMenu.currentApp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -213,7 +212,7 @@ Popup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
visible: contextMenu.currentApp && contextMenu.currentApp.desktopEntry && contextMenu.currentApp.desktopEntry.actions && contextMenu.currentApp.desktopEntry.actions.length > 0
|
visible: desktopEntry && desktopEntry.actions && desktopEntry.actions.length > 0
|
||||||
width: parent.width - Theme.spacingS * 2
|
width: parent.width - Theme.spacingS * 2
|
||||||
height: 5
|
height: 5
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
@@ -228,8 +227,7 @@ Popup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
implicitWidth: launchRow.implicitWidth + Theme.spacingS * 2
|
width: parent.width
|
||||||
width: implicitWidth
|
|
||||||
height: 32
|
height: 32
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
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"
|
||||||
@@ -290,8 +288,7 @@ Popup {
|
|||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
visible: SessionService.hasPrimeRun
|
visible: SessionService.hasPrimeRun
|
||||||
implicitWidth: primeRunRow.implicitWidth + Theme.spacingS * 2
|
width: parent.width
|
||||||
width: implicitWidth
|
|
||||||
height: 32
|
height: 32
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: primeRunMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
color: primeRunMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
@@ -327,9 +324,9 @@ Popup {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
if (contextMenu.currentApp && contextMenu.currentApp.desktopEntry) {
|
if (desktopEntry) {
|
||||||
SessionService.launchDesktopEntry(contextMenu.currentApp.desktopEntry, true)
|
SessionService.launchDesktopEntry(desktopEntry, true)
|
||||||
if (appLauncher) {
|
if (appLauncher && contextMenu.currentApp) {
|
||||||
appLauncher.appLaunched(contextMenu.currentApp)
|
appLauncher.appLaunched(contextMenu.currentApp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,17 +13,37 @@ 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()
|
||||||
|
|
||||||
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() {
|
||||||
@@ -32,17 +52,20 @@ DankModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onDialogClosed: {
|
onDialogClosed: {
|
||||||
if (contentLoader.item) {
|
if (spotlightContent) {
|
||||||
if (contentLoader.item.appLauncher) {
|
if (spotlightContent.appLauncher) {
|
||||||
contentLoader.item.appLauncher.searchQuery = ""
|
spotlightContent.appLauncher.searchQuery = ""
|
||||||
contentLoader.item.appLauncher.selectedIndex = 0
|
spotlightContent.appLauncher.selectedIndex = 0
|
||||||
contentLoader.item.appLauncher.setCategory(I18n.tr("All"))
|
spotlightContent.appLauncher.setCategory(I18n.tr("All"))
|
||||||
}
|
}
|
||||||
if (contentLoader.item.resetScroll) {
|
if (spotlightContent.fileSearchController) {
|
||||||
contentLoader.item.resetScroll()
|
spotlightContent.fileSearchController.reset()
|
||||||
}
|
}
|
||||||
if (contentLoader.item.searchField) {
|
if (spotlightContent.resetScroll) {
|
||||||
contentLoader.item.searchField.text = ""
|
spotlightContent.resetScroll()
|
||||||
|
}
|
||||||
|
if (spotlightContent.searchField) {
|
||||||
|
spotlightContent.searchField.text = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,10 +91,10 @@ DankModal {
|
|||||||
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()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -79,7 +102,6 @@ DankModal {
|
|||||||
onBackgroundClicked: () => {
|
onBackgroundClicked: () => {
|
||||||
return hide()
|
return hide()
|
||||||
}
|
}
|
||||||
content: spotlightContent
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onCloseAllModalsExcept(excludedModal) {
|
function onCloseAllModalsExcept(excludedModal) {
|
||||||
@@ -92,27 +114,43 @@ DankModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
function open(): string {
|
function open(): string {
|
||||||
spotlightModal.show()
|
spotlightModal.show()
|
||||||
return "SPOTLIGHT_OPEN_SUCCESS"
|
return "SPOTLIGHT_OPEN_SUCCESS"
|
||||||
}
|
}
|
||||||
|
|
||||||
function close(): string {
|
function close(): string {
|
||||||
spotlightModal.hide()
|
spotlightModal.hide()
|
||||||
return "SPOTLIGHT_CLOSE_SUCCESS"
|
return "SPOTLIGHT_CLOSE_SUCCESS"
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle(): string {
|
function toggle(): string {
|
||||||
spotlightModal.toggle()
|
spotlightModal.toggle()
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ Rectangle {
|
|||||||
resultsGrid.contentY = 0
|
resultsGrid.contentY = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
height: parent.height - y
|
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
clip: true
|
clip: true
|
||||||
@@ -90,19 +88,32 @@ Rectangle {
|
|||||||
width: resultsList.iconSize
|
width: resultsList.iconSize
|
||||||
height: resultsList.iconSize
|
height: resultsList.iconSize
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: model.icon !== undefined && model.icon !== ""
|
||||||
|
|
||||||
|
property string iconValue: model.icon || ""
|
||||||
|
property bool isMaterial: iconValue.indexOf("material:") === 0
|
||||||
|
property string materialName: isMaterial ? iconValue.substring(9) : ""
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: parent.materialName
|
||||||
|
size: resultsList.iconSize
|
||||||
|
color: Theme.surfaceText
|
||||||
|
visible: parent.isMaterial
|
||||||
|
}
|
||||||
|
|
||||||
IconImage {
|
IconImage {
|
||||||
id: listIconImg
|
id: listIconImg
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: Quickshell.iconPath(model.icon, true)
|
source: parent.isMaterial ? "" : Quickshell.iconPath(parent.iconValue, true)
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
visible: status === Image.Ready
|
visible: !parent.isMaterial && status === Image.Ready
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: !listIconImg.visible
|
visible: !parent.isMaterial && !listIconImg.visible
|
||||||
color: Theme.surfaceLight
|
color: Theme.surfaceLight
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.width: 1
|
border.width: 1
|
||||||
@@ -120,7 +131,7 @@ Rectangle {
|
|||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
width: parent.width - resultsList.iconSize - Theme.spacingL
|
width: (model.icon !== undefined && model.icon !== "") ? (parent.width - resultsList.iconSize - Theme.spacingL) : parent.width
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
@@ -130,6 +141,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 {
|
||||||
@@ -162,7 +175,7 @@ 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 globalPos = mapToItem(null, mouse.x, mouse.y)
|
const globalPos = mapToItem(null, mouse.x, mouse.y)
|
||||||
const modalPos = resultsContainer.parent.mapFromItem(null, globalPos.x, globalPos.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)
|
||||||
@@ -253,20 +266,33 @@ Rectangle {
|
|||||||
width: iconSize
|
width: iconSize
|
||||||
height: iconSize
|
height: iconSize
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
visible: model.icon !== undefined && model.icon !== ""
|
||||||
|
|
||||||
|
property string iconValue: model.icon || ""
|
||||||
|
property bool isMaterial: iconValue.indexOf("material:") === 0
|
||||||
|
property string materialName: isMaterial ? iconValue.substring(9) : ""
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: parent.materialName
|
||||||
|
size: parent.iconSize
|
||||||
|
color: Theme.surfaceText
|
||||||
|
visible: parent.isMaterial
|
||||||
|
}
|
||||||
|
|
||||||
IconImage {
|
IconImage {
|
||||||
id: gridIconImg
|
id: gridIconImg
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: Quickshell.iconPath(model.icon, true)
|
source: parent.isMaterial ? "" : Quickshell.iconPath(parent.iconValue, true)
|
||||||
smooth: true
|
smooth: true
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
visible: status === Image.Ready
|
visible: !parent.isMaterial && status === Image.Ready
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: !gridIconImg.visible
|
visible: !parent.isMaterial && !gridIconImg.visible
|
||||||
color: Theme.surfaceLight
|
color: Theme.surfaceLight
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.width: 1
|
border.width: 1
|
||||||
@@ -291,8 +317,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,7 +340,7 @@ 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 globalPos = mapToItem(null, mouse.x, mouse.y)
|
const globalPos = mapToItem(null, mouse.x, mouse.y)
|
||||||
const modalPos = resultsContainer.parent.mapFromItem(null, globalPos.x, globalPos.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)
|
||||||
|
|||||||
@@ -12,10 +12,35 @@ DankModal {
|
|||||||
property string wifiUsernameInput: ""
|
property string wifiUsernameInput: ""
|
||||||
property bool requiresEnterprise: false
|
property bool requiresEnterprise: false
|
||||||
|
|
||||||
|
property string wifiAnonymousIdentityInput: ""
|
||||||
|
property string wifiDomainInput: ""
|
||||||
|
|
||||||
|
property bool isPromptMode: false
|
||||||
|
property string promptToken: ""
|
||||||
|
property string promptReason: ""
|
||||||
|
property var promptFields: []
|
||||||
|
property string promptSetting: ""
|
||||||
|
|
||||||
|
property bool isVpnPrompt: false
|
||||||
|
property string connectionName: ""
|
||||||
|
property string vpnServiceType: ""
|
||||||
|
property string connectionType: ""
|
||||||
|
|
||||||
function show(ssid) {
|
function show(ssid) {
|
||||||
wifiPasswordSSID = ssid
|
wifiPasswordSSID = ssid
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = ""
|
||||||
|
wifiAnonymousIdentityInput = ""
|
||||||
|
wifiDomainInput = ""
|
||||||
|
isPromptMode = false
|
||||||
|
promptToken = ""
|
||||||
|
promptReason = ""
|
||||||
|
promptFields = []
|
||||||
|
promptSetting = ""
|
||||||
|
isVpnPrompt = false
|
||||||
|
connectionName = ""
|
||||||
|
vpnServiceType = ""
|
||||||
|
connectionType = ""
|
||||||
|
|
||||||
const network = NetworkService.wifiNetworks.find(n => n.ssid === ssid)
|
const network = NetworkService.wifiNetworks.find(n => n.ssid === ssid)
|
||||||
requiresEnterprise = network?.enterprise || false
|
requiresEnterprise = network?.enterprise || false
|
||||||
@@ -32,13 +57,55 @@ DankModal {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showFromPrompt(token, ssid, setting, fields, hints, reason, connType, connName, vpnService) {
|
||||||
|
isPromptMode = true
|
||||||
|
promptToken = token
|
||||||
|
promptReason = reason
|
||||||
|
promptFields = fields || []
|
||||||
|
promptSetting = setting || "802-11-wireless-security"
|
||||||
|
connectionType = connType || "802-11-wireless"
|
||||||
|
connectionName = connName || ssid || ""
|
||||||
|
vpnServiceType = vpnService || ""
|
||||||
|
|
||||||
|
isVpnPrompt = (connectionType === "vpn" || connectionType === "wireguard")
|
||||||
|
wifiPasswordSSID = isVpnPrompt ? connectionName : ssid
|
||||||
|
|
||||||
|
requiresEnterprise = setting === "802-1x"
|
||||||
|
|
||||||
|
if (reason === "wrong-password") {
|
||||||
|
wifiPasswordInput = ""
|
||||||
|
wifiUsernameInput = ""
|
||||||
|
} else {
|
||||||
|
wifiPasswordInput = ""
|
||||||
|
wifiUsernameInput = ""
|
||||||
|
wifiAnonymousIdentityInput = ""
|
||||||
|
wifiDomainInput = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
open()
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (contentLoader.item) {
|
||||||
|
if (reason === "wrong-password" && contentLoader.item.passwordInput) {
|
||||||
|
contentLoader.item.passwordInput.text = ""
|
||||||
|
contentLoader.item.passwordInput.forceActiveFocus()
|
||||||
|
} else 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: requiresEnterprise ? 310 : 230
|
height: requiresEnterprise ? 430 : 230
|
||||||
onShouldBeVisibleChanged: () => {
|
onShouldBeVisibleChanged: () => {
|
||||||
if (!shouldBeVisible) {
|
if (!shouldBeVisible) {
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = ""
|
||||||
|
wifiAnonymousIdentityInput = ""
|
||||||
|
wifiDomainInput = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onOpened: {
|
onOpened: {
|
||||||
@@ -53,9 +120,14 @@ DankModal {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
onBackgroundClicked: () => {
|
onBackgroundClicked: () => {
|
||||||
|
if (isPromptMode) {
|
||||||
|
NetworkService.cancelCredentials(promptToken)
|
||||||
|
}
|
||||||
close()
|
close()
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = ""
|
||||||
|
wifiAnonymousIdentityInput = ""
|
||||||
|
wifiDomainInput = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
@@ -81,9 +153,14 @@ DankModal {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
focus: true
|
focus: true
|
||||||
Keys.onEscapePressed: event => {
|
Keys.onEscapePressed: event => {
|
||||||
|
if (isPromptMode) {
|
||||||
|
NetworkService.cancelCredentials(promptToken)
|
||||||
|
}
|
||||||
close()
|
close()
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = ""
|
||||||
|
wifiAnonymousIdentityInput = ""
|
||||||
|
wifiDomainInput = ""
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,18 +177,42 @@ DankModal {
|
|||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: I18n.tr("Connect to Wi-Fi")
|
text: {
|
||||||
|
if (isVpnPrompt) {
|
||||||
|
return I18n.tr("Connect to VPN")
|
||||||
|
}
|
||||||
|
return I18n.tr("Connect to Wi-Fi")
|
||||||
|
}
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
Column {
|
||||||
text: requiresEnterprise ? `Enter credentials for "${wifiPasswordSSID}"` : `Enter password for "${wifiPasswordSSID}"`
|
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
|
||||||
color: Theme.surfaceTextMedium
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
elide: Text.ElideRight
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
if (isVpnPrompt) {
|
||||||
|
return I18n.tr("Enter password for ") + wifiPasswordSSID
|
||||||
|
}
|
||||||
|
const prefix = requiresEnterprise ? I18n.tr("Enter credentials for ") : I18n.tr("Enter password for ")
|
||||||
|
return prefix + wifiPasswordSSID
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceTextMedium
|
||||||
|
width: parent.width
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
visible: isPromptMode && promptReason === "wrong-password"
|
||||||
|
text: I18n.tr("Incorrect password")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.error
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,9 +221,14 @@ DankModal {
|
|||||||
iconSize: Theme.iconSize - 4
|
iconSize: Theme.iconSize - 4
|
||||||
iconColor: Theme.surfaceText
|
iconColor: Theme.surfaceText
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
|
if (isPromptMode) {
|
||||||
|
NetworkService.cancelCredentials(promptToken)
|
||||||
|
}
|
||||||
close()
|
close()
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = ""
|
||||||
|
wifiAnonymousIdentityInput = ""
|
||||||
|
wifiDomainInput = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,7 +240,7 @@ DankModal {
|
|||||||
color: Theme.surfaceHover
|
color: Theme.surfaceHover
|
||||||
border.color: usernameInput.activeFocus ? Theme.primary : Theme.outlineStrong
|
border.color: usernameInput.activeFocus ? Theme.primary : Theme.outlineStrong
|
||||||
border.width: usernameInput.activeFocus ? 2 : 1
|
border.width: usernameInput.activeFocus ? 2 : 1
|
||||||
visible: requiresEnterprise
|
visible: requiresEnterprise && !isVpnPrompt
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -150,7 +256,7 @@ DankModal {
|
|||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
textColor: Theme.surfaceText
|
textColor: Theme.surfaceText
|
||||||
text: wifiUsernameInput
|
text: wifiUsernameInput
|
||||||
placeholderText: "Username"
|
placeholderText: I18n.tr("Username")
|
||||||
backgroundColor: "transparent"
|
backgroundColor: "transparent"
|
||||||
enabled: root.shouldBeVisible
|
enabled: root.shouldBeVisible
|
||||||
onTextEdited: () => {
|
onTextEdited: () => {
|
||||||
@@ -187,7 +293,7 @@ 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: requiresEnterprise ? "Password" : ""
|
placeholderText: (requiresEnterprise && !isVpnPrompt) ? I18n.tr("Password") : ""
|
||||||
backgroundColor: "transparent"
|
backgroundColor: "transparent"
|
||||||
focus: !requiresEnterprise
|
focus: !requiresEnterprise
|
||||||
enabled: root.shouldBeVisible
|
enabled: root.shouldBeVisible
|
||||||
@@ -195,11 +301,33 @@ DankModal {
|
|||||||
wifiPasswordInput = text
|
wifiPasswordInput = text
|
||||||
}
|
}
|
||||||
onAccepted: () => {
|
onAccepted: () => {
|
||||||
const username = requiresEnterprise ? usernameInput.text : ""
|
if (isPromptMode) {
|
||||||
NetworkService.connectToWifi(wifiPasswordSSID, passwordInput.text, username)
|
const secrets = {}
|
||||||
|
if (isVpnPrompt) {
|
||||||
|
if (passwordInput.text) secrets["password"] = passwordInput.text
|
||||||
|
} else if (promptSetting === "802-11-wireless-security") {
|
||||||
|
secrets["psk"] = passwordInput.text
|
||||||
|
} else if (promptSetting === "802-1x") {
|
||||||
|
if (usernameInput.text) secrets["identity"] = usernameInput.text
|
||||||
|
if (passwordInput.text) secrets["password"] = passwordInput.text
|
||||||
|
if (wifiAnonymousIdentityInput) secrets["anonymous-identity"] = wifiAnonymousIdentityInput
|
||||||
|
}
|
||||||
|
NetworkService.submitCredentials(promptToken, secrets, true)
|
||||||
|
} else {
|
||||||
|
const username = requiresEnterprise ? usernameInput.text : ""
|
||||||
|
NetworkService.connectToWifi(
|
||||||
|
wifiPasswordSSID,
|
||||||
|
passwordInput.text,
|
||||||
|
username,
|
||||||
|
wifiAnonymousIdentityInput,
|
||||||
|
wifiDomainInput
|
||||||
|
)
|
||||||
|
}
|
||||||
close()
|
close()
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = ""
|
||||||
|
wifiAnonymousIdentityInput = ""
|
||||||
|
wifiDomainInput = ""
|
||||||
passwordInput.text = ""
|
passwordInput.text = ""
|
||||||
if (requiresEnterprise) usernameInput.text = ""
|
if (requiresEnterprise) usernameInput.text = ""
|
||||||
}
|
}
|
||||||
@@ -235,6 +363,70 @@ DankModal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
visible: requiresEnterprise && !isVpnPrompt
|
||||||
|
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 && !isVpnPrompt
|
||||||
|
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
|
||||||
|
|
||||||
@@ -310,9 +502,14 @@ DankModal {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
|
if (isPromptMode) {
|
||||||
|
NetworkService.cancelCredentials(promptToken)
|
||||||
|
}
|
||||||
close()
|
close()
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = ""
|
||||||
|
wifiAnonymousIdentityInput = ""
|
||||||
|
wifiDomainInput = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -322,7 +519,12 @@ 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: requiresEnterprise ? (usernameInput.text.length > 0 && passwordInput.text.length > 0) : passwordInput.text.length > 0
|
enabled: {
|
||||||
|
if (isVpnPrompt) {
|
||||||
|
return passwordInput.text.length > 0
|
||||||
|
}
|
||||||
|
return requiresEnterprise ? (usernameInput.text.length > 0 && passwordInput.text.length > 0) : passwordInput.text.length > 0
|
||||||
|
}
|
||||||
opacity: enabled ? 1 : 0.5
|
opacity: enabled ? 1 : 0.5
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
@@ -343,11 +545,33 @@ DankModal {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
enabled: parent.enabled
|
enabled: parent.enabled
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
const username = requiresEnterprise ? usernameInput.text : ""
|
if (isPromptMode) {
|
||||||
NetworkService.connectToWifi(wifiPasswordSSID, passwordInput.text, username)
|
const secrets = {}
|
||||||
|
if (isVpnPrompt) {
|
||||||
|
if (passwordInput.text) secrets["password"] = passwordInput.text
|
||||||
|
} else if (promptSetting === "802-11-wireless-security") {
|
||||||
|
secrets["psk"] = passwordInput.text
|
||||||
|
} else if (promptSetting === "802-1x") {
|
||||||
|
if (usernameInput.text) secrets["identity"] = usernameInput.text
|
||||||
|
if (passwordInput.text) secrets["password"] = passwordInput.text
|
||||||
|
if (wifiAnonymousIdentityInput) secrets["anonymous-identity"] = wifiAnonymousIdentityInput
|
||||||
|
}
|
||||||
|
NetworkService.submitCredentials(promptToken, secrets, true)
|
||||||
|
} else {
|
||||||
|
const username = requiresEnterprise ? usernameInput.text : ""
|
||||||
|
NetworkService.connectToWifi(
|
||||||
|
wifiPasswordSSID,
|
||||||
|
passwordInput.text,
|
||||||
|
username,
|
||||||
|
wifiAnonymousIdentityInput,
|
||||||
|
wifiDomainInput
|
||||||
|
)
|
||||||
|
}
|
||||||
close()
|
close()
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = ""
|
||||||
wifiUsernameInput = ""
|
wifiUsernameInput = ""
|
||||||
|
wifiAnonymousIdentityInput = ""
|
||||||
|
wifiDomainInput = ""
|
||||||
passwordInput.text = ""
|
passwordInput.text = ""
|
||||||
if (requiresEnterprise) usernameInput.text = ""
|
if (requiresEnterprise) usernameInput.text = ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -404,16 +404,29 @@ DankPopout {
|
|||||||
width: appList.iconSize
|
width: appList.iconSize
|
||||||
height: appList.iconSize
|
height: appList.iconSize
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: model.icon !== undefined && model.icon !== ""
|
||||||
|
|
||||||
|
property string iconValue: model.icon || ""
|
||||||
|
property bool isMaterial: iconValue.indexOf("material:") === 0
|
||||||
|
property string materialName: isMaterial ? iconValue.substring(9) : ""
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: parent.materialName
|
||||||
|
size: appList.iconSize - Theme.spacingM
|
||||||
|
color: Theme.surfaceText
|
||||||
|
visible: parent.isMaterial
|
||||||
|
}
|
||||||
|
|
||||||
IconImage {
|
IconImage {
|
||||||
id: listIconImg
|
id: listIconImg
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingXS
|
anchors.margins: Theme.spacingXS
|
||||||
source: Quickshell.iconPath(model.icon, true)
|
source: parent.isMaterial ? "" : Quickshell.iconPath(parent.iconValue, true)
|
||||||
smooth: true
|
smooth: true
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
visible: status === Image.Ready
|
visible: !parent.isMaterial && status === Image.Ready
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -421,7 +434,7 @@ DankPopout {
|
|||||||
anchors.leftMargin: Theme.spacingS
|
anchors.leftMargin: Theme.spacingS
|
||||||
anchors.rightMargin: Theme.spacingS
|
anchors.rightMargin: Theme.spacingS
|
||||||
anchors.bottomMargin: Theme.spacingM
|
anchors.bottomMargin: Theme.spacingM
|
||||||
visible: !listIconImg.visible
|
visible: !parent.isMaterial && listIconImg.status !== Image.Ready
|
||||||
color: Theme.surfaceLight
|
color: Theme.surfaceLight
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.width: 0
|
border.width: 0
|
||||||
@@ -435,11 +448,12 @@ DankPopout {
|
|||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
width: parent.width - appList.iconSize - Theme.spacingL
|
width: (model.icon !== undefined && model.icon !== "") ? (parent.width - appList.iconSize - Theme.spacingL) : parent.width
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
@@ -449,6 +463,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 {
|
||||||
@@ -511,6 +527,7 @@ DankPopout {
|
|||||||
property int baseCellWidth: adaptiveColumns ? Math.max(minCellWidth, Math.min(maxCellWidth, width / columns)) : (width - Theme.spacingS * 2) / columns
|
property int baseCellWidth: adaptiveColumns ? Math.max(minCellWidth, Math.min(maxCellWidth, width / columns)) : (width - Theme.spacingS * 2) / columns
|
||||||
property int baseCellHeight: baseCellWidth + 20
|
property int baseCellHeight: baseCellWidth + 20
|
||||||
property int actualColumns: adaptiveColumns ? Math.floor(width / cellWidth) : columns
|
property int actualColumns: adaptiveColumns ? Math.floor(width / cellWidth) : columns
|
||||||
|
|
||||||
property int remainingSpace: width - (actualColumns * cellWidth)
|
property int remainingSpace: width - (actualColumns * cellWidth)
|
||||||
|
|
||||||
signal keyboardNavigationReset
|
signal keyboardNavigationReset
|
||||||
@@ -576,6 +593,19 @@ DankPopout {
|
|||||||
width: iconSize
|
width: iconSize
|
||||||
height: iconSize
|
height: iconSize
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
visible: model.icon !== undefined && model.icon !== ""
|
||||||
|
|
||||||
|
property string iconValue: model.icon || ""
|
||||||
|
property bool isMaterial: iconValue.indexOf("material:") === 0
|
||||||
|
property string materialName: isMaterial ? iconValue.substring(9) : ""
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: parent.materialName
|
||||||
|
size: parent.iconSize - Theme.spacingL
|
||||||
|
color: Theme.surfaceText
|
||||||
|
visible: parent.isMaterial
|
||||||
|
}
|
||||||
|
|
||||||
IconImage {
|
IconImage {
|
||||||
id: gridIconImg
|
id: gridIconImg
|
||||||
@@ -584,10 +614,10 @@ DankPopout {
|
|||||||
anchors.leftMargin: Theme.spacingS
|
anchors.leftMargin: Theme.spacingS
|
||||||
anchors.rightMargin: Theme.spacingS
|
anchors.rightMargin: Theme.spacingS
|
||||||
anchors.bottomMargin: Theme.spacingS
|
anchors.bottomMargin: Theme.spacingS
|
||||||
source: Quickshell.iconPath(model.icon, true)
|
source: parent.isMaterial ? "" : Quickshell.iconPath(parent.iconValue, true)
|
||||||
smooth: true
|
smooth: true
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
visible: status === Image.Ready
|
visible: !parent.isMaterial && status === Image.Ready
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -595,7 +625,7 @@ DankPopout {
|
|||||||
anchors.leftMargin: Theme.spacingS
|
anchors.leftMargin: Theme.spacingS
|
||||||
anchors.rightMargin: Theme.spacingS
|
anchors.rightMargin: Theme.spacingS
|
||||||
anchors.bottomMargin: Theme.spacingS
|
anchors.bottomMargin: Theme.spacingS
|
||||||
visible: !gridIconImg.visible
|
visible: !parent.isMaterial && gridIconImg.status !== Image.Ready
|
||||||
color: Theme.surfaceLight
|
color: Theme.surfaceLight
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.width: 0
|
border.width: 0
|
||||||
@@ -620,8 +650,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -665,8 +695,8 @@ DankPopout {
|
|||||||
id: contextMenu
|
id: contextMenu
|
||||||
|
|
||||||
property var currentApp: null
|
property var currentApp: null
|
||||||
|
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: (currentApp && currentApp.desktopEntry) ? (currentApp.desktopEntry.id || currentApp.desktopEntry.execString || "") : ""
|
readonly property string appId: desktopEntry ? (desktopEntry.id || 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) {
|
||||||
@@ -768,7 +798,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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -797,7 +827,7 @@ DankPopout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: contextMenu.currentApp && contextMenu.currentApp.desktopEntry && contextMenu.currentApp.desktopEntry.actions ? contextMenu.currentApp.desktopEntry.actions : []
|
model: contextMenu.desktopEntry && contextMenu.desktopEntry.actions ? contextMenu.desktopEntry.actions : []
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: Math.max(parent.width, actionRow.implicitWidth + Theme.spacingS * 2)
|
width: Math.max(parent.width, actionRow.implicitWidth + Theme.spacingS * 2)
|
||||||
@@ -842,9 +872,11 @@ DankPopout {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (modelData && contextMenu.currentApp && contextMenu.currentApp.desktopEntry) {
|
if (modelData && contextMenu.desktopEntry) {
|
||||||
SessionService.launchDesktopAction(contextMenu.currentApp.desktopEntry, modelData)
|
SessionService.launchDesktopAction(contextMenu.desktopEntry, modelData)
|
||||||
appLauncher.appLaunched(contextMenu.currentApp)
|
if (contextMenu.currentApp) {
|
||||||
|
appLauncher.appLaunched(contextMenu.currentApp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
contextMenu.hide()
|
contextMenu.hide()
|
||||||
}
|
}
|
||||||
@@ -853,7 +885,7 @@ DankPopout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
visible: contextMenu.currentApp && contextMenu.currentApp.desktopEntry && contextMenu.currentApp.desktopEntry.actions && contextMenu.currentApp.desktopEntry.actions.length > 0
|
visible: contextMenu.desktopEntry && contextMenu.desktopEntry.actions && contextMenu.desktopEntry.actions.length > 0
|
||||||
width: parent.width - Theme.spacingS * 2
|
width: parent.width - Theme.spacingS * 2
|
||||||
height: 5
|
height: 5
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
@@ -963,9 +995,11 @@ DankPopout {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (contextMenu.currentApp && contextMenu.currentApp.desktopEntry) {
|
if (contextMenu.desktopEntry) {
|
||||||
SessionService.launchDesktopEntry(contextMenu.currentApp.desktopEntry, true)
|
SessionService.launchDesktopEntry(contextMenu.desktopEntry, true)
|
||||||
appLauncher.appLaunched(contextMenu.currentApp)
|
if (contextMenu.currentApp) {
|
||||||
|
appLauncher.appLaunched(contextMenu.currentApp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
contextMenu.hide()
|
contextMenu.hide()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ import qs.Widgets
|
|||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
// DEVELOPER NOTE: This component manages the AppDrawer launcher (accessed via DankBar icon).
|
||||||
|
// Changes to launcher behavior, especially item rendering, filtering, or model structure,
|
||||||
|
// likely require corresponding updates in Modals/Spotlight/SpotlightResults.qml and vice versa.
|
||||||
|
|
||||||
property string searchQuery: ""
|
property string searchQuery: ""
|
||||||
property string selectedCategory: I18n.tr("All")
|
property string selectedCategory: I18n.tr("All")
|
||||||
property string viewMode: "list" // "list" or "grid"
|
property string viewMode: "list" // "list" or "grid"
|
||||||
@@ -49,6 +53,13 @@ Item {
|
|||||||
function onPluginListUpdated() { updateCategories() }
|
function onPluginListUpdated() { updateCategories() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: SettingsData
|
||||||
|
function onSortAppsAlphabeticallyChanged() {
|
||||||
|
updateFilteredModel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function updateFilteredModel() {
|
function updateFilteredModel() {
|
||||||
@@ -123,16 +134,22 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 seenNames = new Set()
|
||||||
@@ -150,7 +167,7 @@ Item {
|
|||||||
filteredModel.append({
|
filteredModel.append({
|
||||||
"name": app.name || "",
|
"name": app.name || "",
|
||||||
"exec": app.execString || app.exec || app.action || "",
|
"exec": app.execString || app.exec || app.action || "",
|
||||||
"icon": app.icon || "application-x-executable",
|
"icon": app.icon !== undefined ? app.icon : (isPluginItem ? "" : "application-x-executable"),
|
||||||
"comment": app.comment || "",
|
"comment": app.comment || "",
|
||||||
"categories": app.categories || [],
|
"categories": app.categories || [],
|
||||||
"isPlugin": isPluginItem,
|
"isPlugin": isPluginItem,
|
||||||
@@ -272,12 +289,12 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const triggers = PluginService.getAllPluginTriggers()
|
const triggers = PluginService.getAllPluginTriggers()
|
||||||
|
|
||||||
for (const trigger in triggers) {
|
for (const trigger in triggers) {
|
||||||
if (query.startsWith(trigger)) {
|
if (query.startsWith(trigger)) {
|
||||||
const pluginId = triggers[trigger]
|
const pluginId = triggers[trigger]
|
||||||
const plugin = PluginService.getLauncherPlugin(pluginId)
|
const plugin = PluginService.getLauncherPlugin(pluginId)
|
||||||
|
|
||||||
if (plugin) {
|
if (plugin) {
|
||||||
const remainingQuery = query.substring(trigger.length).trim()
|
const remainingQuery = query.substring(trigger.length).trim()
|
||||||
const result = {
|
const result = {
|
||||||
@@ -291,7 +308,7 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { triggered: false, pluginCategory: "", query: "" }
|
return { triggered: false, pluginCategory: "", query: "" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
244
Modules/BlurredWallpaperBackground.qml
Normal file
244
Modules/BlurredWallpaperBackground.qml
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Io
|
||||||
|
import qs.Common
|
||||||
|
import qs.Widgets
|
||||||
|
import qs.Modules
|
||||||
|
|
||||||
|
Variants {
|
||||||
|
model: {
|
||||||
|
if (SessionData.isGreeterMode) {
|
||||||
|
return Quickshell.screens
|
||||||
|
}
|
||||||
|
return SettingsData.getFilteredScreens("wallpaper")
|
||||||
|
}
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: blurWallpaperWindow
|
||||||
|
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
screen: modelData
|
||||||
|
|
||||||
|
WlrLayershell.layer: WlrLayer.Background
|
||||||
|
WlrLayershell.namespace: "dms:blurwallpaper"
|
||||||
|
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
||||||
|
|
||||||
|
anchors.top: true
|
||||||
|
anchors.bottom: true
|
||||||
|
anchors.left: true
|
||||||
|
anchors.right: true
|
||||||
|
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
property string source: SessionData.getMonitorWallpaper(modelData.name) || ""
|
||||||
|
property bool isColorSource: source.startsWith("#")
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: SessionData
|
||||||
|
function onIsLightModeChanged() {
|
||||||
|
if (SessionData.perModeWallpaper) {
|
||||||
|
var newSource = SessionData.getMonitorWallpaper(modelData.name) || ""
|
||||||
|
if (newSource !== root.source) {
|
||||||
|
root.source = newSource
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFillMode(modeName) {
|
||||||
|
switch (modeName) {
|
||||||
|
case "Stretch":
|
||||||
|
return Image.Stretch
|
||||||
|
case "Fit":
|
||||||
|
case "PreserveAspectFit":
|
||||||
|
return Image.PreserveAspectFit
|
||||||
|
case "Fill":
|
||||||
|
case "PreserveAspectCrop":
|
||||||
|
return Image.PreserveAspectCrop
|
||||||
|
case "Tile":
|
||||||
|
return Image.Tile
|
||||||
|
case "TileVertically":
|
||||||
|
return Image.TileVertically
|
||||||
|
case "TileHorizontally":
|
||||||
|
return Image.TileHorizontally
|
||||||
|
case "Pad":
|
||||||
|
return Image.Pad
|
||||||
|
default:
|
||||||
|
return Image.PreserveAspectCrop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WallpaperEngineProc {
|
||||||
|
id: weProc
|
||||||
|
monitor: modelData.name
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (source) {
|
||||||
|
const formattedSource = source.startsWith("file://") ? source : "file://" + source
|
||||||
|
setWallpaperImmediate(formattedSource)
|
||||||
|
}
|
||||||
|
isInitialized = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onDestruction: {
|
||||||
|
weProc.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
property bool isInitialized: false
|
||||||
|
property real transitionProgress: 0
|
||||||
|
readonly property bool transitioning: transitionAnimation.running
|
||||||
|
|
||||||
|
onSourceChanged: {
|
||||||
|
const isWE = source.startsWith("we:")
|
||||||
|
const isColor = source.startsWith("#")
|
||||||
|
|
||||||
|
if (isWE) {
|
||||||
|
setWallpaperImmediate("")
|
||||||
|
weProc.start(source.substring(3))
|
||||||
|
} else {
|
||||||
|
weProc.stop()
|
||||||
|
if (!source) {
|
||||||
|
setWallpaperImmediate("")
|
||||||
|
} else if (isColor) {
|
||||||
|
setWallpaperImmediate("")
|
||||||
|
} else {
|
||||||
|
if (!isInitialized || !currentWallpaper.source) {
|
||||||
|
setWallpaperImmediate(source.startsWith("file://") ? source : "file://" + source)
|
||||||
|
isInitialized = true
|
||||||
|
} else {
|
||||||
|
changeWallpaper(source.startsWith("file://") ? source : "file://" + source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setWallpaperImmediate(newSource) {
|
||||||
|
transitionAnimation.stop()
|
||||||
|
root.transitionProgress = 0.0
|
||||||
|
currentWallpaper.source = newSource
|
||||||
|
nextWallpaper.source = ""
|
||||||
|
currentWallpaper.opacity = 1
|
||||||
|
nextWallpaper.opacity = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeWallpaper(newPath) {
|
||||||
|
if (newPath === currentWallpaper.source)
|
||||||
|
return
|
||||||
|
if (!newPath || newPath.startsWith("#"))
|
||||||
|
return
|
||||||
|
|
||||||
|
if (root.transitioning) {
|
||||||
|
transitionAnimation.stop()
|
||||||
|
root.transitionProgress = 0
|
||||||
|
currentWallpaper.source = nextWallpaper.source
|
||||||
|
nextWallpaper.source = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentWallpaper.source) {
|
||||||
|
setWallpaperImmediate(newPath)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
nextWallpaper.source = newPath
|
||||||
|
|
||||||
|
if (nextWallpaper.status === Image.Ready) {
|
||||||
|
transitionAnimation.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
anchors.fill: parent
|
||||||
|
active: !root.source || root.isColorSource
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
|
sourceComponent: DankBackdrop {
|
||||||
|
screenName: modelData.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: currentWallpaper
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: false
|
||||||
|
opacity: 1
|
||||||
|
asynchronous: true
|
||||||
|
smooth: true
|
||||||
|
cache: true
|
||||||
|
fillMode: root.getFillMode(SettingsData.wallpaperFillMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: nextWallpaper
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: false
|
||||||
|
opacity: 0
|
||||||
|
asynchronous: true
|
||||||
|
smooth: true
|
||||||
|
cache: true
|
||||||
|
fillMode: root.getFillMode(SettingsData.wallpaperFillMode)
|
||||||
|
|
||||||
|
onStatusChanged: {
|
||||||
|
if (status !== Image.Ready)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (!root.transitioning) {
|
||||||
|
transitionAnimation.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: blurredLayer
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
MultiEffect {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: currentWallpaper
|
||||||
|
blurEnabled: true
|
||||||
|
blur: 0.8
|
||||||
|
blurMax: 75
|
||||||
|
opacity: 1 - root.transitionProgress
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiEffect {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: nextWallpaper
|
||||||
|
blurEnabled: true
|
||||||
|
blur: 0.8
|
||||||
|
blurMax: 75
|
||||||
|
opacity: root.transitionProgress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
id: transitionAnimation
|
||||||
|
target: root
|
||||||
|
property: "transitionProgress"
|
||||||
|
from: 0.0
|
||||||
|
to: 1.0
|
||||||
|
duration: 1000
|
||||||
|
easing.type: Easing.InOutCubic
|
||||||
|
onFinished: {
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (nextWallpaper.source && nextWallpaper.status === Image.Ready && !nextWallpaper.source.toString().startsWith("#")) {
|
||||||
|
currentWallpaper.source = nextWallpaper.source
|
||||||
|
}
|
||||||
|
nextWallpaper.source = ""
|
||||||
|
currentWallpaper.opacity = 1
|
||||||
|
nextWallpaper.opacity = 0
|
||||||
|
root.transitionProgress = 0.0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,27 +10,24 @@ PluginComponent {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
Ref {
|
Ref {
|
||||||
service: VpnService
|
service: DMSNetworkService
|
||||||
}
|
}
|
||||||
|
|
||||||
ccWidgetIcon: VpnService.isBusy ? "sync" : (VpnService.connected ? "vpn_lock" : "vpn_key_off")
|
|
||||||
|
ccWidgetIcon: DMSNetworkService.isBusy ? "sync" : (DMSNetworkService.connected ? "vpn_lock" : "vpn_key_off")
|
||||||
ccWidgetPrimaryText: "VPN"
|
ccWidgetPrimaryText: "VPN"
|
||||||
ccWidgetSecondaryText: {
|
ccWidgetSecondaryText: {
|
||||||
if (!VpnService.connected)
|
if (!DMSNetworkService.connected)
|
||||||
return "Disconnected"
|
return "Disconnected"
|
||||||
const names = VpnService.activeNames || []
|
const names = DMSNetworkService.activeNames || []
|
||||||
if (names.length <= 1)
|
if (names.length <= 1)
|
||||||
return names[0] || "Connected"
|
return names[0] || "Connected"
|
||||||
return names[0] + " +" + (names.length - 1)
|
return names[0] + " +" + (names.length - 1)
|
||||||
}
|
}
|
||||||
ccWidgetIsActive: VpnService.connected
|
ccWidgetIsActive: DMSNetworkService.connected
|
||||||
|
|
||||||
onCcWidgetToggled: {
|
onCcWidgetToggled: {
|
||||||
if (VpnService.connected) {
|
DMSNetworkService.toggleVpn()
|
||||||
VpnService.disconnectAllActive()
|
|
||||||
} else if (VpnService.profiles.length > 0) {
|
|
||||||
VpnService.connect(VpnService.profiles[0].uuid)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ccDetailContent: Component {
|
ccDetailContent: Component {
|
||||||
@@ -52,9 +49,9 @@ PluginComponent {
|
|||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: {
|
text: {
|
||||||
if (!VpnService.connected)
|
if (!DMSNetworkService.connected)
|
||||||
return "Active: None"
|
return "Active: None"
|
||||||
const names = VpnService.activeNames || []
|
const names = DMSNetworkService.activeNames || []
|
||||||
if (names.length <= 1)
|
if (names.length <= 1)
|
||||||
return "Active: " + (names[0] || "VPN")
|
return "Active: " + (names[0] || "VPN")
|
||||||
return "Active: " + names[0] + " +" + (names.length - 1)
|
return "Active: " + names[0] + " +" + (names.length - 1)
|
||||||
@@ -62,19 +59,20 @@ PluginComponent {
|
|||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
}
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumWidth: parent.width - 120
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
height: 28
|
height: 28
|
||||||
radius: 14
|
radius: 14
|
||||||
color: discAllArea.containsMouse ? Theme.errorHover : Theme.surfaceLight
|
color: discAllArea.containsMouse ? Theme.errorHover : Theme.surfaceLight
|
||||||
visible: VpnService.connected
|
visible: DMSNetworkService.connected
|
||||||
width: 110
|
width: 110
|
||||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||||
|
opacity: DMSNetworkService.isBusy ? 0.5 : 1.0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -98,8 +96,9 @@ PluginComponent {
|
|||||||
id: discAllArea
|
id: discAllArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: DMSNetworkService.isBusy ? Qt.BusyCursor : Qt.PointingHandCursor
|
||||||
onClicked: VpnService.disconnectAllActive()
|
enabled: !DMSNetworkService.isBusy
|
||||||
|
onClicked: DMSNetworkService.disconnectAllActive()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,7 +122,7 @@ PluginComponent {
|
|||||||
|
|
||||||
Item {
|
Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: VpnService.profiles.length === 0 ? 120 : 0
|
height: DMSNetworkService.profiles.length === 0 ? 120 : 0
|
||||||
visible: height > 0
|
visible: height > 0
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -154,7 +153,7 @@ PluginComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: VpnService.profiles
|
model: DMSNetworkService.profiles
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
required property var modelData
|
required property var modelData
|
||||||
@@ -162,9 +161,10 @@ PluginComponent {
|
|||||||
width: parent ? parent.width : 300
|
width: parent ? parent.width : 300
|
||||||
height: 50
|
height: 50
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: rowArea.containsMouse ? Theme.primaryHoverLight : (VpnService.isActiveUuid(modelData.uuid) ? Theme.primaryPressed : Theme.surfaceLight)
|
color: rowArea.containsMouse ? Theme.primaryHoverLight : (DMSNetworkService.isActiveUuid(modelData.uuid) ? Theme.primaryPressed : Theme.surfaceLight)
|
||||||
border.width: VpnService.isActiveUuid(modelData.uuid) ? 2 : 1
|
border.width: DMSNetworkService.isActiveUuid(modelData.uuid) ? 2 : 1
|
||||||
border.color: VpnService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.outlineLight
|
border.color: DMSNetworkService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.outlineLight
|
||||||
|
opacity: DMSNetworkService.isBusy ? 0.5 : 1.0
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -174,20 +174,24 @@ PluginComponent {
|
|||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
name: VpnService.isActiveUuid(modelData.uuid) ? "vpn_lock" : "vpn_key_off"
|
name: DMSNetworkService.isActiveUuid(modelData.uuid) ? "vpn_lock" : "vpn_key_off"
|
||||||
size: Theme.iconSize - 4
|
size: Theme.iconSize - 4
|
||||||
color: VpnService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
color: DMSNetworkService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
spacing: 2
|
spacing: 2
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: modelData.name
|
text: modelData.name
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: VpnService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
color: DMSNetworkService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
@@ -233,8 +237,9 @@ PluginComponent {
|
|||||||
id: rowArea
|
id: rowArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: DMSNetworkService.isBusy ? Qt.BusyCursor : Qt.PointingHandCursor
|
||||||
onClicked: VpnService.toggle(modelData.uuid)
|
enabled: !DMSNetworkService.isBusy
|
||||||
|
onClicked: DMSNetworkService.toggle(modelData.uuid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ Item {
|
|||||||
property string expandedSection: ""
|
property string expandedSection: ""
|
||||||
property var expandedWidgetData: null
|
property var expandedWidgetData: null
|
||||||
property var bluetoothCodecSelector: null
|
property var bluetoothCodecSelector: null
|
||||||
|
property string screenName: ""
|
||||||
|
|
||||||
property var pluginDetailInstance: null
|
property var pluginDetailInstance: null
|
||||||
property var widgetModel: null
|
property var widgetModel: null
|
||||||
|
property var collapseCallback: null
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: pluginDetailLoader
|
id: pluginDetailLoader
|
||||||
@@ -32,6 +34,54 @@ Item {
|
|||||||
sourceComponent: null
|
sourceComponent: null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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: {
|
onExpandedSectionChanged: {
|
||||||
if (pluginDetailInstance) {
|
if (pluginDetailInstance) {
|
||||||
pluginDetailInstance.destroy()
|
pluginDetailInstance.destroy()
|
||||||
@@ -91,6 +141,12 @@ Item {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (root.expandedSection.startsWith("brightnessSlider_")) {
|
||||||
|
coreDetailLoader.sourceComponent = brightnessDetailComponent
|
||||||
|
coreDetailLoader.active = parent.height > 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
switch (root.expandedSection) {
|
switch (root.expandedSection) {
|
||||||
case "network":
|
case "network":
|
||||||
case "wifi": coreDetailLoader.sourceComponent = networkDetailComponent; break
|
case "wifi": coreDetailLoader.sourceComponent = networkDetailComponent; break
|
||||||
@@ -144,22 +200,15 @@ 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 || []
|
initialDeviceName: root.expandedWidgetData?.deviceName || ""
|
||||||
const newWidgets = widgets.map(w => {
|
instanceId: root.expandedWidgetData?.instanceId || ""
|
||||||
if (w.id === "diskUsage" && w.instanceId === root.expandedWidgetData.instanceId) {
|
screenName: root.screenName
|
||||||
const updatedWidget = Object.assign({}, w)
|
|
||||||
updatedWidget.mountPath = newMountPath
|
|
||||||
return updatedWidget
|
|
||||||
}
|
|
||||||
return w
|
|
||||||
})
|
|
||||||
SettingsData.setControlCenterWidgets(newWidgets)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15,11 +15,18 @@ Column {
|
|||||||
property var expandedWidgetData: null
|
property var expandedWidgetData: null
|
||||||
property var bluetoothCodecSelector: null
|
property var bluetoothCodecSelector: null
|
||||||
property bool darkModeTransitionPending: false
|
property bool darkModeTransitionPending: false
|
||||||
|
property string screenName: ""
|
||||||
|
property var parentScreen: null
|
||||||
|
|
||||||
signal expandClicked(var widgetData, int globalIndex)
|
signal expandClicked(var widgetData, int globalIndex)
|
||||||
signal removeWidget(int index)
|
signal removeWidget(int index)
|
||||||
signal moveWidget(int fromIndex, int toIndex)
|
signal moveWidget(int fromIndex, int toIndex)
|
||||||
signal toggleWidgetSize(int index)
|
signal toggleWidgetSize(int index)
|
||||||
|
signal collapseRequested()
|
||||||
|
|
||||||
|
function requestCollapse() {
|
||||||
|
collapseRequested()
|
||||||
|
}
|
||||||
|
|
||||||
spacing: editMode ? Theme.spacingL : Theme.spacingS
|
spacing: editMode ? Theme.spacingL : Theme.spacingS
|
||||||
|
|
||||||
@@ -82,7 +89,7 @@ Column {
|
|||||||
const widgets = SettingsData.controlCenterWidgets || []
|
const widgets = SettingsData.controlCenterWidgets || []
|
||||||
for (var i = 0; i < widgets.length; i++) {
|
for (var i = 0; i < widgets.length; i++) {
|
||||||
if (widgets[i].id === modelData.id) {
|
if (widgets[i].id === modelData.id) {
|
||||||
if (modelData.id === "diskUsage") {
|
if (modelData.id === "diskUsage" || modelData.id === "brightnessSlider") {
|
||||||
if (widgets[i].instanceId === modelData.instanceId) {
|
if (widgets[i].instanceId === modelData.instanceId) {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
@@ -164,6 +171,11 @@ Column {
|
|||||||
return rowWidgets.some(w => w.id === "diskUsage" && w.instanceId === expandedInstanceId)
|
return rowWidgets.some(w => w.id === "diskUsage" && w.instanceId === expandedInstanceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (root.expandedSection.startsWith("brightnessSlider_") && root.expandedWidgetData) {
|
||||||
|
const expandedInstanceId = root.expandedWidgetData.instanceId
|
||||||
|
return rowWidgets.some(w => w.id === "brightnessSlider" && w.instanceId === expandedInstanceId)
|
||||||
|
}
|
||||||
|
|
||||||
return rowIndex === root.expandedRowIndex
|
return rowIndex === root.expandedRowIndex
|
||||||
}
|
}
|
||||||
visible: active
|
visible: active
|
||||||
@@ -171,6 +183,8 @@ Column {
|
|||||||
expandedWidgetData: root.expandedWidgetData
|
expandedWidgetData: root.expandedWidgetData
|
||||||
bluetoothCodecSelector: root.bluetoothCodecSelector
|
bluetoothCodecSelector: root.bluetoothCodecSelector
|
||||||
widgetModel: root.model
|
widgetModel: root.model
|
||||||
|
collapseCallback: root.requestCollapse
|
||||||
|
screenName: root.screenName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -205,9 +219,13 @@ Column {
|
|||||||
{
|
{
|
||||||
if (NetworkService.wifiToggling)
|
if (NetworkService.wifiToggling)
|
||||||
return "sync"
|
return "sync"
|
||||||
if (NetworkService.networkStatus === "ethernet")
|
|
||||||
|
const status = NetworkService.networkStatus
|
||||||
|
if (status === "ethernet")
|
||||||
return "settings_ethernet"
|
return "settings_ethernet"
|
||||||
if (NetworkService.networkStatus === "wifi")
|
if (status === "vpn")
|
||||||
|
return NetworkService.ethernetConnected ? "settings_ethernet" : NetworkService.wifiSignalIcon
|
||||||
|
if (status === "wifi")
|
||||||
return NetworkService.wifiSignalIcon
|
return NetworkService.wifiSignalIcon
|
||||||
if (NetworkService.wifiEnabled)
|
if (NetworkService.wifiEnabled)
|
||||||
return "wifi_off"
|
return "wifi_off"
|
||||||
@@ -219,18 +237,6 @@ Column {
|
|||||||
return "bluetooth_disabled"
|
return "bluetooth_disabled"
|
||||||
if (!BluetoothService.adapter || !BluetoothService.adapter.enabled)
|
if (!BluetoothService.adapter || !BluetoothService.adapter.enabled)
|
||||||
return "bluetooth_disabled"
|
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"
|
return "bluetooth"
|
||||||
}
|
}
|
||||||
case "audioOutput":
|
case "audioOutput":
|
||||||
@@ -264,9 +270,17 @@ Column {
|
|||||||
{
|
{
|
||||||
if (NetworkService.wifiToggling)
|
if (NetworkService.wifiToggling)
|
||||||
return NetworkService.wifiEnabled ? "Disabling WiFi..." : "Enabling WiFi..."
|
return NetworkService.wifiEnabled ? "Disabling WiFi..." : "Enabling WiFi..."
|
||||||
if (NetworkService.networkStatus === "ethernet")
|
|
||||||
|
const status = NetworkService.networkStatus
|
||||||
|
if (status === "ethernet")
|
||||||
return "Ethernet"
|
return "Ethernet"
|
||||||
if (NetworkService.networkStatus === "wifi" && NetworkService.currentWifiSSID)
|
if (status === "vpn") {
|
||||||
|
if (NetworkService.ethernetConnected)
|
||||||
|
return "Ethernet"
|
||||||
|
if (NetworkService.wifiConnected && NetworkService.currentWifiSSID)
|
||||||
|
return NetworkService.currentWifiSSID
|
||||||
|
}
|
||||||
|
if (status === "wifi" && NetworkService.currentWifiSSID)
|
||||||
return NetworkService.currentWifiSSID
|
return NetworkService.currentWifiSSID
|
||||||
if (NetworkService.wifiEnabled)
|
if (NetworkService.wifiEnabled)
|
||||||
return "Not connected"
|
return "Not connected"
|
||||||
@@ -296,9 +310,17 @@ Column {
|
|||||||
{
|
{
|
||||||
if (NetworkService.wifiToggling)
|
if (NetworkService.wifiToggling)
|
||||||
return "Please wait..."
|
return "Please wait..."
|
||||||
if (NetworkService.networkStatus === "ethernet")
|
|
||||||
|
const status = NetworkService.networkStatus
|
||||||
|
if (status === "ethernet")
|
||||||
return "Connected"
|
return "Connected"
|
||||||
if (NetworkService.networkStatus === "wifi")
|
if (status === "vpn") {
|
||||||
|
if (NetworkService.ethernetConnected)
|
||||||
|
return "Connected"
|
||||||
|
if (NetworkService.wifiConnected)
|
||||||
|
return NetworkService.wifiSignalStrength > 0 ? NetworkService.wifiSignalStrength + "%" : "Connected"
|
||||||
|
}
|
||||||
|
if (status === "wifi")
|
||||||
return NetworkService.wifiSignalStrength > 0 ? NetworkService.wifiSignalStrength + "%" : "Connected"
|
return NetworkService.wifiSignalStrength > 0 ? NetworkService.wifiSignalStrength + "%" : "Connected"
|
||||||
if (NetworkService.wifiEnabled)
|
if (NetworkService.wifiEnabled)
|
||||||
return "Select network"
|
return "Select network"
|
||||||
@@ -330,7 +352,10 @@ Column {
|
|||||||
return "Select device"
|
return "Select device"
|
||||||
if (AudioService.sink.audio.muted)
|
if (AudioService.sink.audio.muted)
|
||||||
return "Muted"
|
return "Muted"
|
||||||
return Math.round(AudioService.sink.audio.volume * 100) + "%"
|
const volume = AudioService.sink.audio.volume
|
||||||
|
if (typeof volume !== "number" || isNaN(volume))
|
||||||
|
return "0%"
|
||||||
|
return Math.round(volume * 100) + "%"
|
||||||
}
|
}
|
||||||
case "audioInput":
|
case "audioInput":
|
||||||
{
|
{
|
||||||
@@ -338,7 +363,10 @@ Column {
|
|||||||
return "Select device"
|
return "Select device"
|
||||||
if (AudioService.source.audio.muted)
|
if (AudioService.source.audio.muted)
|
||||||
return "Muted"
|
return "Muted"
|
||||||
return Math.round(AudioService.source.audio.volume * 100) + "%"
|
const volume = AudioService.source.audio.volume
|
||||||
|
if (typeof volume !== "number" || isNaN(volume))
|
||||||
|
return "0%"
|
||||||
|
return Math.round(volume * 100) + "%"
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return widgetDef?.description || ""
|
return widgetDef?.description || ""
|
||||||
@@ -350,9 +378,13 @@ Column {
|
|||||||
{
|
{
|
||||||
if (NetworkService.wifiToggling)
|
if (NetworkService.wifiToggling)
|
||||||
return false
|
return false
|
||||||
if (NetworkService.networkStatus === "ethernet")
|
|
||||||
|
const status = NetworkService.networkStatus
|
||||||
|
if (status === "ethernet")
|
||||||
return true
|
return true
|
||||||
if (NetworkService.networkStatus === "wifi")
|
if (status === "vpn")
|
||||||
|
return NetworkService.ethernetConnected || NetworkService.wifiConnected
|
||||||
|
if (status === "wifi")
|
||||||
return true
|
return true
|
||||||
return NetworkService.wifiEnabled
|
return NetworkService.wifiEnabled
|
||||||
}
|
}
|
||||||
@@ -467,10 +499,21 @@ Column {
|
|||||||
height: 16
|
height: 16
|
||||||
|
|
||||||
BrightnessSliderRow {
|
BrightnessSliderRow {
|
||||||
|
id: brightnessSliderRow
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 14
|
height: 14
|
||||||
|
deviceName: widgetData.deviceName || ""
|
||||||
|
instanceId: widgetData.instanceId || ""
|
||||||
|
screenName: root.screenName
|
||||||
|
parentScreen: root.parentScreen
|
||||||
property color sliderTrackColor: Theme.surfaceContainerHigh
|
property color sliderTrackColor: Theme.surfaceContainerHigh
|
||||||
|
|
||||||
|
onIconClicked: {
|
||||||
|
if (!root.editMode && DisplayService.devices && DisplayService.devices.length > 1) {
|
||||||
|
root.expandClicked(widgetData, widgetIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -600,8 +643,9 @@ Column {
|
|||||||
}
|
}
|
||||||
case "darkMode":
|
case "darkMode":
|
||||||
{
|
{
|
||||||
|
const newMode = !SessionData.isLightMode
|
||||||
Theme.screenTransition()
|
Theme.screenTransition()
|
||||||
Theme.setLightMode(!SessionData.isLightMode)
|
Theme.setLightMode(newMode)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case "doNotDisturb":
|
case "doNotDisturb":
|
||||||
@@ -680,8 +724,9 @@ Column {
|
|||||||
}
|
}
|
||||||
case "darkMode":
|
case "darkMode":
|
||||||
{
|
{
|
||||||
|
const newMode = !SessionData.isLightMode
|
||||||
Theme.screenTransition()
|
Theme.screenTransition()
|
||||||
Theme.setLightMode(!SessionData.isLightMode)
|
Theme.setLightMode(newMode)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case "doNotDisturb":
|
case "doNotDisturb":
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ 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 {
|
||||||
|
|||||||
@@ -73,21 +73,21 @@ DankPopout {
|
|||||||
onShouldBeVisibleChanged: {
|
onShouldBeVisibleChanged: {
|
||||||
if (shouldBeVisible) {
|
if (shouldBeVisible) {
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (NetworkService.activeService) {
|
if (NetworkService.activeService) {
|
||||||
NetworkService.activeService.autoRefreshEnabled = NetworkService.wifiEnabled
|
NetworkService.activeService.autoRefreshEnabled = NetworkService.wifiEnabled
|
||||||
}
|
}
|
||||||
if (UserInfoService)
|
if (UserInfoService)
|
||||||
UserInfoService.getUptime()
|
UserInfoService.getUptime()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (NetworkService.activeService) {
|
if (NetworkService.activeService) {
|
||||||
NetworkService.activeService.autoRefreshEnabled = false
|
NetworkService.activeService.autoRefreshEnabled = false
|
||||||
}
|
}
|
||||||
if (BluetoothService.adapter && BluetoothService.adapter.discovering)
|
if (BluetoothService.adapter && BluetoothService.adapter.discovering)
|
||||||
BluetoothService.adapter.discovering = false
|
BluetoothService.adapter.discovering = false
|
||||||
editMode = false
|
editMode = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,8 +108,7 @@ DankPopout {
|
|||||||
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: 0
|
||||||
antialiasing: true
|
antialiasing: true
|
||||||
smooth: true
|
smooth: true
|
||||||
@@ -155,18 +154,23 @@ DankPopout {
|
|||||||
model: widgetModel
|
model: widgetModel
|
||||||
bluetoothCodecSelector: bluetoothCodecSelector
|
bluetoothCodecSelector: bluetoothCodecSelector
|
||||||
colorPickerModal: root.colorPickerModal
|
colorPickerModal: root.colorPickerModal
|
||||||
|
screenName: root.triggerScreen?.name || ""
|
||||||
|
parentScreen: root.triggerScreen
|
||||||
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 {
|
||||||
@@ -174,12 +178,13 @@ DankPopout {
|
|||||||
visible: editMode
|
visible: editMode
|
||||||
popoutContent: controlContent
|
popoutContent: controlContent
|
||||||
availableWidgets: {
|
availableWidgets: {
|
||||||
if (!editMode) return []
|
if (!editMode)
|
||||||
|
return []
|
||||||
const existingIds = (SettingsData.controlCenterWidgets || []).map(w => w.id)
|
const existingIds = (SettingsData.controlCenterWidgets || []).map(w => w.id)
|
||||||
const allWidgets = widgetModel.baseWidgetDefinitions.concat(widgetModel.getPluginWidgets())
|
const allWidgets = widgetModel.baseWidgetDefinitions.concat(widgetModel.getPluginWidgets())
|
||||||
return allWidgets.filter(w => w.allowMultiple || !existingIds.includes(w.id))
|
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()
|
||||||
}
|
}
|
||||||
@@ -202,10 +207,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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -230,4 +235,4 @@ DankPopout {
|
|||||||
|
|
||||||
property var colorPickerModal: null
|
property var colorPickerModal: null
|
||||||
property var powerMenuModalLoader: null
|
property var powerMenuModalLoader: null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ Rectangle {
|
|||||||
color: Theme.surfaceContainerHigh
|
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: 0
|
border.width: 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: headerRow
|
id: headerRow
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -27,7 +27,7 @@ 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: I18n.tr("Input Devices")
|
text: I18n.tr("Input Devices")
|
||||||
@@ -37,7 +37,7 @@ Rectangle {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: volumeSlider
|
id: volumeSlider
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -105,7 +105,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DankFlickable {
|
DankFlickable {
|
||||||
id: audioContent
|
id: audioContent
|
||||||
anchors.top: hasInputVolumeSliderInCC ? headerRow.bottom : volumeSlider.bottom
|
anchors.top: hasInputVolumeSliderInCC ? headerRow.bottom : volumeSlider.bottom
|
||||||
@@ -116,34 +116,34 @@ Rectangle {
|
|||||||
anchors.topMargin: hasInputVolumeSliderInCC ? Theme.spacingM : Theme.spacingS
|
anchors.topMargin: hasInputVolumeSliderInCC ? Theme.spacingM : Theme.spacingS
|
||||||
contentHeight: audioColumn.height
|
contentHeight: audioColumn.height
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: audioColumn
|
id: audioColumn
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: Pipewire.nodes.values.filter(node => {
|
model: Pipewire.nodes.values.filter(node => {
|
||||||
return node.audio && !node.isSink && !node.isStream
|
return node.audio && !node.isSink && !node.isStream
|
||||||
})
|
})
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
required property var modelData
|
required property var modelData
|
||||||
required property int index
|
required property int index
|
||||||
|
|
||||||
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) : Theme.surfaceContainerHighest
|
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: 0
|
border.width: 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.leftMargin: Theme.spacingM
|
anchors.leftMargin: Theme.spacingM
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
name: {
|
name: {
|
||||||
if (modelData.name.includes("bluez"))
|
if (modelData.name.includes("bluez"))
|
||||||
@@ -157,11 +157,11 @@ Rectangle {
|
|||||||
color: modelData === AudioService.source ? Theme.primary : Theme.surfaceText
|
color: modelData === AudioService.source ? Theme.primary : Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
width: parent.parent.width - parent.parent.anchors.leftMargin - parent.spacing - Theme.iconSize - Theme.spacingM
|
width: parent.parent.width - parent.parent.anchors.leftMargin - parent.spacing - Theme.iconSize - Theme.spacingM
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: AudioService.displayName(modelData)
|
text: AudioService.displayName(modelData)
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
@@ -171,7 +171,7 @@ Rectangle {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
wrapMode: Text.NoWrap
|
wrapMode: Text.NoWrap
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: modelData === AudioService.source ? "Active" : "Available"
|
text: modelData === AudioService.source ? "Active" : "Available"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
@@ -182,7 +182,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: deviceMouseArea
|
id: deviceMouseArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ Rectangle {
|
|||||||
color: Theme.surfaceContainerHigh
|
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: 0
|
border.width: 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: headerRow
|
id: headerRow
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -27,7 +27,7 @@ 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: I18n.tr("Audio Devices")
|
text: I18n.tr("Audio Devices")
|
||||||
@@ -121,34 +121,34 @@ Rectangle {
|
|||||||
anchors.topMargin: volumeSlider.visible ? Theme.spacingS : Theme.spacingM
|
anchors.topMargin: volumeSlider.visible ? Theme.spacingS : Theme.spacingM
|
||||||
contentHeight: audioColumn.height
|
contentHeight: audioColumn.height
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: audioColumn
|
id: audioColumn
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: Pipewire.nodes.values.filter(node => {
|
model: Pipewire.nodes.values.filter(node => {
|
||||||
return node.audio && node.isSink && !node.isStream
|
return node.audio && node.isSink && !node.isStream
|
||||||
})
|
})
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
required property var modelData
|
required property var modelData
|
||||||
required property int index
|
required property int index
|
||||||
|
|
||||||
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) : Theme.surfaceContainerHighest
|
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: 0
|
border.width: 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.leftMargin: Theme.spacingM
|
anchors.leftMargin: Theme.spacingM
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
name: {
|
name: {
|
||||||
if (modelData.name.includes("bluez"))
|
if (modelData.name.includes("bluez"))
|
||||||
@@ -164,11 +164,11 @@ Rectangle {
|
|||||||
color: modelData === AudioService.sink ? Theme.primary : Theme.surfaceText
|
color: modelData === AudioService.sink ? Theme.primary : Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
width: parent.parent.width - parent.parent.anchors.leftMargin - parent.spacing - Theme.iconSize - Theme.spacingM
|
width: parent.parent.width - parent.parent.anchors.leftMargin - parent.spacing - Theme.iconSize - Theme.spacingM
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: AudioService.displayName(modelData)
|
text: AudioService.displayName(modelData)
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
@@ -178,7 +178,7 @@ Rectangle {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
wrapMode: Text.NoWrap
|
wrapMode: Text.NoWrap
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: modelData === AudioService.sink ? "Active" : "Available"
|
text: modelData === AudioService.sink ? "Active" : "Available"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
@@ -189,7 +189,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: deviceMouseArea
|
id: deviceMouseArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|||||||
@@ -83,12 +83,12 @@ Item {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
preventStealing: true
|
preventStealing: true
|
||||||
propagateComposedEvents: false
|
propagateComposedEvents: false
|
||||||
|
|
||||||
onClicked: root.hide()
|
onClicked: root.hide()
|
||||||
onWheel: (wheel) => { wheel.accepted = true }
|
onWheel: (wheel) => { wheel.accepted = true }
|
||||||
onPositionChanged: (mouse) => { mouse.accepted = true }
|
onPositionChanged: (mouse) => { mouse.accepted = true }
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: modalBackground
|
id: modalBackground
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|||||||
@@ -5,18 +5,45 @@ import Quickshell.Bluetooth
|
|||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
import qs.Modals
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
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: Theme.surfaceContainerHigh
|
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: 0
|
border.width: 0
|
||||||
|
|
||||||
property var bluetoothCodecModalRef: null
|
property var bluetoothCodecModalRef: null
|
||||||
|
property var devicesBeingPaired: new Set()
|
||||||
|
|
||||||
signal showCodecSelector(var device)
|
signal showCodecSelector(var device)
|
||||||
|
|
||||||
|
function isDeviceBeingPaired(deviceAddress) {
|
||||||
|
return devicesBeingPaired.has(deviceAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePairDevice(device) {
|
||||||
|
if (!device) return
|
||||||
|
|
||||||
|
const deviceAddr = device.address
|
||||||
|
devicesBeingPaired.add(deviceAddr)
|
||||||
|
devicesBeingPairedChanged()
|
||||||
|
|
||||||
|
BluetoothService.pairDevice(device, function(response) {
|
||||||
|
devicesBeingPaired.delete(deviceAddr)
|
||||||
|
devicesBeingPairedChanged()
|
||||||
|
|
||||||
|
if (response.error) {
|
||||||
|
ToastService.showError(I18n.tr("Pairing failed"), response.error)
|
||||||
|
} else if (!BluetoothService.enhancedPairingAvailable) {
|
||||||
|
ToastService.showSuccess(I18n.tr("Device paired"))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function updateDeviceCodecDisplay(deviceAddress, codecName) {
|
function updateDeviceCodecDisplay(deviceAddress, codecName) {
|
||||||
for (let i = 0; i < pairedRepeater.count; i++) {
|
for (let i = 0; i < pairedRepeater.count; i++) {
|
||||||
let item = pairedRepeater.itemAt(i)
|
let item = pairedRepeater.itemAt(i)
|
||||||
@@ -26,7 +53,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: headerRow
|
id: headerRow
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -36,7 +63,7 @@ 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: I18n.tr("Bluetooth Settings")
|
text: I18n.tr("Bluetooth Settings")
|
||||||
@@ -45,12 +72,12 @@ Rectangle {
|
|||||||
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 - scanButton.width - Theme.spacingM)
|
width: Math.max(0, parent.width - headerText.implicitWidth - scanButton.width - Theme.spacingM)
|
||||||
height: parent.height
|
height: parent.height
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: scanButton
|
id: scanButton
|
||||||
width: 100
|
width: 100
|
||||||
@@ -64,18 +91,18 @@ Rectangle {
|
|||||||
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: 0
|
border.width: 0
|
||||||
visible: BluetoothService.adapter && BluetoothService.adapter.enabled
|
visible: BluetoothService.adapter && BluetoothService.adapter.enabled
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
name: BluetoothService.adapter && BluetoothService.adapter.discovering ? "stop" : "bluetooth_searching"
|
name: BluetoothService.adapter && BluetoothService.adapter.discovering ? "stop" : "bluetooth_searching"
|
||||||
size: 18
|
size: 18
|
||||||
color: BluetoothService.adapter && BluetoothService.adapter.enabled ? Theme.primary : Theme.surfaceVariantText
|
color: BluetoothService.adapter && BluetoothService.adapter.enabled ? Theme.primary : Theme.surfaceVariantText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: BluetoothService.adapter && BluetoothService.adapter.discovering ? "Scanning" : "Scan"
|
text: BluetoothService.adapter && BluetoothService.adapter.discovering ? "Scanning" : "Scan"
|
||||||
color: BluetoothService.adapter && BluetoothService.adapter.enabled ? Theme.primary : Theme.surfaceVariantText
|
color: BluetoothService.adapter && BluetoothService.adapter.enabled ? Theme.primary : Theme.surfaceVariantText
|
||||||
@@ -84,7 +111,7 @@ Rectangle {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: scanMouseArea
|
id: scanMouseArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -98,7 +125,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DankFlickable {
|
DankFlickable {
|
||||||
id: bluetoothContent
|
id: bluetoothContent
|
||||||
anchors.top: headerRow.bottom
|
anchors.top: headerRow.bottom
|
||||||
@@ -110,19 +137,19 @@ Rectangle {
|
|||||||
visible: BluetoothService.adapter && BluetoothService.adapter.enabled
|
visible: BluetoothService.adapter && BluetoothService.adapter.enabled
|
||||||
contentHeight: bluetoothColumn.height
|
contentHeight: bluetoothColumn.height
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: bluetoothColumn
|
id: bluetoothColumn
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
id: pairedRepeater
|
id: pairedRepeater
|
||||||
model: {
|
model: {
|
||||||
if (!BluetoothService.adapter || !BluetoothService.adapter.devices)
|
if (!BluetoothService.adapter || !BluetoothService.adapter.devices)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
let devices = [...BluetoothService.adapter.devices.values.filter(dev => dev && (dev.paired || dev.trusted))]
|
let devices = [...BluetoothService.adapter.devices.values.filter(dev => dev && (dev.paired || dev.trusted))]
|
||||||
devices.sort((a, b) => {
|
devices.sort((a, b) => {
|
||||||
if (a.connected && !b.connected) return -1
|
if (a.connected && !b.connected) return -1
|
||||||
@@ -131,17 +158,17 @@ Rectangle {
|
|||||||
})
|
})
|
||||||
return devices
|
return devices
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
required property var modelData
|
required property var modelData
|
||||||
required property int index
|
required property int index
|
||||||
|
|
||||||
property string currentCodec: BluetoothService.deviceCodecs[modelData.address] || ""
|
property string currentCodec: BluetoothService.deviceCodecs[modelData.address] || ""
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 50
|
height: 50
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (modelData.connected && BluetoothService.isAudioDevice(modelData)) {
|
if (modelData.connected && BluetoothService.isAudioDevice(modelData)) {
|
||||||
BluetoothService.refreshDeviceCodec(modelData)
|
BluetoothService.refreshDeviceCodec(modelData)
|
||||||
@@ -162,13 +189,13 @@ Rectangle {
|
|||||||
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: 0
|
border.width: 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.leftMargin: Theme.spacingM
|
anchors.leftMargin: Theme.spacingM
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
name: BluetoothService.getDeviceIcon(modelData)
|
name: BluetoothService.getDeviceIcon(modelData)
|
||||||
size: Theme.iconSize - 4
|
size: Theme.iconSize - 4
|
||||||
@@ -181,11 +208,11 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
width: 200
|
width: 200
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: modelData.name || modelData.deviceName || "Unknown Device"
|
text: modelData.name || modelData.deviceName || "Unknown Device"
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
@@ -194,10 +221,10 @@ Rectangle {
|
|||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
width: parent.width
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: {
|
text: {
|
||||||
if (modelData.state === BluetoothDeviceState.Connecting)
|
if (modelData.state === BluetoothDeviceState.Connecting)
|
||||||
@@ -218,12 +245,12 @@ Rectangle {
|
|||||||
return Theme.surfaceVariantText
|
return Theme.surfaceVariantText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: {
|
text: {
|
||||||
if (modelData.batteryAvailable && modelData.battery > 0)
|
if (modelData.batteryAvailable && modelData.battery > 0)
|
||||||
return "• " + Math.round(modelData.battery * 100) + "%"
|
return "• " + Math.round(modelData.battery * 100) + "%"
|
||||||
|
|
||||||
var btBattery = BatteryService.bluetoothDevices.find(dev => {
|
var btBattery = BatteryService.bluetoothDevices.find(dev => {
|
||||||
return dev.name === (modelData.name || modelData.deviceName) ||
|
return dev.name === (modelData.name || modelData.deviceName) ||
|
||||||
dev.name.toLowerCase().includes((modelData.name || modelData.deviceName).toLowerCase()) ||
|
dev.name.toLowerCase().includes((modelData.name || modelData.deviceName).toLowerCase()) ||
|
||||||
@@ -235,7 +262,7 @@ Rectangle {
|
|||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
visible: text.length > 0
|
visible: text.length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: modelData.signalStrength !== undefined && modelData.signalStrength > 0 ? "• " + modelData.signalStrength + "%" : ""
|
text: modelData.signalStrength !== undefined && modelData.signalStrength > 0 ? "• " + modelData.signalStrength + "%" : ""
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
@@ -245,7 +272,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DankActionButton {
|
DankActionButton {
|
||||||
id: pairedOptionsButton
|
id: pairedOptionsButton
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
@@ -262,7 +289,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: deviceMouseArea
|
id: deviceMouseArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -279,26 +306,26 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 1
|
height: 1
|
||||||
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
visible: pairedRepeater.count > 0 && availableRepeater.count > 0
|
visible: pairedRepeater.count > 0 && availableRepeater.count > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 80
|
height: 80
|
||||||
visible: BluetoothService.adapter && BluetoothService.adapter.discovering && availableRepeater.count === 0
|
visible: BluetoothService.adapter && BluetoothService.adapter.discovering && availableRepeater.count === 0
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
name: "sync"
|
name: "sync"
|
||||||
size: 24
|
size: 24
|
||||||
color: Qt.rgba(Theme.surfaceText.r || 0.8, Theme.surfaceText.g || 0.8, Theme.surfaceText.b || 0.8, 0.4)
|
color: Qt.rgba(Theme.surfaceText.r || 0.8, Theme.surfaceText.g || 0.8, Theme.surfaceText.b || 0.8, 0.4)
|
||||||
|
|
||||||
RotationAnimation on rotation {
|
RotationAnimation on rotation {
|
||||||
running: parent.visible && BluetoothService.adapter && BluetoothService.adapter.discovering && availableRepeater.count === 0
|
running: parent.visible && BluetoothService.adapter && BluetoothService.adapter.discovering && availableRepeater.count === 0
|
||||||
loops: Animation.Infinite
|
loops: Animation.Infinite
|
||||||
@@ -308,52 +335,52 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
id: availableRepeater
|
id: availableRepeater
|
||||||
model: {
|
model: {
|
||||||
if (!BluetoothService.adapter || !BluetoothService.adapter.discovering || !Bluetooth.devices)
|
if (!BluetoothService.adapter || !BluetoothService.adapter.discovering || !Bluetooth.devices)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
var filtered = Bluetooth.devices.values.filter(dev => {
|
var filtered = Bluetooth.devices.values.filter(dev => {
|
||||||
return dev && !dev.paired && !dev.pairing && !dev.blocked &&
|
return dev && !dev.paired && !dev.pairing && !dev.blocked &&
|
||||||
(dev.signalStrength === undefined || dev.signalStrength > 0)
|
(dev.signalStrength === undefined || dev.signalStrength > 0)
|
||||||
})
|
})
|
||||||
return BluetoothService.sortDevices(filtered)
|
return BluetoothService.sortDevices(filtered)
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
required property var modelData
|
required property var modelData
|
||||||
required property int index
|
required property int index
|
||||||
|
|
||||||
property bool canConnect: BluetoothService.canConnect(modelData)
|
property bool canConnect: BluetoothService.canConnect(modelData)
|
||||||
property bool isBusy: BluetoothService.isDeviceBusy(modelData)
|
property bool isBusy: BluetoothService.isDeviceBusy(modelData) || isDeviceBeingPaired(modelData.address)
|
||||||
|
|
||||||
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) : Theme.surfaceContainerHighest
|
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: 0
|
border.width: 0
|
||||||
opacity: canConnect ? 1 : 0.6
|
opacity: (canConnect && !isBusy) ? 1 : 0.6
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.leftMargin: Theme.spacingM
|
anchors.leftMargin: Theme.spacingM
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
name: BluetoothService.getDeviceIcon(modelData)
|
name: BluetoothService.getDeviceIcon(modelData)
|
||||||
size: Theme.iconSize - 4
|
size: Theme.iconSize - 4
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
width: 200
|
width: 200
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: modelData.name || modelData.deviceName || "Unknown Device"
|
text: modelData.name || modelData.deviceName || "Unknown Device"
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
@@ -361,20 +388,20 @@ Rectangle {
|
|||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
width: parent.width
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: {
|
text: {
|
||||||
if (modelData.pairing) return "Pairing..."
|
if (modelData.pairing || isBusy) return "Pairing..."
|
||||||
if (modelData.blocked) return "Blocked"
|
if (modelData.blocked) return "Blocked"
|
||||||
return BluetoothService.getSignalStrength(modelData)
|
return BluetoothService.getSignalStrength(modelData)
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: modelData.signalStrength !== undefined && modelData.signalStrength > 0 ? "• " + modelData.signalStrength + "%" : ""
|
text: modelData.signalStrength !== undefined && modelData.signalStrength > 0 ? "• " + modelData.signalStrength + "%" : ""
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
@@ -384,21 +411,21 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: Theme.spacingM
|
anchors.rightMargin: Theme.spacingM
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
text: {
|
text: {
|
||||||
if (modelData.pairing) return "Pairing..."
|
if (isBusy) return "Pairing..."
|
||||||
if (!canConnect) return "Cannot pair"
|
if (!canConnect) return "Cannot pair"
|
||||||
return "Pair"
|
return "Pair"
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: canConnect ? Theme.primary : Theme.surfaceVariantText
|
color: (canConnect && !isBusy) ? Theme.primary : Theme.surfaceVariantText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: availableMouseArea
|
id: availableMouseArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -406,20 +433,18 @@ Rectangle {
|
|||||||
cursorShape: canConnect && !isBusy ? Qt.PointingHandCursor : Qt.ArrowCursor
|
cursorShape: canConnect && !isBusy ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
enabled: canConnect && !isBusy
|
enabled: canConnect && !isBusy
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (modelData) {
|
root.handlePairDevice(modelData)
|
||||||
BluetoothService.connectDeviceWithTrust(modelData)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 60
|
height: 60
|
||||||
visible: !BluetoothService.adapter
|
visible: !BluetoothService.adapter
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: I18n.tr("No Bluetooth adapter found")
|
text: I18n.tr("No Bluetooth adapter found")
|
||||||
@@ -429,25 +454,25 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu {
|
Menu {
|
||||||
id: bluetoothContextMenu
|
id: bluetoothContextMenu
|
||||||
width: 150
|
width: 150
|
||||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
|
||||||
|
|
||||||
property var currentDevice: null
|
property var currentDevice: null
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: Theme.popupBackground()
|
color: Theme.popupBackground()
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.width: 0
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: bluetoothContextMenu.currentDevice && bluetoothContextMenu.currentDevice.connected ? "Disconnect" : "Connect"
|
text: bluetoothContextMenu.currentDevice && bluetoothContextMenu.currentDevice.connected ? "Disconnect" : "Connect"
|
||||||
height: 32
|
height: 32
|
||||||
|
|
||||||
contentItem: StyledText {
|
contentItem: StyledText {
|
||||||
text: parent.text
|
text: parent.text
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
@@ -455,12 +480,12 @@ Rectangle {
|
|||||||
leftPadding: Theme.spacingS
|
leftPadding: Theme.spacingS
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: parent.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
color: parent.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
||||||
radius: Theme.cornerRadius / 2
|
radius: Theme.cornerRadius / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
if (bluetoothContextMenu.currentDevice) {
|
if (bluetoothContextMenu.currentDevice) {
|
||||||
if (bluetoothContextMenu.currentDevice.connected) {
|
if (bluetoothContextMenu.currentDevice.connected) {
|
||||||
@@ -471,12 +496,12 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: I18n.tr("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
|
||||||
|
|
||||||
contentItem: StyledText {
|
contentItem: StyledText {
|
||||||
text: parent.text
|
text: parent.text
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
@@ -484,23 +509,23 @@ Rectangle {
|
|||||||
leftPadding: Theme.spacingS
|
leftPadding: Theme.spacingS
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: parent.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
color: parent.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
||||||
radius: Theme.cornerRadius / 2
|
radius: Theme.cornerRadius / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
if (bluetoothContextMenu.currentDevice) {
|
if (bluetoothContextMenu.currentDevice) {
|
||||||
showCodecSelector(bluetoothContextMenu.currentDevice)
|
showCodecSelector(bluetoothContextMenu.currentDevice)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: I18n.tr("Forget Device")
|
text: I18n.tr("Forget Device")
|
||||||
height: 32
|
height: 32
|
||||||
|
|
||||||
contentItem: StyledText {
|
contentItem: StyledText {
|
||||||
text: parent.text
|
text: parent.text
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
@@ -508,18 +533,38 @@ Rectangle {
|
|||||||
leftPadding: Theme.spacingS
|
leftPadding: Theme.spacingS
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: parent.hovered ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.08) : "transparent"
|
color: parent.hovered ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.08) : "transparent"
|
||||||
radius: Theme.cornerRadius / 2
|
radius: Theme.cornerRadius / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
if (bluetoothContextMenu.currentDevice) {
|
if (bluetoothContextMenu.currentDevice) {
|
||||||
bluetoothContextMenu.currentDevice.forget()
|
if (BluetoothService.enhancedPairingAvailable) {
|
||||||
|
const devicePath = BluetoothService.getDevicePath(bluetoothContextMenu.currentDevice)
|
||||||
|
DMSService.bluetoothRemove(devicePath, response => {
|
||||||
|
if (response.error) {
|
||||||
|
ToastService.showError(I18n.tr("Failed to remove device"), response.error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
bluetoothContextMenu.currentDevice.forget()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BluetoothPairingModal {
|
||||||
|
id: bluetoothPairingModal
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: DMSService
|
||||||
|
|
||||||
|
function onBluetoothPairingRequest(data) {
|
||||||
|
bluetoothPairingModal.show(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
307
Modules/ControlCenter/Details/BrightnessDetail.qml
Normal file
307
Modules/ControlCenter/Details/BrightnessDetail.qml
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Quickshell
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string initialDeviceName: ""
|
||||||
|
property string instanceId: ""
|
||||||
|
property string screenName: ""
|
||||||
|
|
||||||
|
signal deviceNameChanged(string newDeviceName)
|
||||||
|
|
||||||
|
property string currentDeviceName: ""
|
||||||
|
|
||||||
|
function resolveDeviceName() {
|
||||||
|
if (!DisplayService.brightnessAvailable || !DisplayService.devices || DisplayService.devices.length === 0) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (screenName && screenName.length > 0) {
|
||||||
|
const pins = SettingsData.brightnessDevicePins || {}
|
||||||
|
const pinnedDevice = pins[screenName]
|
||||||
|
if (pinnedDevice && pinnedDevice.length > 0) {
|
||||||
|
const found = DisplayService.devices.find(dev => dev.name === pinnedDevice)
|
||||||
|
if (found) {
|
||||||
|
return found.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (initialDeviceName && initialDeviceName.length > 0) {
|
||||||
|
const found = DisplayService.devices.find(dev => dev.name === initialDeviceName)
|
||||||
|
if (found) {
|
||||||
|
return found.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentDeviceNameFromService = DisplayService.currentDevice
|
||||||
|
if (currentDeviceNameFromService) {
|
||||||
|
const found = DisplayService.devices.find(dev => dev.name === currentDeviceNameFromService)
|
||||||
|
if (found) {
|
||||||
|
return found.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DisplayService.devices.length > 0 ? DisplayService.devices[0].name : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
currentDeviceName = resolveDeviceName()
|
||||||
|
}
|
||||||
|
|
||||||
|
property bool isPinnedToScreen: {
|
||||||
|
if (!screenName || screenName.length === 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const pins = SettingsData.brightnessDevicePins || {}
|
||||||
|
return pins[screenName] === currentDeviceName
|
||||||
|
}
|
||||||
|
|
||||||
|
function togglePinToScreen() {
|
||||||
|
if (!screenName || screenName.length === 0 || !currentDeviceName || currentDeviceName.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const pins = JSON.parse(JSON.stringify(SettingsData.brightnessDevicePins || {}))
|
||||||
|
|
||||||
|
if (isPinnedToScreen) {
|
||||||
|
delete pins[screenName]
|
||||||
|
} else {
|
||||||
|
pins[screenName] = currentDeviceName
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsData.setBrightnessDevicePins(pins)
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitHeight: brightnessContent.height + Theme.spacingM
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.surfaceContainerHigh
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 40
|
||||||
|
visible: screenName && screenName.length > 0 && DisplayService.devices && DisplayService.devices.length > 1
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.surfaceContainerHighest
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "monitor"
|
||||||
|
size: Theme.iconSize
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: screenName || "Unknown Monitor"
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: pinRow.width + Theme.spacingS * 2
|
||||||
|
height: 28
|
||||||
|
radius: height / 2
|
||||||
|
color: isPinnedToScreen ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Theme.withAlpha(Theme.surfaceText, 0.05)
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: pinRow
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: isPinnedToScreen ? "push_pin" : "push_pin"
|
||||||
|
size: 16
|
||||||
|
color: isPinnedToScreen ? Theme.primary : Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: isPinnedToScreen ? "Pinned" : "Pin"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: isPinnedToScreen ? Theme.primary : Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: root.togglePinToScreen()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: DisplayService.devices || []
|
||||||
|
delegate: Rectangle {
|
||||||
|
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 = DisplayService.getDeviceBrightness(modelData.name)
|
||||||
|
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: Math.round(DisplayService.getDeviceBrightness(modelData.name)) + "%"
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,7 +28,31 @@ Rectangle {
|
|||||||
Component.onDestruction: {
|
Component.onDestruction: {
|
||||||
NetworkService.removeRef()
|
NetworkService.removeRef()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property int currentPreferenceIndex: {
|
||||||
|
if (DMSService.apiVersion < 5) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NetworkService.backend !== "networkmanager" || DMSService.apiVersion <= 10) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: headerRow
|
id: headerRow
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -38,7 +62,7 @@ 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: I18n.tr("Network Settings")
|
text: I18n.tr("Network Settings")
|
||||||
@@ -47,32 +71,16 @@ Rectangle {
|
|||||||
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
|
visible: NetworkService.backend === "networkmanager" && DMSService.apiVersion > 10
|
||||||
|
|
||||||
property int currentPreferenceIndex: {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
model: ["Ethernet", "WiFi"]
|
model: ["Ethernet", "WiFi"]
|
||||||
currentIndex: currentPreferenceIndex
|
currentIndex: currentPreferenceIndex
|
||||||
@@ -84,7 +92,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: wifiToggleContent
|
id: wifiToggleContent
|
||||||
anchors.top: headerRow.bottom
|
anchors.top: headerRow.bottom
|
||||||
@@ -92,7 +100,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.wifiToggling
|
visible: currentPreferenceIndex === 1 && NetworkService.wifiToggling
|
||||||
height: visible ? 80 : 0
|
height: visible ? 80 : 0
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -123,7 +131,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: wifiOffContent
|
id: wifiOffContent
|
||||||
anchors.top: headerRow.bottom
|
anchors.top: headerRow.bottom
|
||||||
@@ -131,21 +139,21 @@ 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 {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingL
|
spacing: Theme.spacingL
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
name: "wifi_off"
|
name: "wifi_off"
|
||||||
size: 48
|
size: 48
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
text: I18n.tr("WiFi is off")
|
text: I18n.tr("WiFi is off")
|
||||||
@@ -154,7 +162,7 @@ Rectangle {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
width: 120
|
width: 120
|
||||||
@@ -163,7 +171,7 @@ Rectangle {
|
|||||||
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: 0
|
border.width: 0
|
||||||
border.color: Theme.primary
|
border.color: Theme.primary
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: I18n.tr("Enable WiFi")
|
text: I18n.tr("Enable WiFi")
|
||||||
@@ -171,7 +179,7 @@ Rectangle {
|
|||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: enableWifiButton
|
id: enableWifiButton
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -179,7 +187,179 @@ Rectangle {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: NetworkService.toggleWifiRadio()
|
onClicked: NetworkService.toggleWifiRadio()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 && NetworkService.backend === "networkmanager" && DMSService.apiVersion > 10
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,15 +372,15 @@ 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
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 200
|
height: 200
|
||||||
@@ -221,7 +401,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: sortedNetworks
|
model: sortedNetworks
|
||||||
|
|
||||||
@@ -239,20 +419,20 @@ Rectangle {
|
|||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
required property var modelData
|
required property var modelData
|
||||||
required property int index
|
required property int index
|
||||||
|
|
||||||
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) : Theme.surfaceContainerHighest
|
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: 0
|
border.width: 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.leftMargin: Theme.spacingM
|
anchors.leftMargin: Theme.spacingM
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
name: {
|
name: {
|
||||||
let strength = modelData.signal || 0
|
let strength = modelData.signal || 0
|
||||||
@@ -264,11 +444,11 @@ Rectangle {
|
|||||||
color: modelData.ssid === NetworkService.currentWifiSSID ? Theme.primary : Theme.surfaceText
|
color: modelData.ssid === NetworkService.currentWifiSSID ? Theme.primary : Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
width: 200
|
width: 200
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: modelData.ssid || "Unknown Network"
|
text: modelData.ssid || "Unknown Network"
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
@@ -282,27 +462,27 @@ Rectangle {
|
|||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DankActionButton {
|
DankActionButton {
|
||||||
id: optionsButton
|
id: optionsButton
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
@@ -323,7 +503,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: networkMouseArea
|
id: networkMouseArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -333,7 +513,11 @@ 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) {
|
||||||
wifiPasswordModal.show(modelData.ssid)
|
if (DMSService.apiVersion >= 7) {
|
||||||
|
NetworkService.connectToWifi(modelData.ssid)
|
||||||
|
} else if (PopoutService.wifiPasswordModal) {
|
||||||
|
PopoutService.wifiPasswordModal.show(modelData.ssid)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
NetworkService.connectToWifi(modelData.ssid)
|
NetworkService.connectToWifi(modelData.ssid)
|
||||||
}
|
}
|
||||||
@@ -341,34 +525,34 @@ Rectangle {
|
|||||||
event.accepted = true
|
event.accepted = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu {
|
Menu {
|
||||||
id: networkContextMenu
|
id: networkContextMenu
|
||||||
width: 150
|
width: 150
|
||||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
|
||||||
|
|
||||||
property string currentSSID: ""
|
property string currentSSID: ""
|
||||||
property bool currentSecured: false
|
property bool currentSecured: false
|
||||||
property bool currentConnected: false
|
property bool currentConnected: false
|
||||||
property bool currentSaved: false
|
property bool currentSaved: false
|
||||||
property int currentSignal: 0
|
property int currentSignal: 0
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: Theme.popupBackground()
|
color: Theme.popupBackground()
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.width: 0
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: networkContextMenu.currentConnected ? "Disconnect" : "Connect"
|
text: networkContextMenu.currentConnected ? "Disconnect" : "Connect"
|
||||||
height: 32
|
height: 32
|
||||||
|
|
||||||
contentItem: StyledText {
|
contentItem: StyledText {
|
||||||
text: parent.text
|
text: parent.text
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
@@ -376,29 +560,33 @@ Rectangle {
|
|||||||
leftPadding: Theme.spacingS
|
leftPadding: Theme.spacingS
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: parent.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
color: parent.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
||||||
radius: Theme.cornerRadius / 2
|
radius: Theme.cornerRadius / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
if (networkContextMenu.currentConnected) {
|
if (networkContextMenu.currentConnected) {
|
||||||
NetworkService.disconnectWifi()
|
NetworkService.disconnectWifi()
|
||||||
} else {
|
} else {
|
||||||
if (networkContextMenu.currentSecured && !networkContextMenu.currentSaved) {
|
if (networkContextMenu.currentSecured && !networkContextMenu.currentSaved) {
|
||||||
wifiPasswordModal.show(networkContextMenu.currentSSID)
|
if (DMSService.apiVersion >= 7) {
|
||||||
|
NetworkService.connectToWifi(networkContextMenu.currentSSID)
|
||||||
|
} else if (PopoutService.wifiPasswordModal) {
|
||||||
|
PopoutService.wifiPasswordModal.show(networkContextMenu.currentSSID)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
NetworkService.connectToWifi(networkContextMenu.currentSSID)
|
NetworkService.connectToWifi(networkContextMenu.currentSSID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: I18n.tr("Network Info")
|
text: I18n.tr("Network Info")
|
||||||
height: 32
|
height: 32
|
||||||
|
|
||||||
contentItem: StyledText {
|
contentItem: StyledText {
|
||||||
text: parent.text
|
text: parent.text
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
@@ -406,23 +594,23 @@ Rectangle {
|
|||||||
leftPadding: Theme.spacingS
|
leftPadding: Theme.spacingS
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: parent.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
color: parent.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
||||||
radius: Theme.cornerRadius / 2
|
radius: Theme.cornerRadius / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
let networkData = NetworkService.getNetworkInfo(networkContextMenu.currentSSID)
|
let networkData = NetworkService.getNetworkInfo(networkContextMenu.currentSSID)
|
||||||
networkInfoModal.showNetworkInfo(networkContextMenu.currentSSID, networkData)
|
networkInfoModal.showNetworkInfo(networkContextMenu.currentSSID, networkData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: I18n.tr("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
|
||||||
|
|
||||||
contentItem: StyledText {
|
contentItem: StyledText {
|
||||||
text: parent.text
|
text: parent.text
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
@@ -430,25 +618,23 @@ Rectangle {
|
|||||||
leftPadding: Theme.spacingS
|
leftPadding: Theme.spacingS
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: parent.hovered ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.08) : "transparent"
|
color: parent.hovered ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.08) : "transparent"
|
||||||
radius: Theme.cornerRadius / 2
|
radius: Theme.cornerRadius / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
NetworkService.forgetWifiNetwork(networkContextMenu.currentSSID)
|
NetworkService.forgetWifiNetwork(networkContextMenu.currentSSID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WifiPasswordModal {
|
|
||||||
id: wifiPasswordModal
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkInfoModal {
|
NetworkInfoModal {
|
||||||
id: networkInfoModal
|
id: networkInfoModal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NetworkWiredInfoModal {
|
||||||
|
id: networkWiredInfoModal
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -105,7 +105,8 @@ QtObject {
|
|||||||
"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",
|
||||||
@@ -142,8 +143,8 @@ QtObject {
|
|||||||
"description": "VPN connections",
|
"description": "VPN connections",
|
||||||
"icon": "vpn_key",
|
"icon": "vpn_key",
|
||||||
"type": "builtin_plugin",
|
"type": "builtin_plugin",
|
||||||
"enabled": VpnService.available,
|
"enabled": DMSNetworkService.available,
|
||||||
"warning": !VpnService.available ? "VPN not available" : undefined,
|
"warning": !DMSNetworkService.available ? "VPN not available" : undefined,
|
||||||
"isBuiltinPlugin": true
|
"isBuiltinPlugin": true
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Services.Pipewire
|
|
||||||
import qs.Common
|
|
||||||
import qs.Services
|
|
||||||
import qs.Widgets
|
|
||||||
import qs.Modules.ControlCenter.Widgets
|
|
||||||
|
|
||||||
CompoundPill {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property var defaultSource: AudioService.source
|
|
||||||
|
|
||||||
iconName: {
|
|
||||||
if (!defaultSource) return "mic_off"
|
|
||||||
|
|
||||||
let volume = defaultSource.audio.volume
|
|
||||||
let muted = defaultSource.audio.muted
|
|
||||||
|
|
||||||
if (muted || volume === 0.0) return "mic_off"
|
|
||||||
return "mic"
|
|
||||||
}
|
|
||||||
|
|
||||||
isActive: defaultSource && !defaultSource.audio.muted
|
|
||||||
|
|
||||||
primaryText: {
|
|
||||||
if (!defaultSource) {
|
|
||||||
return "No input device"
|
|
||||||
}
|
|
||||||
return defaultSource.description || "Audio Input"
|
|
||||||
}
|
|
||||||
|
|
||||||
secondaryText: {
|
|
||||||
if (!defaultSource) {
|
|
||||||
return "Select device"
|
|
||||||
}
|
|
||||||
if (defaultSource.audio.muted) {
|
|
||||||
return "Muted"
|
|
||||||
}
|
|
||||||
return Math.round(defaultSource.audio.volume * 100) + "%"
|
|
||||||
}
|
|
||||||
|
|
||||||
onToggled: {
|
|
||||||
if (defaultSource && defaultSource.audio) {
|
|
||||||
defaultSource.audio.muted = !defaultSource.audio.muted
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onWheelEvent: function (wheelEvent) {
|
|
||||||
if (!defaultSource || !defaultSource.audio) return
|
|
||||||
let delta = wheelEvent.angleDelta.y
|
|
||||||
let currentVolume = defaultSource.audio.volume * 100
|
|
||||||
let newVolume
|
|
||||||
if (delta > 0)
|
|
||||||
newVolume = Math.min(100, currentVolume + 5)
|
|
||||||
else
|
|
||||||
newVolume = Math.max(0, currentVolume - 5)
|
|
||||||
defaultSource.audio.muted = false
|
|
||||||
defaultSource.audio.volume = newVolume / 100
|
|
||||||
wheelEvent.accepted = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Services.Pipewire
|
|
||||||
import qs.Common
|
|
||||||
import qs.Services
|
|
||||||
import qs.Widgets
|
|
||||||
import qs.Modules.ControlCenter.Widgets
|
|
||||||
|
|
||||||
CompoundPill {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property var defaultSink: AudioService.sink
|
|
||||||
|
|
||||||
iconName: {
|
|
||||||
if (!defaultSink) return "volume_off"
|
|
||||||
|
|
||||||
let volume = defaultSink.audio.volume
|
|
||||||
let muted = defaultSink.audio.muted
|
|
||||||
|
|
||||||
if (muted || volume === 0.0) return "volume_off"
|
|
||||||
if (volume <= 0.33) return "volume_down"
|
|
||||||
if (volume <= 0.66) return "volume_up"
|
|
||||||
return "volume_up"
|
|
||||||
}
|
|
||||||
|
|
||||||
isActive: defaultSink && !defaultSink.audio.muted
|
|
||||||
|
|
||||||
primaryText: {
|
|
||||||
if (!defaultSink) {
|
|
||||||
return "No output device"
|
|
||||||
}
|
|
||||||
return defaultSink.description || "Audio Output"
|
|
||||||
}
|
|
||||||
|
|
||||||
secondaryText: {
|
|
||||||
if (!defaultSink) {
|
|
||||||
return "Select device"
|
|
||||||
}
|
|
||||||
if (defaultSink.audio.muted) {
|
|
||||||
return "Muted"
|
|
||||||
}
|
|
||||||
return Math.round(defaultSink.audio.volume * 100) + "%"
|
|
||||||
}
|
|
||||||
|
|
||||||
onToggled: {
|
|
||||||
if (defaultSink && defaultSink.audio) {
|
|
||||||
defaultSink.audio.muted = !defaultSink.audio.muted
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onWheelEvent: function (wheelEvent) {
|
|
||||||
if (!defaultSink || !defaultSink.audio) return
|
|
||||||
let delta = wheelEvent.angleDelta.y
|
|
||||||
let currentVolume = defaultSink.audio.volume * 100
|
|
||||||
let newVolume
|
|
||||||
if (delta > 0)
|
|
||||||
newVolume = Math.min(100, currentVolume + 5)
|
|
||||||
else
|
|
||||||
newVolume = Math.max(0, currentVolume - 5)
|
|
||||||
defaultSink.audio.muted = false
|
|
||||||
defaultSink.audio.volume = newVolume / 100
|
|
||||||
AudioService.volumeChanged()
|
|
||||||
wheelEvent.accepted = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +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
|
|
||||||
|
|
||||||
property var 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
|
|
||||||
}
|
|
||||||
|
|
||||||
iconName: {
|
|
||||||
if (!BluetoothService.available) {
|
|
||||||
return "bluetooth_disabled"
|
|
||||||
}
|
|
||||||
if (!BluetoothService.adapter || !BluetoothService.adapter.enabled) {
|
|
||||||
return "bluetooth_disabled"
|
|
||||||
}
|
|
||||||
return "bluetooth"
|
|
||||||
}
|
|
||||||
|
|
||||||
isActive: !!(BluetoothService.available && BluetoothService.adapter && BluetoothService.adapter.enabled)
|
|
||||||
showExpandArea: BluetoothService.available
|
|
||||||
|
|
||||||
primaryText: {
|
|
||||||
if (!BluetoothService.available) {
|
|
||||||
return "Bluetooth"
|
|
||||||
}
|
|
||||||
if (!BluetoothService.adapter) {
|
|
||||||
return "No adapter"
|
|
||||||
}
|
|
||||||
if (!BluetoothService.adapter.enabled) {
|
|
||||||
return "Disabled"
|
|
||||||
}
|
|
||||||
return "Enabled"
|
|
||||||
}
|
|
||||||
|
|
||||||
secondaryText: {
|
|
||||||
if (!BluetoothService.available) {
|
|
||||||
return "No adapters"
|
|
||||||
}
|
|
||||||
if (!BluetoothService.adapter || !BluetoothService.adapter.enabled) {
|
|
||||||
return "Off"
|
|
||||||
}
|
|
||||||
if (primaryDevice) {
|
|
||||||
return primaryDevice.name || primaryDevice.alias || primaryDevice.deviceName || "Connected Device"
|
|
||||||
}
|
|
||||||
return "No devices"
|
|
||||||
}
|
|
||||||
|
|
||||||
onToggled: {
|
|
||||||
if (BluetoothService.available && BluetoothService.adapter) {
|
|
||||||
BluetoothService.adapter.enabled = !BluetoothService.adapter.enabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,9 +8,62 @@ import qs.Widgets
|
|||||||
Row {
|
Row {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
property string deviceName: ""
|
||||||
|
property string instanceId: ""
|
||||||
|
property string screenName: ""
|
||||||
|
property var parentScreen: null
|
||||||
|
|
||||||
|
signal iconClicked()
|
||||||
|
|
||||||
height: 40
|
height: 40
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
|
property string targetDeviceName: {
|
||||||
|
if (!DisplayService.brightnessAvailable || !DisplayService.devices || DisplayService.devices.length === 0) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (screenName && screenName.length > 0) {
|
||||||
|
const pins = SettingsData.brightnessDevicePins || {}
|
||||||
|
const pinnedDevice = pins[screenName]
|
||||||
|
if (pinnedDevice && pinnedDevice.length > 0) {
|
||||||
|
const found = DisplayService.devices.find(dev => dev.name === pinnedDevice)
|
||||||
|
if (found) {
|
||||||
|
return found.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (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
|
||||||
@@ -24,25 +77,22 @@ Row {
|
|||||||
id: iconArea
|
id: iconArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: DisplayService.devices.length > 1 ? Qt.PointingHandCursor : Qt.ArrowCursor
|
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: {
|
onEntered: {
|
||||||
tooltipLoader.active = true
|
tooltipLoader.active = true
|
||||||
if (tooltipLoader.item) {
|
if (tooltipLoader.item) {
|
||||||
const tooltipText = DisplayService.currentDevice ? "bl device: " + DisplayService.currentDevice : "Backlight Control"
|
const tooltipText = targetDevice ? "bl device: " + targetDevice.name : "Backlight Control"
|
||||||
const p = iconArea.mapToItem(null, iconArea.width / 2, 0)
|
const globalPos = iconArea.mapToGlobal(iconArea.width / 2, iconArea.height / 2)
|
||||||
tooltipLoader.item.show(tooltipText, p.x, p.y - 40, null)
|
const screenY = root.parentScreen?.y ?? 0
|
||||||
|
const relativeY = globalPos.y - screenY - 55
|
||||||
|
tooltipLoader.item.show(tooltipText, globalPos.x, relativeY, root.parentScreen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,15 +106,23 @@ Row {
|
|||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,88 +130,19 @@ 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, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
thumbOutlineColor: Theme.surfaceContainer
|
thumbOutlineColor: Theme.surfaceContainer
|
||||||
trackColor: Theme.surfaceContainerHigh
|
trackColor: Theme.surfaceContainerHigh
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu {
|
|
||||||
id: deviceMenu
|
|
||||||
width: 200
|
|
||||||
closePolicy: Popup.CloseOnEscape
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: tooltipLoader
|
id: tooltipLoader
|
||||||
active: false
|
active: false
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ Rectangle {
|
|||||||
property real maximumValue: 1.0
|
property real maximumValue: 1.0
|
||||||
property real minimumValue: 0.0
|
property real minimumValue: 0.0
|
||||||
property bool enabled: true
|
property bool enabled: true
|
||||||
|
|
||||||
signal sliderValueChanged(real value)
|
signal sliderValueChanged(real value)
|
||||||
|
|
||||||
width: parent ? parent.width : 200
|
width: parent ? parent.width : 200
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
import Quickshell.Hyprland
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
@@ -16,6 +18,8 @@ Item {
|
|||||||
anchors.topMargin: -(SettingsData.dankBarGothCornersEnabled && !axis.isVertical && axis.edge === "bottom" ? 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)
|
anchors.bottomMargin: -(SettingsData.dankBarGothCornersEnabled && !axis.isVertical && axis.edge === "top" ? barWindow._wingR : 0)
|
||||||
|
|
||||||
|
readonly property real dpr: CompositorService.getScreenScale(barWindow.screen)
|
||||||
|
|
||||||
function requestRepaint() {
|
function requestRepaint() {
|
||||||
debounceTimer.restart()
|
debounceTimer.restart()
|
||||||
}
|
}
|
||||||
@@ -38,12 +42,12 @@ Item {
|
|||||||
renderTarget: Canvas.FramebufferObject
|
renderTarget: Canvas.FramebufferObject
|
||||||
renderStrategy: Canvas.Cooperative
|
renderStrategy: Canvas.Cooperative
|
||||||
|
|
||||||
readonly property real correctWidth: root.width
|
readonly property real correctWidth: Theme.px(root.width, dpr)
|
||||||
readonly property real correctHeight: root.height
|
readonly property real correctHeight: Theme.px(root.height, dpr)
|
||||||
canvasSize: Qt.size(Math.ceil(correctWidth), Math.ceil(correctHeight))
|
canvasSize: Qt.size(correctWidth, correctHeight)
|
||||||
|
|
||||||
property real wing: SettingsData.dankBarGothCornersEnabled ? barWindow._wingR : 0
|
property real wing: SettingsData.dankBarGothCornersEnabled ? Theme.px(barWindow._wingR, dpr) : 0
|
||||||
property real rt: SettingsData.dankBarSquareCorners ? 0 : Theme.cornerRadius
|
property real rt: SettingsData.dankBarSquareCorners ? 0 : Theme.px(Theme.cornerRadius, dpr)
|
||||||
|
|
||||||
onWingChanged: root.requestRepaint()
|
onWingChanged: root.requestRepaint()
|
||||||
onRtChanged: root.requestRepaint()
|
onRtChanged: root.requestRepaint()
|
||||||
@@ -52,6 +56,11 @@ Item {
|
|||||||
onVisibleChanged: if (visible) root.requestRepaint()
|
onVisibleChanged: if (visible) root.requestRepaint()
|
||||||
Component.onCompleted: root.requestRepaint()
|
Component.onCompleted: root.requestRepaint()
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: root
|
||||||
|
function onDprChanged() { root.requestRepaint() }
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: barWindow
|
target: barWindow
|
||||||
function on_BgColorChanged() { root.requestRepaint() }
|
function on_BgColorChanged() { root.requestRepaint() }
|
||||||
@@ -101,7 +110,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx.reset()
|
ctx.reset()
|
||||||
ctx.clearRect(0, 0, Math.ceil(W), Math.ceil(H_raw))
|
ctx.clearRect(0, 0, W, H_raw)
|
||||||
|
|
||||||
ctx.save()
|
ctx.save()
|
||||||
if (isBottom) {
|
if (isBottom) {
|
||||||
@@ -130,12 +139,12 @@ Item {
|
|||||||
renderTarget: Canvas.FramebufferObject
|
renderTarget: Canvas.FramebufferObject
|
||||||
renderStrategy: Canvas.Cooperative
|
renderStrategy: Canvas.Cooperative
|
||||||
|
|
||||||
readonly property real correctWidth: root.width
|
readonly property real correctWidth: Theme.px(root.width, dpr)
|
||||||
readonly property real correctHeight: root.height
|
readonly property real correctHeight: Theme.px(root.height, dpr)
|
||||||
canvasSize: Qt.size(Math.ceil(correctWidth), Math.ceil(correctHeight))
|
canvasSize: Qt.size(correctWidth, correctHeight)
|
||||||
|
|
||||||
property real wing: SettingsData.dankBarGothCornersEnabled ? barWindow._wingR : 0
|
property real wing: SettingsData.dankBarGothCornersEnabled ? Theme.px(barWindow._wingR, dpr) : 0
|
||||||
property real rt: SettingsData.dankBarSquareCorners ? 0 : Theme.cornerRadius
|
property real rt: SettingsData.dankBarSquareCorners ? 0 : Theme.px(Theme.cornerRadius, dpr)
|
||||||
property real alphaTint: (barWindow._bgColor?.a ?? 1) < 0.99 ? (Theme.stateLayerOpacity ?? 0) : 0
|
property real alphaTint: (barWindow._bgColor?.a ?? 1) < 0.99 ? (Theme.stateLayerOpacity ?? 0) : 0
|
||||||
|
|
||||||
onWingChanged: root.requestRepaint()
|
onWingChanged: root.requestRepaint()
|
||||||
@@ -146,6 +155,11 @@ Item {
|
|||||||
onVisibleChanged: if (visible) root.requestRepaint()
|
onVisibleChanged: if (visible) root.requestRepaint()
|
||||||
Component.onCompleted: root.requestRepaint()
|
Component.onCompleted: root.requestRepaint()
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: root
|
||||||
|
function onDprChanged() { root.requestRepaint() }
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: barWindow
|
target: barWindow
|
||||||
function on_BgColorChanged() { root.requestRepaint() }
|
function on_BgColorChanged() { root.requestRepaint() }
|
||||||
@@ -195,7 +209,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx.reset()
|
ctx.reset()
|
||||||
ctx.clearRect(0, 0, Math.ceil(W), Math.ceil(H_raw))
|
ctx.clearRect(0, 0, W, H_raw)
|
||||||
|
|
||||||
ctx.save()
|
ctx.save()
|
||||||
if (isBottom) {
|
if (isBottom) {
|
||||||
@@ -220,19 +234,20 @@ Item {
|
|||||||
Canvas {
|
Canvas {
|
||||||
id: barBorder
|
id: barBorder
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
antialiasing: false
|
|
||||||
visible: SettingsData.dankBarBorderEnabled
|
visible: SettingsData.dankBarBorderEnabled
|
||||||
renderTarget: Canvas.FramebufferObject
|
renderTarget: Canvas.FramebufferObject
|
||||||
renderStrategy: Canvas.Cooperative
|
renderStrategy: Canvas.Cooperative
|
||||||
|
|
||||||
readonly property real correctWidth: root.width
|
readonly property real correctWidth: Theme.px(root.width, dpr)
|
||||||
readonly property real correctHeight: root.height
|
readonly property real correctHeight: Theme.px(root.height, dpr)
|
||||||
canvasSize: Qt.size(Math.ceil(correctWidth), Math.ceil(correctHeight))
|
canvasSize: Qt.size(correctWidth, correctHeight)
|
||||||
|
|
||||||
property real wing: SettingsData.dankBarGothCornersEnabled ? barWindow._wingR : 0
|
property real wing: SettingsData.dankBarGothCornersEnabled ? Theme.px(barWindow._wingR, dpr) : 0
|
||||||
property real rt: SettingsData.dankBarSquareCorners ? 0 : Theme.cornerRadius
|
property real rt: SettingsData.dankBarSquareCorners ? 0 : Theme.px(Theme.cornerRadius, dpr)
|
||||||
property bool borderEnabled: SettingsData.dankBarBorderEnabled
|
property bool borderEnabled: SettingsData.dankBarBorderEnabled
|
||||||
|
|
||||||
|
antialiasing: rt > 0 || wing > 0
|
||||||
|
|
||||||
onWingChanged: root.requestRepaint()
|
onWingChanged: root.requestRepaint()
|
||||||
onRtChanged: root.requestRepaint()
|
onRtChanged: root.requestRepaint()
|
||||||
onBorderEnabledChanged: root.requestRepaint()
|
onBorderEnabledChanged: root.requestRepaint()
|
||||||
@@ -241,6 +256,11 @@ Item {
|
|||||||
onVisibleChanged: if (visible) root.requestRepaint()
|
onVisibleChanged: if (visible) root.requestRepaint()
|
||||||
Component.onCompleted: root.requestRepaint()
|
Component.onCompleted: root.requestRepaint()
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: root
|
||||||
|
function onDprChanged() { root.requestRepaint() }
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: Theme
|
target: Theme
|
||||||
function onIsLightModeChanged() { root.requestRepaint() }
|
function onIsLightModeChanged() { root.requestRepaint() }
|
||||||
@@ -257,6 +277,7 @@ Item {
|
|||||||
function onDankBarBorderThicknessChanged() { root.requestRepaint() }
|
function onDankBarBorderThicknessChanged() { root.requestRepaint() }
|
||||||
function onDankBarSpacingChanged() { root.requestRepaint() }
|
function onDankBarSpacingChanged() { root.requestRepaint() }
|
||||||
function onDankBarSquareCornersChanged() { root.requestRepaint() }
|
function onDankBarSquareCornersChanged() { root.requestRepaint() }
|
||||||
|
function onDankBarTransparencyChanged() { root.requestRepaint() }
|
||||||
}
|
}
|
||||||
|
|
||||||
onPaint: {
|
onPaint: {
|
||||||
@@ -276,40 +297,8 @@ Item {
|
|||||||
const spacing = SettingsData.dankBarSpacing
|
const spacing = SettingsData.dankBarSpacing
|
||||||
const hasEdgeGap = spacing > 0 || RT > 0
|
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.reset()
|
||||||
ctx.clearRect(0, 0, Math.ceil(W), Math.ceil(H_raw))
|
ctx.clearRect(0, 0, W, H_raw)
|
||||||
|
|
||||||
ctx.save()
|
ctx.save()
|
||||||
if (isBottom) {
|
if (isBottom) {
|
||||||
@@ -323,20 +312,85 @@ Item {
|
|||||||
ctx.rotate(Math.PI / 2)
|
ctx.rotate(Math.PI / 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
drawTopBorder()
|
const uiThickness = Math.max(1, SettingsData.dankBarBorderThickness ?? 1)
|
||||||
ctx.restore()
|
const devThickness = Math.max(1, Math.round(Theme.px(uiThickness, dpr)))
|
||||||
|
|
||||||
const key = SettingsData.dankBarBorderColor || "surfaceText"
|
const key = SettingsData.dankBarBorderColor || "surfaceText"
|
||||||
const base = (key === "surfaceText") ? Theme.surfaceText
|
const base = (key === "surfaceText") ? Theme.surfaceText
|
||||||
: (key === "primary") ? Theme.primary
|
: (key === "primary") ? Theme.primary
|
||||||
: Theme.secondary
|
: Theme.secondary
|
||||||
const color = Theme.withAlpha(base, SettingsData.dankBarBorderOpacity ?? 1.0)
|
const color = Theme.withAlpha(base, SettingsData.dankBarBorderOpacity ?? 1.0)
|
||||||
const thickness = Math.max(1, SettingsData.dankBarBorderThickness ?? 1)
|
|
||||||
|
|
||||||
ctx.globalCompositeOperation = "source-over"
|
ctx.globalCompositeOperation = "source-over"
|
||||||
ctx.lineWidth = thickness
|
ctx.fillStyle = color
|
||||||
ctx.strokeStyle = color
|
|
||||||
ctx.stroke()
|
function drawTopBorder() {
|
||||||
|
if (!hasEdgeGap) {
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.rect(0, H - devThickness, W, devThickness)
|
||||||
|
ctx.fill()
|
||||||
|
} else {
|
||||||
|
const thk = devThickness
|
||||||
|
const RTi = Math.max(0, RT - thk)
|
||||||
|
const Ri = Math.max(0, R - thk)
|
||||||
|
|
||||||
|
ctx.beginPath()
|
||||||
|
|
||||||
|
if (R > 0 && Ri > 0) {
|
||||||
|
ctx.moveTo(RT, 0)
|
||||||
|
ctx.lineTo(W - RT, 0)
|
||||||
|
ctx.arcTo(W, 0, W, RT, RT)
|
||||||
|
ctx.lineTo(W, H)
|
||||||
|
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)
|
||||||
|
ctx.lineTo(0, RT)
|
||||||
|
ctx.arcTo(0, 0, RT, 0, RT)
|
||||||
|
ctx.closePath()
|
||||||
|
|
||||||
|
ctx.moveTo(RT, thk)
|
||||||
|
ctx.arcTo(thk, thk, thk, RT, RTi)
|
||||||
|
ctx.lineTo(thk, H + R)
|
||||||
|
ctx.arc(R, H + R, Ri, -Math.PI, -Math.PI / 2, false)
|
||||||
|
ctx.lineTo(W - R, H + thk)
|
||||||
|
ctx.arc(W - R, H + R, Ri, -Math.PI / 2, 0, false)
|
||||||
|
ctx.lineTo(W - thk, H + R)
|
||||||
|
ctx.lineTo(W - thk, RT)
|
||||||
|
ctx.arcTo(W - thk, thk, W - RT, thk, RTi)
|
||||||
|
ctx.lineTo(RT, thk)
|
||||||
|
ctx.closePath()
|
||||||
|
} else {
|
||||||
|
ctx.moveTo(RT, 0)
|
||||||
|
ctx.lineTo(W - RT, 0)
|
||||||
|
ctx.arcTo(W, 0, W, RT, RT)
|
||||||
|
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.moveTo(RT, thk)
|
||||||
|
ctx.arcTo(thk, thk, thk, RT, RTi)
|
||||||
|
ctx.lineTo(thk, H - RT)
|
||||||
|
ctx.arcTo(thk, H - thk, RT, H - thk, RTi)
|
||||||
|
ctx.lineTo(W - RT, H - thk)
|
||||||
|
ctx.arcTo(W - thk, H - thk, W - thk, H - RT, RTi)
|
||||||
|
ctx.lineTo(W - thk, RT)
|
||||||
|
ctx.arcTo(W - thk, thk, W - RT, thk, RTi)
|
||||||
|
ctx.lineTo(RT, thk)
|
||||||
|
ctx.closePath()
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.fill("evenodd")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drawTopBorder()
|
||||||
|
ctx.restore()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,10 @@ Item {
|
|||||||
property var parentScreen: null
|
property var parentScreen: null
|
||||||
property real widgetThickness: 30
|
property real widgetThickness: 30
|
||||||
property real barThickness: 48
|
property real barThickness: 48
|
||||||
|
property bool overrideAxisLayout: false
|
||||||
|
property bool forceVerticalLayout: false
|
||||||
|
|
||||||
readonly property bool isVertical: axis?.isVertical ?? false
|
readonly property bool isVertical: overrideAxisLayout ? forceVerticalLayout : (axis?.isVertical ?? false)
|
||||||
readonly property real spacing: noBackground ? 2 : Theme.spacingXS
|
readonly property real spacing: noBackground ? 2 : Theme.spacingXS
|
||||||
|
|
||||||
property var centerWidgets: []
|
property var centerWidgets: []
|
||||||
@@ -370,9 +372,6 @@ Item {
|
|||||||
}
|
}
|
||||||
item.widthChanged.connect(() => layoutTimer.restart())
|
item.widthChanged.connect(() => layoutTimer.restart())
|
||||||
item.heightChanged.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) {
|
if (root.axis && "axis" in item) {
|
||||||
item.axis = Qt.binding(() => root.axis)
|
item.axis = Qt.binding(() => root.axis)
|
||||||
}
|
}
|
||||||
@@ -396,8 +395,47 @@ Item {
|
|||||||
if ("barThickness" in item) {
|
if ("barThickness" in item) {
|
||||||
item.barThickness = Qt.binding(() => root.barThickness)
|
item.barThickness = Qt.binding(() => root.barThickness)
|
||||||
}
|
}
|
||||||
|
if ("sectionSpacing" in item) {
|
||||||
|
item.sectionSpacing = Qt.binding(() => root.spacing)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("isFirst" in item) {
|
||||||
|
item.isFirst = Qt.binding(() => {
|
||||||
|
for (var i = 0; i < centerRepeater.count; i++) {
|
||||||
|
const checkItem = centerRepeater.itemAt(i)
|
||||||
|
if (checkItem && checkItem.active && checkItem.item) {
|
||||||
|
return checkItem.item === item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("isLast" in item) {
|
||||||
|
item.isLast = Qt.binding(() => {
|
||||||
|
for (var i = centerRepeater.count - 1; i >= 0; i--) {
|
||||||
|
const checkItem = centerRepeater.itemAt(i)
|
||||||
|
if (checkItem && checkItem.active && checkItem.item) {
|
||||||
|
return checkItem.item === item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("isLeftBarEdge" in item) {
|
||||||
|
item.isLeftBarEdge = false
|
||||||
|
}
|
||||||
|
if ("isRightBarEdge" in item) {
|
||||||
|
item.isRightBarEdge = false
|
||||||
|
}
|
||||||
|
if ("isTopBarEdge" in item) {
|
||||||
|
item.isTopBarEdge = false
|
||||||
|
}
|
||||||
|
if ("isBottomBarEdge" in item) {
|
||||||
|
item.isBottomBarEdge = false
|
||||||
|
}
|
||||||
|
|
||||||
// Inject PluginService for plugin widgets
|
|
||||||
if (item.pluginService !== undefined) {
|
if (item.pluginService !== undefined) {
|
||||||
var parts = model.widgetId.split(":")
|
var parts = model.widgetId.split(":")
|
||||||
var pluginId = parts[0]
|
var pluginId = parts[0]
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -11,8 +11,10 @@ Item {
|
|||||||
property var parentScreen: null
|
property var parentScreen: null
|
||||||
property real widgetThickness: 30
|
property real widgetThickness: 30
|
||||||
property real barThickness: 48
|
property real barThickness: 48
|
||||||
|
property bool overrideAxisLayout: false
|
||||||
|
property bool forceVerticalLayout: false
|
||||||
|
|
||||||
readonly property bool isVertical: axis?.isVertical ?? false
|
readonly property bool isVertical: overrideAxisLayout ? forceVerticalLayout : (axis?.isVertical ?? false)
|
||||||
|
|
||||||
implicitHeight: layoutLoader.item ? (layoutLoader.item.implicitHeight || layoutLoader.item.height) : 0
|
implicitHeight: layoutLoader.item ? (layoutLoader.item.implicitHeight || layoutLoader.item.height) : 0
|
||||||
implicitWidth: layoutLoader.item ? (layoutLoader.item.implicitWidth || layoutLoader.item.width) : 0
|
implicitWidth: layoutLoader.item ? (layoutLoader.item.implicitWidth || layoutLoader.item.width) : 0
|
||||||
@@ -26,10 +28,13 @@ Item {
|
|||||||
Component {
|
Component {
|
||||||
id: rowComp
|
id: rowComp
|
||||||
Row {
|
Row {
|
||||||
spacing: noBackground ? 2 : Theme.spacingXS
|
readonly property real widgetSpacing: noBackground ? 2 : Theme.spacingXS
|
||||||
|
spacing: widgetSpacing
|
||||||
Repeater {
|
Repeater {
|
||||||
|
id: rowRepeater
|
||||||
model: root.widgetsModel
|
model: root.widgetsModel
|
||||||
Item {
|
Item {
|
||||||
|
readonly property real rowSpacing: parent.widgetSpacing
|
||||||
width: widgetLoader.item ? widgetLoader.item.width : 0
|
width: widgetLoader.item ? widgetLoader.item.width : 0
|
||||||
height: widgetLoader.item ? widgetLoader.item.height : 0
|
height: widgetLoader.item ? widgetLoader.item.height : 0
|
||||||
WidgetHost {
|
WidgetHost {
|
||||||
@@ -45,6 +50,11 @@ Item {
|
|||||||
parentScreen: root.parentScreen
|
parentScreen: root.parentScreen
|
||||||
widgetThickness: root.widgetThickness
|
widgetThickness: root.widgetThickness
|
||||||
barThickness: root.barThickness
|
barThickness: root.barThickness
|
||||||
|
isFirst: model.index === 0
|
||||||
|
isLast: model.index === rowRepeater.count - 1
|
||||||
|
sectionSpacing: parent.rowSpacing
|
||||||
|
isLeftBarEdge: true
|
||||||
|
isRightBarEdge: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,10 +65,13 @@ Item {
|
|||||||
id: columnComp
|
id: columnComp
|
||||||
Column {
|
Column {
|
||||||
width: Math.max(parent.width, 200)
|
width: Math.max(parent.width, 200)
|
||||||
spacing: noBackground ? 2 : Theme.spacingXS
|
readonly property real widgetSpacing: noBackground ? 2 : Theme.spacingXS
|
||||||
|
spacing: widgetSpacing
|
||||||
Repeater {
|
Repeater {
|
||||||
|
id: columnRepeater
|
||||||
model: root.widgetsModel
|
model: root.widgetsModel
|
||||||
Item {
|
Item {
|
||||||
|
readonly property real columnSpacing: parent.widgetSpacing
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: widgetLoader.item ? widgetLoader.item.height : 0
|
height: widgetLoader.item ? widgetLoader.item.height : 0
|
||||||
WidgetHost {
|
WidgetHost {
|
||||||
@@ -74,6 +87,11 @@ Item {
|
|||||||
parentScreen: root.parentScreen
|
parentScreen: root.parentScreen
|
||||||
widgetThickness: root.widgetThickness
|
widgetThickness: root.widgetThickness
|
||||||
barThickness: root.barThickness
|
barThickness: root.barThickness
|
||||||
|
isFirst: model.index === 0
|
||||||
|
isLast: model.index === columnRepeater.count - 1
|
||||||
|
sectionSpacing: parent.columnSpacing
|
||||||
|
isTopBarEdge: true
|
||||||
|
isBottomBarEdge: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ DankPopout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: BatteryService.batteryAvailable ? BatteryService.batteryStatus : "Management"
|
text: BatteryService.batteryStatus
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
color: {
|
color: {
|
||||||
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
|
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
|
||||||
@@ -247,6 +247,7 @@ DankPopout {
|
|||||||
return Theme.surfaceText;
|
return Theme.surfaceText;
|
||||||
}
|
}
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
|
visible: BatteryService.batteryAvailable
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -364,6 +365,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: {
|
||||||
|
|||||||
@@ -14,7 +14,16 @@ DankPopout {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
Ref {
|
Ref {
|
||||||
service: VpnService
|
service: DMSNetworkService
|
||||||
|
}
|
||||||
|
|
||||||
|
property bool wasVisible: false
|
||||||
|
|
||||||
|
onShouldBeVisibleChanged: {
|
||||||
|
if (shouldBeVisible && !wasVisible) {
|
||||||
|
DMSNetworkService.getState()
|
||||||
|
}
|
||||||
|
wasVisible = shouldBeVisible
|
||||||
}
|
}
|
||||||
|
|
||||||
property var triggerScreen: null
|
property var triggerScreen: null
|
||||||
@@ -161,11 +170,11 @@ DankPopout {
|
|||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: {
|
text: {
|
||||||
if (!VpnService.connected) {
|
if (!DMSNetworkService.connected) {
|
||||||
return "Active: None";
|
return "Active: None";
|
||||||
}
|
}
|
||||||
|
|
||||||
const names = VpnService.activeNames || [];
|
const names = DMSNetworkService.activeNames || [];
|
||||||
if (names.length <= 1) {
|
if (names.length <= 1) {
|
||||||
return "Active: " + (names[0] || "VPN");
|
return "Active: " + (names[0] || "VPN");
|
||||||
}
|
}
|
||||||
@@ -175,11 +184,10 @@ DankPopout {
|
|||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
}
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
height: 1
|
Layout.maximumWidth: parent.width - 140
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removed Quick Connect for clarity
|
// Removed Quick Connect for clarity
|
||||||
@@ -193,11 +201,12 @@ DankPopout {
|
|||||||
height: 28
|
height: 28
|
||||||
radius: 14
|
radius: 14
|
||||||
color: discAllArea.containsMouse ? Theme.errorHover : Theme.surfaceLight
|
color: discAllArea.containsMouse ? Theme.errorHover : Theme.surfaceLight
|
||||||
visible: VpnService.connected
|
visible: DMSNetworkService.connected
|
||||||
width: 130
|
width: 130
|
||||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||||
border.width: 0
|
border.width: 0
|
||||||
border.color: Theme.outlineLight
|
border.color: Theme.outlineLight
|
||||||
|
opacity: DMSNetworkService.isBusy ? 0.5 : 1.0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -223,8 +232,9 @@ DankPopout {
|
|||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: DMSNetworkService.isBusy ? Qt.BusyCursor : Qt.PointingHandCursor
|
||||||
onClicked: VpnService.disconnectAllActive()
|
enabled: !DMSNetworkService.isBusy
|
||||||
|
onClicked: DMSNetworkService.disconnectAllActive()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -251,7 +261,7 @@ DankPopout {
|
|||||||
|
|
||||||
Item {
|
Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: VpnService.profiles.length === 0 ? 120 : 0
|
height: DMSNetworkService.profiles.length === 0 ? 120 : 0
|
||||||
visible: height > 0
|
visible: height > 0
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -284,7 +294,7 @@ DankPopout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: VpnService.profiles
|
model: DMSNetworkService.profiles
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
required property var modelData
|
required property var modelData
|
||||||
@@ -292,9 +302,10 @@ DankPopout {
|
|||||||
width: parent ? parent.width : 300
|
width: parent ? parent.width : 300
|
||||||
height: 50
|
height: 50
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: rowArea.containsMouse ? Theme.primaryHoverLight : (VpnService.isActiveUuid(modelData.uuid) ? Theme.primaryPressed : Theme.surfaceLight)
|
color: rowArea.containsMouse ? Theme.primaryHoverLight : (DMSNetworkService.isActiveUuid(modelData.uuid) ? Theme.primaryPressed : Theme.surfaceLight)
|
||||||
border.width: VpnService.isActiveUuid(modelData.uuid) ? 2 : 1
|
border.width: DMSNetworkService.isActiveUuid(modelData.uuid) ? 2 : 1
|
||||||
border.color: VpnService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.outlineLight
|
border.color: DMSNetworkService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.outlineLight
|
||||||
|
opacity: DMSNetworkService.isBusy ? 0.5 : 1.0
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -304,20 +315,24 @@ DankPopout {
|
|||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
name: VpnService.isActiveUuid(modelData.uuid) ? "vpn_lock" : "vpn_key_off"
|
name: DMSNetworkService.isActiveUuid(modelData.uuid) ? "vpn_lock" : "vpn_key_off"
|
||||||
size: Theme.iconSize - 4
|
size: Theme.iconSize - 4
|
||||||
color: VpnService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
color: DMSNetworkService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
spacing: 2
|
spacing: 2
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: modelData.name
|
text: modelData.name
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: VpnService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
color: DMSNetworkService.isActiveUuid(modelData.uuid) ? Theme.primary : Theme.surfaceText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
@@ -391,8 +406,9 @@ DankPopout {
|
|||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: DMSNetworkService.isBusy ? Qt.BusyCursor : Qt.PointingHandCursor
|
||||||
onClicked: VpnService.toggle(modelData.uuid)
|
enabled: !DMSNetworkService.isBusy
|
||||||
|
onClicked: DMSNetworkService.toggle(modelData.uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,10 @@ Item {
|
|||||||
property var parentScreen: null
|
property var parentScreen: null
|
||||||
property real widgetThickness: 30
|
property real widgetThickness: 30
|
||||||
property real barThickness: 48
|
property real barThickness: 48
|
||||||
|
property bool overrideAxisLayout: false
|
||||||
|
property bool forceVerticalLayout: false
|
||||||
|
|
||||||
readonly property bool isVertical: axis?.isVertical ?? false
|
readonly property bool isVertical: overrideAxisLayout ? forceVerticalLayout : (axis?.isVertical ?? false)
|
||||||
|
|
||||||
implicitHeight: layoutLoader.item ? layoutLoader.item.implicitHeight : 0
|
implicitHeight: layoutLoader.item ? layoutLoader.item.implicitHeight : 0
|
||||||
implicitWidth: layoutLoader.item ? layoutLoader.item.implicitWidth : 0
|
implicitWidth: layoutLoader.item ? layoutLoader.item.implicitWidth : 0
|
||||||
@@ -27,11 +29,14 @@ Item {
|
|||||||
Component {
|
Component {
|
||||||
id: rowComp
|
id: rowComp
|
||||||
Row {
|
Row {
|
||||||
spacing: noBackground ? 2 : Theme.spacingXS
|
readonly property real widgetSpacing: noBackground ? 2 : Theme.spacingXS
|
||||||
|
spacing: widgetSpacing
|
||||||
anchors.right: parent ? parent.right : undefined
|
anchors.right: parent ? parent.right : undefined
|
||||||
Repeater {
|
Repeater {
|
||||||
|
id: rowRepeater
|
||||||
model: root.widgetsModel
|
model: root.widgetsModel
|
||||||
Item {
|
Item {
|
||||||
|
readonly property real rowSpacing: parent.widgetSpacing
|
||||||
width: widgetLoader.item ? widgetLoader.item.width : 0
|
width: widgetLoader.item ? widgetLoader.item.width : 0
|
||||||
height: widgetLoader.item ? widgetLoader.item.height : 0
|
height: widgetLoader.item ? widgetLoader.item.height : 0
|
||||||
WidgetHost {
|
WidgetHost {
|
||||||
@@ -47,6 +52,11 @@ Item {
|
|||||||
parentScreen: root.parentScreen
|
parentScreen: root.parentScreen
|
||||||
widgetThickness: root.widgetThickness
|
widgetThickness: root.widgetThickness
|
||||||
barThickness: root.barThickness
|
barThickness: root.barThickness
|
||||||
|
isFirst: model.index === 0
|
||||||
|
isLast: model.index === rowRepeater.count - 1
|
||||||
|
sectionSpacing: parent.rowSpacing
|
||||||
|
isLeftBarEdge: false
|
||||||
|
isRightBarEdge: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,10 +67,13 @@ Item {
|
|||||||
id: columnComp
|
id: columnComp
|
||||||
Column {
|
Column {
|
||||||
width: parent ? parent.width : 0
|
width: parent ? parent.width : 0
|
||||||
spacing: noBackground ? 2 : Theme.spacingXS
|
readonly property real widgetSpacing: noBackground ? 2 : Theme.spacingXS
|
||||||
|
spacing: widgetSpacing
|
||||||
Repeater {
|
Repeater {
|
||||||
|
id: columnRepeater
|
||||||
model: root.widgetsModel
|
model: root.widgetsModel
|
||||||
Item {
|
Item {
|
||||||
|
readonly property real columnSpacing: parent.widgetSpacing
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: widgetLoader.item ? widgetLoader.item.height : 0
|
height: widgetLoader.item ? widgetLoader.item.height : 0
|
||||||
WidgetHost {
|
WidgetHost {
|
||||||
@@ -76,6 +89,11 @@ Item {
|
|||||||
parentScreen: root.parentScreen
|
parentScreen: root.parentScreen
|
||||||
widgetThickness: root.widgetThickness
|
widgetThickness: root.widgetThickness
|
||||||
barThickness: root.barThickness
|
barThickness: root.barThickness
|
||||||
|
isFirst: model.index === 0
|
||||||
|
isLast: model.index === columnRepeater.count - 1
|
||||||
|
sectionSpacing: parent.columnSpacing
|
||||||
|
isTopBarEdge: false
|
||||||
|
isBottomBarEdge: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,20 @@ Loader {
|
|||||||
property var parentScreen: null
|
property var parentScreen: null
|
||||||
property real widgetThickness: 30
|
property real widgetThickness: 30
|
||||||
property real barThickness: 48
|
property real barThickness: 48
|
||||||
|
property bool isFirst: false
|
||||||
|
property bool isLast: false
|
||||||
|
property real sectionSpacing: 0
|
||||||
|
property bool isLeftBarEdge: false
|
||||||
|
property bool isRightBarEdge: false
|
||||||
|
property bool isTopBarEdge: false
|
||||||
|
property bool isBottomBarEdge: false
|
||||||
|
|
||||||
asynchronous: false
|
asynchronous: false
|
||||||
|
|
||||||
active: getWidgetVisible(widgetId, DgopService.dgopAvailable) &&
|
readonly property bool orientationMatches: (axis?.isVertical ?? false) === isInColumn
|
||||||
|
|
||||||
|
active: orientationMatches &&
|
||||||
|
getWidgetVisible(widgetId, DgopService.dgopAvailable) &&
|
||||||
(widgetId !== "music" || MprisController.activePlayer !== null)
|
(widgetId !== "music" || MprisController.activePlayer !== null)
|
||||||
sourceComponent: getWidgetComponent(widgetId, components)
|
sourceComponent: getWidgetComponent(widgetId, components)
|
||||||
opacity: getWidgetEnabled(widgetData?.enabled) ? 1 : 0
|
opacity: getWidgetEnabled(widgetData?.enabled) ? 1 : 0
|
||||||
@@ -73,12 +83,65 @@ Loader {
|
|||||||
restoreMode: Binding.RestoreNone
|
restoreMode: Binding.RestoreNone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: root.item
|
||||||
|
when: root.item && "isFirst" in root.item
|
||||||
|
property: "isFirst"
|
||||||
|
value: root.isFirst
|
||||||
|
restoreMode: Binding.RestoreNone
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: root.item
|
||||||
|
when: root.item && "isLast" in root.item
|
||||||
|
property: "isLast"
|
||||||
|
value: root.isLast
|
||||||
|
restoreMode: Binding.RestoreNone
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: root.item
|
||||||
|
when: root.item && "sectionSpacing" in root.item
|
||||||
|
property: "sectionSpacing"
|
||||||
|
value: root.sectionSpacing
|
||||||
|
restoreMode: Binding.RestoreNone
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: root.item
|
||||||
|
when: root.item && "isLeftBarEdge" in root.item
|
||||||
|
property: "isLeftBarEdge"
|
||||||
|
value: root.isLeftBarEdge
|
||||||
|
restoreMode: Binding.RestoreNone
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: root.item
|
||||||
|
when: root.item && "isRightBarEdge" in root.item
|
||||||
|
property: "isRightBarEdge"
|
||||||
|
value: root.isRightBarEdge
|
||||||
|
restoreMode: Binding.RestoreNone
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: root.item
|
||||||
|
when: root.item && "isTopBarEdge" in root.item
|
||||||
|
property: "isTopBarEdge"
|
||||||
|
value: root.isTopBarEdge
|
||||||
|
restoreMode: Binding.RestoreNone
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: root.item
|
||||||
|
when: root.item && "isBottomBarEdge" in root.item
|
||||||
|
property: "isBottomBarEdge"
|
||||||
|
value: root.isBottomBarEdge
|
||||||
|
restoreMode: Binding.RestoreNone
|
||||||
|
}
|
||||||
|
|
||||||
onLoaded: {
|
onLoaded: {
|
||||||
if (item) {
|
if (item) {
|
||||||
contentItemReady(item)
|
contentItemReady(item)
|
||||||
if (widgetId === "spacer") {
|
|
||||||
item.spacerSize = Qt.binding(() => spacerSize)
|
|
||||||
}
|
|
||||||
if (axis && "isVertical" in item) {
|
if (axis && "isVertical" in item) {
|
||||||
try {
|
try {
|
||||||
item.isVertical = axis.isVertical
|
item.isVertical = axis.isVertical
|
||||||
@@ -166,4 +229,4 @@ Loader {
|
|||||||
function getWidgetEnabled(enabled) {
|
function getWidgetEnabled(enabled) {
|
||||||
return enabled !== false
|
return enabled !== false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,126 +1,114 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell.Services.UPower
|
import Quickshell.Services.UPower
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Modules.Plugins
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
BasePill {
|
||||||
id: battery
|
id: battery
|
||||||
|
|
||||||
property bool isVertical: axis?.isVertical ?? false
|
|
||||||
property var axis: null
|
|
||||||
property bool batteryPopupVisible: false
|
property bool batteryPopupVisible: false
|
||||||
property string section: "right"
|
property var popoutTarget: null
|
||||||
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()
|
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
|
visible: true
|
||||||
|
|
||||||
Column {
|
content: Component {
|
||||||
id: batteryColumn
|
Item {
|
||||||
visible: battery.isVertical
|
implicitWidth: battery.isVerticalOrientation ? (battery.widgetThickness - battery.horizontalPadding * 2) : batteryContent.implicitWidth
|
||||||
anchors.centerIn: parent
|
implicitHeight: battery.isVerticalOrientation ? batteryColumn.implicitHeight : (battery.widgetThickness - battery.horizontalPadding * 2)
|
||||||
spacing: 1
|
|
||||||
|
|
||||||
DankIcon {
|
Column {
|
||||||
name: BatteryService.getBatteryIcon()
|
id: batteryColumn
|
||||||
size: Theme.barIconSize(barThickness)
|
visible: battery.isVerticalOrientation
|
||||||
color: {
|
anchors.centerIn: parent
|
||||||
if (!BatteryService.batteryAvailable) {
|
spacing: 1
|
||||||
return Theme.surfaceText
|
|
||||||
|
DankIcon {
|
||||||
|
name: BatteryService.getBatteryIcon()
|
||||||
|
size: Theme.barIconSize(battery.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
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
|
StyledText {
|
||||||
return Theme.error
|
text: BatteryService.batteryLevel.toString()
|
||||||
|
font.pixelSize: Theme.barTextSize(battery.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
visible: BatteryService.batteryAvailable
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BatteryService.isCharging || BatteryService.isPluggedIn) {
|
|
||||||
return Theme.primary
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme.surfaceText
|
|
||||||
}
|
}
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
Row {
|
||||||
text: BatteryService.batteryLevel.toString()
|
id: batteryContent
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
visible: !battery.isVerticalOrientation
|
||||||
font.weight: Font.Medium
|
anchors.centerIn: parent
|
||||||
color: Theme.surfaceText
|
spacing: SettingsData.dankBarNoBackground ? 1 : 2
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
visible: BatteryService.batteryAvailable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
DankIcon {
|
||||||
id: batteryContent
|
name: BatteryService.getBatteryIcon()
|
||||||
visible: !battery.isVertical
|
size: Theme.barIconSize(battery.barThickness, -4)
|
||||||
anchors.centerIn: parent
|
color: {
|
||||||
spacing: SettingsData.dankBarNoBackground ? 1 : 2
|
if (!BatteryService.batteryAvailable) {
|
||||||
|
return Theme.surfaceText;
|
||||||
|
}
|
||||||
|
|
||||||
DankIcon {
|
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
|
||||||
name: BatteryService.getBatteryIcon()
|
return Theme.error;
|
||||||
size: Theme.barIconSize(barThickness, -4)
|
}
|
||||||
color: {
|
|
||||||
if (!BatteryService.batteryAvailable) {
|
if (BatteryService.isCharging || BatteryService.isPluggedIn) {
|
||||||
return Theme.surfaceText;
|
return Theme.primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Theme.surfaceText;
|
||||||
|
}
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
|
StyledText {
|
||||||
return Theme.error;
|
text: `${BatteryService.batteryLevel}%`
|
||||||
|
font.pixelSize: Theme.barTextSize(battery.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: BatteryService.batteryAvailable
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
MouseArea {
|
||||||
id: batteryArea
|
x: -battery.leftMargin
|
||||||
|
y: -battery.topMargin
|
||||||
anchors.fill: parent
|
width: battery.width + battery.leftMargin + battery.rightMargin
|
||||||
hoverEnabled: true
|
height: battery.height + battery.topMargin + battery.bottomMargin
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
acceptedButtons: Qt.LeftButton
|
||||||
onPressed: {
|
onPressed: {
|
||||||
if (popupTarget && popupTarget.setTriggerPosition) {
|
if (popoutTarget && popoutTarget.setTriggerPosition) {
|
||||||
const globalPos = mapToGlobal(0, 0)
|
const globalPos = battery.visualContent.mapToGlobal(0, 0)
|
||||||
const currentScreen = parentScreen || Screen
|
const currentScreen = parentScreen || Screen
|
||||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
|
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, battery.visualWidth)
|
||||||
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
||||||
}
|
}
|
||||||
toggleBatteryPopup();
|
toggleBatteryPopup()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,57 +1,25 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Modules.Plugins
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Item {
|
BasePill {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isActive: false
|
property bool isActive: false
|
||||||
property bool isVertical: axis?.isVertical ?? false
|
|
||||||
property var axis: null
|
|
||||||
property string section: "right"
|
|
||||||
property var clipboardHistoryModal: null
|
property var clipboardHistoryModal: 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 clicked()
|
content: Component {
|
||||||
|
Item {
|
||||||
|
implicitWidth: root.widgetThickness - root.horizontalPadding * 2
|
||||||
|
implicitHeight: root.widgetThickness - root.horizontalPadding * 2
|
||||||
|
|
||||||
width: widgetThickness
|
DankIcon {
|
||||||
height: widgetThickness
|
anchors.centerIn: parent
|
||||||
|
name: "content_paste"
|
||||||
MouseArea {
|
size: Theme.barIconSize(root.barThickness)
|
||||||
id: clipboardArea
|
color: Theme.surfaceText
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
acceptedButtons: Qt.LeftButton
|
|
||||||
onPressed: {
|
|
||||||
root.clicked()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: clipboardContent
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
|
||||||
color: {
|
|
||||||
if (SettingsData.dankBarNoBackground) {
|
|
||||||
return "transparent"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseColor = clipboardArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor
|
|
||||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency)
|
|
||||||
}
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
name: "content_paste"
|
|
||||||
size: Theme.barIconSize(barThickness)
|
|
||||||
color: Theme.surfaceText
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,247 +1,257 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Modules.Plugins
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
BasePill {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isVertical: axis?.isVertical ?? false
|
|
||||||
property var axis: null
|
|
||||||
property bool compactMode: false
|
property bool compactMode: false
|
||||||
property string section: "center"
|
|
||||||
property var popupTarget: null
|
|
||||||
property var parentScreen: null
|
|
||||||
property real barThickness: 48
|
|
||||||
property real widgetThickness: 30
|
|
||||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS
|
|
||||||
|
|
||||||
signal clockClicked
|
signal clockClicked
|
||||||
|
|
||||||
width: isVertical ? widgetThickness : (clockRow.implicitWidth + horizontalPadding * 2)
|
content: Component {
|
||||||
height: isVertical ? (clockColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
|
|
||||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
|
||||||
color: {
|
|
||||||
if (SettingsData.dankBarNoBackground) {
|
|
||||||
return "transparent";
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseColor = clockMouseArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
|
||||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: clockColumn
|
|
||||||
visible: root.isVertical
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: -2
|
|
||||||
|
|
||||||
Row {
|
|
||||||
spacing: 0
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
if (SettingsData.use24HourClock) {
|
|
||||||
return String(systemClock?.date?.getHours()).padStart(2, '0').charAt(0)
|
|
||||||
} else {
|
|
||||||
const hours = systemClock?.date?.getHours()
|
|
||||||
const display = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours
|
|
||||||
return String(display).padStart(2, '0').charAt(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
color: Theme.surfaceText
|
|
||||||
font.weight: Font.Normal
|
|
||||||
width: 9
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
if (SettingsData.use24HourClock) {
|
|
||||||
return String(systemClock?.date?.getHours()).padStart(2, '0').charAt(1)
|
|
||||||
} else {
|
|
||||||
const hours = systemClock?.date?.getHours()
|
|
||||||
const display = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours
|
|
||||||
return String(display).padStart(2, '0').charAt(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
color: Theme.surfaceText
|
|
||||||
font.weight: Font.Normal
|
|
||||||
width: 9
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
spacing: 0
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: String(systemClock?.date?.getMinutes()).padStart(2, '0').charAt(0)
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
color: Theme.surfaceText
|
|
||||||
font.weight: Font.Normal
|
|
||||||
width: 9
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: String(systemClock?.date?.getMinutes()).padStart(2, '0').charAt(1)
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
color: Theme.surfaceText
|
|
||||||
font.weight: Font.Normal
|
|
||||||
width: 9
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
width: 12
|
implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : clockRow.implicitWidth
|
||||||
height: Theme.spacingM
|
implicitHeight: root.isVerticalOrientation ? clockColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2)
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
|
|
||||||
Rectangle {
|
Column {
|
||||||
width: 12
|
id: clockColumn
|
||||||
height: 1
|
visible: root.isVerticalOrientation
|
||||||
color: Theme.outlineButton
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
}
|
spacing: -2
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
spacing: 0
|
spacing: 0
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: {
|
text: {
|
||||||
const locale = Qt.locale()
|
if (SettingsData.use24HourClock) {
|
||||||
const dateFormatShort = locale.dateFormat(Locale.ShortFormat)
|
return String(systemClock?.date?.getHours()).padStart(2, '0').charAt(0)
|
||||||
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M')
|
} else {
|
||||||
const value = dayFirst ? String(systemClock?.date?.getDate()).padStart(2, '0') : String(systemClock?.date?.getMonth() + 1).padStart(2, '0')
|
const hours = systemClock?.date?.getHours()
|
||||||
return value.charAt(0)
|
const display = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours
|
||||||
}
|
return String(display).padStart(2, '0').charAt(0)
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
}
|
||||||
color: Theme.primary
|
}
|
||||||
font.weight: Font.Light
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
width: 9
|
color: Theme.surfaceText
|
||||||
horizontalAlignment: Text.AlignHCenter
|
width: 9
|
||||||
}
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignBottom
|
||||||
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: {
|
text: {
|
||||||
const locale = Qt.locale()
|
if (SettingsData.use24HourClock) {
|
||||||
const dateFormatShort = locale.dateFormat(Locale.ShortFormat)
|
return String(systemClock?.date?.getHours()).padStart(2, '0').charAt(1)
|
||||||
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M')
|
} else {
|
||||||
const value = dayFirst ? String(systemClock?.date?.getDate()).padStart(2, '0') : String(systemClock?.date?.getMonth() + 1).padStart(2, '0')
|
const hours = systemClock?.date?.getHours()
|
||||||
return value.charAt(1)
|
const display = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours
|
||||||
}
|
return String(display).padStart(2, '0').charAt(1)
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
}
|
||||||
color: Theme.primary
|
}
|
||||||
font.weight: Font.Light
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
width: 9
|
color: Theme.surfaceText
|
||||||
horizontalAlignment: Text.AlignHCenter
|
width: 9
|
||||||
}
|
horizontalAlignment: Text.AlignHCenter
|
||||||
}
|
verticalAlignment: Text.AlignBottom
|
||||||
|
}
|
||||||
Row {
|
|
||||||
spacing: 0
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
const locale = Qt.locale()
|
|
||||||
const dateFormatShort = locale.dateFormat(Locale.ShortFormat)
|
|
||||||
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M')
|
|
||||||
const value = dayFirst ? String(systemClock?.date?.getMonth() + 1).padStart(2, '0') : String(systemClock?.date?.getDate()).padStart(2, '0')
|
|
||||||
return value.charAt(0)
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
color: Theme.primary
|
|
||||||
font.weight: Font.Light
|
|
||||||
width: 9
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
const locale = Qt.locale()
|
|
||||||
const dateFormatShort = locale.dateFormat(Locale.ShortFormat)
|
|
||||||
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M')
|
|
||||||
const value = dayFirst ? String(systemClock?.date?.getMonth() + 1).padStart(2, '0') : String(systemClock?.date?.getDate()).padStart(2, '0')
|
|
||||||
return value.charAt(1)
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
color: Theme.primary
|
|
||||||
font.weight: Font.Light
|
|
||||||
width: 9
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
id: clockRow
|
|
||||||
|
|
||||||
visible: !root.isVertical
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: Theme.spacingS
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
const format = SettingsData.use24HourClock ? "HH:mm" : "h:mm AP"
|
|
||||||
return systemClock?.date?.toLocaleTimeString(Qt.locale(), format)
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: "•"
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.outlineButton
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
visible: !SettingsData.clockCompactMode
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
if (SettingsData.clockDateFormat && SettingsData.clockDateFormat.length > 0) {
|
|
||||||
return systemClock?.date?.toLocaleDateString(Qt.locale(), SettingsData.clockDateFormat)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return systemClock?.date?.toLocaleDateString(Qt.locale(), "ddd d")
|
Row {
|
||||||
}
|
spacing: 0
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
visible: !SettingsData.clockCompactMode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SystemClock {
|
StyledText {
|
||||||
id: systemClock
|
text: String(systemClock?.date?.getMinutes()).padStart(2, '0').charAt(0)
|
||||||
precision: SystemClock.Seconds
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
width: 9
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignBottom
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: String(systemClock?.date?.getMinutes()).padStart(2, '0').charAt(1)
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
width: 9
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignBottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
visible: SettingsData.showSeconds
|
||||||
|
spacing: 0
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: String(systemClock?.date?.getSeconds()).padStart(2, '0').charAt(0)
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
width: 9
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignBottom
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: String(systemClock?.date?.getSeconds()).padStart(2, '0').charAt(1)
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
width: 9
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignBottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 12
|
||||||
|
height: Theme.spacingM
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 12
|
||||||
|
height: 1
|
||||||
|
color: Theme.outlineButton
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: 0
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
const locale = Qt.locale()
|
||||||
|
const dateFormatShort = locale.dateFormat(Locale.ShortFormat)
|
||||||
|
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M')
|
||||||
|
const value = dayFirst ? String(systemClock?.date?.getDate()).padStart(2, '0') : String(systemClock?.date?.getMonth() + 1).padStart(2, '0')
|
||||||
|
return value.charAt(0)
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.primary
|
||||||
|
width: 9
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignBottom
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
const locale = Qt.locale()
|
||||||
|
const dateFormatShort = locale.dateFormat(Locale.ShortFormat)
|
||||||
|
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M')
|
||||||
|
const value = dayFirst ? String(systemClock?.date?.getDate()).padStart(2, '0') : String(systemClock?.date?.getMonth() + 1).padStart(2, '0')
|
||||||
|
return value.charAt(1)
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.primary
|
||||||
|
width: 9
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignBottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: 0
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
const locale = Qt.locale()
|
||||||
|
const dateFormatShort = locale.dateFormat(Locale.ShortFormat)
|
||||||
|
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M')
|
||||||
|
const value = dayFirst ? String(systemClock?.date?.getMonth() + 1).padStart(2, '0') : String(systemClock?.date?.getDate()).padStart(2, '0')
|
||||||
|
return value.charAt(0)
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.primary
|
||||||
|
width: 9
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignBottom
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
const locale = Qt.locale()
|
||||||
|
const dateFormatShort = locale.dateFormat(Locale.ShortFormat)
|
||||||
|
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M')
|
||||||
|
const value = dayFirst ? String(systemClock?.date?.getMonth() + 1).padStart(2, '0') : String(systemClock?.date?.getDate()).padStart(2, '0')
|
||||||
|
return value.charAt(1)
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.primary
|
||||||
|
width: 9
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignBottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: clockRow
|
||||||
|
visible: !root.isVerticalOrientation
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: timeText
|
||||||
|
text: {
|
||||||
|
return systemClock?.date?.toLocaleTimeString(Qt.locale(), SettingsData.getEffectiveTimeFormat())
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.baseline: dateText.baseline
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: middleDot
|
||||||
|
text: "•"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.outlineButton
|
||||||
|
anchors.baseline: dateText.baseline
|
||||||
|
visible: !SettingsData.clockCompactMode
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: dateText
|
||||||
|
text: {
|
||||||
|
if (SettingsData.clockDateFormat && SettingsData.clockDateFormat.length > 0) {
|
||||||
|
return systemClock?.date?.toLocaleDateString(Qt.locale(), SettingsData.clockDateFormat)
|
||||||
|
}
|
||||||
|
return systemClock?.date?.toLocaleDateString(Qt.locale(), "ddd d")
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: !SettingsData.clockCompactMode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemClock {
|
||||||
|
id: systemClock
|
||||||
|
precision: SystemClock.Seconds
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: clockMouseArea
|
x: -root.leftMargin
|
||||||
|
y: -root.topMargin
|
||||||
anchors.fill: parent
|
width: root.width + root.leftMargin + root.rightMargin
|
||||||
hoverEnabled: true
|
height: root.height + root.topMargin + root.bottomMargin
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onPressed: {
|
onPressed: {
|
||||||
if (popupTarget && popupTarget.setTriggerPosition) {
|
if (root.popoutTarget && root.popoutTarget.setTriggerPosition) {
|
||||||
const globalPos = mapToGlobal(0, 0)
|
const globalPos = root.visualContent.mapToGlobal(0, 0)
|
||||||
const currentScreen = parentScreen || Screen
|
const currentScreen = root.parentScreen || Screen
|
||||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
|
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, root.barThickness, root.visualWidth)
|
||||||
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
root.popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, root.section, currentScreen)
|
||||||
}
|
}
|
||||||
root.clockClicked()
|
root.clockClicked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,54 +1,35 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Modules.Plugins
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
BasePill {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isVertical: axis?.isVertical ?? false
|
|
||||||
property var axis: null
|
|
||||||
property bool isActive: false
|
property bool isActive: 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 clicked()
|
|
||||||
|
|
||||||
width: isVertical ? widgetThickness : (colorPickerIcon.width + horizontalPadding * 2)
|
|
||||||
height: isVertical ? (colorPickerIcon.height + horizontalPadding * 2) : widgetThickness
|
|
||||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
|
||||||
color: {
|
|
||||||
if (SettingsData.dankBarNoBackground) {
|
|
||||||
return "transparent";
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseColor = colorPickerArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
|
||||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
|
||||||
}
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
id: colorPickerIcon
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
name: "palette"
|
|
||||||
size: Theme.barIconSize(barThickness, -4)
|
|
||||||
color: colorPickerArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: colorPickerArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onPressed: {
|
|
||||||
root.colorPickerRequested();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
signal colorPickerRequested()
|
signal colorPickerRequested()
|
||||||
|
|
||||||
}
|
content: Component {
|
||||||
|
Item {
|
||||||
|
implicitWidth: root.widgetThickness - root.horizontalPadding * 2
|
||||||
|
implicitHeight: root.widgetThickness - root.horizontalPadding * 2
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: "palette"
|
||||||
|
size: Theme.barIconSize(root.barThickness, -4)
|
||||||
|
color: root.isActive ? Theme.primary : Theme.surfaceText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
z: 1
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onPressed: {
|
||||||
|
root.colorPickerRequested()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,268 +1,255 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Modules.Plugins
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
BasePill {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isVertical: axis?.isVertical ?? false
|
|
||||||
property var axis: null
|
|
||||||
property bool isActive: false
|
property bool isActive: false
|
||||||
property string section: "right"
|
property var popoutTarget: null
|
||||||
property var popupTarget: null
|
|
||||||
property var parentScreen: null
|
|
||||||
property var widgetData: null
|
property var widgetData: null
|
||||||
property bool showNetworkIcon: SettingsData.controlCenterShowNetworkIcon
|
property bool showNetworkIcon: SettingsData.controlCenterShowNetworkIcon
|
||||||
property bool showBluetoothIcon: SettingsData.controlCenterShowBluetoothIcon
|
property bool showBluetoothIcon: SettingsData.controlCenterShowBluetoothIcon
|
||||||
property bool showAudioIcon: SettingsData.controlCenterShowAudioIcon
|
property bool showAudioIcon: SettingsData.controlCenterShowAudioIcon
|
||||||
property real widgetThickness: 30
|
|
||||||
property real barThickness: 48
|
|
||||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
|
||||||
|
|
||||||
signal clicked()
|
content: Component {
|
||||||
|
Item {
|
||||||
|
implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : controlIndicators.implicitWidth
|
||||||
|
implicitHeight: root.isVerticalOrientation ? controlColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2)
|
||||||
|
|
||||||
width: isVertical ? widgetThickness : (controlIndicators.implicitWidth + horizontalPadding * 2)
|
Column {
|
||||||
height: isVertical ? (controlColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
|
id: controlColumn
|
||||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
visible: root.isVerticalOrientation
|
||||||
color: {
|
anchors.centerIn: parent
|
||||||
if (SettingsData.dankBarNoBackground) {
|
spacing: Theme.spacingXS
|
||||||
return "transparent";
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseColor = controlCenterArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
DankIcon {
|
||||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
name: {
|
||||||
}
|
if (NetworkService.wifiToggling) {
|
||||||
|
return "sync"
|
||||||
|
}
|
||||||
|
|
||||||
Column {
|
const status = NetworkService.networkStatus
|
||||||
id: controlColumn
|
if (status === "ethernet") {
|
||||||
visible: root.isVertical
|
return "lan"
|
||||||
anchors.centerIn: parent
|
}
|
||||||
spacing: Theme.spacingXS
|
|
||||||
|
|
||||||
DankIcon {
|
if (status === "vpn") {
|
||||||
name: {
|
return NetworkService.ethernetConnected ? "lan" : NetworkService.wifiSignalIcon
|
||||||
if (NetworkService.wifiToggling) {
|
}
|
||||||
return "sync"
|
|
||||||
|
return NetworkService.wifiSignalIcon
|
||||||
|
}
|
||||||
|
size: Theme.barIconSize(root.barThickness)
|
||||||
|
color: {
|
||||||
|
if (NetworkService.wifiToggling) {
|
||||||
|
return Theme.primary
|
||||||
|
}
|
||||||
|
|
||||||
|
return NetworkService.networkStatus !== "disconnected" ? Theme.primary : Theme.outlineButton
|
||||||
|
}
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
visible: root.showNetworkIcon && NetworkService.networkAvailable
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NetworkService.networkStatus === "ethernet") {
|
DankIcon {
|
||||||
return "lan"
|
name: "bluetooth"
|
||||||
|
size: Theme.barIconSize(root.barThickness)
|
||||||
|
color: BluetoothService.connected ? Theme.primary : Theme.outlineButton
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
visible: root.showBluetoothIcon && BluetoothService.available && BluetoothService.enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
return NetworkService.wifiSignalIcon
|
Rectangle {
|
||||||
}
|
width: audioIconV.implicitWidth + 4
|
||||||
size: Theme.barIconSize(barThickness)
|
height: audioIconV.implicitHeight + 4
|
||||||
color: {
|
color: "transparent"
|
||||||
if (NetworkService.wifiToggling) {
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
return Theme.primary
|
visible: root.showAudioIcon
|
||||||
}
|
|
||||||
|
|
||||||
return NetworkService.networkStatus !== "disconnected" ? Theme.primary : Theme.outlineButton
|
DankIcon {
|
||||||
}
|
id: audioIconV
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
visible: root.showNetworkIcon && NetworkService.networkAvailable
|
|
||||||
}
|
|
||||||
|
|
||||||
DankIcon {
|
name: {
|
||||||
name: "bluetooth"
|
if (AudioService.sink && AudioService.sink.audio) {
|
||||||
size: Theme.barIconSize(barThickness)
|
if (AudioService.sink.audio.muted || AudioService.sink.audio.volume === 0) {
|
||||||
color: BluetoothService.enabled ? Theme.primary : Theme.outlineButton
|
return "volume_off"
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
} else if (AudioService.sink.audio.volume * 100 < 33) {
|
||||||
visible: root.showBluetoothIcon && BluetoothService.available && BluetoothService.enabled
|
return "volume_down"
|
||||||
}
|
} else {
|
||||||
|
return "volume_up"
|
||||||
Rectangle {
|
}
|
||||||
width: audioIconV.implicitWidth + 4
|
}
|
||||||
height: audioIconV.implicitHeight + 4
|
|
||||||
color: "transparent"
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
visible: root.showAudioIcon
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
id: audioIconV
|
|
||||||
|
|
||||||
name: {
|
|
||||||
if (AudioService.sink && AudioService.sink.audio) {
|
|
||||||
if (AudioService.sink.audio.muted || AudioService.sink.audio.volume === 0) {
|
|
||||||
return "volume_off"
|
|
||||||
} else if (AudioService.sink.audio.volume * 100 < 33) {
|
|
||||||
return "volume_down"
|
|
||||||
} else {
|
|
||||||
return "volume_up"
|
return "volume_up"
|
||||||
}
|
}
|
||||||
|
size: Theme.barIconSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.centerIn: parent
|
||||||
}
|
}
|
||||||
return "volume_up"
|
|
||||||
}
|
|
||||||
size: Theme.barIconSize(barThickness)
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
acceptedButtons: Qt.NoButton
|
||||||
acceptedButtons: Qt.NoButton
|
onWheel: function(wheelEvent) {
|
||||||
onWheel: function(wheelEvent) {
|
let delta = wheelEvent.angleDelta.y
|
||||||
let delta = wheelEvent.angleDelta.y
|
let currentVolume = (AudioService.sink && AudioService.sink.audio && AudioService.sink.audio.volume * 100) || 0
|
||||||
let currentVolume = (AudioService.sink && AudioService.sink.audio && AudioService.sink.audio.volume * 100) || 0
|
let newVolume
|
||||||
let newVolume
|
if (delta > 0) {
|
||||||
if (delta > 0) {
|
newVolume = Math.min(100, currentVolume + 5)
|
||||||
newVolume = Math.min(100, currentVolume + 5)
|
} else {
|
||||||
} else {
|
newVolume = Math.max(0, currentVolume - 5)
|
||||||
newVolume = Math.max(0, currentVolume - 5)
|
}
|
||||||
}
|
if (AudioService.sink && AudioService.sink.audio) {
|
||||||
if (AudioService.sink && AudioService.sink.audio) {
|
AudioService.sink.audio.muted = false
|
||||||
AudioService.sink.audio.muted = false
|
AudioService.sink.audio.volume = newVolume / 100
|
||||||
AudioService.sink.audio.volume = newVolume / 100
|
}
|
||||||
AudioService.volumeChanged()
|
wheelEvent.accepted = true
|
||||||
}
|
|
||||||
wheelEvent.accepted = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "settings"
|
|
||||||
size: Theme.barIconSize(barThickness)
|
|
||||||
color: controlCenterArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
visible: !root.showNetworkIcon && !root.showBluetoothIcon && !root.showAudioIcon
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
id: controlIndicators
|
|
||||||
visible: !root.isVertical
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: Theme.spacingXS
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
id: networkIcon
|
|
||||||
|
|
||||||
name: {
|
|
||||||
if (NetworkService.wifiToggling) {
|
|
||||||
return "sync";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NetworkService.networkStatus === "ethernet") {
|
|
||||||
return "lan";
|
|
||||||
}
|
|
||||||
|
|
||||||
return NetworkService.wifiSignalIcon;
|
|
||||||
}
|
|
||||||
size: Theme.barIconSize(barThickness)
|
|
||||||
color: {
|
|
||||||
if (NetworkService.wifiToggling) {
|
|
||||||
return Theme.primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NetworkService.networkStatus !== "disconnected" ? Theme.primary : Theme.outlineButton;
|
|
||||||
}
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
visible: root.showNetworkIcon && NetworkService.networkAvailable
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
id: bluetoothIcon
|
|
||||||
|
|
||||||
name: "bluetooth"
|
|
||||||
size: Theme.barIconSize(barThickness)
|
|
||||||
color: BluetoothService.enabled ? Theme.primary : Theme.outlineButton
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
visible: root.showBluetoothIcon && BluetoothService.available && BluetoothService.enabled
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: audioIcon.implicitWidth + 4
|
|
||||||
height: audioIcon.implicitHeight + 4
|
|
||||||
color: "transparent"
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
visible: root.showAudioIcon
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
id: audioIcon
|
|
||||||
|
|
||||||
name: {
|
|
||||||
if (AudioService.sink && AudioService.sink.audio) {
|
|
||||||
if (AudioService.sink.audio.muted || AudioService.sink.audio.volume === 0) {
|
|
||||||
return "volume_off";
|
|
||||||
} else if (AudioService.sink.audio.volume * 100 < 33) {
|
|
||||||
return "volume_down";
|
|
||||||
} else {
|
|
||||||
return "volume_up";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "volume_up";
|
|
||||||
}
|
}
|
||||||
size: Theme.barIconSize(barThickness)
|
|
||||||
color: Theme.surfaceText
|
DankIcon {
|
||||||
|
name: "settings"
|
||||||
|
size: Theme.barIconSize(root.barThickness)
|
||||||
|
color: root.isActive ? Theme.primary : Theme.surfaceText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
visible: !root.showNetworkIcon && !root.showBluetoothIcon && !root.showAudioIcon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: controlIndicators
|
||||||
|
visible: !root.isVerticalOrientation
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
}
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
MouseArea {
|
DankIcon {
|
||||||
id: audioWheelArea
|
id: networkIcon
|
||||||
|
|
||||||
anchors.fill: parent
|
name: {
|
||||||
hoverEnabled: true
|
if (NetworkService.wifiToggling) {
|
||||||
acceptedButtons: Qt.NoButton
|
return "sync"
|
||||||
onWheel: function(wheelEvent) {
|
}
|
||||||
let delta = wheelEvent.angleDelta.y;
|
|
||||||
let currentVolume = (AudioService.sink && AudioService.sink.audio && AudioService.sink.audio.volume * 100) || 0;
|
const status = NetworkService.networkStatus
|
||||||
let newVolume;
|
if (status === "ethernet") {
|
||||||
if (delta > 0) {
|
return "lan"
|
||||||
newVolume = Math.min(100, currentVolume + 5);
|
}
|
||||||
} else {
|
|
||||||
newVolume = Math.max(0, currentVolume - 5);
|
if (status === "vpn") {
|
||||||
|
return NetworkService.ethernetConnected ? "lan" : NetworkService.wifiSignalIcon
|
||||||
|
}
|
||||||
|
|
||||||
|
return NetworkService.wifiSignalIcon
|
||||||
}
|
}
|
||||||
if (AudioService.sink && AudioService.sink.audio) {
|
size: Theme.barIconSize(root.barThickness)
|
||||||
AudioService.sink.audio.muted = false;
|
color: {
|
||||||
AudioService.sink.audio.volume = newVolume / 100;
|
if (NetworkService.wifiToggling) {
|
||||||
AudioService.volumeChanged();
|
return Theme.primary
|
||||||
|
}
|
||||||
|
|
||||||
|
return NetworkService.networkStatus !== "disconnected" ? Theme.primary : Theme.outlineButton
|
||||||
}
|
}
|
||||||
wheelEvent.accepted = true;
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: root.showNetworkIcon && NetworkService.networkAvailable
|
||||||
|
}
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
id: bluetoothIcon
|
||||||
|
|
||||||
|
name: "bluetooth"
|
||||||
|
size: Theme.barIconSize(root.barThickness)
|
||||||
|
color: BluetoothService.connected ? Theme.primary : Theme.outlineButton
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: root.showBluetoothIcon && BluetoothService.available && BluetoothService.enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: audioIcon.implicitWidth + 4
|
||||||
|
height: audioIcon.implicitHeight + 4
|
||||||
|
color: "transparent"
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: root.showAudioIcon
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
id: audioIcon
|
||||||
|
|
||||||
|
name: {
|
||||||
|
if (AudioService.sink && AudioService.sink.audio) {
|
||||||
|
if (AudioService.sink.audio.muted || AudioService.sink.audio.volume === 0) {
|
||||||
|
return "volume_off";
|
||||||
|
} else if (AudioService.sink.audio.volume * 100 < 33) {
|
||||||
|
return "volume_down";
|
||||||
|
} else {
|
||||||
|
return "volume_up";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "volume_up";
|
||||||
|
}
|
||||||
|
size: Theme.barIconSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: audioWheelArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.NoButton
|
||||||
|
onWheel: function(wheelEvent) {
|
||||||
|
let delta = wheelEvent.angleDelta.y;
|
||||||
|
let currentVolume = (AudioService.sink && AudioService.sink.audio && AudioService.sink.audio.volume * 100) || 0;
|
||||||
|
let newVolume;
|
||||||
|
if (delta > 0) {
|
||||||
|
newVolume = Math.min(100, currentVolume + 5);
|
||||||
|
} else {
|
||||||
|
newVolume = Math.max(0, currentVolume - 5);
|
||||||
|
}
|
||||||
|
if (AudioService.sink && AudioService.sink.audio) {
|
||||||
|
AudioService.sink.audio.muted = false;
|
||||||
|
AudioService.sink.audio.volume = newVolume / 100;
|
||||||
|
}
|
||||||
|
wheelEvent.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "mic"
|
||||||
|
size: Theme.barIconSize(root.barThickness)
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "settings"
|
||||||
|
size: Theme.barIconSize(root.barThickness)
|
||||||
|
color: root.isActive ? Theme.primary : Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: !root.showNetworkIcon && !root.showBluetoothIcon && !root.showAudioIcon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "mic"
|
|
||||||
size: Theme.barIconSize(barThickness)
|
|
||||||
color: Theme.primary
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
visible: false // TODO: Add mic detection
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback settings icon when all other icons are hidden
|
|
||||||
DankIcon {
|
|
||||||
name: "settings"
|
|
||||||
size: Theme.barIconSize(barThickness)
|
|
||||||
color: controlCenterArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
visible: !root.showNetworkIcon && !root.showBluetoothIcon && !root.showAudioIcon
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: controlCenterArea
|
x: -root.leftMargin
|
||||||
|
y: -root.topMargin
|
||||||
anchors.fill: parent
|
width: root.width + root.leftMargin + root.rightMargin
|
||||||
hoverEnabled: true
|
height: root.height + root.topMargin + root.bottomMargin
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
acceptedButtons: Qt.LeftButton
|
||||||
onPressed: {
|
onPressed: {
|
||||||
if (popupTarget && popupTarget.setTriggerPosition) {
|
if (popoutTarget && popoutTarget.setTriggerPosition) {
|
||||||
const globalPos = mapToGlobal(0, 0)
|
const globalPos = root.visualContent.mapToGlobal(0, 0)
|
||||||
const currentScreen = parentScreen || Screen
|
const currentScreen = parentScreen || Screen
|
||||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
|
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth)
|
||||||
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
||||||
}
|
}
|
||||||
root.clicked();
|
root.clicked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,20 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Modules.Plugins
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
BasePill {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isVertical: axis?.isVertical ?? false
|
|
||||||
property var axis: null
|
|
||||||
property bool showPercentage: true
|
property bool showPercentage: true
|
||||||
property bool showIcon: true
|
property bool showIcon: true
|
||||||
property var toggleProcessList
|
property var toggleProcessList
|
||||||
property string section: "right"
|
property var popoutTarget: null
|
||||||
property var popupTarget: null
|
|
||||||
property var parentScreen: null
|
|
||||||
property real barThickness: 48
|
|
||||||
property real widgetThickness: 30
|
|
||||||
property var widgetData: null
|
property var widgetData: null
|
||||||
property bool minimumWidth: (widgetData && widgetData.minimumWidth !== undefined) ? widgetData.minimumWidth : true
|
property bool minimumWidth: (widgetData && widgetData.minimumWidth !== undefined) ? widgetData.minimumWidth : true
|
||||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
|
||||||
|
|
||||||
width: isVertical ? widgetThickness : (cpuContent.implicitWidth + horizontalPadding * 2)
|
|
||||||
height: isVertical ? (cpuColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
|
|
||||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
|
||||||
color: {
|
|
||||||
if (SettingsData.dankBarNoBackground) {
|
|
||||||
return "transparent";
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseColor = cpuArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
|
||||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
|
||||||
}
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
DgopService.addRef(["cpu"]);
|
DgopService.addRef(["cpu"]);
|
||||||
}
|
}
|
||||||
@@ -39,120 +22,119 @@ Rectangle {
|
|||||||
DgopService.removeRef(["cpu"]);
|
DgopService.removeRef(["cpu"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
content: Component {
|
||||||
id: cpuArea
|
Item {
|
||||||
|
implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : cpuContent.implicitWidth
|
||||||
|
implicitHeight: root.isVerticalOrientation ? cpuColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2)
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: cpuColumn
|
||||||
|
visible: root.isVerticalOrientation
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 1
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "memory"
|
||||||
|
size: Theme.barIconSize(root.barThickness)
|
||||||
|
color: {
|
||||||
|
if (DgopService.cpuUsage > 80) {
|
||||||
|
return Theme.tempDanger;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DgopService.cpuUsage > 60) {
|
||||||
|
return Theme.tempWarning;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Theme.surfaceText;
|
||||||
|
}
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
if (DgopService.cpuUsage === undefined || DgopService.cpuUsage === null || DgopService.cpuUsage === 0) {
|
||||||
|
return "--";
|
||||||
|
}
|
||||||
|
|
||||||
|
return DgopService.cpuUsage.toFixed(0);
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: cpuContent
|
||||||
|
visible: !root.isVerticalOrientation
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 3
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "memory"
|
||||||
|
size: Theme.barIconSize(root.barThickness)
|
||||||
|
color: {
|
||||||
|
if (DgopService.cpuUsage > 80) {
|
||||||
|
return Theme.tempDanger;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DgopService.cpuUsage > 60) {
|
||||||
|
return Theme.tempWarning;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Theme.surfaceText;
|
||||||
|
}
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
if (DgopService.cpuUsage === undefined || DgopService.cpuUsage === null || DgopService.cpuUsage === 0) {
|
||||||
|
return "--%";
|
||||||
|
}
|
||||||
|
|
||||||
|
return DgopService.cpuUsage.toFixed(0) + "%";
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
elide: Text.ElideNone
|
||||||
|
|
||||||
|
StyledTextMetrics {
|
||||||
|
id: cpuBaseline
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
text: "100%"
|
||||||
|
}
|
||||||
|
|
||||||
|
width: root.minimumWidth ? Math.max(cpuBaseline.width, paintedWidth) : paintedWidth
|
||||||
|
|
||||||
|
Behavior on width {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 120
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
acceptedButtons: Qt.LeftButton
|
||||||
onPressed: {
|
onPressed: {
|
||||||
if (popupTarget && popupTarget.setTriggerPosition) {
|
if (popoutTarget && popoutTarget.setTriggerPosition) {
|
||||||
const globalPos = mapToGlobal(0, 0)
|
const globalPos = root.visualContent.mapToGlobal(0, 0)
|
||||||
const currentScreen = parentScreen || Screen
|
const currentScreen = parentScreen || Screen
|
||||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
|
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth)
|
||||||
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
||||||
}
|
}
|
||||||
DgopService.setSortBy("cpu");
|
DgopService.setSortBy("cpu");
|
||||||
if (root.toggleProcessList) {
|
if (root.toggleProcessList) {
|
||||||
root.toggleProcessList();
|
root.toggleProcessList();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
|
||||||
id: cpuColumn
|
|
||||||
visible: root.isVertical
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 1
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "memory"
|
|
||||||
size: Theme.barIconSize(barThickness)
|
|
||||||
color: {
|
|
||||||
if (DgopService.cpuUsage > 80) {
|
|
||||||
return Theme.tempDanger;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DgopService.cpuUsage > 60) {
|
|
||||||
return Theme.tempWarning;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme.surfaceText;
|
|
||||||
}
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
if (DgopService.cpuUsage === undefined || DgopService.cpuUsage === null || DgopService.cpuUsage === 0) {
|
|
||||||
return "--";
|
|
||||||
}
|
|
||||||
|
|
||||||
return DgopService.cpuUsage.toFixed(0);
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
id: cpuContent
|
|
||||||
visible: !root.isVertical
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 3
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "memory"
|
|
||||||
size: Theme.barIconSize(barThickness)
|
|
||||||
color: {
|
|
||||||
if (DgopService.cpuUsage > 80) {
|
|
||||||
return Theme.tempDanger;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DgopService.cpuUsage > 60) {
|
|
||||||
return Theme.tempWarning;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme.surfaceText;
|
|
||||||
}
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
if (DgopService.cpuUsage === undefined || DgopService.cpuUsage === null || DgopService.cpuUsage === 0) {
|
|
||||||
return "--%";
|
|
||||||
}
|
|
||||||
|
|
||||||
return DgopService.cpuUsage.toFixed(0) + "%";
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
elide: Text.ElideNone
|
|
||||||
|
|
||||||
StyledTextMetrics {
|
|
||||||
id: cpuBaseline
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
text: "100%"
|
|
||||||
}
|
|
||||||
|
|
||||||
width: root.minimumWidth ? Math.max(cpuBaseline.width, paintedWidth) : paintedWidth
|
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 120
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,20 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Modules.Plugins
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
BasePill {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isVertical: axis?.isVertical ?? false
|
|
||||||
property var axis: null
|
|
||||||
property bool showPercentage: true
|
property bool showPercentage: true
|
||||||
property bool showIcon: true
|
property bool showIcon: true
|
||||||
property var toggleProcessList
|
property var toggleProcessList
|
||||||
property string section: "right"
|
property var popoutTarget: null
|
||||||
property var popupTarget: null
|
|
||||||
property var parentScreen: null
|
|
||||||
property real barThickness: 48
|
|
||||||
property real widgetThickness: 30
|
|
||||||
property var widgetData: null
|
property var widgetData: null
|
||||||
property bool minimumWidth: (widgetData && widgetData.minimumWidth !== undefined) ? widgetData.minimumWidth : true
|
property bool minimumWidth: (widgetData && widgetData.minimumWidth !== undefined) ? widgetData.minimumWidth : true
|
||||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
|
||||||
|
|
||||||
width: isVertical ? widgetThickness : (cpuTempContent.implicitWidth + horizontalPadding * 2)
|
|
||||||
height: isVertical ? (cpuTempColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
|
|
||||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
|
||||||
color: {
|
|
||||||
if (SettingsData.dankBarNoBackground) {
|
|
||||||
return "transparent";
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseColor = cpuTempArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
|
||||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
|
||||||
}
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
DgopService.addRef(["cpu"]);
|
DgopService.addRef(["cpu"]);
|
||||||
}
|
}
|
||||||
@@ -39,121 +22,119 @@ Rectangle {
|
|||||||
DgopService.removeRef(["cpu"]);
|
DgopService.removeRef(["cpu"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
content: Component {
|
||||||
id: cpuTempArea
|
Item {
|
||||||
|
implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : cpuTempContent.implicitWidth
|
||||||
|
implicitHeight: root.isVerticalOrientation ? cpuTempColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2)
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: cpuTempColumn
|
||||||
|
visible: root.isVerticalOrientation
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 1
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "device_thermostat"
|
||||||
|
size: Theme.barIconSize(root.barThickness)
|
||||||
|
color: {
|
||||||
|
if (DgopService.cpuTemperature > 85) {
|
||||||
|
return Theme.tempDanger;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DgopService.cpuTemperature > 69) {
|
||||||
|
return Theme.tempWarning;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Theme.surfaceText;
|
||||||
|
}
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
if (DgopService.cpuTemperature === undefined || DgopService.cpuTemperature === null || DgopService.cpuTemperature < 0) {
|
||||||
|
return "--";
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.round(DgopService.cpuTemperature).toString();
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: cpuTempContent
|
||||||
|
visible: !root.isVerticalOrientation
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 3
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "device_thermostat"
|
||||||
|
size: Theme.barIconSize(root.barThickness)
|
||||||
|
color: {
|
||||||
|
if (DgopService.cpuTemperature > 85) {
|
||||||
|
return Theme.tempDanger;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DgopService.cpuTemperature > 69) {
|
||||||
|
return Theme.tempWarning;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Theme.surfaceText;
|
||||||
|
}
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
if (DgopService.cpuTemperature === undefined || DgopService.cpuTemperature === null || DgopService.cpuTemperature < 0) {
|
||||||
|
return "--°";
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.round(DgopService.cpuTemperature) + "°";
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
elide: Text.ElideNone
|
||||||
|
|
||||||
|
StyledTextMetrics {
|
||||||
|
id: tempBaseline
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
text: "100°"
|
||||||
|
}
|
||||||
|
|
||||||
|
width: root.minimumWidth ? Math.max(tempBaseline.width, paintedWidth) : paintedWidth
|
||||||
|
|
||||||
|
Behavior on width {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 120
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
acceptedButtons: Qt.LeftButton
|
||||||
onPressed: {
|
onPressed: {
|
||||||
if (popupTarget && popupTarget.setTriggerPosition) {
|
if (popoutTarget && popoutTarget.setTriggerPosition) {
|
||||||
const globalPos = mapToGlobal(0, 0)
|
const globalPos = root.visualContent.mapToGlobal(0, 0)
|
||||||
const currentScreen = parentScreen || Screen
|
const currentScreen = parentScreen || Screen
|
||||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
|
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth)
|
||||||
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
||||||
}
|
}
|
||||||
DgopService.setSortBy("cpu");
|
DgopService.setSortBy("cpu");
|
||||||
if (root.toggleProcessList) {
|
if (root.toggleProcessList) {
|
||||||
root.toggleProcessList();
|
root.toggleProcessList();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
|
||||||
id: cpuTempColumn
|
|
||||||
visible: root.isVertical
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 1
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "device_thermostat"
|
|
||||||
size: Theme.barIconSize(barThickness)
|
|
||||||
color: {
|
|
||||||
if (DgopService.cpuTemperature > 85) {
|
|
||||||
return Theme.tempDanger;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DgopService.cpuTemperature > 69) {
|
|
||||||
return Theme.tempWarning;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme.surfaceText;
|
|
||||||
}
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
if (DgopService.cpuTemperature === undefined || DgopService.cpuTemperature === null || DgopService.cpuTemperature < 0) {
|
|
||||||
return "--";
|
|
||||||
}
|
|
||||||
|
|
||||||
return Math.round(DgopService.cpuTemperature).toString();
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
id: cpuTempContent
|
|
||||||
visible: !root.isVertical
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 3
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "device_thermostat"
|
|
||||||
size: Theme.barIconSize(barThickness)
|
|
||||||
color: {
|
|
||||||
if (DgopService.cpuTemperature > 85) {
|
|
||||||
return Theme.tempDanger;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DgopService.cpuTemperature > 69) {
|
|
||||||
return Theme.tempWarning;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme.surfaceText;
|
|
||||||
}
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
if (DgopService.cpuTemperature === undefined || DgopService.cpuTemperature === null || DgopService.cpuTemperature < 0) {
|
|
||||||
return "--°";
|
|
||||||
}
|
|
||||||
|
|
||||||
return Math.round(DgopService.cpuTemperature) + "°";
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
elide: Text.ElideNone
|
|
||||||
|
|
||||||
StyledTextMetrics {
|
|
||||||
id: tempBaseline
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
text: "100°"
|
|
||||||
}
|
|
||||||
|
|
||||||
width: root.minimumWidth ? Math.max(tempBaseline.width, paintedWidth) : paintedWidth
|
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 120
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,36 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Modules.Plugins
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
BasePill {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isVertical: axis?.isVertical ?? false
|
|
||||||
property var axis: null
|
|
||||||
property var widgetData: null
|
property var widgetData: null
|
||||||
property var parentScreen: null
|
|
||||||
property real widgetThickness: 30
|
|
||||||
property real barThickness: 48
|
|
||||||
property string mountPath: (widgetData && widgetData.mountPath !== undefined) ? widgetData.mountPath : "/"
|
property string mountPath: (widgetData && widgetData.mountPath !== undefined) ? widgetData.mountPath : "/"
|
||||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
property bool isHovered: mouseArea.containsMouse
|
||||||
|
|
||||||
property var selectedMount: {
|
property var selectedMount: {
|
||||||
if (!DgopService.diskMounts || DgopService.diskMounts.length === 0) {
|
if (!DgopService.diskMounts || DgopService.diskMounts.length === 0) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force re-evaluation when mountPath changes
|
|
||||||
const currentMountPath = root.mountPath || "/"
|
const currentMountPath = root.mountPath || "/"
|
||||||
|
|
||||||
// First try to find exact match
|
|
||||||
for (let i = 0; i < DgopService.diskMounts.length; i++) {
|
for (let i = 0; i < DgopService.diskMounts.length; i++) {
|
||||||
if (DgopService.diskMounts[i].mount === currentMountPath) {
|
if (DgopService.diskMounts[i].mount === currentMountPath) {
|
||||||
return DgopService.diskMounts[i]
|
return DgopService.diskMounts[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to root
|
|
||||||
for (let i = 0; i < DgopService.diskMounts.length; i++) {
|
for (let i = 0; i < DgopService.diskMounts.length; i++) {
|
||||||
if (DgopService.diskMounts[i].mount === "/") {
|
if (DgopService.diskMounts[i].mount === "/") {
|
||||||
return DgopService.diskMounts[i]
|
return DgopService.diskMounts[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Last resort - first mount
|
|
||||||
return DgopService.diskMounts[0] || null
|
return DgopService.diskMounts[0] || null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,17 +42,6 @@ Rectangle {
|
|||||||
return parseFloat(percentStr) || 0
|
return parseFloat(percentStr) || 0
|
||||||
}
|
}
|
||||||
|
|
||||||
width: isVertical ? widgetThickness : (diskContent.implicitWidth + horizontalPadding * 2)
|
|
||||||
height: isVertical ? (diskColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
|
|
||||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
|
||||||
color: {
|
|
||||||
if (SettingsData.dankBarNoBackground) {
|
|
||||||
return "transparent"
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseColor = Theme.widgetBaseBackgroundColor
|
|
||||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency)
|
|
||||||
}
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
DgopService.addRef(["diskmounts"])
|
DgopService.addRef(["diskmounts"])
|
||||||
}
|
}
|
||||||
@@ -70,7 +51,6 @@ Rectangle {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onWidgetDataChanged() {
|
function onWidgetDataChanged() {
|
||||||
// Force property re-evaluation by triggering change detection
|
|
||||||
root.mountPath = Qt.binding(() => {
|
root.mountPath = Qt.binding(() => {
|
||||||
return (root.widgetData && root.widgetData.mountPath !== undefined) ? root.widgetData.mountPath : "/"
|
return (root.widgetData && root.widgetData.mountPath !== undefined) ? root.widgetData.mountPath : "/"
|
||||||
})
|
})
|
||||||
@@ -82,21 +62,18 @@ Rectangle {
|
|||||||
|
|
||||||
const currentMountPath = root.mountPath || "/"
|
const currentMountPath = root.mountPath || "/"
|
||||||
|
|
||||||
// First try to find exact match
|
|
||||||
for (let i = 0; i < DgopService.diskMounts.length; i++) {
|
for (let i = 0; i < DgopService.diskMounts.length; i++) {
|
||||||
if (DgopService.diskMounts[i].mount === currentMountPath) {
|
if (DgopService.diskMounts[i].mount === currentMountPath) {
|
||||||
return DgopService.diskMounts[i]
|
return DgopService.diskMounts[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to root
|
|
||||||
for (let i = 0; i < DgopService.diskMounts.length; i++) {
|
for (let i = 0; i < DgopService.diskMounts.length; i++) {
|
||||||
if (DgopService.diskMounts[i].mount === "/") {
|
if (DgopService.diskMounts[i].mount === "/") {
|
||||||
return DgopService.diskMounts[i]
|
return DgopService.diskMounts[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Last resort - first mount
|
|
||||||
return DgopService.diskMounts[0] || null
|
return DgopService.diskMounts[0] || null
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -104,6 +81,112 @@ Rectangle {
|
|||||||
target: SettingsData
|
target: SettingsData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
content: Component {
|
||||||
|
Item {
|
||||||
|
implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : diskContent.implicitWidth
|
||||||
|
implicitHeight: root.isVerticalOrientation ? diskColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2)
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: diskColumn
|
||||||
|
visible: root.isVerticalOrientation
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 1
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "storage"
|
||||||
|
size: Theme.barIconSize(root.barThickness)
|
||||||
|
color: {
|
||||||
|
if (root.diskUsagePercent > 90) {
|
||||||
|
return Theme.tempDanger
|
||||||
|
}
|
||||||
|
if (root.diskUsagePercent > 75) {
|
||||||
|
return Theme.tempWarning
|
||||||
|
}
|
||||||
|
return Theme.surfaceText
|
||||||
|
}
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
if (root.diskUsagePercent === undefined || root.diskUsagePercent === null || root.diskUsagePercent === 0) {
|
||||||
|
return "--"
|
||||||
|
}
|
||||||
|
return root.diskUsagePercent.toFixed(0)
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: diskContent
|
||||||
|
visible: !root.isVerticalOrientation
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 3
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "storage"
|
||||||
|
size: Theme.barIconSize(root.barThickness)
|
||||||
|
color: {
|
||||||
|
if (root.diskUsagePercent > 90) {
|
||||||
|
return Theme.tempDanger
|
||||||
|
}
|
||||||
|
if (root.diskUsagePercent > 75) {
|
||||||
|
return Theme.tempWarning
|
||||||
|
}
|
||||||
|
return Theme.surfaceText
|
||||||
|
}
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
if (!root.selectedMount) {
|
||||||
|
return "--"
|
||||||
|
}
|
||||||
|
return root.selectedMount.mount
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
elide: Text.ElideNone
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
if (root.diskUsagePercent === undefined || root.diskUsagePercent === null || root.diskUsagePercent === 0) {
|
||||||
|
return "--%"
|
||||||
|
}
|
||||||
|
return root.diskUsagePercent.toFixed(0) + "%"
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
elide: Text.ElideNone
|
||||||
|
|
||||||
|
StyledTextMetrics {
|
||||||
|
id: diskBaseline
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
text: "100%"
|
||||||
|
}
|
||||||
|
|
||||||
|
width: Math.max(diskBaseline.width, paintedWidth)
|
||||||
|
|
||||||
|
Behavior on width {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 120
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: tooltipLoader
|
id: tooltipLoader
|
||||||
active: false
|
active: false
|
||||||
@@ -111,11 +194,12 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: diskArea
|
id: mouseArea
|
||||||
|
z: 1
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: root.isVertical
|
hoverEnabled: root.isVerticalOrientation
|
||||||
onEntered: {
|
onEntered: {
|
||||||
if (root.isVertical && root.selectedMount) {
|
if (root.isVerticalOrientation && root.selectedMount) {
|
||||||
tooltipLoader.active = true
|
tooltipLoader.active = true
|
||||||
if (tooltipLoader.item) {
|
if (tooltipLoader.item) {
|
||||||
const globalPos = mapToGlobal(width / 2, height / 2)
|
const globalPos = mapToGlobal(width / 2, height / 2)
|
||||||
@@ -136,107 +220,4 @@ Rectangle {
|
|||||||
tooltipLoader.active = false
|
tooltipLoader.active = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Column {
|
|
||||||
id: diskColumn
|
|
||||||
visible: root.isVertical
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 1
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "storage"
|
|
||||||
size: Theme.barIconSize(barThickness)
|
|
||||||
color: {
|
|
||||||
if (root.diskUsagePercent > 90) {
|
|
||||||
return Theme.tempDanger
|
|
||||||
}
|
|
||||||
if (root.diskUsagePercent > 75) {
|
|
||||||
return Theme.tempWarning
|
|
||||||
}
|
|
||||||
return Theme.surfaceText
|
|
||||||
}
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
if (root.diskUsagePercent === undefined || root.diskUsagePercent === null || root.diskUsagePercent === 0) {
|
|
||||||
return "--"
|
|
||||||
}
|
|
||||||
return root.diskUsagePercent.toFixed(0)
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
id: diskContent
|
|
||||||
visible: !root.isVertical
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 3
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "storage"
|
|
||||||
size: Theme.barIconSize(barThickness)
|
|
||||||
color: {
|
|
||||||
if (root.diskUsagePercent > 90) {
|
|
||||||
return Theme.tempDanger
|
|
||||||
}
|
|
||||||
if (root.diskUsagePercent > 75) {
|
|
||||||
return Theme.tempWarning
|
|
||||||
}
|
|
||||||
return Theme.surfaceText
|
|
||||||
}
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
if (!root.selectedMount) {
|
|
||||||
return "--"
|
|
||||||
}
|
|
||||||
return root.selectedMount.mount
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
elide: Text.ElideNone
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
if (root.diskUsagePercent === undefined || root.diskUsagePercent === null || root.diskUsagePercent === 0) {
|
|
||||||
return "--%"
|
|
||||||
}
|
|
||||||
return root.diskUsagePercent.toFixed(0) + "%"
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
elide: Text.ElideNone
|
|
||||||
|
|
||||||
StyledTextMetrics {
|
|
||||||
id: diskBaseline
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
text: "100%"
|
|
||||||
}
|
|
||||||
|
|
||||||
width: Math.max(diskBaseline.width, paintedWidth)
|
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 120
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,25 +4,20 @@ import Quickshell.Wayland
|
|||||||
import Quickshell.Widgets
|
import Quickshell.Widgets
|
||||||
import Quickshell.Hyprland
|
import Quickshell.Hyprland
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Modules.Plugins
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
BasePill {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isVertical: axis?.isVertical ?? false
|
|
||||||
property var axis: null
|
|
||||||
property var parentScreen
|
|
||||||
property bool compactMode: SettingsData.focusedWindowCompactMode
|
property bool compactMode: SettingsData.focusedWindowCompactMode
|
||||||
property int availableWidth: 400
|
property int availableWidth: 400
|
||||||
property real widgetThickness: 30
|
|
||||||
property real barThickness: 48
|
|
||||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS
|
|
||||||
readonly property int baseWidth: contentRow.implicitWidth + horizontalPadding * 2
|
|
||||||
readonly property int maxNormalWidth: 456
|
readonly property int maxNormalWidth: 456
|
||||||
readonly property int maxCompactWidth: 288
|
readonly property int maxCompactWidth: 288
|
||||||
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
||||||
property var activeDesktopEntry: null
|
property var activeDesktopEntry: null
|
||||||
|
property bool isHovered: mouseArea.containsMouse
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
updateDesktopEntry()
|
updateDesktopEntry()
|
||||||
@@ -74,161 +69,157 @@ Rectangle {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const hyprlandToplevels = Array.from(Hyprland.toplevels.values)
|
try {
|
||||||
const activeHyprToplevel = hyprlandToplevels.find(t => t.wayland === activeWindow)
|
if (!Hyprland.toplevels) return false
|
||||||
|
const hyprlandToplevels = Array.from(Hyprland.toplevels.values)
|
||||||
|
const activeHyprToplevel = hyprlandToplevels.find(t => t?.wayland === activeWindow)
|
||||||
|
|
||||||
if (!activeHyprToplevel || !activeHyprToplevel.workspace) {
|
if (!activeHyprToplevel || !activeHyprToplevel.workspace) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return activeHyprToplevel.workspace.id === Hyprland.focusedWorkspace.id
|
||||||
|
} catch (e) {
|
||||||
|
console.error("FocusedApp: hasWindowsOnCurrentWorkspace error:", e)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return activeHyprToplevel.workspace.id === Hyprland.focusedWorkspace.id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return activeWindow && activeWindow.title
|
return activeWindow && activeWindow.title
|
||||||
}
|
}
|
||||||
|
|
||||||
width: !hasWindowsOnCurrentWorkspace ? 0 : (isVertical ? widgetThickness : (compactMode ? Math.min(baseWidth, maxCompactWidth) : Math.min(baseWidth, maxNormalWidth)))
|
|
||||||
height: !hasWindowsOnCurrentWorkspace ? 0 : (isVertical ? widgetThickness : widgetThickness)
|
|
||||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
|
||||||
color: {
|
|
||||||
if (!activeWindow || !activeWindow.title) {
|
|
||||||
return "transparent";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SettingsData.dankBarNoBackground) {
|
|
||||||
return "transparent";
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseColor = mouseArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
|
||||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
|
||||||
}
|
|
||||||
clip: true
|
|
||||||
visible: hasWindowsOnCurrentWorkspace
|
visible: hasWindowsOnCurrentWorkspace
|
||||||
|
|
||||||
IconImage {
|
content: Component {
|
||||||
id: appIcon
|
Item {
|
||||||
anchors.centerIn: parent
|
implicitWidth: {
|
||||||
width: 18
|
if (!root.hasWindowsOnCurrentWorkspace) return 0
|
||||||
height: 18
|
if (root.isVerticalOrientation) return root.widgetThickness - root.horizontalPadding * 2
|
||||||
visible: root.isVertical && activeWindow && status === Image.Ready
|
const baseWidth = contentRow.implicitWidth
|
||||||
source: {
|
return compactMode ? Math.min(baseWidth, maxCompactWidth - root.horizontalPadding * 2) : Math.min(baseWidth, maxNormalWidth - root.horizontalPadding * 2)
|
||||||
if (!activeWindow || !activeWindow.appId) return ""
|
|
||||||
const moddedId = Paths.moddedAppId(activeWindow.appId)
|
|
||||||
if (moddedId.toLowerCase().includes("steam_app")) return ""
|
|
||||||
return Quickshell.iconPath(activeDesktopEntry?.icon, true)
|
|
||||||
}
|
|
||||||
smooth: true
|
|
||||||
mipmap: true
|
|
||||||
asynchronous: true
|
|
||||||
}
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
size: 18
|
|
||||||
name: "sports_esports"
|
|
||||||
color: Theme.surfaceText
|
|
||||||
visible: {
|
|
||||||
if (!root.isVertical || !activeWindow || !activeWindow.appId) return false
|
|
||||||
const moddedId = Paths.moddedAppId(activeWindow.appId)
|
|
||||||
return moddedId.toLowerCase().includes("steam_app")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
visible: {
|
|
||||||
if (!root.isVertical || !activeWindow || !activeWindow.appId) return false
|
|
||||||
if (appIcon.status === Image.Ready) return false
|
|
||||||
const moddedId = Paths.moddedAppId(activeWindow.appId)
|
|
||||||
return !moddedId.toLowerCase().includes("steam_app")
|
|
||||||
}
|
|
||||||
text: {
|
|
||||||
if (!activeWindow || !activeWindow.appId) return "?"
|
|
||||||
if (activeDesktopEntry && activeDesktopEntry.name) {
|
|
||||||
return activeDesktopEntry.name.charAt(0).toUpperCase()
|
|
||||||
}
|
}
|
||||||
return activeWindow.appId.charAt(0).toUpperCase()
|
implicitHeight: root.widgetThickness - root.horizontalPadding * 2
|
||||||
}
|
clip: false
|
||||||
font.pixelSize: 10
|
|
||||||
color: Theme.surfaceText
|
|
||||||
font.weight: Font.Medium
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
IconImage {
|
||||||
id: contentRow
|
id: appIcon
|
||||||
|
anchors.centerIn: parent
|
||||||
anchors.centerIn: parent
|
width: 18
|
||||||
spacing: Theme.spacingS
|
height: 18
|
||||||
visible: !root.isVertical
|
visible: root.isVerticalOrientation && activeWindow && status === Image.Ready
|
||||||
|
source: {
|
||||||
StyledText {
|
if (!activeWindow || !activeWindow.appId) return ""
|
||||||
id: appText
|
const moddedId = Paths.moddedAppId(activeWindow.appId)
|
||||||
|
if (moddedId.toLowerCase().includes("steam_app")) return ""
|
||||||
text: {
|
return Quickshell.iconPath(activeDesktopEntry?.icon, true)
|
||||||
if (!activeWindow || !activeWindow.appId) {
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
smooth: true
|
||||||
const desktopEntry = DesktopEntries.heuristicLookup(activeWindow.appId);
|
mipmap: true
|
||||||
return desktopEntry && desktopEntry.name ? desktopEntry.name : activeWindow.appId;
|
asynchronous: true
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
elide: Text.ElideRight
|
|
||||||
maximumLineCount: 1
|
|
||||||
width: Math.min(implicitWidth, compactMode ? 80 : 180)
|
|
||||||
visible: !compactMode && text.length > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
DankIcon {
|
||||||
text: "•"
|
anchors.centerIn: parent
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
size: 18
|
||||||
color: Theme.outlineButton
|
name: "sports_esports"
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
color: Theme.surfaceText
|
||||||
visible: !compactMode && appText.text && titleText.text
|
visible: {
|
||||||
}
|
if (!root.isVerticalOrientation || !activeWindow || !activeWindow.appId) return false
|
||||||
|
const moddedId = Paths.moddedAppId(activeWindow.appId)
|
||||||
StyledText {
|
return moddedId.toLowerCase().includes("steam_app")
|
||||||
id: titleText
|
|
||||||
|
|
||||||
text: {
|
|
||||||
const title = activeWindow && activeWindow.title ? activeWindow.title : "";
|
|
||||||
const appName = appText.text;
|
|
||||||
if (!title || !appName) {
|
|
||||||
return title;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (title.endsWith(" - " + appName)) {
|
|
||||||
return title.substring(0, title.length - (" - " + appName).length);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (title.endsWith(appName)) {
|
|
||||||
return title.substring(0, title.length - appName.length).replace(/ - $/, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
return title;
|
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
elide: Text.ElideRight
|
|
||||||
maximumLineCount: 1
|
|
||||||
width: Math.min(implicitWidth, compactMode ? 280 : 250)
|
|
||||||
visible: text.length > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
visible: {
|
||||||
|
if (!root.isVerticalOrientation || !activeWindow || !activeWindow.appId) return false
|
||||||
|
if (appIcon.status === Image.Ready) return false
|
||||||
|
const moddedId = Paths.moddedAppId(activeWindow.appId)
|
||||||
|
return !moddedId.toLowerCase().includes("steam_app")
|
||||||
|
}
|
||||||
|
text: {
|
||||||
|
if (!activeWindow || !activeWindow.appId) return "?"
|
||||||
|
if (activeDesktopEntry && activeDesktopEntry.name) {
|
||||||
|
return activeDesktopEntry.name.charAt(0).toUpperCase()
|
||||||
|
}
|
||||||
|
return activeWindow.appId.charAt(0).toUpperCase()
|
||||||
|
}
|
||||||
|
font.pixelSize: 10
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: contentRow
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
visible: !root.isVerticalOrientation
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: appText
|
||||||
|
text: {
|
||||||
|
if (!activeWindow || !activeWindow.appId) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const desktopEntry = DesktopEntries.heuristicLookup(activeWindow.appId);
|
||||||
|
return desktopEntry && desktopEntry.name ? desktopEntry.name : activeWindow.appId;
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
elide: Text.ElideRight
|
||||||
|
maximumLineCount: 1
|
||||||
|
width: Math.min(implicitWidth, compactMode ? 80 : 180)
|
||||||
|
visible: !compactMode && text.length > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "•"
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.outlineButton
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: !compactMode && appText.text && titleText.text
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: titleText
|
||||||
|
text: {
|
||||||
|
const title = activeWindow && activeWindow.title ? activeWindow.title : "";
|
||||||
|
const appName = appText.text;
|
||||||
|
if (!title || !appName) {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (title.endsWith(" - " + appName)) {
|
||||||
|
return title.substring(0, title.length - (" - " + appName).length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (title.endsWith(appName)) {
|
||||||
|
return title.substring(0, title.length - appName.length).replace(/ - $/, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
elide: Text.ElideRight
|
||||||
|
maximumLineCount: 1
|
||||||
|
width: Math.min(implicitWidth, compactMode ? 280 : 250)
|
||||||
|
visible: text.length > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: root.isVertical
|
hoverEnabled: root.isVerticalOrientation
|
||||||
|
acceptedButtons: Qt.NoButton
|
||||||
onEntered: {
|
onEntered: {
|
||||||
if (root.isVertical && activeWindow && activeWindow.appId && root.parentScreen) {
|
if (root.isVerticalOrientation && activeWindow && activeWindow.appId && root.parentScreen) {
|
||||||
tooltipLoader.active = true
|
tooltipLoader.active = true
|
||||||
if (tooltipLoader.item) {
|
if (tooltipLoader.item) {
|
||||||
const globalPos = mapToGlobal(width / 2, height / 2)
|
const globalPos = mapToGlobal(width / 2, height / 2)
|
||||||
@@ -260,14 +251,4 @@ Rectangle {
|
|||||||
active: false
|
active: false
|
||||||
sourceComponent: DankTooltip {}
|
sourceComponent: DankTooltip {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,20 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Modules.Plugins
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
BasePill {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isVertical: axis?.isVertical ?? false
|
|
||||||
property var axis: null
|
|
||||||
property bool showPercentage: true
|
property bool showPercentage: true
|
||||||
property bool showIcon: true
|
property bool showIcon: true
|
||||||
property var toggleProcessList
|
property var toggleProcessList
|
||||||
property string section: "right"
|
property var popoutTarget: null
|
||||||
property var popupTarget: null
|
|
||||||
property var parentScreen: null
|
|
||||||
property var widgetData: null
|
property var widgetData: null
|
||||||
property real barThickness: 48
|
|
||||||
property real widgetThickness: 30
|
|
||||||
property int selectedGpuIndex: (widgetData && widgetData.selectedGpuIndex !== undefined) ? widgetData.selectedGpuIndex : 0
|
property int selectedGpuIndex: (widgetData && widgetData.selectedGpuIndex !== undefined) ? widgetData.selectedGpuIndex : 0
|
||||||
property bool minimumWidth: (widgetData && widgetData.minimumWidth !== undefined) ? widgetData.minimumWidth : true
|
property bool minimumWidth: (widgetData && widgetData.minimumWidth !== undefined) ? widgetData.minimumWidth : true
|
||||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
|
||||||
property real displayTemp: {
|
property real displayTemp: {
|
||||||
if (!DgopService.availableGpus || DgopService.availableGpus.length === 0) {
|
if (!DgopService.availableGpus || DgopService.availableGpus.length === 0) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -34,7 +28,6 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateWidgetPciId(pciId) {
|
function updateWidgetPciId(pciId) {
|
||||||
// Find and update this widget's pciId in the settings
|
|
||||||
const sections = ["left", "center", "right"];
|
const sections = ["left", "center", "right"];
|
||||||
for (let s = 0; s < sections.length; s++) {
|
for (let s = 0; s < sections.length; s++) {
|
||||||
const sectionId = sections[s];
|
const sectionId = sections[s];
|
||||||
@@ -68,17 +61,6 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
width: isVertical ? widgetThickness : (gpuTempContent.implicitWidth + horizontalPadding * 2)
|
|
||||||
height: isVertical ? (gpuTempColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
|
|
||||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
|
||||||
color: {
|
|
||||||
if (SettingsData.dankBarNoBackground) {
|
|
||||||
return "transparent";
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseColor = gpuArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
|
||||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
|
||||||
}
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
DgopService.addRef(["gpu"]);
|
DgopService.addRef(["gpu"]);
|
||||||
if (widgetData && widgetData.pciId) {
|
if (widgetData && widgetData.pciId) {
|
||||||
@@ -92,12 +74,10 @@ Rectangle {
|
|||||||
if (widgetData && widgetData.pciId) {
|
if (widgetData && widgetData.pciId) {
|
||||||
DgopService.removeGpuPciId(widgetData.pciId);
|
DgopService.removeGpuPciId(widgetData.pciId);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onWidgetDataChanged() {
|
function onWidgetDataChanged() {
|
||||||
// Force property re-evaluation by triggering change detection
|
|
||||||
root.selectedGpuIndex = Qt.binding(() => {
|
root.selectedGpuIndex = Qt.binding(() => {
|
||||||
return (root.widgetData && root.widgetData.selectedGpuIndex !== undefined) ? root.widgetData.selectedGpuIndex : 0;
|
return (root.widgetData && root.widgetData.selectedGpuIndex !== undefined) ? root.widgetData.selectedGpuIndex : 0;
|
||||||
});
|
});
|
||||||
@@ -106,122 +86,122 @@ Rectangle {
|
|||||||
target: SettingsData
|
target: SettingsData
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
content: Component {
|
||||||
id: gpuArea
|
Item {
|
||||||
|
implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : gpuTempContent.implicitWidth
|
||||||
|
implicitHeight: root.isVerticalOrientation ? gpuTempColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2)
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: gpuTempColumn
|
||||||
|
visible: root.isVerticalOrientation
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 1
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "auto_awesome_mosaic"
|
||||||
|
size: Theme.barIconSize(root.barThickness)
|
||||||
|
color: {
|
||||||
|
if (root.displayTemp > 80) {
|
||||||
|
return Theme.tempDanger;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root.displayTemp > 65) {
|
||||||
|
return Theme.tempWarning;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Theme.surfaceText;
|
||||||
|
}
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
if (root.displayTemp === undefined || root.displayTemp === null || root.displayTemp === 0) {
|
||||||
|
return "--";
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.round(root.displayTemp).toString();
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: gpuTempContent
|
||||||
|
visible: !root.isVerticalOrientation
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 3
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "auto_awesome_mosaic"
|
||||||
|
size: Theme.barIconSize(root.barThickness)
|
||||||
|
color: {
|
||||||
|
if (root.displayTemp > 80) {
|
||||||
|
return Theme.tempDanger;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root.displayTemp > 65) {
|
||||||
|
return Theme.tempWarning;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Theme.surfaceText;
|
||||||
|
}
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
if (root.displayTemp === undefined || root.displayTemp === null || root.displayTemp === 0) {
|
||||||
|
return "--°";
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.round(root.displayTemp) + "°";
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
elide: Text.ElideNone
|
||||||
|
|
||||||
|
StyledTextMetrics {
|
||||||
|
id: gpuTempBaseline
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
text: "100°"
|
||||||
|
}
|
||||||
|
|
||||||
|
width: root.minimumWidth ? Math.max(gpuTempBaseline.width, paintedWidth) : paintedWidth
|
||||||
|
|
||||||
|
Behavior on width {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 120
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
acceptedButtons: Qt.LeftButton
|
||||||
onPressed: {
|
onPressed: {
|
||||||
if (popupTarget && popupTarget.setTriggerPosition) {
|
if (popoutTarget && popoutTarget.setTriggerPosition) {
|
||||||
const globalPos = mapToGlobal(0, 0)
|
const globalPos = root.visualContent.mapToGlobal(0, 0)
|
||||||
const currentScreen = parentScreen || Screen
|
const currentScreen = parentScreen || Screen
|
||||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
|
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth)
|
||||||
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
||||||
}
|
}
|
||||||
DgopService.setSortBy("cpu");
|
DgopService.setSortBy("cpu");
|
||||||
if (root.toggleProcessList) {
|
if (root.toggleProcessList) {
|
||||||
root.toggleProcessList();
|
root.toggleProcessList();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
|
||||||
id: gpuTempColumn
|
|
||||||
visible: root.isVertical
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 1
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "auto_awesome_mosaic"
|
|
||||||
size: Theme.barIconSize(barThickness)
|
|
||||||
color: {
|
|
||||||
if (root.displayTemp > 80) {
|
|
||||||
return Theme.tempDanger;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (root.displayTemp > 65) {
|
|
||||||
return Theme.tempWarning;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme.surfaceText;
|
|
||||||
}
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
if (root.displayTemp === undefined || root.displayTemp === null || root.displayTemp === 0) {
|
|
||||||
return "--";
|
|
||||||
}
|
|
||||||
|
|
||||||
return Math.round(root.displayTemp).toString();
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
id: gpuTempContent
|
|
||||||
visible: !root.isVertical
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 3
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "auto_awesome_mosaic"
|
|
||||||
size: Theme.barIconSize(barThickness)
|
|
||||||
color: {
|
|
||||||
if (root.displayTemp > 80) {
|
|
||||||
return Theme.tempDanger;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (root.displayTemp > 65) {
|
|
||||||
return Theme.tempWarning;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme.surfaceText;
|
|
||||||
}
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
if (root.displayTemp === undefined || root.displayTemp === null || root.displayTemp === 0) {
|
|
||||||
return "--°";
|
|
||||||
}
|
|
||||||
|
|
||||||
return Math.round(root.displayTemp) + "°";
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
elide: Text.ElideNone
|
|
||||||
|
|
||||||
StyledTextMetrics {
|
|
||||||
id: gpuTempBaseline
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
text: "100°"
|
|
||||||
}
|
|
||||||
|
|
||||||
width: root.minimumWidth ? Math.max(gpuTempBaseline.width, paintedWidth) : paintedWidth
|
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 120
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: autoSaveTimer
|
id: autoSaveTimer
|
||||||
|
|
||||||
@@ -231,13 +211,10 @@ Rectangle {
|
|||||||
if (DgopService.availableGpus && DgopService.availableGpus.length > 0) {
|
if (DgopService.availableGpus && DgopService.availableGpus.length > 0) {
|
||||||
const firstGpu = DgopService.availableGpus[0];
|
const firstGpu = DgopService.availableGpus[0];
|
||||||
if (firstGpu && firstGpu.pciId) {
|
if (firstGpu && firstGpu.pciId) {
|
||||||
// Save the first GPU's PCI ID to this widget's settings
|
|
||||||
updateWidgetPciId(firstGpu.pciId);
|
updateWidgetPciId(firstGpu.pciId);
|
||||||
DgopService.addGpuPciId(firstGpu.pciId);
|
DgopService.addGpuPciId(firstGpu.pciId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,51 +2,33 @@ import QtQuick
|
|||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Modules.Plugins
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
BasePill {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isVertical: axis?.isVertical ?? false
|
content: Component {
|
||||||
property var axis: null
|
Item {
|
||||||
property string section: "right"
|
implicitWidth: root.widgetThickness - root.horizontalPadding * 2
|
||||||
property var popupTarget: null
|
implicitHeight: root.widgetThickness - root.horizontalPadding * 2
|
||||||
property var parentScreen: null
|
|
||||||
property real widgetThickness: 30
|
|
||||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
|
||||||
|
|
||||||
width: isVertical ? widgetThickness : (idleIcon.width + horizontalPadding * 2)
|
DankIcon {
|
||||||
height: isVertical ? (idleIcon.height + horizontalPadding * 2) : widgetThickness
|
anchors.centerIn: parent
|
||||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
name: SessionService.idleInhibited ? "motion_sensor_active" : "motion_sensor_idle"
|
||||||
color: {
|
size: Theme.barIconSize(root.barThickness, -4)
|
||||||
if (SettingsData.dankBarNoBackground) {
|
color: Theme.surfaceText
|
||||||
return "transparent";
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseColor = mouseArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
|
||||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
|
||||||
}
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
id: idleIcon
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
name: SessionService.idleInhibited ? "motion_sensor_active" : "motion_sensor_idle"
|
|
||||||
size: Theme.barIconSize(barThickness, -4)
|
|
||||||
color: Theme.surfaceText
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
z: 1
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
SessionService.toggleIdleInhibit();
|
SessionService.toggleIdleInhibit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,38 +3,70 @@ import QtQuick.Controls
|
|||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Modules.Plugins
|
||||||
import qs.Modules.ProcessList
|
import qs.Modules.ProcessList
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
BasePill {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isVertical: axis?.isVertical ?? false
|
property bool compactMode: SettingsData.keyboardLayoutNameCompactMode
|
||||||
property var axis: null
|
property string currentLayout: CompositorService.isNiri ? NiriService.getCurrentKeyboardLayoutName() : ""
|
||||||
property real widgetThickness: 30
|
|
||||||
property real barThickness: 48
|
|
||||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
|
||||||
property string currentLayout: ""
|
|
||||||
property string hyprlandKeyboard: ""
|
property string hyprlandKeyboard: ""
|
||||||
|
|
||||||
width: isVertical ? widgetThickness : (contentRow.implicitWidth + horizontalPadding * 2)
|
content: Component {
|
||||||
height: isVertical ? (contentColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
|
Item {
|
||||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : contentRow.implicitWidth
|
||||||
color: {
|
implicitHeight: root.isVerticalOrientation ? contentColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2)
|
||||||
if (SettingsData.dankBarNoBackground) {
|
|
||||||
return "transparent";
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseColor = mouseArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
Column {
|
||||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
id: contentColumn
|
||||||
|
visible: root.isVerticalOrientation
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 1
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "keyboard"
|
||||||
|
size: Theme.barIconSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
if (!root.currentLayout) return ""
|
||||||
|
const parts = root.currentLayout.split(" ")
|
||||||
|
if (parts.length > 0) {
|
||||||
|
return parts[0].substring(0, 2).toUpperCase()
|
||||||
|
}
|
||||||
|
return root.currentLayout.substring(0, 2).toUpperCase()
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: contentRow
|
||||||
|
visible: !root.isVerticalOrientation
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: root.currentLayout
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
z: 1
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (CompositorService.isNiri) {
|
if (CompositorService.isNiri) {
|
||||||
@@ -51,95 +83,55 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Component.onCompleted: {
|
||||||
id: contentColumn
|
if (CompositorService.isHyprland) {
|
||||||
|
updateLayout()
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 1
|
|
||||||
visible: root.isVertical
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "keyboard"
|
|
||||||
size: Theme.barIconSize(barThickness)
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
StyledText {
|
function updateLayout() {
|
||||||
text: {
|
if (CompositorService.isHyprland) {
|
||||||
if (!currentLayout) return ""
|
Proc.runCommand(null, ["hyprctl", "-j", "devices"], (output, exitCode) => {
|
||||||
const parts = currentLayout.split(" ")
|
if (exitCode !== 0) {
|
||||||
if (parts.length > 0) {
|
root.currentLayout = "Unknown"
|
||||||
return parts[0].substring(0, 2).toUpperCase()
|
return
|
||||||
}
|
}
|
||||||
return currentLayout.substring(0, 2).toUpperCase()
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
id: contentRow
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: Theme.spacingS
|
|
||||||
visible: !root.isVertical
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: currentLayout
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: hyprlandLayoutProcess
|
|
||||||
running: false
|
|
||||||
command: ["hyprctl", "-j", "devices"]
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(text)
|
const data = JSON.parse(output)
|
||||||
// Find the main keyboard and get its active keymap
|
|
||||||
const mainKeyboard = data.keyboards.find(kb => kb.main === true)
|
const mainKeyboard = data.keyboards.find(kb => kb.main === true)
|
||||||
root.hyprlandKeyboard = mainKeyboard.name
|
root.hyprlandKeyboard = mainKeyboard.name
|
||||||
if (mainKeyboard && mainKeyboard.active_keymap) {
|
|
||||||
root.currentLayout = mainKeyboard.active_keymap
|
if (mainKeyboard) {
|
||||||
|
const layout = mainKeyboard.layout
|
||||||
|
const variant = mainKeyboard.variant
|
||||||
|
const index = mainKeyboard.active_layout_index
|
||||||
|
|
||||||
|
if (root.compactMode && layout && variant && index !== undefined) {
|
||||||
|
const layouts = mainKeyboard.layout.split(",")
|
||||||
|
const variants = mainKeyboard.variant.split(",")
|
||||||
|
const index = mainKeyboard.active_layout_index
|
||||||
|
|
||||||
|
if (layouts[index] && variants[index] !== undefined) {
|
||||||
|
if (variants[index] === "") {
|
||||||
|
root.currentLayout = layouts[index]
|
||||||
|
} else {
|
||||||
|
root.currentLayout = layouts[index] + "-" + variants[index]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
root.currentLayout = "Unknown"
|
||||||
|
}
|
||||||
|
} else if (mainKeyboard && mainKeyboard.active_keymap) {
|
||||||
|
root.currentLayout = mainKeyboard.active_keymap
|
||||||
|
} else {
|
||||||
|
root.currentLayout = "Unknown"
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
root.currentLayout = "Unknown"
|
root.currentLayout = "Unknown"
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
root.currentLayout = "Unknown"
|
root.currentLayout = "Unknown"
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: updateTimer
|
|
||||||
interval: 1000
|
|
||||||
running: true
|
|
||||||
repeat: true
|
|
||||||
onTriggered: {
|
|
||||||
updateLayout()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
updateLayout()
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateLayout() {
|
|
||||||
if (CompositorService.isNiri) {
|
|
||||||
root.currentLayout = NiriService.getCurrentKeyboardLayoutName()
|
|
||||||
} else if (CompositorService.isHyprland) {
|
|
||||||
hyprlandLayoutProcess.running = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,124 +3,121 @@ import QtQuick.Effects
|
|||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
import Quickshell.Widgets
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Modules.Plugins
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Item {
|
BasePill {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isActive: false
|
property bool isActive: false
|
||||||
property bool isVertical: axis?.isVertical ?? false
|
property var hyprlandOverviewLoader: null
|
||||||
property var axis: null
|
|
||||||
property string section: "left"
|
|
||||||
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 clicked()
|
content: Component {
|
||||||
|
Item {
|
||||||
|
implicitWidth: root.widgetThickness - root.horizontalPadding * 2
|
||||||
|
implicitHeight: root.widgetThickness - root.horizontalPadding * 2
|
||||||
|
|
||||||
width: widgetThickness
|
DankIcon {
|
||||||
height: widgetThickness
|
visible: SettingsData.launcherLogoMode === "apps"
|
||||||
|
anchors.centerIn: parent
|
||||||
MouseArea {
|
name: "apps"
|
||||||
id: launcherArea
|
size: Theme.barIconSize(root.barThickness, -4)
|
||||||
|
color: Theme.surfaceText
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
|
||||||
onPressed: function (mouse){
|
|
||||||
if (mouse.button === Qt.RightButton) {
|
|
||||||
if (CompositorService.isNiri) {
|
|
||||||
NiriService.toggleOverview()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
root.clicked();
|
SystemLogo {
|
||||||
if (popupTarget && popupTarget.setTriggerPosition) {
|
visible: SettingsData.launcherLogoMode === "os"
|
||||||
const globalPos = mapToGlobal(0, 0);
|
anchors.centerIn: parent
|
||||||
const currentScreen = parentScreen || Screen;
|
width: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset)
|
||||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width);
|
height: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset)
|
||||||
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen);
|
colorOverride: Theme.effectiveLogoColor
|
||||||
|
brightnessOverride: SettingsData.launcherLogoBrightness
|
||||||
|
contrastOverride: SettingsData.launcherLogoContrast
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
visible: SettingsData.launcherLogoMode === "dank"
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset)
|
||||||
|
height: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset)
|
||||||
|
smooth: true
|
||||||
|
mipmap: true
|
||||||
|
asynchronous: true
|
||||||
|
source: "file://" + Theme.shellDir + "/assets/danklogo.svg"
|
||||||
|
layer.enabled: Theme.effectiveLogoColor !== ""
|
||||||
|
layer.smooth: true
|
||||||
|
layer.mipmap: true
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
saturation: 0
|
||||||
|
colorization: 1
|
||||||
|
colorizationColor: Theme.effectiveLogoColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
visible: SettingsData.launcherLogoMode === "compositor" && (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl || CompositorService.isSway)
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset)
|
||||||
|
height: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset)
|
||||||
|
smooth: true
|
||||||
|
asynchronous: true
|
||||||
|
source: {
|
||||||
|
if (CompositorService.isNiri) {
|
||||||
|
return "file://" + Theme.shellDir + "/assets/niri.svg"
|
||||||
|
} else if (CompositorService.isHyprland) {
|
||||||
|
return "file://" + Theme.shellDir + "/assets/hyprland.svg"
|
||||||
|
} else if (CompositorService.isDwl) {
|
||||||
|
return "file://" + Theme.shellDir + "/assets/mango.png"
|
||||||
|
} else if (CompositorService.isSway) {
|
||||||
|
return "file://" + Theme.shellDir + "/assets/sway.svg"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
layer.enabled: Theme.effectiveLogoColor !== ""
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
saturation: 0
|
||||||
|
colorization: 1
|
||||||
|
colorizationColor: Theme.effectiveLogoColor
|
||||||
|
brightness: {
|
||||||
|
SettingsData.launcherLogoBrightness
|
||||||
|
}
|
||||||
|
contrast: {
|
||||||
|
SettingsData.launcherLogoContrast
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
visible: SettingsData.launcherLogoMode === "custom" && SettingsData.launcherLogoCustomPath !== ""
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset)
|
||||||
|
height: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset)
|
||||||
|
smooth: true
|
||||||
|
asynchronous: true
|
||||||
|
source: SettingsData.launcherLogoCustomPath ? "file://" + SettingsData.launcherLogoCustomPath.replace("file://", "") : ""
|
||||||
|
layer.enabled: Theme.effectiveLogoColor !== ""
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
saturation: 0
|
||||||
|
colorization: 1
|
||||||
|
colorizationColor: Theme.effectiveLogoColor
|
||||||
|
brightness: SettingsData.launcherLogoBrightness
|
||||||
|
contrast: SettingsData.launcherLogoContrast
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
MouseArea {
|
||||||
id: launcherContent
|
id: customMouseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
cursorShape: Qt.PointingHandCursor
|
||||||
color: {
|
acceptedButtons: Qt.RightButton
|
||||||
if (SettingsData.dankBarNoBackground) {
|
onPressed: function (mouse){
|
||||||
return "transparent";
|
if (CompositorService.isNiri) {
|
||||||
}
|
NiriService.toggleOverview()
|
||||||
|
} else if (root.hyprlandOverviewLoader?.item) {
|
||||||
const baseColor = launcherArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
root.hyprlandOverviewLoader.item.overviewOpen = !root.hyprlandOverviewLoader.item.overviewOpen
|
||||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
|
||||||
}
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
visible: SettingsData.launcherLogoMode === "apps"
|
|
||||||
anchors.centerIn: parent
|
|
||||||
name: "apps"
|
|
||||||
size: Theme.barIconSize(barThickness, -4)
|
|
||||||
color: Theme.surfaceText
|
|
||||||
}
|
|
||||||
|
|
||||||
SystemLogo {
|
|
||||||
visible: SettingsData.launcherLogoMode === "os"
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: Theme.barIconSize(barThickness, SettingsData.launcherLogoSizeOffset)
|
|
||||||
height: Theme.barIconSize(barThickness, SettingsData.launcherLogoSizeOffset)
|
|
||||||
colorOverride: Theme.effectiveLogoColor
|
|
||||||
brightnessOverride: SettingsData.launcherLogoBrightness
|
|
||||||
contrastOverride: SettingsData.launcherLogoContrast
|
|
||||||
}
|
|
||||||
|
|
||||||
IconImage {
|
|
||||||
visible: SettingsData.launcherLogoMode === "compositor"
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: Theme.barIconSize(barThickness, SettingsData.launcherLogoSizeOffset)
|
|
||||||
height: Theme.barIconSize(barThickness, SettingsData.launcherLogoSizeOffset)
|
|
||||||
smooth: true
|
|
||||||
asynchronous: true
|
|
||||||
source: {
|
|
||||||
if (CompositorService.isNiri) {
|
|
||||||
return "file://" + Theme.shellDir + "/assets/niri.svg"
|
|
||||||
} else if (CompositorService.isHyprland) {
|
|
||||||
return "file://" + Theme.shellDir + "/assets/hyprland.svg"
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
layer.enabled: Theme.effectiveLogoColor !== ""
|
|
||||||
layer.effect: MultiEffect {
|
|
||||||
saturation: 0
|
|
||||||
colorization: 1
|
|
||||||
colorizationColor: Theme.effectiveLogoColor
|
|
||||||
brightness: SettingsData.launcherLogoBrightness
|
|
||||||
contrast: SettingsData.launcherLogoContrast
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IconImage {
|
|
||||||
visible: SettingsData.launcherLogoMode === "custom" && SettingsData.launcherLogoCustomPath !== ""
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: Theme.barIconSize(barThickness, SettingsData.launcherLogoSizeOffset)
|
|
||||||
height: Theme.barIconSize(barThickness, SettingsData.launcherLogoSizeOffset)
|
|
||||||
smooth: true
|
|
||||||
asynchronous: true
|
|
||||||
source: SettingsData.launcherLogoCustomPath ? "file://" + SettingsData.launcherLogoCustomPath.replace("file://", "") : ""
|
|
||||||
layer.enabled: Theme.effectiveLogoColor !== ""
|
|
||||||
layer.effect: MultiEffect {
|
|
||||||
saturation: 0
|
|
||||||
colorization: 1
|
|
||||||
colorizationColor: Theme.effectiveLogoColor
|
|
||||||
brightness: SettingsData.launcherLogoBrightness
|
|
||||||
contrast: SettingsData.launcherLogoContrast
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,452 +1,328 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell.Services.Mpris
|
import Quickshell.Services.Mpris
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Modules.Plugins
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
BasePill {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isVertical: axis?.isVertical ?? false
|
|
||||||
property var axis: null
|
|
||||||
readonly property MprisPlayer activePlayer: MprisController.activePlayer
|
readonly property MprisPlayer activePlayer: MprisController.activePlayer
|
||||||
readonly property bool playerAvailable: activePlayer !== null
|
readonly property bool playerAvailable: activePlayer !== null
|
||||||
property bool compactMode: false
|
property bool compactMode: false
|
||||||
readonly property int textWidth: {
|
readonly property int textWidth: {
|
||||||
switch (SettingsData.mediaSize) {
|
switch (SettingsData.mediaSize) {
|
||||||
case 0:
|
case 0:
|
||||||
return 0; // No text in small mode
|
return 0;
|
||||||
case 2:
|
case 2:
|
||||||
return 180; // Large text area
|
return 180;
|
||||||
default:
|
default:
|
||||||
return 120; // Medium text area
|
return 120;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
readonly property int currentContentWidth: {
|
readonly property int currentContentWidth: {
|
||||||
if (isVertical) {
|
if (isVerticalOrientation) {
|
||||||
return widgetThickness;
|
return widgetThickness - horizontalPadding * 2;
|
||||||
}
|
}
|
||||||
const controlsWidth = 20 + Theme.spacingXS + 24 + Theme.spacingXS + 20;
|
const controlsWidth = 20 + Theme.spacingXS + 24 + Theme.spacingXS + 20;
|
||||||
const audioVizWidth = 20;
|
const audioVizWidth = 20;
|
||||||
const contentWidth = audioVizWidth + Theme.spacingXS + controlsWidth;
|
const contentWidth = audioVizWidth + Theme.spacingXS + controlsWidth;
|
||||||
return contentWidth + (textWidth > 0 ? textWidth + Theme.spacingXS : 0) + horizontalPadding * 2;
|
return contentWidth + (textWidth > 0 ? textWidth + Theme.spacingXS : 0);
|
||||||
}
|
}
|
||||||
readonly property int currentContentHeight: {
|
readonly property int currentContentHeight: {
|
||||||
if (!isVertical) {
|
if (!isVerticalOrientation) {
|
||||||
return widgetThickness;
|
return widgetThickness - horizontalPadding * 2;
|
||||||
}
|
}
|
||||||
const audioVizHeight = 20;
|
const audioVizHeight = 20;
|
||||||
const playButtonHeight = 24;
|
const playButtonHeight = 24;
|
||||||
return audioVizHeight + Theme.spacingXS + playButtonHeight + horizontalPadding * 2;
|
return audioVizHeight + Theme.spacingXS + playButtonHeight;
|
||||||
}
|
}
|
||||||
property string section: "center"
|
|
||||||
property var popupTarget: null
|
|
||||||
property var parentScreen: null
|
|
||||||
property real barThickness: 48
|
|
||||||
property real widgetThickness: 30
|
|
||||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
|
||||||
|
|
||||||
signal clicked()
|
content: Component {
|
||||||
|
Item {
|
||||||
width: currentContentWidth
|
implicitWidth: root.playerAvailable ? root.currentContentWidth : 0
|
||||||
height: currentContentHeight
|
implicitHeight: root.playerAvailable ? root.currentContentHeight : 0
|
||||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
opacity: root.playerAvailable ? 1 : 0
|
||||||
color: {
|
|
||||||
if (SettingsData.dankBarNoBackground) {
|
|
||||||
return "transparent";
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseColor = Theme.widgetBaseBackgroundColor;
|
|
||||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
|
||||||
}
|
|
||||||
states: [
|
|
||||||
State {
|
|
||||||
name: "shown"
|
|
||||||
when: playerAvailable
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
target: root
|
|
||||||
opacity: 1
|
|
||||||
width: currentContentWidth
|
|
||||||
height: currentContentHeight
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: "hidden"
|
|
||||||
when: !playerAvailable
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
target: root
|
|
||||||
opacity: 0
|
|
||||||
width: isVertical ? widgetThickness : 0
|
|
||||||
height: isVertical ? 0 : widgetThickness
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
]
|
|
||||||
transitions: [
|
|
||||||
Transition {
|
|
||||||
from: "shown"
|
|
||||||
to: "hidden"
|
|
||||||
|
|
||||||
SequentialAnimation {
|
|
||||||
PauseAnimation {
|
|
||||||
duration: 500
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
properties: isVertical ? "opacity,height" : "opacity,width"
|
|
||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
Behavior on implicitWidth {
|
||||||
Transition {
|
NumberAnimation {
|
||||||
from: "hidden"
|
duration: Theme.shortDuration
|
||||||
to: "shown"
|
easing.type: Theme.standardEasing
|
||||||
|
|
||||||
NumberAnimation {
|
|
||||||
properties: isVertical ? "opacity,height" : "opacity,width"
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: verticalLayout
|
|
||||||
visible: root.isVertical
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: Theme.spacingXS
|
|
||||||
|
|
||||||
AudioVisualization {
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: {
|
|
||||||
if (root.popupTarget && root.popupTarget.setTriggerPosition) {
|
|
||||||
const globalPos = parent.mapToGlobal(0, 0)
|
|
||||||
const currentScreen = root.parentScreen || Screen
|
|
||||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, root.barThickness, parent.width)
|
|
||||||
root.popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, root.section, currentScreen)
|
|
||||||
}
|
|
||||||
root.clicked()
|
|
||||||
}
|
|
||||||
onEntered: {
|
|
||||||
tooltipLoader.active = true
|
|
||||||
if (tooltipLoader.item && activePlayer) {
|
|
||||||
const globalPos = parent.mapToGlobal(parent.width / 2, parent.height / 2)
|
|
||||||
const screenX = root.parentScreen ? root.parentScreen.x : 0
|
|
||||||
const screenY = root.parentScreen ? root.parentScreen.y : 0
|
|
||||||
const relativeY = globalPos.y - screenY
|
|
||||||
const tooltipX = root.axis?.edge === "left" ? (Theme.barHeight + SettingsData.dankBarSpacing + Theme.spacingXS) : (root.parentScreen.width - Theme.barHeight - SettingsData.dankBarSpacing - Theme.spacingXS)
|
|
||||||
|
|
||||||
let identity = activePlayer.identity || ""
|
|
||||||
let isWebMedia = identity.toLowerCase().includes("firefox") || identity.toLowerCase().includes("chrome") || identity.toLowerCase().includes("chromium")
|
|
||||||
let title = activePlayer.trackTitle || "Unknown Track"
|
|
||||||
let subtitle = ""
|
|
||||||
if (isWebMedia && activePlayer.trackTitle) {
|
|
||||||
subtitle = activePlayer.trackArtist || identity
|
|
||||||
} else {
|
|
||||||
subtitle = activePlayer.trackArtist || ""
|
|
||||||
}
|
|
||||||
let tooltipText = subtitle.length > 0 ? title + " • " + subtitle : title
|
|
||||||
|
|
||||||
const isLeft = root.axis?.edge === "left"
|
|
||||||
tooltipLoader.item.show(tooltipText, screenX + tooltipX, relativeY, root.parentScreen, isLeft, !isLeft)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onExited: {
|
|
||||||
if (tooltipLoader.item) {
|
|
||||||
tooltipLoader.item.hide()
|
|
||||||
}
|
|
||||||
tooltipLoader.active = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
Behavior on implicitHeight {
|
||||||
width: 24
|
NumberAnimation {
|
||||||
height: 24
|
duration: Theme.shortDuration
|
||||||
radius: 12
|
easing.type: Theme.standardEasing
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
}
|
||||||
color: activePlayer && activePlayer.playbackState === 1 ? Theme.primary : Theme.primaryHover
|
}
|
||||||
visible: root.playerAvailable
|
|
||||||
opacity: activePlayer ? 1 : 0.3
|
|
||||||
|
|
||||||
DankIcon {
|
Column {
|
||||||
|
id: verticalLayout
|
||||||
|
visible: root.isVerticalOrientation
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
name: activePlayer && activePlayer.playbackState === 1 ? "pause" : "play_arrow"
|
spacing: Theme.spacingXS
|
||||||
size: 14
|
|
||||||
color: activePlayer && activePlayer.playbackState === 1 ? Theme.background : Theme.primary
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
AudioVisualization {
|
||||||
anchors.fill: parent
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
enabled: root.playerAvailable
|
|
||||||
hoverEnabled: enabled
|
MouseArea {
|
||||||
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
anchors.fill: parent
|
||||||
acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: (mouse) => {
|
onClicked: {
|
||||||
if (!activePlayer) return
|
if (root.popoutTarget && root.popoutTarget.setTriggerPosition) {
|
||||||
if (mouse.button === Qt.LeftButton) {
|
const globalPos = parent.mapToGlobal(0, 0)
|
||||||
activePlayer.togglePlaying()
|
const currentScreen = root.parentScreen || Screen
|
||||||
} else if (mouse.button === Qt.MiddleButton) {
|
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, root.barThickness, parent.width)
|
||||||
activePlayer.previous()
|
root.popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, root.section, currentScreen)
|
||||||
} else if (mouse.button === Qt.RightButton) {
|
}
|
||||||
activePlayer.next()
|
root.clicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 24
|
||||||
|
height: 24
|
||||||
|
radius: 12
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
color: activePlayer && activePlayer.playbackState === 1 ? Theme.primary : Theme.primaryHover
|
||||||
|
visible: root.playerAvailable
|
||||||
|
opacity: activePlayer ? 1 : 0.3
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: activePlayer && activePlayer.playbackState === 1 ? "pause" : "play_arrow"
|
||||||
|
size: 14
|
||||||
|
color: activePlayer && activePlayer.playbackState === 1 ? Theme.background : Theme.primary
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
enabled: root.playerAvailable
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
|
||||||
|
onClicked: (mouse) => {
|
||||||
|
if (!activePlayer) return
|
||||||
|
if (mouse.button === Qt.LeftButton) {
|
||||||
|
activePlayer.togglePlaying()
|
||||||
|
} else if (mouse.button === Qt.MiddleButton) {
|
||||||
|
activePlayer.previous()
|
||||||
|
} else if (mouse.button === Qt.RightButton) {
|
||||||
|
activePlayer.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
Row {
|
||||||
id: tooltipLoader
|
id: mediaRow
|
||||||
active: false
|
visible: !root.isVerticalOrientation
|
||||||
sourceComponent: DankTooltip {}
|
anchors.centerIn: parent
|
||||||
}
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: mediaRow
|
id: mediaInfo
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
visible: !root.isVertical
|
AudioVisualization {
|
||||||
anchors.centerIn: parent
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
spacing: Theme.spacingXS
|
|
||||||
|
|
||||||
Row {
|
|
||||||
id: mediaInfo
|
|
||||||
|
|
||||||
spacing: Theme.spacingXS
|
|
||||||
|
|
||||||
AudioVisualization {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: textContainer
|
|
||||||
|
|
||||||
property string displayText: {
|
|
||||||
if (!activePlayer || !activePlayer.trackTitle) {
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let identity = activePlayer.identity || "";
|
Rectangle {
|
||||||
let isWebMedia = identity.toLowerCase().includes("firefox") || identity.toLowerCase().includes("chrome") || identity.toLowerCase().includes("chromium") || identity.toLowerCase().includes("edge") || identity.toLowerCase().includes("safari");
|
id: textContainer
|
||||||
let title = "";
|
property string displayText: {
|
||||||
let subtitle = "";
|
if (!activePlayer || !activePlayer.trackTitle) {
|
||||||
if (isWebMedia && activePlayer.trackTitle) {
|
return "";
|
||||||
title = activePlayer.trackTitle;
|
}
|
||||||
subtitle = activePlayer.trackArtist || identity;
|
|
||||||
} else {
|
let identity = activePlayer.identity || "";
|
||||||
title = activePlayer.trackTitle || "Unknown Track";
|
let isWebMedia = identity.toLowerCase().includes("firefox") || identity.toLowerCase().includes("chrome") || identity.toLowerCase().includes("chromium") || identity.toLowerCase().includes("edge") || identity.toLowerCase().includes("safari");
|
||||||
subtitle = activePlayer.trackArtist || "";
|
let title = "";
|
||||||
|
let subtitle = "";
|
||||||
|
if (isWebMedia && activePlayer.trackTitle) {
|
||||||
|
title = activePlayer.trackTitle;
|
||||||
|
subtitle = activePlayer.trackArtist || identity;
|
||||||
|
} else {
|
||||||
|
title = activePlayer.trackTitle || "Unknown Track";
|
||||||
|
subtitle = activePlayer.trackArtist || "";
|
||||||
|
}
|
||||||
|
return subtitle.length > 0 ? title + " • " + subtitle : title;
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: textWidth
|
||||||
|
height: root.widgetThickness
|
||||||
|
visible: SettingsData.mediaSize > 0
|
||||||
|
clip: true
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: mediaText
|
||||||
|
property bool needsScrolling: implicitWidth > textContainer.width
|
||||||
|
property real scrollOffset: 0
|
||||||
|
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: textContainer.displayText
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
x: needsScrolling ? -scrollOffset : 0
|
||||||
|
onTextChanged: {
|
||||||
|
scrollOffset = 0;
|
||||||
|
scrollAnimation.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
SequentialAnimation {
|
||||||
|
id: scrollAnimation
|
||||||
|
running: mediaText.needsScrolling && textContainer.visible
|
||||||
|
loops: Animation.Infinite
|
||||||
|
|
||||||
|
PauseAnimation {
|
||||||
|
duration: 2000
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
target: mediaText
|
||||||
|
property: "scrollOffset"
|
||||||
|
from: 0
|
||||||
|
to: mediaText.implicitWidth - textContainer.width + 5
|
||||||
|
duration: Math.max(1000, (mediaText.implicitWidth - textContainer.width + 5) * 60)
|
||||||
|
easing.type: Easing.Linear
|
||||||
|
}
|
||||||
|
|
||||||
|
PauseAnimation {
|
||||||
|
duration: 2000
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
target: mediaText
|
||||||
|
property: "scrollOffset"
|
||||||
|
to: 0
|
||||||
|
duration: Math.max(1000, (mediaText.implicitWidth - textContainer.width + 5) * 60)
|
||||||
|
easing.type: Easing.Linear
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
enabled: root.playerAvailable
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onPressed: {
|
||||||
|
if (root.popoutTarget && root.popoutTarget.setTriggerPosition) {
|
||||||
|
const globalPos = mapToGlobal(0, 0)
|
||||||
|
const currentScreen = root.parentScreen || Screen
|
||||||
|
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, root.barThickness, root.width)
|
||||||
|
root.popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, root.section, currentScreen)
|
||||||
|
}
|
||||||
|
root.clicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return subtitle.length > 0 ? title + " • " + subtitle : title;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
Row {
|
||||||
width: textWidth
|
spacing: Theme.spacingXS
|
||||||
height: 20
|
|
||||||
visible: SettingsData.mediaSize > 0
|
|
||||||
clip: true
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
id: mediaText
|
|
||||||
|
|
||||||
property bool needsScrolling: implicitWidth > textContainer.width
|
|
||||||
property real scrollOffset: 0
|
|
||||||
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
text: textContainer.displayText
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
Rectangle {
|
||||||
color: Theme.surfaceText
|
width: 20
|
||||||
font.weight: Font.Medium
|
height: 20
|
||||||
wrapMode: Text.NoWrap
|
radius: 10
|
||||||
x: needsScrolling ? -scrollOffset : 0
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
onTextChanged: {
|
color: prevArea.containsMouse ? Theme.primaryHover : "transparent"
|
||||||
scrollOffset = 0;
|
visible: root.playerAvailable
|
||||||
scrollAnimation.restart();
|
opacity: (activePlayer && activePlayer.canGoPrevious) ? 1 : 0.3
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: "skip_previous"
|
||||||
|
size: 12
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: prevArea
|
||||||
|
anchors.fill: parent
|
||||||
|
enabled: root.playerAvailable
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (activePlayer) {
|
||||||
|
activePlayer.previous();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SequentialAnimation {
|
Rectangle {
|
||||||
id: scrollAnimation
|
width: 24
|
||||||
|
height: 24
|
||||||
|
radius: 12
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
color: activePlayer && activePlayer.playbackState === 1 ? Theme.primary : Theme.primaryHover
|
||||||
|
visible: root.playerAvailable
|
||||||
|
opacity: activePlayer ? 1 : 0.3
|
||||||
|
|
||||||
running: mediaText.needsScrolling && textContainer.visible
|
DankIcon {
|
||||||
loops: Animation.Infinite
|
anchors.centerIn: parent
|
||||||
|
name: activePlayer && activePlayer.playbackState === 1 ? "pause" : "play_arrow"
|
||||||
PauseAnimation {
|
size: 14
|
||||||
duration: 2000
|
color: activePlayer && activePlayer.playbackState === 1 ? Theme.background : Theme.primary
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
MouseArea {
|
||||||
target: mediaText
|
anchors.fill: parent
|
||||||
property: "scrollOffset"
|
enabled: root.playerAvailable
|
||||||
from: 0
|
cursorShape: Qt.PointingHandCursor
|
||||||
to: mediaText.implicitWidth - textContainer.width + 5
|
onClicked: {
|
||||||
duration: Math.max(1000, (mediaText.implicitWidth - textContainer.width + 5) * 60)
|
if (activePlayer) {
|
||||||
easing.type: Easing.Linear
|
activePlayer.togglePlaying();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PauseAnimation {
|
|
||||||
duration: 2000
|
|
||||||
}
|
|
||||||
|
|
||||||
NumberAnimation {
|
|
||||||
target: mediaText
|
|
||||||
property: "scrollOffset"
|
|
||||||
to: 0
|
|
||||||
duration: Math.max(1000, (mediaText.implicitWidth - textContainer.width + 5) * 60)
|
|
||||||
easing.type: Easing.Linear
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
Rectangle {
|
||||||
|
width: 20
|
||||||
|
height: 20
|
||||||
|
radius: 10
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
color: nextArea.containsMouse ? Theme.primaryHover : "transparent"
|
||||||
|
visible: playerAvailable
|
||||||
|
opacity: (activePlayer && activePlayer.canGoNext) ? 1 : 0.3
|
||||||
|
|
||||||
MouseArea {
|
DankIcon {
|
||||||
anchors.fill: parent
|
anchors.centerIn: parent
|
||||||
enabled: root.playerAvailable && root.opacity > 0 && root.width > 0 && textContainer.visible
|
name: "skip_next"
|
||||||
hoverEnabled: enabled
|
size: 12
|
||||||
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
color: Theme.surfaceText
|
||||||
onPressed: {
|
}
|
||||||
if (root.popupTarget && root.popupTarget.setTriggerPosition) {
|
|
||||||
const globalPos = mapToGlobal(0, 0)
|
MouseArea {
|
||||||
const currentScreen = root.parentScreen || Screen
|
id: nextArea
|
||||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.width)
|
anchors.fill: parent
|
||||||
root.popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, root.section, currentScreen)
|
enabled: root.playerAvailable
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (activePlayer) {
|
||||||
|
activePlayer.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
root.clicked()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
spacing: Theme.spacingXS
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: 20
|
|
||||||
height: 20
|
|
||||||
radius: 10
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
color: prevArea.containsMouse ? Theme.primaryHover : "transparent"
|
|
||||||
visible: root.playerAvailable
|
|
||||||
opacity: (activePlayer && activePlayer.canGoPrevious) ? 1 : 0.3
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
name: "skip_previous"
|
|
||||||
size: 12
|
|
||||||
color: Theme.surfaceText
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: prevArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
enabled: root.playerAvailable && root.width > 0
|
|
||||||
hoverEnabled: enabled
|
|
||||||
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
|
||||||
onClicked: {
|
|
||||||
if (activePlayer) {
|
|
||||||
activePlayer.previous();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: 24
|
|
||||||
height: 24
|
|
||||||
radius: 12
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
color: activePlayer && activePlayer.playbackState === 1 ? Theme.primary : Theme.primaryHover
|
|
||||||
visible: root.playerAvailable
|
|
||||||
opacity: activePlayer ? 1 : 0.3
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
name: activePlayer && activePlayer.playbackState === 1 ? "pause" : "play_arrow"
|
|
||||||
size: 14
|
|
||||||
color: activePlayer && activePlayer.playbackState === 1 ? Theme.background : Theme.primary
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
enabled: root.playerAvailable && root.width > 0
|
|
||||||
hoverEnabled: enabled
|
|
||||||
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
|
||||||
onClicked: {
|
|
||||||
if (activePlayer) {
|
|
||||||
activePlayer.togglePlaying();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: 20
|
|
||||||
height: 20
|
|
||||||
radius: 10
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
color: nextArea.containsMouse ? Theme.primaryHover : "transparent"
|
|
||||||
visible: playerAvailable
|
|
||||||
opacity: (activePlayer && activePlayer.canGoNext) ? 1 : 0.3
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
name: "skip_next"
|
|
||||||
size: 12
|
|
||||||
color: Theme.surfaceText
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: nextArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
enabled: root.playerAvailable && root.width > 0
|
|
||||||
hoverEnabled: enabled
|
|
||||||
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
|
||||||
onClicked: {
|
|
||||||
if (activePlayer) {
|
|
||||||
activePlayer.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on height {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,194 +1,161 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Modules.Plugins
|
||||||
import qs.Modules.ProcessList
|
import qs.Modules.ProcessList
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
BasePill {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isVertical: axis?.isVertical ?? false
|
|
||||||
property var axis: null
|
|
||||||
property int availableWidth: 400
|
|
||||||
readonly property int baseWidth: contentRow.implicitWidth + Theme.spacingS * 2
|
|
||||||
readonly property int maxNormalWidth: 456
|
|
||||||
property real widgetThickness: 30
|
|
||||||
property real barThickness: 48
|
|
||||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
|
||||||
|
|
||||||
function formatNetworkSpeed(bytesPerSec) {
|
function formatNetworkSpeed(bytesPerSec) {
|
||||||
if (bytesPerSec < 1024) {
|
if (bytesPerSec < 1024) {
|
||||||
return bytesPerSec.toFixed(0) + " B/s";
|
return bytesPerSec.toFixed(0) + " B/s"
|
||||||
} else if (bytesPerSec < 1024 * 1024) {
|
} else if (bytesPerSec < 1024 * 1024) {
|
||||||
return (bytesPerSec / 1024).toFixed(1) + " KB/s";
|
return (bytesPerSec / 1024).toFixed(1) + " KB/s"
|
||||||
} else if (bytesPerSec < 1024 * 1024 * 1024) {
|
} else if (bytesPerSec < 1024 * 1024 * 1024) {
|
||||||
return (bytesPerSec / (1024 * 1024)).toFixed(1) + " MB/s";
|
return (bytesPerSec / (1024 * 1024)).toFixed(1) + " MB/s"
|
||||||
} else {
|
} else {
|
||||||
return (bytesPerSec / (1024 * 1024 * 1024)).toFixed(1) + " GB/s";
|
return (bytesPerSec / (1024 * 1024 * 1024)).toFixed(1) + " GB/s"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
width: isVertical ? widgetThickness : (contentRow.implicitWidth + horizontalPadding * 2)
|
|
||||||
height: isVertical ? (contentColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
|
|
||||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
|
||||||
color: {
|
|
||||||
if (SettingsData.dankBarNoBackground) {
|
|
||||||
return "transparent";
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseColor = networkArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
|
||||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
|
||||||
}
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
DgopService.addRef(["network"]);
|
DgopService.addRef(["network"])
|
||||||
}
|
}
|
||||||
Component.onDestruction: {
|
Component.onDestruction: {
|
||||||
DgopService.removeRef(["network"]);
|
DgopService.removeRef(["network"])
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
content: Component {
|
||||||
id: networkArea
|
Item {
|
||||||
|
implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : contentRow.implicitWidth
|
||||||
|
implicitHeight: root.isVerticalOrientation ? contentColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2)
|
||||||
|
|
||||||
anchors.fill: parent
|
Column {
|
||||||
hoverEnabled: true
|
id: contentColumn
|
||||||
cursorShape: Qt.PointingHandCursor
|
anchors.centerIn: parent
|
||||||
}
|
spacing: 2
|
||||||
|
visible: root.isVerticalOrientation
|
||||||
|
|
||||||
Column {
|
DankIcon {
|
||||||
id: contentColumn
|
name: "network_check"
|
||||||
|
size: Theme.barIconSize(root.barThickness)
|
||||||
anchors.centerIn: parent
|
color: Theme.surfaceText
|
||||||
spacing: 2
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
visible: root.isVertical
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "network_check"
|
|
||||||
size: Theme.barIconSize(barThickness)
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
const rate = DgopService.networkRxRate
|
|
||||||
if (rate < 1024) return rate.toFixed(0)
|
|
||||||
if (rate < 1024 * 1024) return (rate / 1024).toFixed(0) + "K"
|
|
||||||
return (rate / (1024 * 1024)).toFixed(0) + "M"
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.info
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
const rate = DgopService.networkTxRate
|
|
||||||
if (rate < 1024) return rate.toFixed(0)
|
|
||||||
if (rate < 1024 * 1024) return (rate / 1024).toFixed(0) + "K"
|
|
||||||
return (rate / (1024 * 1024)).toFixed(0) + "M"
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.error
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
id: contentRow
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: Theme.spacingS
|
|
||||||
visible: !root.isVertical
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "network_check"
|
|
||||||
size: Theme.barIconSize(barThickness)
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: 4
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: "↓"
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
color: Theme.info
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: DgopService.networkRxRate > 0 ? formatNetworkSpeed(DgopService.networkRxRate) : "0 B/s"
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
elide: Text.ElideNone
|
|
||||||
wrapMode: Text.NoWrap
|
|
||||||
|
|
||||||
StyledTextMetrics {
|
|
||||||
id: rxBaseline
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
text: "88.8 MB/s"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
width: Math.max(rxBaseline.width, paintedWidth)
|
StyledText {
|
||||||
|
text: {
|
||||||
|
const rate = DgopService.networkRxRate
|
||||||
|
if (rate < 1024) return rate.toFixed(0)
|
||||||
|
if (rate < 1024 * 1024) return (rate / 1024).toFixed(0) + "K"
|
||||||
|
return (rate / (1024 * 1024)).toFixed(0) + "M"
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.info
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
Behavior on width {
|
StyledText {
|
||||||
NumberAnimation {
|
text: {
|
||||||
duration: 120
|
const rate = DgopService.networkTxRate
|
||||||
easing.type: Easing.OutCubic
|
if (rate < 1024) return rate.toFixed(0)
|
||||||
|
if (rate < 1024 * 1024) return (rate / 1024).toFixed(0) + "K"
|
||||||
|
return (rate / (1024 * 1024)).toFixed(0) + "M"
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.error
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: contentRow
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
visible: !root.isVerticalOrientation
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "network_check"
|
||||||
|
size: Theme.barIconSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "↓"
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.info
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: DgopService.networkRxRate > 0 ? root.formatNetworkSpeed(DgopService.networkRxRate) : "0 B/s"
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
elide: Text.ElideNone
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
|
||||||
|
StyledTextMetrics {
|
||||||
|
id: rxBaseline
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
text: "88.8 MB/s"
|
||||||
|
}
|
||||||
|
|
||||||
|
width: Math.max(rxBaseline.width, paintedWidth)
|
||||||
|
|
||||||
|
Behavior on width {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 120
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "↑"
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.error
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: DgopService.networkTxRate > 0 ? root.formatNetworkSpeed(DgopService.networkTxRate) : "0 B/s"
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
elide: Text.ElideNone
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
|
||||||
|
StyledTextMetrics {
|
||||||
|
id: txBaseline
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
text: "88.8 MB/s"
|
||||||
|
}
|
||||||
|
|
||||||
|
width: Math.max(txBaseline.width, paintedWidth)
|
||||||
|
|
||||||
|
Behavior on width {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 120
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: 4
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: "↑"
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
color: Theme.error
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: DgopService.networkTxRate > 0 ? formatNetworkSpeed(DgopService.networkTxRate) : "0 B/s"
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
elide: Text.ElideNone
|
|
||||||
wrapMode: Text.NoWrap
|
|
||||||
|
|
||||||
StyledTextMetrics {
|
|
||||||
id: txBaseline
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
text: "88.8 MB/s"
|
|
||||||
}
|
|
||||||
|
|
||||||
width: Math.max(txBaseline.width, paintedWidth)
|
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 120
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,13 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell.Hyprland
|
import Quickshell.Hyprland
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Modules.Plugins
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
BasePill {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isVertical: axis?.isVertical ?? false
|
|
||||||
property var axis: null
|
|
||||||
property string section: "right"
|
|
||||||
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 clicked()
|
|
||||||
|
|
||||||
readonly property string focusedScreenName: (
|
readonly property string focusedScreenName: (
|
||||||
CompositorService.isHyprland && typeof Hyprland !== "undefined" && Hyprland.focusedWorkspace && Hyprland.focusedWorkspace.monitor ? (Hyprland.focusedWorkspace.monitor.name || "") :
|
CompositorService.isHyprland && typeof Hyprland !== "undefined" && Hyprland.focusedWorkspace && Hyprland.focusedWorkspace.monitor ? (Hyprland.focusedWorkspace.monitor.name || "") :
|
||||||
CompositorService.isNiri && typeof NiriService !== "undefined" && NiriService.currentOutput ? NiriService.currentOutput : ""
|
CompositorService.isNiri && typeof NiriService !== "undefined" && NiriService.currentOutput ? NiriService.currentOutput : ""
|
||||||
@@ -43,54 +34,30 @@ Rectangle {
|
|||||||
readonly property var notepadInstance: resolveNotepadInstance()
|
readonly property var notepadInstance: resolveNotepadInstance()
|
||||||
readonly property bool isActive: notepadInstance?.isVisible ?? false
|
readonly property bool isActive: notepadInstance?.isVisible ?? false
|
||||||
|
|
||||||
width: isVertical ? widgetThickness : (notepadIcon.width + horizontalPadding * 2)
|
content: Component {
|
||||||
height: isVertical ? (notepadIcon.height + horizontalPadding * 2) : widgetThickness
|
Item {
|
||||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
implicitWidth: root.widgetThickness - root.horizontalPadding * 2
|
||||||
color: {
|
implicitHeight: root.widgetThickness - root.horizontalPadding * 2
|
||||||
if (SettingsData.dankBarNoBackground) {
|
|
||||||
return "transparent";
|
DankIcon {
|
||||||
|
id: notepadIcon
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: "assignment"
|
||||||
|
size: Theme.barIconSize(root.barThickness, -4)
|
||||||
|
color: root.isActive ? Theme.primary : Theme.surfaceText
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseColor = notepadArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
|
||||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
|
||||||
}
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
id: notepadIcon
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
name: "assignment"
|
|
||||||
size: Theme.barIconSize(barThickness, -4)
|
|
||||||
color: notepadArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: 6
|
|
||||||
height: 6
|
|
||||||
radius: 3
|
|
||||||
color: Theme.primary
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.rightMargin: SettingsData.dankBarNoBackground ? 0 : 4
|
|
||||||
anchors.topMargin: SettingsData.dankBarNoBackground ? 0 : 4
|
|
||||||
visible: NotepadStorageService.tabs && NotepadStorageService.tabs.length > 0
|
|
||||||
opacity: 0.8
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: notepadArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
acceptedButtons: Qt.LeftButton
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
const inst = root.notepadInstance
|
const inst = root.notepadInstance
|
||||||
if (inst) {
|
if (inst) {
|
||||||
inst.toggle()
|
inst.toggle()
|
||||||
}
|
}
|
||||||
root.clicked()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,76 +1,36 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Modules.Plugins
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Item {
|
BasePill {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool hasUnread: false
|
property bool hasUnread: false
|
||||||
property bool isActive: false
|
property bool isActive: false
|
||||||
property bool isVertical: axis?.isVertical ?? false
|
|
||||||
property var axis: null
|
|
||||||
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 clicked()
|
content: Component {
|
||||||
|
Item {
|
||||||
|
implicitWidth: root.widgetThickness - root.horizontalPadding * 2
|
||||||
|
implicitHeight: root.widgetThickness - root.horizontalPadding * 2
|
||||||
|
|
||||||
width: widgetThickness
|
DankIcon {
|
||||||
height: widgetThickness
|
id: notifIcon
|
||||||
|
anchors.centerIn: parent
|
||||||
MouseArea {
|
name: SessionData.doNotDisturb ? "notifications_off" : "notifications"
|
||||||
id: notificationArea
|
size: Theme.barIconSize(root.barThickness, -4)
|
||||||
|
color: SessionData.doNotDisturb ? Theme.error : (root.isActive ? Theme.primary : Theme.surfaceText)
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
acceptedButtons: Qt.LeftButton
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
root.clicked()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: notificationContent
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
|
||||||
color: {
|
|
||||||
if (SettingsData.dankBarNoBackground) {
|
|
||||||
return "transparent"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseColor = notificationArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor
|
Rectangle {
|
||||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency)
|
width: 6
|
||||||
}
|
height: 6
|
||||||
|
radius: 3
|
||||||
DankIcon {
|
color: Theme.error
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.right: notifIcon.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.top: notifIcon.top
|
||||||
name: SessionData.doNotDisturb ? "notifications_off" : "notifications"
|
visible: root.hasUnread
|
||||||
size: Theme.barIconSize(barThickness, -4)
|
}
|
||||||
color: SessionData.doNotDisturb ? Theme.error : (notificationArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText)
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: 8
|
|
||||||
height: 8
|
|
||||||
radius: 4
|
|
||||||
color: Theme.error
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.rightMargin: SettingsData.dankBarNoBackground ? 0 : 6
|
|
||||||
anchors.topMargin: SettingsData.dankBarNoBackground ? 0 : 6
|
|
||||||
visible: root.hasUnread
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import qs.Common
|
|||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isVertical: axis?.isVertical ?? false
|
property bool isVertical: axis?.isVertical ?? false
|
||||||
@@ -19,26 +19,167 @@ Rectangle {
|
|||||||
readonly property int activeCount: PrivacyService.microphoneActive + PrivacyService.cameraActive + PrivacyService.screensharingActive
|
readonly property int activeCount: PrivacyService.microphoneActive + PrivacyService.cameraActive + PrivacyService.screensharingActive
|
||||||
readonly property real contentWidth: hasActivePrivacy ? (activeCount * 18 + (activeCount - 1) * Theme.spacingXS) : 0
|
readonly property real contentWidth: hasActivePrivacy ? (activeCount * 18 + (activeCount - 1) * Theme.spacingXS) : 0
|
||||||
readonly property real contentHeight: hasActivePrivacy ? (activeCount * 18 + (activeCount - 1) * Theme.spacingXS) : 0
|
readonly property real contentHeight: hasActivePrivacy ? (activeCount * 18 + (activeCount - 1) * Theme.spacingXS) : 0
|
||||||
|
readonly property real visualWidth: isVertical ? widgetThickness : (hasActivePrivacy ? (contentWidth + horizontalPadding * 2) : 0)
|
||||||
|
readonly property real visualHeight: isVertical ? (hasActivePrivacy ? (contentHeight + horizontalPadding * 2) : 0) : widgetThickness
|
||||||
|
|
||||||
width: isVertical ? widgetThickness : (hasActivePrivacy ? (contentWidth + horizontalPadding * 2) : 0)
|
width: isVertical ? barThickness : visualWidth
|
||||||
height: isVertical ? (hasActivePrivacy ? (contentHeight + horizontalPadding * 2) : 0) : (hasActivePrivacy ? widgetThickness : 0)
|
height: isVertical ? visualHeight : barThickness
|
||||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
|
||||||
visible: hasActivePrivacy
|
visible: hasActivePrivacy
|
||||||
opacity: hasActivePrivacy ? 1 : 0
|
opacity: hasActivePrivacy ? 1 : 0
|
||||||
enabled: hasActivePrivacy
|
enabled: hasActivePrivacy
|
||||||
color: {
|
|
||||||
if (SettingsData.dankBarNoBackground) {
|
Rectangle {
|
||||||
return "transparent";
|
id: visualContent
|
||||||
|
width: root.visualWidth
|
||||||
|
height: root.visualHeight
|
||||||
|
anchors.centerIn: parent
|
||||||
|
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
||||||
|
color: {
|
||||||
|
if (SettingsData.dankBarNoBackground) {
|
||||||
|
return "transparent"
|
||||||
|
}
|
||||||
|
|
||||||
|
return Qt.rgba(privacyArea.containsMouse ? Theme.errorPressed.r : Theme.errorHover.r, privacyArea.containsMouse ? Theme.errorPressed.g : Theme.errorHover.g, privacyArea.containsMouse ? Theme.errorPressed.b : Theme.errorHover.b, (privacyArea.containsMouse ? Theme.errorPressed.a : Theme.errorHover.a) * Theme.widgetTransparency)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Qt.rgba(privacyArea.containsMouse ? Theme.errorPressed.r : Theme.errorHover.r, privacyArea.containsMouse ? Theme.errorPressed.g : Theme.errorHover.g, privacyArea.containsMouse ? Theme.errorPressed.b : Theme.errorHover.b, (privacyArea.containsMouse ? Theme.errorPressed.a : Theme.errorHover.a) * Theme.widgetTransparency);
|
Column {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
visible: root.isVertical && root.hasActivePrivacy
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 18
|
||||||
|
height: 18
|
||||||
|
visible: PrivacyService.microphoneActive
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: {
|
||||||
|
const sourceAudio = AudioService.source?.audio
|
||||||
|
const muted = !sourceAudio || sourceAudio.muted || sourceAudio.volume === 0.0
|
||||||
|
if (muted) return "mic_off"
|
||||||
|
return "mic"
|
||||||
|
}
|
||||||
|
size: Theme.iconSizeSmall
|
||||||
|
color: Theme.error
|
||||||
|
filled: true
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 18
|
||||||
|
height: 18
|
||||||
|
visible: PrivacyService.cameraActive
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "camera_video"
|
||||||
|
size: Theme.iconSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
filled: true
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 6
|
||||||
|
height: 6
|
||||||
|
radius: 3
|
||||||
|
color: Theme.error
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.rightMargin: -2
|
||||||
|
anchors.topMargin: -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 18
|
||||||
|
height: 18
|
||||||
|
visible: PrivacyService.screensharingActive
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "screen_share"
|
||||||
|
size: Theme.iconSizeSmall
|
||||||
|
color: Theme.warning
|
||||||
|
filled: true
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
visible: !root.isVertical && root.hasActivePrivacy
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 18
|
||||||
|
height: 18
|
||||||
|
visible: PrivacyService.microphoneActive
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: {
|
||||||
|
const sourceAudio = AudioService.source?.audio
|
||||||
|
const muted = !sourceAudio || sourceAudio.muted || sourceAudio.volume === 0.0
|
||||||
|
if (muted) return "mic_off"
|
||||||
|
return "mic"
|
||||||
|
}
|
||||||
|
size: Theme.iconSizeSmall
|
||||||
|
color: Theme.error
|
||||||
|
filled: true
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 18
|
||||||
|
height: 18
|
||||||
|
visible: PrivacyService.cameraActive
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "camera_video"
|
||||||
|
size: Theme.iconSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
filled: true
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 6
|
||||||
|
height: 6
|
||||||
|
radius: 3
|
||||||
|
color: Theme.error
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.rightMargin: -2
|
||||||
|
anchors.topMargin: -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 18
|
||||||
|
height: 18
|
||||||
|
visible: PrivacyService.screensharingActive
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "screen_share"
|
||||||
|
size: Theme.iconSizeSmall
|
||||||
|
color: Theme.warning
|
||||||
|
filled: true
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
// Privacy indicator click handler
|
|
||||||
|
|
||||||
id: privacyArea
|
id: privacyArea
|
||||||
|
z: -1
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: hasActivePrivacy
|
hoverEnabled: hasActivePrivacy
|
||||||
enabled: hasActivePrivacy
|
enabled: hasActivePrivacy
|
||||||
@@ -47,141 +188,8 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: Theme.spacingXS
|
|
||||||
visible: root.isVertical && hasActivePrivacy
|
|
||||||
|
|
||||||
Item {
|
|
||||||
width: 18
|
|
||||||
height: 18
|
|
||||||
visible: PrivacyService.microphoneActive
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "mic"
|
|
||||||
size: Theme.iconSizeSmall
|
|
||||||
color: Theme.error
|
|
||||||
filled: true
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
width: 18
|
|
||||||
height: 18
|
|
||||||
visible: PrivacyService.cameraActive
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "camera_video"
|
|
||||||
size: Theme.iconSizeSmall
|
|
||||||
color: Theme.surfaceText
|
|
||||||
filled: true
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: 6
|
|
||||||
height: 6
|
|
||||||
radius: 3
|
|
||||||
color: Theme.error
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.rightMargin: -2
|
|
||||||
anchors.topMargin: -1
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
width: 18
|
|
||||||
height: 18
|
|
||||||
visible: PrivacyService.screensharingActive
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "screen_share"
|
|
||||||
size: Theme.iconSizeSmall
|
|
||||||
color: Theme.warning
|
|
||||||
filled: true
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: Theme.spacingXS
|
|
||||||
visible: !root.isVertical && hasActivePrivacy
|
|
||||||
|
|
||||||
Item {
|
|
||||||
width: 18
|
|
||||||
height: 18
|
|
||||||
visible: PrivacyService.microphoneActive
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "mic"
|
|
||||||
size: Theme.iconSizeSmall
|
|
||||||
color: Theme.error
|
|
||||||
filled: true
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
width: 18
|
|
||||||
height: 18
|
|
||||||
visible: PrivacyService.cameraActive
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "camera_video"
|
|
||||||
size: Theme.iconSizeSmall
|
|
||||||
color: Theme.surfaceText
|
|
||||||
filled: true
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: 6
|
|
||||||
height: 6
|
|
||||||
radius: 3
|
|
||||||
color: Theme.error
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.rightMargin: -2
|
|
||||||
anchors.topMargin: -1
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
width: 18
|
|
||||||
height: 18
|
|
||||||
visible: PrivacyService.screensharingActive
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "screen_share"
|
|
||||||
size: Theme.iconSizeSmall
|
|
||||||
color: Theme.warning
|
|
||||||
filled: true
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: tooltip
|
id: tooltip
|
||||||
|
|
||||||
width: tooltipText.contentWidth + Theme.spacingM * 2
|
width: tooltipText.contentWidth + Theme.spacingM * 2
|
||||||
height: tooltipText.contentHeight + Theme.spacingS * 2
|
height: tooltipText.contentHeight + Theme.spacingS * 2
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
@@ -196,7 +204,6 @@ Rectangle {
|
|||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
id: tooltipText
|
id: tooltipText
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: PrivacyService.getPrivacySummary()
|
text: PrivacyService.getPrivacySummary()
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
font.pixelSize: Theme.barTextSize(barThickness)
|
||||||
@@ -222,9 +229,7 @@ Rectangle {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on width {
|
Behavior on width {
|
||||||
@@ -234,7 +239,6 @@ Rectangle {
|
|||||||
duration: Theme.mediumDuration
|
duration: Theme.mediumDuration
|
||||||
easing.type: Theme.emphasizedEasing
|
easing.type: Theme.emphasizedEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on height {
|
Behavior on height {
|
||||||
@@ -244,7 +248,5 @@ Rectangle {
|
|||||||
duration: Theme.mediumDuration
|
duration: Theme.mediumDuration
|
||||||
easing.type: Theme.emphasizedEasing
|
easing.type: Theme.emphasizedEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,19 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Modules.Plugins
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
BasePill {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isVertical: axis?.isVertical ?? false
|
|
||||||
property var axis: null
|
|
||||||
property bool showPercentage: true
|
property bool showPercentage: true
|
||||||
property bool showIcon: true
|
property bool showIcon: true
|
||||||
property var toggleProcessList
|
property var toggleProcessList
|
||||||
property string section: "right"
|
property var popoutTarget: null
|
||||||
property var popupTarget: null
|
|
||||||
property var parentScreen: null
|
|
||||||
property real barThickness: 48
|
|
||||||
property real widgetThickness: 30
|
|
||||||
property var widgetData: null
|
property var widgetData: null
|
||||||
property bool minimumWidth: (widgetData && widgetData.minimumWidth !== undefined) ? widgetData.minimumWidth : true
|
property bool minimumWidth: (widgetData && widgetData.minimumWidth !== undefined) ? widgetData.minimumWidth : true
|
||||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
|
||||||
|
|
||||||
width: isVertical ? widgetThickness : (ramContent.implicitWidth + horizontalPadding * 2)
|
|
||||||
height: isVertical ? (ramColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
|
|
||||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
|
||||||
color: {
|
|
||||||
if (SettingsData.dankBarNoBackground) {
|
|
||||||
return "transparent";
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseColor = ramArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
|
||||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
DgopService.addRef(["memory"]);
|
DgopService.addRef(["memory"]);
|
||||||
@@ -40,120 +22,119 @@ Rectangle {
|
|||||||
DgopService.removeRef(["memory"]);
|
DgopService.removeRef(["memory"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
content: Component {
|
||||||
id: ramArea
|
Item {
|
||||||
|
implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : ramContent.implicitWidth
|
||||||
|
implicitHeight: root.isVerticalOrientation ? ramColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2)
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: ramColumn
|
||||||
|
visible: root.isVerticalOrientation
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 1
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "developer_board"
|
||||||
|
size: Theme.barIconSize(root.barThickness)
|
||||||
|
color: {
|
||||||
|
if (DgopService.memoryUsage > 90) {
|
||||||
|
return Theme.tempDanger;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DgopService.memoryUsage > 75) {
|
||||||
|
return Theme.tempWarning;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Theme.surfaceText;
|
||||||
|
}
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
if (DgopService.memoryUsage === undefined || DgopService.memoryUsage === null || DgopService.memoryUsage === 0) {
|
||||||
|
return "--";
|
||||||
|
}
|
||||||
|
|
||||||
|
return DgopService.memoryUsage.toFixed(0);
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: ramContent
|
||||||
|
visible: !root.isVerticalOrientation
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 3
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "developer_board"
|
||||||
|
size: Theme.barIconSize(root.barThickness)
|
||||||
|
color: {
|
||||||
|
if (DgopService.memoryUsage > 90) {
|
||||||
|
return Theme.tempDanger;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DgopService.memoryUsage > 75) {
|
||||||
|
return Theme.tempWarning;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Theme.surfaceText;
|
||||||
|
}
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
if (DgopService.memoryUsage === undefined || DgopService.memoryUsage === null || DgopService.memoryUsage === 0) {
|
||||||
|
return "--%";
|
||||||
|
}
|
||||||
|
|
||||||
|
return DgopService.memoryUsage.toFixed(0) + "%";
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
elide: Text.ElideNone
|
||||||
|
|
||||||
|
StyledTextMetrics {
|
||||||
|
id: ramBaseline
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
text: "100%"
|
||||||
|
}
|
||||||
|
|
||||||
|
width: root.minimumWidth ? Math.max(ramBaseline.width, paintedWidth) : paintedWidth
|
||||||
|
|
||||||
|
Behavior on width {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 120
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
acceptedButtons: Qt.LeftButton
|
||||||
onPressed: {
|
onPressed: {
|
||||||
if (popupTarget && popupTarget.setTriggerPosition) {
|
if (popoutTarget && popoutTarget.setTriggerPosition) {
|
||||||
const globalPos = mapToGlobal(0, 0)
|
const globalPos = root.visualContent.mapToGlobal(0, 0)
|
||||||
const currentScreen = parentScreen || Screen
|
const currentScreen = parentScreen || Screen
|
||||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
|
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth)
|
||||||
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
||||||
}
|
}
|
||||||
DgopService.setSortBy("memory");
|
DgopService.setSortBy("memory");
|
||||||
if (root.toggleProcessList) {
|
if (root.toggleProcessList) {
|
||||||
root.toggleProcessList();
|
root.toggleProcessList();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
|
||||||
id: ramColumn
|
|
||||||
visible: root.isVertical
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 1
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "developer_board"
|
|
||||||
size: Theme.barIconSize(barThickness)
|
|
||||||
color: {
|
|
||||||
if (DgopService.memoryUsage > 90) {
|
|
||||||
return Theme.tempDanger;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DgopService.memoryUsage > 75) {
|
|
||||||
return Theme.tempWarning;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme.surfaceText;
|
|
||||||
}
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
if (DgopService.memoryUsage === undefined || DgopService.memoryUsage === null || DgopService.memoryUsage === 0) {
|
|
||||||
return "--";
|
|
||||||
}
|
|
||||||
|
|
||||||
return DgopService.memoryUsage.toFixed(0);
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
id: ramContent
|
|
||||||
visible: !root.isVertical
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 3
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "developer_board"
|
|
||||||
size: Theme.barIconSize(barThickness)
|
|
||||||
color: {
|
|
||||||
if (DgopService.memoryUsage > 90) {
|
|
||||||
return Theme.tempDanger;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DgopService.memoryUsage > 75) {
|
|
||||||
return Theme.tempWarning;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme.surfaceText;
|
|
||||||
}
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
if (DgopService.memoryUsage === undefined || DgopService.memoryUsage === null || DgopService.memoryUsage === 0) {
|
|
||||||
return "--%";
|
|
||||||
}
|
|
||||||
|
|
||||||
return DgopService.memoryUsage.toFixed(0) + "%";
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
elide: Text.ElideNone
|
|
||||||
|
|
||||||
StyledTextMetrics {
|
|
||||||
id: ramBaseline
|
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
font.weight: Font.Medium
|
|
||||||
text: "100%"
|
|
||||||
}
|
|
||||||
|
|
||||||
width: root.minimumWidth ? Math.max(ramBaseline.width, paintedWidth) : paintedWidth
|
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 120
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ import Quickshell.Widgets
|
|||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isVertical: axis?.isVertical ?? false
|
property bool isVertical: axis?.isVertical ?? false
|
||||||
@@ -15,26 +15,49 @@ Rectangle {
|
|||||||
property var parentWindow: null
|
property var parentWindow: null
|
||||||
property var parentScreen: null
|
property var parentScreen: null
|
||||||
property real widgetThickness: 30
|
property real widgetThickness: 30
|
||||||
|
property real barThickness: 48
|
||||||
property bool isAtBottom: false
|
property bool isAtBottom: false
|
||||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS
|
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS
|
||||||
readonly property int calculatedSize: SystemTray.items.values.length > 0 ? SystemTray.items.values.length * 24 + horizontalPadding * 2 : 0
|
readonly property var hiddenTrayIds: {
|
||||||
|
const envValue = Quickshell.env("DMS_HIDE_TRAYIDS") || ""
|
||||||
width: isVertical ? widgetThickness : calculatedSize
|
return envValue ? envValue.split(",").map(id => id.trim().toLowerCase()) : []
|
||||||
height: isVertical ? calculatedSize : widgetThickness
|
}
|
||||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
readonly property var visibleTrayItems: {
|
||||||
color: {
|
if (!hiddenTrayIds.length) {
|
||||||
if (SystemTray.items.values.length === 0) {
|
return SystemTray.items.values
|
||||||
return "transparent";
|
}
|
||||||
}
|
return SystemTray.items.values.filter(item => {
|
||||||
|
const itemId = item?.id || ""
|
||||||
if (SettingsData.dankBarNoBackground) {
|
return !hiddenTrayIds.includes(itemId.toLowerCase())
|
||||||
return "transparent";
|
})
|
||||||
}
|
}
|
||||||
|
readonly property int calculatedSize: visibleTrayItems.length > 0 ? visibleTrayItems.length * 24 + horizontalPadding * 2 : 0
|
||||||
const baseColor = Theme.widgetBaseBackgroundColor;
|
readonly property real visualWidth: isVertical ? widgetThickness : calculatedSize
|
||||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
readonly property real visualHeight: isVertical ? calculatedSize : widgetThickness
|
||||||
|
|
||||||
|
width: isVertical ? barThickness : visualWidth
|
||||||
|
height: isVertical ? visualHeight : barThickness
|
||||||
|
visible: visibleTrayItems.length > 0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: visualBackground
|
||||||
|
width: root.visualWidth
|
||||||
|
height: root.visualHeight
|
||||||
|
anchors.centerIn: parent
|
||||||
|
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
||||||
|
color: {
|
||||||
|
if (visibleTrayItems.length === 0) {
|
||||||
|
return "transparent";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SettingsData.dankBarNoBackground) {
|
||||||
|
return "transparent";
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseColor = Theme.widgetBaseBackgroundColor;
|
||||||
|
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
visible: SystemTray.items.values.length > 0
|
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: layoutLoader
|
id: layoutLoader
|
||||||
@@ -48,84 +71,100 @@ Rectangle {
|
|||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: SystemTray.items.values
|
model: root.visibleTrayItems
|
||||||
|
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
property var trayItem: modelData
|
id: delegateRoot
|
||||||
property string iconSource: {
|
property var trayItem: modelData
|
||||||
let icon = trayItem && trayItem.icon;
|
property string iconSource: {
|
||||||
if (typeof icon === 'string' || icon instanceof String) {
|
let icon = trayItem && trayItem.icon;
|
||||||
if (icon === "") {
|
if (typeof icon === 'string' || icon instanceof String) {
|
||||||
return "";
|
if (icon === "") {
|
||||||
}
|
return "";
|
||||||
if (icon.includes("?path=")) {
|
|
||||||
const split = icon.split("?path=");
|
|
||||||
if (split.length !== 2) {
|
|
||||||
return icon;
|
|
||||||
}
|
}
|
||||||
|
if (icon.includes("?path=")) {
|
||||||
|
const split = icon.split("?path=");
|
||||||
|
if (split.length !== 2) {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
const name = split[0];
|
const name = split[0];
|
||||||
const path = split[1];
|
const path = split[1];
|
||||||
let fileName = name.substring(name.lastIndexOf("/") + 1);
|
let fileName = name.substring(name.lastIndexOf("/") + 1);
|
||||||
if (fileName.startsWith("dropboxstatus")) {
|
if (fileName.startsWith("dropboxstatus")) {
|
||||||
fileName = `hicolor/16x16/status/${fileName}`;
|
fileName = `hicolor/16x16/status/${fileName}`;
|
||||||
|
}
|
||||||
|
return `file://${path}/${fileName}`;
|
||||||
}
|
}
|
||||||
return `file://${path}/${fileName}`;
|
if (icon.startsWith("/") && !icon.startsWith("file://")) {
|
||||||
|
return `file://${icon}`;
|
||||||
|
}
|
||||||
|
return icon;
|
||||||
}
|
}
|
||||||
if (icon.startsWith("/") && !icon.startsWith("file://")) {
|
return "";
|
||||||
return `file://${icon}`;
|
|
||||||
}
|
|
||||||
return icon;
|
|
||||||
}
|
}
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
width: 24
|
width: 24
|
||||||
height: 24
|
height: root.barThickness
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
id: visualContent
|
||||||
radius: Theme.cornerRadius
|
width: 24
|
||||||
color: trayItemArea.containsMouse ? Theme.primaryHover : "transparent"
|
height: 24
|
||||||
|
anchors.centerIn: parent
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: trayItemArea.containsMouse ? Theme.primaryHover : "transparent"
|
||||||
|
|
||||||
|
IconImage {
|
||||||
}
|
id: iconImg
|
||||||
|
anchors.centerIn: parent
|
||||||
IconImage {
|
width: Theme.barIconSize(root.barThickness)
|
||||||
anchors.centerIn: parent
|
height: Theme.barIconSize(root.barThickness)
|
||||||
width: 16
|
source: delegateRoot.iconSource
|
||||||
height: 16
|
asynchronous: true
|
||||||
source: parent.iconSource
|
smooth: true
|
||||||
asynchronous: true
|
mipmap: true
|
||||||
smooth: true
|
visible: status === Image.Ready
|
||||||
mipmap: true
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: trayItemArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: (mouse) => {
|
|
||||||
if (!trayItem) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mouse.button === Qt.LeftButton && !trayItem.onlyMenu) {
|
Text {
|
||||||
trayItem.activate();
|
anchors.centerIn: parent
|
||||||
return ;
|
visible: !iconImg.visible
|
||||||
|
text: {
|
||||||
|
const itemId = trayItem?.id || ""
|
||||||
|
if (!itemId) {
|
||||||
|
return "?"
|
||||||
|
}
|
||||||
|
return itemId.charAt(0).toUpperCase()
|
||||||
|
}
|
||||||
|
font.pixelSize: 10
|
||||||
|
color: Theme.surfaceText
|
||||||
}
|
}
|
||||||
if (trayItem.hasMenu) {
|
}
|
||||||
root.showForTrayItem(trayItem, parent, parentScreen, root.isAtBottom, root.isVertical, root.axis);
|
|
||||||
|
MouseArea {
|
||||||
|
id: trayItemArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: (mouse) => {
|
||||||
|
if (!delegateRoot.trayItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mouse.button === Qt.LeftButton && !delegateRoot.trayItem.onlyMenu) {
|
||||||
|
delegateRoot.trayItem.activate();
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
if (delegateRoot.trayItem.hasMenu) {
|
||||||
|
root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVertical, root.axis);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,79 +174,100 @@ Rectangle {
|
|||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: SystemTray.items.values
|
model: root.visibleTrayItems
|
||||||
|
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
property var trayItem: modelData
|
id: delegateRoot
|
||||||
property string iconSource: {
|
property var trayItem: modelData
|
||||||
let icon = trayItem && trayItem.icon;
|
property string iconSource: {
|
||||||
if (typeof icon === 'string' || icon instanceof String) {
|
let icon = trayItem && trayItem.icon;
|
||||||
if (icon === "") {
|
if (typeof icon === 'string' || icon instanceof String) {
|
||||||
return "";
|
if (icon === "") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (icon.includes("?path=")) {
|
||||||
|
const split = icon.split("?path=");
|
||||||
|
if (split.length !== 2) {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = split[0];
|
||||||
|
const path = split[1];
|
||||||
|
let fileName = name.substring(name.lastIndexOf("/") + 1);
|
||||||
|
if (fileName.startsWith("dropboxstatus")) {
|
||||||
|
fileName = `hicolor/16x16/status/${fileName}`;
|
||||||
|
}
|
||||||
|
return `file://${path}/${fileName}`;
|
||||||
|
}
|
||||||
|
if (icon.startsWith("/") && !icon.startsWith("file://")) {
|
||||||
|
return `file://${icon}`;
|
||||||
|
}
|
||||||
|
return icon;
|
||||||
}
|
}
|
||||||
if (icon.includes("?path=")) {
|
return "";
|
||||||
const split = icon.split("?path=");
|
}
|
||||||
if (split.length !== 2) {
|
|
||||||
return icon;
|
width: root.barThickness
|
||||||
|
height: 24
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: visualContent
|
||||||
|
width: 24
|
||||||
|
height: 24
|
||||||
|
anchors.centerIn: parent
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: trayItemArea.containsMouse ? Theme.primaryHover : "transparent"
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
id: iconImg
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: Theme.barIconSize(root.barThickness)
|
||||||
|
height: Theme.barIconSize(root.barThickness)
|
||||||
|
source: delegateRoot.iconSource
|
||||||
|
asynchronous: true
|
||||||
|
smooth: true
|
||||||
|
mipmap: true
|
||||||
|
visible: status === Image.Ready
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
visible: !iconImg.visible
|
||||||
|
text: {
|
||||||
|
const itemId = trayItem?.id || ""
|
||||||
|
if (!itemId) {
|
||||||
|
return "?"
|
||||||
|
}
|
||||||
|
return itemId.charAt(0).toUpperCase()
|
||||||
|
}
|
||||||
|
font.pixelSize: 10
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: trayItemArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: (mouse) => {
|
||||||
|
if (!delegateRoot.trayItem) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const name = split[0];
|
if (mouse.button === Qt.LeftButton && !delegateRoot.trayItem.onlyMenu) {
|
||||||
const path = split[1];
|
delegateRoot.trayItem.activate();
|
||||||
const fileName = name.substring(name.lastIndexOf("/") + 1);
|
return ;
|
||||||
return `file://${path}/${fileName}`;
|
}
|
||||||
}
|
if (delegateRoot.trayItem.hasMenu) {
|
||||||
if (icon.startsWith("/") && !icon.startsWith("file://")) {
|
root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVertical, root.axis);
|
||||||
return `file://${icon}`;
|
}
|
||||||
}
|
|
||||||
return icon;
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
width: 24
|
|
||||||
height: 24
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: trayItemArea.containsMouse ? Theme.primaryHover : "transparent"
|
|
||||||
}
|
|
||||||
|
|
||||||
IconImage {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: 16
|
|
||||||
height: 16
|
|
||||||
source: parent.iconSource
|
|
||||||
asynchronous: true
|
|
||||||
smooth: true
|
|
||||||
mipmap: true
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: trayItemArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: (mouse) => {
|
|
||||||
if (!trayItem) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mouse.button === Qt.LeftButton && !trayItem.onlyMenu) {
|
|
||||||
trayItem.activate();
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
if (trayItem.hasMenu) {
|
|
||||||
root.showForTrayItem(trayItem, parent, parentScreen, root.isAtBottom, root.isVertical, root.axis);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,7 +518,6 @@ Rectangle {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
id: backArea
|
id: backArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: menuRoot.goBack()
|
onClicked: menuRoot.goBack()
|
||||||
}
|
}
|
||||||
@@ -494,7 +553,6 @@ Rectangle {
|
|||||||
id: itemArea
|
id: itemArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
enabled: !menuEntry?.isSeparator && (menuEntry?.enabled !== false)
|
enabled: !menuEntry?.isSeparator && (menuEntry?.enabled !== false)
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
|||||||
@@ -1,154 +1,136 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Modules.Plugins
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
BasePill {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isVertical: axis?.isVertical ?? false
|
|
||||||
property var axis: null
|
|
||||||
property bool isActive: false
|
property bool isActive: 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))
|
|
||||||
readonly property bool hasUpdates: SystemUpdateService.updateCount > 0
|
readonly property bool hasUpdates: SystemUpdateService.updateCount > 0
|
||||||
readonly property bool isChecking: SystemUpdateService.isChecking
|
readonly property bool isChecking: SystemUpdateService.isChecking
|
||||||
|
|
||||||
signal clicked()
|
Ref {
|
||||||
|
service: SystemUpdateService
|
||||||
width: isVertical ? widgetThickness : (updaterIcon.width + horizontalPadding * 2)
|
|
||||||
height: isVertical ? widgetThickness : widgetThickness
|
|
||||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
|
||||||
color: {
|
|
||||||
if (SettingsData.dankBarNoBackground) {
|
|
||||||
return "transparent";
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseColor = updaterArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
|
||||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DankIcon {
|
content: Component {
|
||||||
id: statusIcon
|
Item {
|
||||||
|
implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : updaterIcon.implicitWidth
|
||||||
|
implicitHeight: root.widgetThickness - root.horizontalPadding * 2
|
||||||
|
|
||||||
anchors.centerIn: parent
|
DankIcon {
|
||||||
visible: root.isVertical
|
id: statusIcon
|
||||||
name: {
|
anchors.centerIn: parent
|
||||||
if (isChecking) return "refresh";
|
visible: root.isVerticalOrientation
|
||||||
if (SystemUpdateService.hasError) return "error";
|
name: {
|
||||||
if (hasUpdates) return "system_update_alt";
|
if (root.isChecking) return "refresh"
|
||||||
return "check_circle";
|
if (SystemUpdateService.hasError) return "error"
|
||||||
}
|
if (root.hasUpdates) return "system_update_alt"
|
||||||
size: Theme.barIconSize(barThickness, -4)
|
return "check_circle"
|
||||||
color: {
|
}
|
||||||
if (SystemUpdateService.hasError) return Theme.error;
|
size: Theme.barIconSize(root.barThickness, -4)
|
||||||
if (hasUpdates) return Theme.primary;
|
color: {
|
||||||
return (updaterArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText);
|
if (SystemUpdateService.hasError) return Theme.error
|
||||||
}
|
if (root.hasUpdates) return Theme.primary
|
||||||
|
return root.isActive ? Theme.primary : Theme.surfaceText
|
||||||
RotationAnimation {
|
|
||||||
id: rotationAnimation
|
|
||||||
target: statusIcon
|
|
||||||
property: "rotation"
|
|
||||||
from: 0
|
|
||||||
to: 360
|
|
||||||
duration: 1000
|
|
||||||
running: isChecking
|
|
||||||
loops: Animation.Infinite
|
|
||||||
|
|
||||||
onRunningChanged: {
|
|
||||||
if (!running) {
|
|
||||||
statusIcon.rotation = 0
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
RotationAnimation {
|
||||||
width: 8
|
id: rotationAnimation
|
||||||
height: 8
|
target: statusIcon
|
||||||
radius: 4
|
property: "rotation"
|
||||||
color: Theme.error
|
from: 0
|
||||||
anchors.right: parent.right
|
to: 360
|
||||||
anchors.top: parent.top
|
duration: 1000
|
||||||
anchors.rightMargin: SettingsData.dankBarNoBackground ? 0 : 6
|
running: root.isChecking
|
||||||
anchors.topMargin: SettingsData.dankBarNoBackground ? 0 : 6
|
loops: Animation.Infinite
|
||||||
visible: root.isVertical && root.hasUpdates && !root.isChecking
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
onRunningChanged: {
|
||||||
id: updaterIcon
|
if (!running) {
|
||||||
|
statusIcon.rotation = 0
|
||||||
anchors.centerIn: parent
|
}
|
||||||
spacing: Theme.spacingXS
|
|
||||||
visible: !root.isVertical
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
id: statusIconHorizontal
|
|
||||||
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
name: {
|
|
||||||
if (isChecking) return "refresh";
|
|
||||||
if (SystemUpdateService.hasError) return "error";
|
|
||||||
if (hasUpdates) return "system_update_alt";
|
|
||||||
return "check_circle";
|
|
||||||
}
|
|
||||||
size: Theme.barIconSize(barThickness, -4)
|
|
||||||
color: {
|
|
||||||
if (SystemUpdateService.hasError) return Theme.error;
|
|
||||||
if (hasUpdates) return Theme.primary;
|
|
||||||
return (updaterArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText);
|
|
||||||
}
|
|
||||||
|
|
||||||
RotationAnimation {
|
|
||||||
id: rotationAnimationHorizontal
|
|
||||||
target: statusIconHorizontal
|
|
||||||
property: "rotation"
|
|
||||||
from: 0
|
|
||||||
to: 360
|
|
||||||
duration: 1000
|
|
||||||
running: isChecking
|
|
||||||
loops: Animation.Infinite
|
|
||||||
|
|
||||||
onRunningChanged: {
|
|
||||||
if (!running) {
|
|
||||||
statusIconHorizontal.rotation = 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
Rectangle {
|
||||||
id: countText
|
width: 8
|
||||||
|
height: 8
|
||||||
|
radius: 4
|
||||||
|
color: Theme.error
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.rightMargin: SettingsData.dankBarNoBackground ? 0 : 6
|
||||||
|
anchors.topMargin: SettingsData.dankBarNoBackground ? 0 : 6
|
||||||
|
visible: root.isVerticalOrientation && root.hasUpdates && !root.isChecking
|
||||||
|
}
|
||||||
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
Row {
|
||||||
text: SystemUpdateService.updateCount.toString()
|
id: updaterIcon
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
anchors.centerIn: parent
|
||||||
font.weight: Font.Medium
|
spacing: Theme.spacingXS
|
||||||
color: Theme.surfaceText
|
visible: !root.isVerticalOrientation
|
||||||
visible: hasUpdates && !isChecking
|
|
||||||
|
DankIcon {
|
||||||
|
id: statusIconHorizontal
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
name: {
|
||||||
|
if (root.isChecking) return "refresh"
|
||||||
|
if (SystemUpdateService.hasError) return "error"
|
||||||
|
if (root.hasUpdates) return "system_update_alt"
|
||||||
|
return "check_circle"
|
||||||
|
}
|
||||||
|
size: Theme.barIconSize(root.barThickness, -4)
|
||||||
|
color: {
|
||||||
|
if (SystemUpdateService.hasError) return Theme.error
|
||||||
|
if (root.hasUpdates) return Theme.primary
|
||||||
|
return root.isActive ? Theme.primary : Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
RotationAnimation {
|
||||||
|
id: rotationAnimationHorizontal
|
||||||
|
target: statusIconHorizontal
|
||||||
|
property: "rotation"
|
||||||
|
from: 0
|
||||||
|
to: 360
|
||||||
|
duration: 1000
|
||||||
|
running: root.isChecking
|
||||||
|
loops: Animation.Infinite
|
||||||
|
|
||||||
|
onRunningChanged: {
|
||||||
|
if (!running) {
|
||||||
|
statusIconHorizontal.rotation = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: countText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: SystemUpdateService.updateCount.toString()
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
visible: root.hasUpdates && !root.isChecking
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: updaterArea
|
z: 1
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onPressed: {
|
onPressed: {
|
||||||
if (popupTarget && popupTarget.setTriggerPosition) {
|
if (popoutTarget && popoutTarget.setTriggerPosition) {
|
||||||
const globalPos = mapToGlobal(0, 0)
|
const globalPos = root.visualContent.mapToGlobal(0, 0)
|
||||||
const currentScreen = parentScreen || Screen
|
const currentScreen = parentScreen || Screen
|
||||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
|
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth)
|
||||||
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
||||||
}
|
}
|
||||||
root.clicked();
|
root.clicked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,46 +1,44 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Modules.Plugins
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
BasePill {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
Ref {
|
Ref {
|
||||||
service: VpnService
|
service: DMSNetworkService
|
||||||
}
|
}
|
||||||
|
|
||||||
property bool isVertical: axis?.isVertical ?? false
|
property var popoutTarget: null
|
||||||
property var axis: null
|
property bool isHovered: clickArea.containsMouse
|
||||||
property int widgetThickness: 28
|
|
||||||
property int barThickness: 32
|
|
||||||
property string section: "right"
|
|
||||||
property var popupTarget: null
|
|
||||||
property var parentScreen: null
|
|
||||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
|
||||||
|
|
||||||
signal toggleVpnPopup()
|
signal toggleVpnPopup()
|
||||||
|
|
||||||
width: isVertical ? widgetThickness : (Theme.iconSize + horizontalPadding * 2)
|
content: Component {
|
||||||
height: isVertical ? (Theme.iconSize + horizontalPadding * 2) : widgetThickness
|
Item {
|
||||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
implicitWidth: root.widgetThickness - root.horizontalPadding * 2
|
||||||
color: {
|
implicitHeight: root.widgetThickness - root.horizontalPadding * 2
|
||||||
if (SettingsData.dankBarNoBackground) {
|
|
||||||
return "transparent";
|
DankIcon {
|
||||||
|
id: icon
|
||||||
|
|
||||||
|
name: DMSNetworkService.connected ? "vpn_lock" : "vpn_key_off"
|
||||||
|
size: Theme.barIconSize(root.barThickness, -4)
|
||||||
|
color: DMSNetworkService.connected ? Theme.primary : Theme.surfaceText
|
||||||
|
opacity: DMSNetworkService.isBusy ? 0.5 : 1.0
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseColor = clickArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
|
||||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
|
||||||
}
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
id: icon
|
|
||||||
|
|
||||||
name: VpnService.isBusy ? "sync" : (VpnService.connected ? "vpn_lock" : "vpn_key_off")
|
|
||||||
size: Theme.barIconSize(barThickness, -4)
|
|
||||||
color: VpnService.connected ? Theme.primary : Theme.surfaceText
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
@@ -54,33 +52,41 @@ Rectangle {
|
|||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: DMSNetworkService.isBusy ? Qt.BusyCursor : Qt.PointingHandCursor
|
||||||
|
acceptedButtons: Qt.LeftButton
|
||||||
|
enabled: !DMSNetworkService.isBusy
|
||||||
onPressed: {
|
onPressed: {
|
||||||
if (popupTarget && popupTarget.setTriggerPosition) {
|
if (popoutTarget && popoutTarget.setTriggerPosition) {
|
||||||
const globalPos = mapToGlobal(0, 0)
|
const globalPos = root.visualContent.mapToGlobal(0, 0)
|
||||||
const currentScreen = parentScreen || Screen
|
const currentScreen = parentScreen || Screen
|
||||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
|
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth)
|
||||||
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
||||||
}
|
}
|
||||||
root.toggleVpnPopup();
|
root.toggleVpnPopup();
|
||||||
}
|
}
|
||||||
onEntered: {
|
onEntered: {
|
||||||
if (root.parentScreen && !(popupTarget && popupTarget.shouldBeVisible)) {
|
if (root.parentScreen && !(popoutTarget && popoutTarget.shouldBeVisible)) {
|
||||||
tooltipLoader.active = true
|
tooltipLoader.active = true
|
||||||
if (tooltipLoader.item) {
|
if (tooltipLoader.item) {
|
||||||
let tooltipText = ""
|
let tooltipText = ""
|
||||||
if (!VpnService.connected) {
|
if (!DMSNetworkService.connected) {
|
||||||
tooltipText = "VPN Disconnected"
|
tooltipText = "VPN Disconnected"
|
||||||
} else {
|
} else {
|
||||||
const names = VpnService.activeNames || []
|
const names = DMSNetworkService.activeNames || []
|
||||||
if (names.length <= 1) {
|
if (names.length <= 1) {
|
||||||
tooltipText = "VPN Connected • " + (names[0] || "")
|
const name = names[0] || ""
|
||||||
|
const maxLength = 25
|
||||||
|
const displayName = name.length > maxLength ? name.substring(0, maxLength) + "..." : name
|
||||||
|
tooltipText = "VPN Connected • " + displayName
|
||||||
} else {
|
} else {
|
||||||
tooltipText = "VPN Connected • " + names[0] + " +" + (names.length - 1)
|
const name = names[0]
|
||||||
|
const maxLength = 20
|
||||||
|
const displayName = name.length > maxLength ? name.substring(0, maxLength) + "..." : name
|
||||||
|
tooltipText = "VPN Connected • " + displayName + " +" + (names.length - 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.isVertical) {
|
if (root.isVerticalOrientation) {
|
||||||
const globalPos = mapToGlobal(width / 2, height / 2)
|
const globalPos = mapToGlobal(width / 2, height / 2)
|
||||||
const screenX = root.parentScreen ? root.parentScreen.x : 0
|
const screenX = root.parentScreen ? root.parentScreen.x : 0
|
||||||
const screenY = root.parentScreen ? root.parentScreen.y : 0
|
const screenY = root.parentScreen ? root.parentScreen.y : 0
|
||||||
@@ -103,5 +109,4 @@ Rectangle {
|
|||||||
tooltipLoader.active = false
|
tooltipLoader.active = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,120 +1,81 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Modules.Plugins
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
BasePill {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isVertical: axis?.isVertical ?? false
|
|
||||||
property var axis: null
|
|
||||||
property string section: "center"
|
|
||||||
property var popupTarget: null
|
|
||||||
property var parentScreen: null
|
|
||||||
property real barThickness: 48
|
|
||||||
property real widgetThickness: 30
|
|
||||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS
|
|
||||||
|
|
||||||
signal clicked()
|
|
||||||
|
|
||||||
visible: SettingsData.weatherEnabled
|
visible: SettingsData.weatherEnabled
|
||||||
width: isVertical ? widgetThickness : (visible ? Math.min(100, weatherRow.implicitWidth + horizontalPadding * 2) : 0)
|
|
||||||
height: isVertical ? (weatherColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
|
|
||||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
|
||||||
color: {
|
|
||||||
if (SettingsData.dankBarNoBackground) {
|
|
||||||
return "transparent";
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseColor = weatherArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
|
||||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref {
|
Ref {
|
||||||
service: WeatherService
|
service: WeatherService
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
content: Component {
|
||||||
id: weatherColumn
|
Item {
|
||||||
visible: root.isVertical
|
implicitWidth: {
|
||||||
anchors.centerIn: parent
|
if (!SettingsData.weatherEnabled) return 0
|
||||||
spacing: 1
|
if (root.isVerticalOrientation) return root.widgetThickness - root.horizontalPadding * 2
|
||||||
|
return Math.min(100 - root.horizontalPadding * 2, weatherRow.implicitWidth)
|
||||||
DankIcon {
|
|
||||||
name: WeatherService.getWeatherIcon(WeatherService.weather.wCode)
|
|
||||||
size: Theme.barIconSize(barThickness, -6)
|
|
||||||
color: Theme.primary
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
const temp = SettingsData.useFahrenheit ? WeatherService.weather.tempF : WeatherService.weather.temp;
|
|
||||||
if (temp === undefined || temp === null || temp === 0) {
|
|
||||||
return "--";
|
|
||||||
}
|
|
||||||
return temp;
|
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
implicitHeight: root.isVerticalOrientation ? weatherColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2)
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
Column {
|
||||||
id: weatherRow
|
id: weatherColumn
|
||||||
|
visible: root.isVerticalOrientation
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 1
|
||||||
|
|
||||||
visible: !root.isVertical
|
DankIcon {
|
||||||
anchors.centerIn: parent
|
name: WeatherService.getWeatherIcon(WeatherService.weather.wCode)
|
||||||
spacing: Theme.spacingXS
|
size: Theme.barIconSize(root.barThickness, -6)
|
||||||
|
color: Theme.primary
|
||||||
DankIcon {
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
name: WeatherService.getWeatherIcon(WeatherService.weather.wCode)
|
|
||||||
size: Theme.barIconSize(barThickness, -6)
|
|
||||||
color: Theme.primary
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
const temp = SettingsData.useFahrenheit ? WeatherService.weather.tempF : WeatherService.weather.temp;
|
|
||||||
if (temp === undefined || temp === null || temp === 0) {
|
|
||||||
return "--°" + (SettingsData.useFahrenheit ? "F" : "C");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return temp + "°" + (SettingsData.useFahrenheit ? "F" : "C");
|
StyledText {
|
||||||
|
text: {
|
||||||
|
const temp = SettingsData.useFahrenheit ? WeatherService.weather.tempF : WeatherService.weather.temp;
|
||||||
|
if (temp === undefined || temp === null || temp === 0) {
|
||||||
|
return "--";
|
||||||
|
}
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
Row {
|
||||||
|
id: weatherRow
|
||||||
|
visible: !root.isVerticalOrientation
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
MouseArea {
|
DankIcon {
|
||||||
id: weatherArea
|
name: WeatherService.getWeatherIcon(WeatherService.weather.wCode)
|
||||||
|
size: Theme.barIconSize(root.barThickness, -6)
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
anchors.fill: parent
|
StyledText {
|
||||||
hoverEnabled: true
|
text: {
|
||||||
cursorShape: Qt.PointingHandCursor
|
const temp = SettingsData.useFahrenheit ? WeatherService.weather.tempF : WeatherService.weather.temp;
|
||||||
onPressed: {
|
if (temp === undefined || temp === null || temp === 0) {
|
||||||
if (popupTarget && popupTarget.setTriggerPosition) {
|
return "--°" + (SettingsData.useFahrenheit ? "F" : "C");
|
||||||
const globalPos = mapToGlobal(0, 0)
|
}
|
||||||
const currentScreen = parentScreen || Screen
|
|
||||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
|
return temp + "°" + (SettingsData.useFahrenheit ? "F" : "C");
|
||||||
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
}
|
||||||
|
font.pixelSize: Theme.barTextSize(root.barThickness)
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
}
|
}
|
||||||
root.clicked();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ import QtQuick.Controls
|
|||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
import Quickshell.Widgets
|
||||||
import Quickshell.Hyprland
|
import Quickshell.Hyprland
|
||||||
|
import Quickshell.I3
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Rectangle {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isVertical: axis?.isVertical ?? false
|
property bool isVertical: axis?.isVertical ?? false
|
||||||
@@ -15,14 +16,39 @@ Rectangle {
|
|||||||
property string screenName: ""
|
property string screenName: ""
|
||||||
property real widgetHeight: 30
|
property real widgetHeight: 30
|
||||||
property real barThickness: 48
|
property real barThickness: 48
|
||||||
|
property var hyprlandOverviewLoader: null
|
||||||
|
property var parentScreen: null
|
||||||
|
property int _desktopEntriesUpdateTrigger: 0
|
||||||
|
readonly property var sortedToplevels: {
|
||||||
|
return CompositorService.filterCurrentWorkspace(CompositorService.sortedToplevels, screenName);
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: DesktopEntries
|
||||||
|
function onApplicationsChanged() {
|
||||||
|
_desktopEntriesUpdateTrigger++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
property int currentWorkspace: {
|
property int currentWorkspace: {
|
||||||
if (CompositorService.isNiri) {
|
if (CompositorService.isNiri) {
|
||||||
return getNiriActiveWorkspace()
|
return getNiriActiveWorkspace()
|
||||||
} else if (CompositorService.isHyprland) {
|
} else if (CompositorService.isHyprland) {
|
||||||
return getHyprlandActiveWorkspace()
|
return getHyprlandActiveWorkspace()
|
||||||
|
} else if (CompositorService.isDwl) {
|
||||||
|
const activeTags = getDwlActiveTags()
|
||||||
|
return activeTags.length > 0 ? activeTags[0] : -1
|
||||||
|
} else if (CompositorService.isSway) {
|
||||||
|
return getSwayActiveWorkspace()
|
||||||
}
|
}
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
property var dwlActiveTags: {
|
||||||
|
if (CompositorService.isDwl) {
|
||||||
|
return getDwlActiveTags()
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
property var workspaceList: {
|
property var workspaceList: {
|
||||||
if (CompositorService.isNiri) {
|
if (CompositorService.isNiri) {
|
||||||
const baseList = getNiriWorkspaces()
|
const baseList = getNiriWorkspaces()
|
||||||
@@ -30,14 +56,44 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
if (CompositorService.isHyprland) {
|
if (CompositorService.isHyprland) {
|
||||||
const baseList = getHyprlandWorkspaces()
|
const baseList = getHyprlandWorkspaces()
|
||||||
// Filter out special workspaces
|
|
||||||
const filteredList = baseList.filter(ws => ws.id > -1)
|
const filteredList = baseList.filter(ws => ws.id > -1)
|
||||||
return SettingsData.showWorkspacePadding ? padWorkspaces(filteredList) : filteredList
|
return SettingsData.showWorkspacePadding ? padWorkspaces(filteredList) : filteredList
|
||||||
}
|
}
|
||||||
|
if (CompositorService.isDwl) {
|
||||||
|
const baseList = getDwlTags()
|
||||||
|
return SettingsData.showWorkspacePadding ? padWorkspaces(baseList) : baseList
|
||||||
|
}
|
||||||
|
if (CompositorService.isSway) {
|
||||||
|
const baseList = getSwayWorkspaces()
|
||||||
|
return SettingsData.showWorkspacePadding ? padWorkspaces(baseList) : baseList
|
||||||
|
}
|
||||||
return [1]
|
return [1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSwayWorkspaces() {
|
||||||
|
const workspaces = I3.workspaces?.values || []
|
||||||
|
if (workspaces.length === 0) return [{"num": 1}]
|
||||||
|
|
||||||
|
if (!root.screenName || !SettingsData.workspacesPerMonitor) {
|
||||||
|
return workspaces.slice().sort((a, b) => a.num - b.num)
|
||||||
|
}
|
||||||
|
|
||||||
|
const monitorWorkspaces = workspaces.filter(ws => ws.monitor?.name === root.screenName)
|
||||||
|
return monitorWorkspaces.length > 0 ? monitorWorkspaces.sort((a, b) => a.num - b.num) : [{"num": 1}]
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSwayActiveWorkspace() {
|
||||||
|
if (!root.screenName || !SettingsData.workspacesPerMonitor) {
|
||||||
|
const focusedWs = I3.workspaces?.values?.find(ws => ws.focused === true)
|
||||||
|
return focusedWs ? focusedWs.num : 1
|
||||||
|
}
|
||||||
|
|
||||||
|
const focusedWs = I3.workspaces?.values?.find(ws => ws.monitor?.name === root.screenName && ws.focused === true)
|
||||||
|
return focusedWs ? focusedWs.num : 1
|
||||||
|
}
|
||||||
|
|
||||||
function getWorkspaceIcons(ws) {
|
function getWorkspaceIcons(ws) {
|
||||||
|
_desktopEntriesUpdateTrigger
|
||||||
if (!SettingsData.showWorkspaceApps || !ws) {
|
if (!SettingsData.showWorkspaceApps || !ws) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
@@ -55,15 +111,35 @@ Rectangle {
|
|||||||
targetWorkspaceId = workspace.id
|
targetWorkspaceId = workspace.id
|
||||||
} else if (CompositorService.isHyprland) {
|
} else if (CompositorService.isHyprland) {
|
||||||
targetWorkspaceId = ws.id !== undefined ? ws.id : ws
|
targetWorkspaceId = ws.id !== undefined ? ws.id : ws
|
||||||
|
} else if (CompositorService.isDwl) {
|
||||||
|
if (typeof ws !== "object" || ws.tag === undefined) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
targetWorkspaceId = ws.tag
|
||||||
|
} else if (CompositorService.isSway) {
|
||||||
|
targetWorkspaceId = ws.num !== undefined ? ws.num : ws
|
||||||
} else {
|
} else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
const wins = CompositorService.isNiri ? (NiriService.windows || []) : CompositorService.sortedToplevels
|
const wins = CompositorService.isNiri ? (NiriService.windows || []) : CompositorService.sortedToplevels
|
||||||
|
|
||||||
|
|
||||||
const byApp = {}
|
const byApp = {}
|
||||||
const isActiveWs = CompositorService.isNiri ? NiriService.allWorkspaces.some(ws => ws.id === targetWorkspaceId && ws.is_active) : targetWorkspaceId === root.currentWorkspace
|
let isActiveWs = false
|
||||||
|
if (CompositorService.isNiri) {
|
||||||
|
isActiveWs = NiriService.allWorkspaces.some(ws => ws.id === targetWorkspaceId && ws.is_active)
|
||||||
|
} else if (CompositorService.isSway) {
|
||||||
|
const focusedWs = I3.workspaces?.values?.find(ws => ws.focused === true)
|
||||||
|
isActiveWs = focusedWs ? (focusedWs.num === targetWorkspaceId) : false
|
||||||
|
} else if (CompositorService.isDwl) {
|
||||||
|
const output = DwlService.getOutputState(root.screenName)
|
||||||
|
if (output && output.tags) {
|
||||||
|
const tag = output.tags.find(t => t.tag === targetWorkspaceId)
|
||||||
|
isActiveWs = tag ? (tag.state === 1) : false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isActiveWs = targetWorkspaceId === root.currentWorkspace
|
||||||
|
}
|
||||||
|
|
||||||
wins.forEach((w, i) => {
|
wins.forEach((w, i) => {
|
||||||
if (!w) {
|
if (!w) {
|
||||||
@@ -73,8 +149,9 @@ Rectangle {
|
|||||||
let winWs = null
|
let winWs = null
|
||||||
if (CompositorService.isNiri) {
|
if (CompositorService.isNiri) {
|
||||||
winWs = w.workspace_id
|
winWs = w.workspace_id
|
||||||
|
} else if (CompositorService.isSway) {
|
||||||
|
winWs = w.workspace?.num
|
||||||
} else {
|
} else {
|
||||||
// For Hyprland, we need to find the corresponding Hyprland toplevel to get workspace
|
|
||||||
const hyprlandToplevels = Array.from(Hyprland.toplevels?.values || [])
|
const hyprlandToplevels = Array.from(Hyprland.toplevels?.values || [])
|
||||||
const hyprToplevel = hyprlandToplevels.find(ht => ht.wayland === w)
|
const hyprToplevel = hyprlandToplevels.find(ht => ht.wayland === w)
|
||||||
winWs = hyprToplevel?.workspace?.id
|
winWs = hyprToplevel?.workspace?.id
|
||||||
@@ -96,14 +173,14 @@ Rectangle {
|
|||||||
"type": "icon",
|
"type": "icon",
|
||||||
"icon": icon,
|
"icon": icon,
|
||||||
"isSteamApp": isSteamApp,
|
"isSteamApp": isSteamApp,
|
||||||
"active": !!(w.activated || (CompositorService.isNiri && w.is_focused)),
|
"active": !!((w.activated || w.is_focused) || (CompositorService.isNiri && w.is_focused)),
|
||||||
"count": 1,
|
"count": 1,
|
||||||
"windowId": w.address || w.id,
|
"windowId": w.address || w.id,
|
||||||
"fallbackText": w.appId || w.class || w.title || ""
|
"fallbackText": w.appId || w.class || w.title || ""
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
byApp[key].count++
|
byApp[key].count++
|
||||||
if (w.activated || (CompositorService.isNiri && w.is_focused)) {
|
if ((w.activated || w.is_focused) || (CompositorService.isNiri && w.is_focused)) {
|
||||||
byApp[key].active = true
|
byApp[key].active = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,10 +191,16 @@ Rectangle {
|
|||||||
|
|
||||||
function padWorkspaces(list) {
|
function padWorkspaces(list) {
|
||||||
const padded = list.slice()
|
const padded = list.slice()
|
||||||
const placeholder = CompositorService.isHyprland ? {
|
let placeholder
|
||||||
"id": -1,
|
if (CompositorService.isHyprland) {
|
||||||
"name": ""
|
placeholder = {"id": -1, "name": ""}
|
||||||
} : -1
|
} else if (CompositorService.isDwl) {
|
||||||
|
placeholder = {"tag": -1}
|
||||||
|
} else if (CompositorService.isSway) {
|
||||||
|
placeholder = {"num": -1}
|
||||||
|
} else {
|
||||||
|
placeholder = -1
|
||||||
|
}
|
||||||
while (padded.length < 3) {
|
while (padded.length < 3) {
|
||||||
padded.push(placeholder)
|
padded.push(placeholder)
|
||||||
}
|
}
|
||||||
@@ -185,7 +268,6 @@ Rectangle {
|
|||||||
return Hyprland.focusedWorkspace ? Hyprland.focusedWorkspace.id : 1
|
return Hyprland.focusedWorkspace ? Hyprland.focusedWorkspace.id : 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the monitor object for this screen
|
|
||||||
const monitors = Hyprland.monitors?.values || []
|
const monitors = Hyprland.monitors?.values || []
|
||||||
const currentMonitor = monitors.find(monitor => monitor.name === root.screenName)
|
const currentMonitor = monitors.find(monitor => monitor.name === root.screenName)
|
||||||
|
|
||||||
@@ -193,18 +275,56 @@ Rectangle {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the monitor's active workspace ID (like original config)
|
|
||||||
return currentMonitor.activeWorkspace?.id ?? 1
|
return currentMonitor.activeWorkspace?.id ?? 1
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property real padding: isVertical
|
function getDwlTags() {
|
||||||
? Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30))
|
if (!DwlService.dwlAvailable) {
|
||||||
: (widgetHeight - workspaceRow.implicitHeight) / 2
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const output = DwlService.getOutputState(root.screenName)
|
||||||
|
if (!output || !output.tags || output.tags.length === 0) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SettingsData.dwlShowAllTags) {
|
||||||
|
return output.tags.map(tag => ({"tag": tag.tag, "state": tag.state, "clients": tag.clients, "focused": tag.focused}))
|
||||||
|
}
|
||||||
|
|
||||||
|
const visibleTagIndices = DwlService.getVisibleTags(root.screenName)
|
||||||
|
return visibleTagIndices.map(tagIndex => {
|
||||||
|
const tagData = output.tags.find(t => t.tag === tagIndex)
|
||||||
|
return {
|
||||||
|
"tag": tagIndex,
|
||||||
|
"state": tagData?.state ?? 0,
|
||||||
|
"clients": tagData?.clients ?? 0,
|
||||||
|
"focused": tagData?.focused ?? false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDwlActiveTags() {
|
||||||
|
if (!DwlService.dwlAvailable) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeTags = DwlService.getActiveTags(root.screenName)
|
||||||
|
return activeTags
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property real padding: Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30))
|
||||||
|
readonly property real visualWidth: isVertical ? widgetHeight : (workspaceRow.implicitWidth + padding * 2)
|
||||||
|
readonly property real visualHeight: isVertical ? (workspaceRow.implicitHeight + padding * 2) : widgetHeight
|
||||||
|
|
||||||
function getRealWorkspaces() {
|
function getRealWorkspaces() {
|
||||||
return root.workspaceList.filter(ws => {
|
return root.workspaceList.filter(ws => {
|
||||||
if (CompositorService.isHyprland) {
|
if (CompositorService.isHyprland) {
|
||||||
return ws && ws.id !== -1
|
return ws && ws.id !== -1
|
||||||
|
} else if (CompositorService.isDwl) {
|
||||||
|
return ws && ws.tag !== -1
|
||||||
|
} else if (CompositorService.isSway) {
|
||||||
|
return ws && ws.num !== -1
|
||||||
}
|
}
|
||||||
return ws !== -1
|
return ws !== -1
|
||||||
})
|
})
|
||||||
@@ -219,53 +339,88 @@ Rectangle {
|
|||||||
|
|
||||||
const currentIndex = realWorkspaces.findIndex(ws => ws === root.currentWorkspace)
|
const currentIndex = realWorkspaces.findIndex(ws => ws === root.currentWorkspace)
|
||||||
const validIndex = currentIndex === -1 ? 0 : currentIndex
|
const validIndex = currentIndex === -1 ? 0 : currentIndex
|
||||||
const nextIndex = direction > 0 ? (validIndex + 1) % realWorkspaces.length : (validIndex - 1 + realWorkspaces.length) % realWorkspaces.length
|
const nextIndex = direction > 0 ? Math.min(validIndex + 1, realWorkspaces.length - 1) : Math.max(validIndex - 1, 0)
|
||||||
|
|
||||||
|
if (nextIndex === validIndex) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
NiriService.switchToWorkspace(realWorkspaces[nextIndex] - 1)
|
NiriService.switchToWorkspace(realWorkspaces[nextIndex] - 1)
|
||||||
} else if (CompositorService.isHyprland) {
|
} else if (CompositorService.isHyprland) {
|
||||||
const command = direction > 0 ? "workspace r+1" : "workspace r-1"
|
const realWorkspaces = getRealWorkspaces()
|
||||||
Hyprland.dispatch(command)
|
if (realWorkspaces.length < 2) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentIndex = realWorkspaces.findIndex(ws => ws.id === root.currentWorkspace)
|
||||||
|
const validIndex = currentIndex === -1 ? 0 : currentIndex
|
||||||
|
const nextIndex = direction > 0 ? Math.min(validIndex + 1, realWorkspaces.length - 1) : Math.max(validIndex - 1, 0)
|
||||||
|
|
||||||
|
if (nextIndex === validIndex) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Hyprland.dispatch(`workspace ${realWorkspaces[nextIndex].id}`)
|
||||||
|
} else if (CompositorService.isDwl) {
|
||||||
|
const realWorkspaces = getRealWorkspaces()
|
||||||
|
if (realWorkspaces.length < 2) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentIndex = realWorkspaces.findIndex(ws => ws.tag === root.currentWorkspace)
|
||||||
|
const validIndex = currentIndex === -1 ? 0 : currentIndex
|
||||||
|
const nextIndex = direction > 0 ? Math.min(validIndex + 1, realWorkspaces.length - 1) : Math.max(validIndex - 1, 0)
|
||||||
|
|
||||||
|
if (nextIndex === validIndex) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
DwlService.switchToTag(root.screenName, realWorkspaces[nextIndex].tag)
|
||||||
|
} else if (CompositorService.isSway) {
|
||||||
|
const realWorkspaces = getRealWorkspaces()
|
||||||
|
if (realWorkspaces.length < 2) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentIndex = realWorkspaces.findIndex(ws => ws.num === root.currentWorkspace)
|
||||||
|
const validIndex = currentIndex === -1 ? 0 : currentIndex
|
||||||
|
const nextIndex = direction > 0 ? Math.min(validIndex + 1, realWorkspaces.length - 1) : Math.max(validIndex - 1, 0)
|
||||||
|
|
||||||
|
if (nextIndex === validIndex) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try { I3.dispatch(`workspace number ${realWorkspaces[nextIndex].num}`) } catch(_){}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
width: isVertical ? widgetHeight : (workspaceRow.implicitWidth + padding * 2)
|
width: isVertical ? barThickness : visualWidth
|
||||||
height: isVertical ? (workspaceRow.implicitHeight + padding * 2) : widgetHeight
|
height: isVertical ? visualHeight : barThickness
|
||||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
visible: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl || CompositorService.isSway
|
||||||
color: {
|
|
||||||
if (SettingsData.dankBarNoBackground)
|
Rectangle {
|
||||||
return "transparent"
|
id: visualBackground
|
||||||
const baseColor = Theme.widgetBaseBackgroundColor
|
width: root.visualWidth
|
||||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency)
|
height: root.visualHeight
|
||||||
|
anchors.centerIn: parent
|
||||||
|
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
||||||
|
color: {
|
||||||
|
if (SettingsData.dankBarNoBackground)
|
||||||
|
return "transparent"
|
||||||
|
const baseColor = Theme.widgetBaseBackgroundColor
|
||||||
|
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
visible: CompositorService.isNiri || CompositorService.isHyprland
|
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
acceptedButtons: Qt.RightButton
|
||||||
acceptedButtons: Qt.NoButton
|
|
||||||
|
|
||||||
property real scrollAccumulator: 0
|
onClicked: mouse => {
|
||||||
property real touchpadThreshold: 500
|
if (mouse.button === Qt.RightButton && CompositorService.isHyprland && root.hyprlandOverviewLoader?.item) {
|
||||||
|
root.hyprlandOverviewLoader.item.overviewOpen = !root.hyprlandOverviewLoader.item.overviewOpen
|
||||||
onWheel: wheel => {
|
}
|
||||||
const deltaY = wheel.angleDelta.y
|
}
|
||||||
const isMouseWheel = Math.abs(deltaY) >= 120 && (Math.abs(deltaY) % 120) === 0
|
|
||||||
const direction = deltaY < 0 ? 1 : -1
|
|
||||||
|
|
||||||
if (isMouseWheel) {
|
|
||||||
switchWorkspace(direction)
|
|
||||||
} else {
|
|
||||||
scrollAccumulator += deltaY
|
|
||||||
|
|
||||||
if (Math.abs(scrollAccumulator) >= touchpadThreshold) {
|
|
||||||
const touchDirection = scrollAccumulator < 0 ? 1 : -1
|
|
||||||
switchWorkspace(touchDirection)
|
|
||||||
scrollAccumulator = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wheel.accepted = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Flow {
|
Flow {
|
||||||
@@ -278,18 +433,26 @@ Rectangle {
|
|||||||
Repeater {
|
Repeater {
|
||||||
model: root.workspaceList
|
model: root.workspaceList
|
||||||
|
|
||||||
Rectangle {
|
Item {
|
||||||
id: delegateRoot
|
id: delegateRoot
|
||||||
|
|
||||||
property bool isActive: {
|
property bool isActive: {
|
||||||
if (CompositorService.isHyprland) {
|
if (CompositorService.isHyprland) {
|
||||||
return modelData && modelData.id === root.currentWorkspace
|
return modelData && modelData.id === root.currentWorkspace
|
||||||
|
} else if (CompositorService.isDwl) {
|
||||||
|
return modelData && root.dwlActiveTags.includes(modelData.tag)
|
||||||
|
} else if (CompositorService.isSway) {
|
||||||
|
return modelData && modelData.num === root.currentWorkspace
|
||||||
}
|
}
|
||||||
return modelData === root.currentWorkspace
|
return modelData === root.currentWorkspace
|
||||||
}
|
}
|
||||||
property bool isPlaceholder: {
|
property bool isPlaceholder: {
|
||||||
if (CompositorService.isHyprland) {
|
if (CompositorService.isHyprland) {
|
||||||
return modelData && modelData.id === -1
|
return modelData && modelData.id === -1
|
||||||
|
} else if (CompositorService.isDwl) {
|
||||||
|
return modelData && modelData.tag === -1
|
||||||
|
} else if (CompositorService.isSway) {
|
||||||
|
return modelData && modelData.num === -1
|
||||||
}
|
}
|
||||||
return modelData === -1
|
return modelData === -1
|
||||||
}
|
}
|
||||||
@@ -300,8 +463,11 @@ Rectangle {
|
|||||||
property bool isUrgent: {
|
property bool isUrgent: {
|
||||||
if (CompositorService.isHyprland) {
|
if (CompositorService.isHyprland) {
|
||||||
return modelData?.urgent ?? false
|
return modelData?.urgent ?? false
|
||||||
}
|
} else if (CompositorService.isNiri) {
|
||||||
if (CompositorService.isNiri) {
|
return loadedIsUrgent
|
||||||
|
} else if (CompositorService.isDwl) {
|
||||||
|
return modelData?.state === 2
|
||||||
|
} else if (CompositorService.isSway) {
|
||||||
return loadedIsUrgent
|
return loadedIsUrgent
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -310,6 +476,67 @@ Rectangle {
|
|||||||
property bool loadedHasIcon: false
|
property bool loadedHasIcon: false
|
||||||
property var loadedIcons: []
|
property var loadedIcons: []
|
||||||
|
|
||||||
|
readonly property real visualWidth: {
|
||||||
|
if (root.isVertical) {
|
||||||
|
return SettingsData.showWorkspaceApps ? widgetHeight * 0.7 : widgetHeight * 0.5
|
||||||
|
} else {
|
||||||
|
if (SettingsData.showWorkspaceApps && loadedIcons.length > 0) {
|
||||||
|
const numIcons = Math.min(loadedIcons.length, SettingsData.maxWorkspaceIcons)
|
||||||
|
const iconsWidth = numIcons * 18 + (numIcons > 0 ? (numIcons - 1) * Theme.spacingXS : 0)
|
||||||
|
const baseWidth = isActive ? root.widgetHeight * 0.9 + Theme.spacingXS : root.widgetHeight * 0.7
|
||||||
|
return baseWidth + iconsWidth
|
||||||
|
}
|
||||||
|
return isActive ? root.widgetHeight * 1.05 : root.widgetHeight * 0.7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
readonly property real visualHeight: {
|
||||||
|
if (root.isVertical) {
|
||||||
|
if (SettingsData.showWorkspaceApps && loadedIcons.length > 0) {
|
||||||
|
const numIcons = Math.min(loadedIcons.length, SettingsData.maxWorkspaceIcons)
|
||||||
|
const iconsHeight = numIcons * 18 + (numIcons > 0 ? (numIcons - 1) * Theme.spacingXS : 0)
|
||||||
|
const baseHeight = isActive ? root.widgetHeight * 0.9 + Theme.spacingXS : root.widgetHeight * 0.7
|
||||||
|
return baseHeight + iconsHeight
|
||||||
|
}
|
||||||
|
return isActive ? root.widgetHeight * 1.05 : root.widgetHeight * 0.7
|
||||||
|
} else {
|
||||||
|
return SettingsData.showWorkspaceApps ? widgetHeight * 0.7 : widgetHeight * 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//DO NOT move this MouseArea. It should be on this level in order for the appMouseArea to work
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: !isPlaceholder
|
||||||
|
cursorShape: isPlaceholder ? Qt.ArrowCursor : Qt.PointingHandCursor
|
||||||
|
enabled: !isPlaceholder
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
onClicked: mouse => {
|
||||||
|
if (isPlaceholder) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const isRightClick = mouse.button === Qt.RightButton
|
||||||
|
|
||||||
|
if (CompositorService.isNiri) {
|
||||||
|
NiriService.switchToWorkspace(modelData - 1)
|
||||||
|
} else if (CompositorService.isHyprland && modelData?.id) {
|
||||||
|
Hyprland.dispatch(`workspace ${modelData.id}`)
|
||||||
|
} else if (CompositorService.isDwl && modelData?.tag !== undefined) {
|
||||||
|
console.log("DWL click - tag:", modelData.tag, "rightClick:", isRightClick)
|
||||||
|
if (isRightClick) {
|
||||||
|
console.log("Calling toggleTag")
|
||||||
|
DwlService.toggleTag(root.screenName, modelData.tag)
|
||||||
|
} else {
|
||||||
|
console.log("Calling switchToTag")
|
||||||
|
DwlService.switchToTag(root.screenName, modelData.tag)
|
||||||
|
}
|
||||||
|
} else if (CompositorService.isSway && modelData?.num) {
|
||||||
|
try { I3.dispatch(`workspace number ${modelData.num}`) } catch(_){}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: dataUpdateTimer
|
id: dataUpdateTimer
|
||||||
interval: 50
|
interval: 50
|
||||||
@@ -328,9 +555,13 @@ Rectangle {
|
|||||||
wsData = NiriService.allWorkspaces.find(ws => ws.idx + 1 === modelData && ws.output === root.screenName) || null;
|
wsData = NiriService.allWorkspaces.find(ws => ws.idx + 1 === modelData && ws.output === root.screenName) || null;
|
||||||
} else if (CompositorService.isHyprland) {
|
} else if (CompositorService.isHyprland) {
|
||||||
wsData = modelData;
|
wsData = modelData;
|
||||||
|
} else if (CompositorService.isDwl) {
|
||||||
|
wsData = modelData;
|
||||||
|
} else if (CompositorService.isSway) {
|
||||||
|
wsData = modelData;
|
||||||
}
|
}
|
||||||
delegateRoot.loadedWorkspaceData = wsData;
|
delegateRoot.loadedWorkspaceData = wsData;
|
||||||
delegateRoot.loadedIsUrgent = wsData?.is_urgent ?? false;
|
delegateRoot.loadedIsUrgent = wsData?.urgent ?? false;
|
||||||
|
|
||||||
var icData = null;
|
var icData = null;
|
||||||
if (wsData?.name) {
|
if (wsData?.name) {
|
||||||
@@ -340,7 +571,11 @@ Rectangle {
|
|||||||
delegateRoot.loadedHasIcon = icData !== null;
|
delegateRoot.loadedHasIcon = icData !== null;
|
||||||
|
|
||||||
if (SettingsData.showWorkspaceApps) {
|
if (SettingsData.showWorkspaceApps) {
|
||||||
delegateRoot.loadedIcons = root.getWorkspaceIcons(CompositorService.isHyprland ? modelData : (modelData === -1 ? null : modelData));
|
if (CompositorService.isDwl || CompositorService.isSway) {
|
||||||
|
delegateRoot.loadedIcons = root.getWorkspaceIcons(modelData);
|
||||||
|
} else {
|
||||||
|
delegateRoot.loadedIcons = root.getWorkspaceIcons(CompositorService.isHyprland ? modelData : (modelData === -1 ? null : modelData));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
delegateRoot.loadedIcons = [];
|
delegateRoot.loadedIcons = [];
|
||||||
}
|
}
|
||||||
@@ -351,94 +586,67 @@ Rectangle {
|
|||||||
dataUpdateTimer.restart()
|
dataUpdateTimer.restart()
|
||||||
}
|
}
|
||||||
|
|
||||||
width: {
|
width: root.isVertical ? root.barThickness : visualWidth
|
||||||
if (root.isVertical) {
|
height: root.isVertical ? visualHeight : root.barThickness
|
||||||
return SettingsData.showWorkspaceApps ? widgetHeight * 0.7 : widgetHeight * 0.5;
|
|
||||||
} else {
|
|
||||||
if (SettingsData.showWorkspaceApps && loadedIcons.length > 0) {
|
|
||||||
const numIcons = Math.min(loadedIcons.length, SettingsData.maxWorkspaceIcons);
|
|
||||||
const iconsWidth = numIcons * 18 + (numIcons > 0 ? (numIcons - 1) * Theme.spacingXS : 0);
|
|
||||||
const baseWidth = isActive ? root.widgetHeight * 0.9 + Theme.spacingXS : root.widgetHeight * 0.7;
|
|
||||||
return baseWidth + iconsWidth;
|
|
||||||
}
|
|
||||||
return isActive ? root.widgetHeight * 1.05 : root.widgetHeight * 0.7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
height: {
|
|
||||||
if (root.isVertical) {
|
|
||||||
if (SettingsData.showWorkspaceApps && loadedIcons.length > 0) {
|
|
||||||
const numIcons = Math.min(loadedIcons.length, SettingsData.maxWorkspaceIcons);
|
|
||||||
const iconsHeight = numIcons * 18 + (numIcons > 0 ? (numIcons - 1) * Theme.spacingXS : 0);
|
|
||||||
const baseHeight = isActive ? root.widgetHeight * 0.9 + Theme.spacingXS : root.widgetHeight * 0.7;
|
|
||||||
return baseHeight + iconsHeight;
|
|
||||||
}
|
|
||||||
return isActive ? root.widgetHeight * 1.05 : root.widgetHeight * 0.7;
|
|
||||||
} else {
|
|
||||||
return SettingsData.showWorkspaceApps ? widgetHeight * 0.7 : widgetHeight * 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
radius: Math.min(width, height) / 2
|
|
||||||
color: isActive ? Theme.primary : isUrgent ? Theme.error : isPlaceholder ? Theme.surfaceTextLight : isHovered ? Theme.outlineButton : Theme.surfaceTextAlpha
|
|
||||||
|
|
||||||
border.width: isUrgent && !isActive ? 2 : 0
|
|
||||||
border.color: isUrgent && !isActive ? Theme.error : Theme.withAlpha(Theme.error, 0)
|
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
enabled: (!SettingsData.showWorkspaceApps || SettingsData.maxWorkspaceIcons <= 3)
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on height {
|
|
||||||
enabled: root.isVertical && (!SettingsData.showWorkspaceApps || SettingsData.maxWorkspaceIcons <= 3)
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on border.width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: mouseArea
|
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: visualContent
|
||||||
|
width: delegateRoot.visualWidth
|
||||||
|
height: delegateRoot.visualHeight
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
width: root.isVertical ? parent.width + Theme.spacingXL : parent.width
|
radius: Theme.cornerRadius
|
||||||
height: root.isVertical ? parent.height : parent.height + Theme.spacingXL
|
color: isActive ? Theme.primary : isUrgent ? Theme.error : isPlaceholder ? Theme.surfaceTextLight : isHovered ? Theme.outlineButton : Theme.surfaceTextAlpha
|
||||||
hoverEnabled: !isPlaceholder
|
|
||||||
cursorShape: isPlaceholder ? Qt.ArrowCursor : Qt.PointingHandCursor
|
|
||||||
enabled: !isPlaceholder
|
|
||||||
onClicked: {
|
|
||||||
if (isPlaceholder) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CompositorService.isNiri) {
|
border.width: {
|
||||||
NiriService.switchToWorkspace(modelData - 1)
|
if (isUrgent && !isActive) return 2
|
||||||
} else if (CompositorService.isHyprland && modelData?.id) {
|
return 0
|
||||||
Hyprland.dispatch(`workspace ${modelData.id}`)
|
}
|
||||||
|
border.color: {
|
||||||
|
if (isUrgent && !isActive) return Theme.error
|
||||||
|
return Theme.withAlpha(Theme.error, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on width {
|
||||||
|
enabled: (!SettingsData.showWorkspaceApps || SettingsData.maxWorkspaceIcons <= 3)
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
Behavior on height {
|
||||||
id: appIconsLoader
|
enabled: root.isVertical && (!SettingsData.showWorkspaceApps || SettingsData.maxWorkspaceIcons <= 3)
|
||||||
anchors.fill: parent
|
NumberAnimation {
|
||||||
active: SettingsData.showWorkspaceApps
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on border.width {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on border.color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: appIconsLoader
|
||||||
|
anchors.fill: parent
|
||||||
|
active: SettingsData.showWorkspaceApps
|
||||||
sourceComponent: Item {
|
sourceComponent: Item {
|
||||||
Loader {
|
Loader {
|
||||||
id: contentRow
|
id: contentRow
|
||||||
@@ -478,7 +686,6 @@ Rectangle {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: appMouseArea
|
id: appMouseArea
|
||||||
hoverEnabled: true
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
enabled: isActive
|
enabled: isActive
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
@@ -547,7 +754,6 @@ Rectangle {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: appMouseArea
|
id: appMouseArea
|
||||||
hoverEnabled: true
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
enabled: isActive
|
enabled: isActive
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
@@ -627,11 +833,29 @@ Rectangle {
|
|||||||
StyledText {
|
StyledText {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: {
|
text: {
|
||||||
const isPlaceholder = CompositorService.isHyprland ? (modelData?.id === -1) : (modelData === -1)
|
let isPlaceholder
|
||||||
|
if (CompositorService.isHyprland) {
|
||||||
|
isPlaceholder = modelData?.id === -1
|
||||||
|
} else if (CompositorService.isDwl) {
|
||||||
|
isPlaceholder = modelData?.tag === -1
|
||||||
|
} else if (CompositorService.isSway) {
|
||||||
|
isPlaceholder = modelData?.num === -1
|
||||||
|
} else {
|
||||||
|
isPlaceholder = modelData === -1
|
||||||
|
}
|
||||||
|
|
||||||
if (isPlaceholder) {
|
if (isPlaceholder) {
|
||||||
return index + 1
|
return index + 1
|
||||||
}
|
}
|
||||||
return CompositorService.isHyprland ? (modelData?.id || "") : (modelData - 1);
|
|
||||||
|
if (CompositorService.isHyprland) {
|
||||||
|
return modelData?.id || ""
|
||||||
|
} else if (CompositorService.isDwl) {
|
||||||
|
return (modelData?.tag !== undefined) ? (modelData.tag + 1) : ""
|
||||||
|
} else if (CompositorService.isSway) {
|
||||||
|
return modelData?.num || ""
|
||||||
|
}
|
||||||
|
return modelData - 1
|
||||||
}
|
}
|
||||||
color: (isActive || isUrgent) ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : isPlaceholder ? Theme.surfaceTextAlpha : Theme.surfaceTextMedium
|
color: (isActive || isUrgent) ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : isPlaceholder ? Theme.surfaceTextAlpha : Theme.surfaceTextMedium
|
||||||
font.pixelSize: Theme.barTextSize(barThickness)
|
font.pixelSize: Theme.barTextSize(barThickness)
|
||||||
@@ -639,8 +863,8 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- LOGIC / TRIGGERS ---
|
|
||||||
Component.onCompleted: updateAllData()
|
Component.onCompleted: updateAllData()
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
@@ -652,12 +876,23 @@ Rectangle {
|
|||||||
enabled: CompositorService.isNiri
|
enabled: CompositorService.isNiri
|
||||||
function onAllWorkspacesChanged() { delegateRoot.updateAllData() }
|
function onAllWorkspacesChanged() { delegateRoot.updateAllData() }
|
||||||
function onWindowUrgentChanged() { delegateRoot.updateAllData() }
|
function onWindowUrgentChanged() { delegateRoot.updateAllData() }
|
||||||
|
function onWindowsChanged() { delegateRoot.updateAllData() }
|
||||||
}
|
}
|
||||||
Connections {
|
Connections {
|
||||||
target: SettingsData
|
target: SettingsData
|
||||||
function onShowWorkspaceAppsChanged() { delegateRoot.updateAllData() }
|
function onShowWorkspaceAppsChanged() { delegateRoot.updateAllData() }
|
||||||
function onWorkspaceNameIconsChanged() { delegateRoot.updateAllData() }
|
function onWorkspaceNameIconsChanged() { delegateRoot.updateAllData() }
|
||||||
}
|
}
|
||||||
|
Connections {
|
||||||
|
target: DwlService
|
||||||
|
enabled: CompositorService.isDwl
|
||||||
|
function onStateChanged() { delegateRoot.updateAllData() }
|
||||||
|
}
|
||||||
|
Connections {
|
||||||
|
target: I3.workspaces
|
||||||
|
enabled: CompositorService.isSway
|
||||||
|
function onValuesChanged() { delegateRoot.updateAllData() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ DankPopout {
|
|||||||
property var triggerScreen: null
|
property var triggerScreen: null
|
||||||
property int currentTabIndex: 0
|
property int currentTabIndex: 0
|
||||||
|
|
||||||
|
keyboardFocusMode: WlrKeyboardFocus.Exclusive
|
||||||
|
|
||||||
function setTriggerPosition(x, y, width, section, screen) {
|
function setTriggerPosition(x, y, width, section, screen) {
|
||||||
triggerSection = section
|
triggerSection = section
|
||||||
triggerScreen = screen
|
triggerScreen = screen
|
||||||
@@ -43,15 +45,57 @@ DankPopout {
|
|||||||
shouldBeVisible: dashVisible
|
shouldBeVisible: dashVisible
|
||||||
visible: shouldBeVisible
|
visible: shouldBeVisible
|
||||||
|
|
||||||
|
property bool __focusArmed: false
|
||||||
|
property bool __contentReady: false
|
||||||
|
|
||||||
|
function __tryFocusOnce() {
|
||||||
|
if (!__focusArmed)
|
||||||
|
return
|
||||||
|
const win = root.window
|
||||||
|
if (!win || !win.visible)
|
||||||
|
return
|
||||||
|
if (!contentLoader.item)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (win.requestActivate)
|
||||||
|
win.requestActivate()
|
||||||
|
contentLoader.item.forceActiveFocus(Qt.TabFocusReason)
|
||||||
|
|
||||||
|
if (contentLoader.item.activeFocus)
|
||||||
|
__focusArmed = false
|
||||||
|
}
|
||||||
|
|
||||||
onDashVisibleChanged: {
|
onDashVisibleChanged: {
|
||||||
if (dashVisible) {
|
if (dashVisible) {
|
||||||
|
__focusArmed = true
|
||||||
|
__contentReady = !!contentLoader.item
|
||||||
open()
|
open()
|
||||||
|
__tryFocusOnce()
|
||||||
} else {
|
} else {
|
||||||
|
__focusArmed = false
|
||||||
|
__contentReady = false
|
||||||
close()
|
close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: contentLoader
|
||||||
|
function onLoaded() {
|
||||||
|
__contentReady = true
|
||||||
|
if (__focusArmed)
|
||||||
|
__tryFocusOnce()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: root.window ? root.window : null
|
||||||
|
enabled: !!root.window
|
||||||
|
function onVisibleChanged() {
|
||||||
|
if (__focusArmed)
|
||||||
|
__tryFocusOnce()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onBackgroundClicked: {
|
onBackgroundClicked: {
|
||||||
dashVisible = false
|
dashVisible = false
|
||||||
}
|
}
|
||||||
@@ -67,26 +111,65 @@ DankPopout {
|
|||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (root.shouldBeVisible) {
|
if (root.shouldBeVisible) {
|
||||||
forceActiveFocus()
|
mainContainer.forceActiveFocus()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onPressed: function(event) {
|
|
||||||
if (event.key === Qt.Key_Escape) {
|
|
||||||
root.dashVisible = false
|
|
||||||
event.accepted = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
|
target: root
|
||||||
function onShouldBeVisibleChanged() {
|
function onShouldBeVisibleChanged() {
|
||||||
if (root.shouldBeVisible) {
|
if (root.shouldBeVisible) {
|
||||||
Qt.callLater(function() {
|
Qt.callLater(function () {
|
||||||
mainContainer.forceActiveFocus()
|
mainContainer.forceActiveFocus()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
target: root
|
}
|
||||||
|
|
||||||
|
Keys.onPressed: function (event) {
|
||||||
|
if (event.key === Qt.Key_Escape) {
|
||||||
|
root.dashVisible = false
|
||||||
|
event.accepted = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === Qt.Key_Tab && !(event.modifiers & Qt.ShiftModifier)) {
|
||||||
|
let nextIndex = root.currentTabIndex + 1
|
||||||
|
while (nextIndex < tabBar.model.length && tabBar.model[nextIndex] && tabBar.model[nextIndex].isAction) {
|
||||||
|
nextIndex++
|
||||||
|
}
|
||||||
|
if (nextIndex >= tabBar.model.length) {
|
||||||
|
nextIndex = 0
|
||||||
|
}
|
||||||
|
root.currentTabIndex = nextIndex
|
||||||
|
event.accepted = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === Qt.Key_Backtab || (event.key === Qt.Key_Tab && (event.modifiers & Qt.ShiftModifier))) {
|
||||||
|
let prevIndex = root.currentTabIndex - 1
|
||||||
|
while (prevIndex >= 0 && tabBar.model[prevIndex] && tabBar.model[prevIndex].isAction) {
|
||||||
|
prevIndex--
|
||||||
|
}
|
||||||
|
if (prevIndex < 0) {
|
||||||
|
prevIndex = tabBar.model.length - 1
|
||||||
|
while (prevIndex >= 0 && tabBar.model[prevIndex] && tabBar.model[prevIndex].isAction) {
|
||||||
|
prevIndex--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (prevIndex >= 0) {
|
||||||
|
root.currentTabIndex = prevIndex
|
||||||
|
}
|
||||||
|
event.accepted = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root.currentTabIndex === 2 && wallpaperTab.handleKeyEvent) {
|
||||||
|
if (wallpaperTab.handleKeyEvent(event)) {
|
||||||
|
event.accepted = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -128,33 +211,56 @@ DankPopout {
|
|||||||
currentIndex: root.currentTabIndex
|
currentIndex: root.currentTabIndex
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
equalWidthTabs: true
|
equalWidthTabs: true
|
||||||
|
enableArrowNavigation: false
|
||||||
|
focus: false
|
||||||
|
activeFocusOnTab: false
|
||||||
|
nextFocusTarget: {
|
||||||
|
const item = pages.currentItem
|
||||||
|
if (!item)
|
||||||
|
return null
|
||||||
|
if (item.focusTarget)
|
||||||
|
return item.focusTarget
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
model: {
|
model: {
|
||||||
let tabs = [
|
let tabs = [{
|
||||||
{ icon: "dashboard", text: I18n.tr("Overview") },
|
"icon": "dashboard",
|
||||||
{ icon: "music_note", text: I18n.tr("Media") }
|
"text": I18n.tr("Overview")
|
||||||
]
|
}, {
|
||||||
|
"icon": "music_note",
|
||||||
|
"text": I18n.tr("Media")
|
||||||
|
}, {
|
||||||
|
"icon": "wallpaper",
|
||||||
|
"text": I18n.tr("Wallpapers")
|
||||||
|
}]
|
||||||
|
|
||||||
if (SettingsData.weatherEnabled) {
|
if (SettingsData.weatherEnabled) {
|
||||||
tabs.push({ icon: "wb_sunny", text: I18n.tr("Weather") })
|
tabs.push({
|
||||||
|
"icon": "wb_sunny",
|
||||||
|
"text": I18n.tr("Weather")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
tabs.push({ icon: "settings", text: I18n.tr("Settings"), isAction: true })
|
tabs.push({
|
||||||
|
"icon": "settings",
|
||||||
|
"text": I18n.tr("Settings"),
|
||||||
|
"isAction": true
|
||||||
|
})
|
||||||
return tabs
|
return tabs
|
||||||
}
|
}
|
||||||
|
|
||||||
onTabClicked: function(index) {
|
onTabClicked: function (index) {
|
||||||
root.currentTabIndex = index
|
root.currentTabIndex = index
|
||||||
}
|
}
|
||||||
|
|
||||||
onActionTriggered: function(index) {
|
onActionTriggered: function (index) {
|
||||||
let settingsIndex = SettingsData.weatherEnabled ? 3 : 2
|
let settingsIndex = SettingsData.weatherEnabled ? 4 : 3
|
||||||
if (index === settingsIndex) {
|
if (index === settingsIndex) {
|
||||||
dashVisible = false
|
dashVisible = false
|
||||||
settingsModal.show()
|
settingsModal.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
@@ -166,9 +272,14 @@ DankPopout {
|
|||||||
id: pages
|
id: pages
|
||||||
width: parent.width
|
width: parent.width
|
||||||
implicitHeight: {
|
implicitHeight: {
|
||||||
if (currentIndex === 0) return overviewTab.implicitHeight
|
if (currentIndex === 0)
|
||||||
if (currentIndex === 1) return mediaTab.implicitHeight
|
return overviewTab.implicitHeight
|
||||||
if (SettingsData.weatherEnabled && currentIndex === 2) return weatherTab.implicitHeight
|
if (currentIndex === 1)
|
||||||
|
return mediaTab.implicitHeight
|
||||||
|
if (currentIndex === 2)
|
||||||
|
return wallpaperTab.implicitHeight
|
||||||
|
if (SettingsData.weatherEnabled && currentIndex === 3)
|
||||||
|
return weatherTab.implicitHeight
|
||||||
return overviewTab.implicitHeight
|
return overviewTab.implicitHeight
|
||||||
}
|
}
|
||||||
currentIndex: root.currentTabIndex
|
currentIndex: root.currentTabIndex
|
||||||
@@ -176,10 +287,14 @@ DankPopout {
|
|||||||
OverviewTab {
|
OverviewTab {
|
||||||
id: overviewTab
|
id: overviewTab
|
||||||
|
|
||||||
|
onCloseDash: {
|
||||||
|
root.dashVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
onSwitchToWeatherTab: {
|
onSwitchToWeatherTab: {
|
||||||
if (SettingsData.weatherEnabled) {
|
if (SettingsData.weatherEnabled) {
|
||||||
tabBar.currentIndex = 2
|
tabBar.currentIndex = 3
|
||||||
tabBar.tabClicked(2)
|
tabBar.tabClicked(3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,12 +308,20 @@ DankPopout {
|
|||||||
id: mediaTab
|
id: mediaTab
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WallpaperTab {
|
||||||
|
id: wallpaperTab
|
||||||
|
active: root.currentTabIndex === 2
|
||||||
|
tabBarItem: tabBar
|
||||||
|
keyForwardTarget: mainContainer
|
||||||
|
targetScreen: root.triggerScreen
|
||||||
|
}
|
||||||
|
|
||||||
WeatherTab {
|
WeatherTab {
|
||||||
id: weatherTab
|
id: weatherTab
|
||||||
visible: SettingsData.weatherEnabled && root.currentTabIndex === 2
|
visible: SettingsData.weatherEnabled && root.currentTabIndex === 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user