Author SHA1 Message Date
AramJonghu 51ec6a7939 feat(makefile): format all cmake files + make fmt rename
C++ / fmt (pull_request) Successful in 5s
JS/TS / fmt & lint (pull_request) Successful in 16s
Python / fmt & lint (pull_request) Successful in 35s
C++ / build & lint (pull_request) Failing after 2m21s
Python / test & nuitka build (pull_request) Failing after 1m36s
Rust / clippy & fmt (pull_request) Successful in 41s
2026-11-02 01:38:41 +01:00
AramJonghu dd64d6947d fix(ci): format properly checks written code only + minor optimisations
C++ / fmt (pull_request) Successful in 5s
JS/TS / fmt & lint (pull_request) Successful in 16s
Python / fmt & lint (pull_request) Successful in 34s
C++ / build & lint (pull_request) Failing after 2m18s
Python / test & nuitka build (pull_request) Failing after 1m35s
Rust / clippy & fmt (pull_request) Successful in 42s
2026-11-02 01:23:05 +01:00
AramJonghu 9d6cd909a5 chore(gitignore): .cargo ignored 2026-11-02 00:45:15 +01:00
AramJonghu 516ec9dd54 chore(format): format files that were unformatted
JS/TS / fmt & lint (pull_request) Successful in 12s
Rust / clippy & fmt (pull_request) Failing after 43s
C++ / fmt (pull_request) Successful in 5s
Python / fmt & lint (pull_request) Successful in 42s
Python / test & nuitka build (pull_request) Failing after 1m34s
C++ / build & lint (pull_request) Failing after 2m17s
2026-10-30 22:57:48 +01:00
AramJonghu b279c5d664 fix(makefile): ruff fmt is now not just a check 2026-10-30 22:46:52 +01:00
AramJonghu 82c307de11 fix(Dockerfile): adds mpv dep
C++ / fmt (pull_request) Failing after 5s
JS/TS / fmt & lint (pull_request) Successful in 13s
C++ / build & lint (pull_request) Failing after 20s
Python / test & nuitka build (pull_request) Canceled after 24s
Python / fmt & lint (pull_request) Canceled after 26s
Rust / clippy & fmt (pull_request) Failing after 3s
2026-10-30 02:51:22 +01:00
AramJonghu 388482c620 ci(rust): fixes and optimizations
C++ / fmt (pull_request) Failing after 5s
C++ / build & lint (pull_request) Failing after 13s
JS/TS / fmt & lint (pull_request) Successful in 11s
Python / fmt & lint (pull_request) Successful in 26s
Rust / clippy & fmt (pull_request) Failing after 50s
Python / test & nuitka build (pull_request) Failing after 1m23s
- removes cargo check since the clippy step builds already
- image switched to rust:alpine
- less correctness during build for speedier compilation
2026-10-30 02:49:22 +01:00
AramJonghu ab4a662013 ci(cpp): introduce caching cmake and build + format optimization 2026-10-30 02:46:17 +01:00
AramJonghu fda39fc4c0 feat(makefile): adds a make formatter as task runner
C++ / fmt (pull_request) Failing after 6s
C++ / build & lint (pull_request) Failing after 10s
JS/TS / fmt & lint (pull_request) Successful in 14s
Python / fmt & lint (pull_request) Successful in 29s
Python / test & nuitka build (pull_request) Failing after 1m25s
Rust / build, clippy & fmt (pull_request) Failing after 4m49s
2026-10-30 02:22:57 +01:00
AramJonghu 9e97569467 fix(ci): cpp clang-tidy now properly reads non-build files only 2026-10-30 02:22:57 +01:00
AramJonghu 630b05ff2c feat(makefile): added no nuitka option for cpp-compile task 2026-10-30 02:22:57 +01:00
AramJonghu b86f865396 chore(gitignore): ordering known files and directories 2026-10-30 02:22:57 +01:00
AramJonghu 34585a639b chore(gitignore): nuitka compile files ignored and categorized 2026-10-30 02:22:57 +01:00
AramJonghu 163370354a fix(clang-tidy): make cpp-lint has proper clang-tidy check 2026-10-30 02:22:57 +01:00
AramJonghu 012d2777c2 chore(Dockerfile): add future dependencies 2026-10-30 02:22:57 +01:00
zachandAramJonghu 9c0b74ff66 Merge branch 'main' into feat/task-runner-ci 2026-10-30 02:22:57 +01:00
AramJonghu 24825250bb feat(makefile): added qmlformat + install -> compile in cpp step 2026-10-30 02:22:57 +01:00
AramJonghu 1364b5ed1a feat(makefile): C++, Python, and Rust checks included (incomplete) 2026-10-30 02:22:57 +01:00
AramJonghu ef4d849b4b feat(makefile): makefile build & install + progress on cpp and py 2026-10-30 02:22:57 +01:00
AramJonghu e12a047da2 format(clang): make cpp-fmt result 2026-10-30 02:22:57 +01:00
AramJonghu 5db2718b04 ci: rework Dockerfile image + buildx dep added 2026-10-30 02:22:57 +01:00
AramJonghu e50a28b877 refactor(workflows): combined steps where it makes sense 2026-10-30 02:22:57 +01:00
AramJonghu 0e23b18f72 fix(ci): missing dependency wayland-scanner 2026-10-30 02:22:57 +01:00
zach 90184bff58 Merge pull request 'feat: video wallpapers' (#163) from feat/video-wallpaper into main
Reviewed-on: #163
2026-10-29 19:55:05 +01:00
zach abdde2450d fix: don't use lambda expression
C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 11s
JS/TS / fmt (pull_request) Successful in 7s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Successful in 30s
Rust / build (pull_request) Successful in 50s
Python / verify (pull_request) Failing after 1m27s
Rust / fmt (pull_request) Successful in 31s
Rust / clippy (pull_request) Successful in 58s
2026-10-29 19:50:02 +01:00
zach fd05b3d4c4 Merge branch 'main' into feat/video-wallpaper
C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 9s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 13s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Failing after 37s
Rust / build (pull_request) Successful in 50s
Python / verify (pull_request) Failing after 1m28s
Rust / fmt (pull_request) Successful in 39s
Rust / clippy (pull_request) Successful in 1m5s
2026-10-29 19:03:10 +01:00
zach 9d8bde1413 fix: update for hyprland 0.57 2026-10-29 11:59:50 +01:00
zach 118b8f482a fix: fix sub-pixel layout arrangement for normalized resolutions
C++ / fmt (pull_request) Failing after 7s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 11s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Failing after 31s
Rust / build (pull_request) Successful in 49s
Python / verify (pull_request) Failing after 1m28s
Rust / fmt (pull_request) Successful in 30s
Rust / clippy (pull_request) Successful in 54s
2026-10-27 23:00:26 +01:00
zach b58b4fdc9c fix: remove more uses of cliprect and finally fix workspace's layer texture
C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 9s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Failing after 33s
Rust / build (pull_request) Successful in 48s
Python / verify (pull_request) Failing after 1m24s
Rust / fmt (pull_request) Successful in 32s
Rust / clippy (pull_request) Successful in 57s
2026-10-27 15:15:57 +01:00
zach a56afd7fac fix: don't link eglDestroyImageKHR
C++ / fmt (pull_request) Failing after 7s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 7s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Failing after 27s
Rust / build (pull_request) Successful in 47s
Python / verify (pull_request) Failing after 1m24s
Rust / fmt (pull_request) Successful in 29s
Rust / clippy (pull_request) Successful in 45s
2026-10-27 02:30:55 +01:00
zach b0cb43f75c feat: add vaapi path for video wallpapers on generic gpus
C++ / fmt (pull_request) Failing after 6s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 11s
JS/TS / fmt (pull_request) Successful in 10s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Failing after 32s
Rust / build (pull_request) Successful in 52s
Python / verify (pull_request) Failing after 1m23s
Rust / fmt (pull_request) Successful in 51s
Rust / clippy (pull_request) Successful in 1m2s
2026-10-27 02:15:16 +01:00
zach e655ec5bc7 feat: add wallpaper transitions, store crop in database per-wallpaper, move CUDA context creation to async thread
C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 9s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 10s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Failing after 28s
Python / verify (pull_request) Failing after 1m29s
Rust / build (pull_request) Successful in 53s
Rust / fmt (pull_request) Successful in 42s
Rust / clippy (pull_request) Successful in 1m4s
2026-10-26 14:47:47 +01:00
zach dc0f222ace fix: don't store dbPath as const
C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 7s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Failing after 27s
Rust / build (pull_request) Successful in 47s
Python / verify (pull_request) Failing after 1m24s
Rust / fmt (pull_request) Successful in 30s
Rust / clippy (pull_request) Successful in 53s
2026-10-26 03:13:33 +01:00
zach de19fd6ac4 fix: restore code removed by incorrect replacement command
C++ / fmt (pull_request) Failing after 5s
C++ / clang-tidy (pull_request) Failing after 4s
C++ / build (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 6s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Failing after 28s
Rust / build (pull_request) Successful in 48s
Python / verify (pull_request) Failing after 1m19s
Rust / fmt (pull_request) Successful in 36s
Rust / clippy (pull_request) Successful in 1m0s
2026-10-26 03:03:20 +01:00
zach 9edbcbd610 feat: skip two-way copy for cuda devices when decoding video wallpapers + cache wallpapers as blobs in new database
C++ / fmt (pull_request) Failing after 11s
C++ / build (pull_request) Failing after 14s
C++ / clang-tidy (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 10s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Failing after 28s
Rust / build (pull_request) Successful in 47s
Python / verify (pull_request) Failing after 1m20s
Rust / fmt (pull_request) Successful in 28s
Rust / clippy (pull_request) Successful in 59s
2026-10-26 02:51:28 +01:00
zach cd44d27b74 chore: suppress more warnings
C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 7s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Successful in 29s
Rust / build (pull_request) Successful in 59s
Python / verify (pull_request) Failing after 1m31s
Rust / fmt (pull_request) Successful in 33s
Rust / clippy (pull_request) Successful in 52s
2026-10-25 02:10:50 +02:00
zach 0d0faecb1c feat: initial video wallpaper implementation 2026-10-25 02:10:50 +02:00
zach 200a6a5d54 fix: suppress private headers cmake warning 2026-10-25 02:07:38 +02:00
zach 2d2231745c fix: remove layer from settings when fading 2026-10-25 01:49:53 +02:00
zach 187e97a081 fix: notifications no longer hide when dashboard is open 2026-10-24 17:29:17 +02:00
zach 0f54d93675 fix: suppress unnecessary log output 2026-10-24 17:12:35 +02:00
171 changed files with 8547 additions and 5717 deletions
+3
View File
@@ -0,0 +1,3 @@
#omz revision: d2b7f557cceaddb16ba89bbf8200d8a8df35c4d8
#omz fpath: /home/zach/.oh-my-zsh/plugins/colored-man-pages /home/zach/.oh-my-zsh/plugins/universalarchive /home/zach/.oh-my-zsh/plugins/tldr /home/zach/.oh-my-zsh/plugins/extract /home/zach/.oh-my-zsh/plugins/copypath /home/zach/.oh-my-zsh/plugins/cp /home/zach/.oh-my-zsh/plugins/conda-env /home/zach/.oh-my-zsh/plugins/command-not-found /home/zach/.oh-my-zsh/plugins/dirhistory /home/zach/.oh-my-zsh/plugins/copybuffer /home/zach/.oh-my-zsh/plugins/copyfile /home/zach/.oh-my-zsh/custom/plugins/fast-syntax-highlighting /home/zach/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting /home/zach/.oh-my-zsh/custom/plugins/zsh-autosuggestions /home/zach/.oh-my-zsh/plugins/archlinux /home/zach/.oh-my-zsh/plugins/web-search /home/zach/.oh-my-zsh/plugins/sudo /home/zach/.oh-my-zsh/plugins/git /home/zach/.oh-my-zsh/functions /home/zach/.oh-my-zsh/completions /home/zach/.oh-my-zsh/custom/functions /home/zach/.oh-my-zsh/custom/completions /home/zach/tools/ripgrep/complete /home/linuxbrew/.linuxbrew/share/zsh/site-functions /home/zach/.oh-my-zsh/plugins/colored-man-pages /home/zach/.oh-my-zsh/plugins/universalarchive /home/zach/.oh-my-zsh/plugins/tldr /home/zach/.oh-my-zsh/plugins/extract /home/zach/.oh-my-zsh/plugins/copypath /home/zach/.oh-my-zsh/plugins/cp /home/zach/.oh-my-zsh/plugins/conda-env /home/zach/.oh-my-zsh/plugins/command-not-found /home/zach/.oh-my-zsh/plugins/dirhistory /home/zach/.oh-my-zsh/plugins/copybuffer /home/zach/.oh-my-zsh/plugins/copyfile /home/zach/.oh-my-zsh/custom/plugins/fast-syntax-highlighting /home/zach/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting /home/zach/.oh-my-zsh/custom/plugins/zsh-autosuggestions /home/zach/.oh-my-zsh/plugins/archlinux /home/zach/.oh-my-zsh/plugins/web-search /home/zach/.oh-my-zsh/plugins/sudo /home/zach/.oh-my-zsh/plugins/git /home/zach/.oh-my-zsh/functions /home/zach/.oh-my-zsh/completions /home/zach/.oh-my-zsh/custom/functions /home/zach/.oh-my-zsh/custom/completions /home/zach/.oh-my-zsh/cache/completions /home/zach/tools/ripgrep/complete /home/linuxbrew/.linuxbrew/share/zsh/site-functions /usr/local/share/zsh/site-functions /usr/share/zsh/site-functions /usr/share/zsh/functions/Calendar /usr/share/zsh/functions/Chpwd /usr/share/zsh/functions/Completion /usr/share/zsh/functions/Completion/Base /usr/share/zsh/functions/Completion/Linux /usr/share/zsh/functions/Completion/Unix /usr/share/zsh/functions/Completion/X /usr/share/zsh/functions/Completion/Zsh /usr/share/zsh/functions/Exceptions /usr/share/zsh/functions/MIME /usr/share/zsh/functions/Math /usr/share/zsh/functions/Misc /usr/share/zsh/functions/Newuser /usr/share/zsh/functions/Prompts /usr/share/zsh/functions/TCP /usr/share/zsh/functions/VCS_Info /usr/share/zsh/functions/VCS_Info/Backends /usr/share/zsh/functions/Zftp /usr/share/zsh/functions/Zle /home/zach/.zfunc
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Docker CLI - name: Install Docker CLI
run: apk add --no-cache docker-cli run: apk add --no-cache docker-cli docker-cli-buildx
- name: Login to registry - name: Login to registry
run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login git.aramjonghu.dev --username aramjonghu --password-stdin run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login git.aramjonghu.dev --username aramjonghu --password-stdin
+22 -21
View File
@@ -17,46 +17,47 @@ jobs:
run: | run: |
find . \( -name '*.cpp' -o -name '*.h' -o -name '*.hpp' \) \ find . \( -name '*.cpp' -o -name '*.h' -o -name '*.hpp' \) \
-not -path './build/*' \ -not -path './build/*' \
-exec clang-format -i --style=file {} + -exec clang-format --dry-run -Werror --style=file {} +
git diff --exit-code && echo "clang-format: passed"
build: build & lint:
runs-on: alpine runs-on: alpine
container: container:
image: git.aramjonghu.dev/aramjonghu/zshell-ci:latest image: git.aramjonghu.dev/aramjonghu/zshell-ci:latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Configure - name: Cache CMake build
run: cmake -B build -G Ninja -DENABLE_MODULES=plugin -DCMAKE_BUILD_TYPE=Release uses: actions/cache@v4
- name: Build
run: ninja -C build
clang-tidy:
runs-on: alpine
container:
image: git.aramjonghu.dev/aramjonghu/zshell-ci:latest
steps:
- name: Checkout
uses: actions/checkout@v4
with: with:
fetch-depth: 0 path: build
key: cpp-build-${{ hashFiles('CMakeLists.txt', '**/CMakeLists.txt') }}
restore-keys: |
cpp-build-
- name: Cache ccache
uses: actions/cache@v4
with:
path: ~/.ccache
key: ccache-${{ runner.os }}-${{ github.sha }}
restore-keys: |
ccache-${{ runner.os }}-
- name: Configure - name: Configure
run: cmake -B build -G Ninja -DENABLE_MODULES=plugin -DCMAKE_BUILD_TYPE=Release run: cmake -B build -G Ninja -DENABLE_MODULES=plugin -DCMAKE_BUILD_TYPE=Release
- name: Build - name: Build
run: ninja -C build run: ninja -C build
env:
CMAKE_CXX_COMPILER_LAUNCHER: ccache
- name: clang-tidy - name: clang-tidy
run: | run: |
sed -i 's/-mno-direct-extern-access //g' build/compile_commands.json sed -i 's/-mno-direct-extern-access //g' build/compile_commands.json
set -o pipefail set -o pipefail
run-clang-tidy -p build -j $(nproc) -header-filter="Plugins/.*" 2>&1 | \ run-clang-tidy -p build -j "$(nproc)" \
grep -v 'Suppressed\|non-user\|non-system\|unknown argument.*mno-direct\|Found compiler error\|warnings generated\.\|^Use -header-filter' -source-filter="^$(pwd)/Plugins/" \
-header-filter=".*Plugins/.*" \
-removed-arg=-mno-direct-extern-access
+1 -15
View File
@@ -4,7 +4,7 @@ on:
pull_request: pull_request:
jobs: jobs:
fmt: fmt & lint:
runs-on: alpine runs-on: alpine
container: node:26-alpine container: node:26-alpine
@@ -24,20 +24,6 @@ jobs:
else else
echo "No JS/TS files found" echo "No JS/TS files found"
fi fi
lint:
runs-on: alpine
container: node:26-alpine
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install tools
run: |
apk add --no-cache \
git
- name: ESLint - name: ESLint
run: | run: |
if [ -n "$(find . \( -iname "*.js" -o -iname "*.jsx" -o -iname "*.ts" -o -iname "*.tsx" -o -iname "*.mjs" -o -iname "*.cjs" \) -print -quit)" ]; then if [ -n "$(find . \( -iname "*.js" -o -iname "*.jsx" -o -iname "*.ts" -o -iname "*.tsx" -o -iname "*.mjs" -o -iname "*.cjs" \) -print -quit)" ]; then
+2 -2
View File
@@ -7,7 +7,7 @@ on:
pull_request: pull_request:
jobs: jobs:
static: fmt & lint:
runs-on: debian runs-on: debian
container: node:26-trixie-slim container: node:26-trixie-slim
@@ -33,7 +33,7 @@ jobs:
. .venv/bin/activate . .venv/bin/activate
ruff check . ruff check .
verify: test & nuitka build:
if: always() if: always()
runs-on: debian runs-on: debian
container: node:26-trixie-slim container: node:26-trixie-slim
+49 -121
View File
@@ -4,149 +4,77 @@ on:
pull_request: pull_request:
jobs: jobs:
build: clippy & fmt:
runs-on: alpine runs-on: alpine
container: node:26-alpine container: rust:alpine
env: env:
CARGO_HOME: ${{ github.workspace }}/.cargo CARGO_HOME: ${{ github.workspace }}/.cargo
steps: steps:
- name: Install node for actions
run: |
apk add --no-cache nodejs git
- name: Install rust components
run: |
rustup component add clippy rustfmt
cargo clippy --version
rustfmt --version
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Cache cargo packages - name: Cache cargo packages
uses: actions/cache@v4 uses: actions/cache@v4
env:
cache-name: cache-cargo-packages
with: with:
path: | path: |
.cargo/registry .cargo/registry
.cargo/git .cargo/git
target target
key: rust-build-${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }} key: rust-build-${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: | restore-keys: |
rust-${{ runner.os }}-build-${{ env.cache-name }}- rust-${{ runner.os }}-cargo-
rust-${{ runner.os }}-build-
rust-
- name: Install tools - name: Clippy (with compile check)
run: | run: |
apk add --no-cache \ manifests=$(find . \( -name .cargo -o -name .git -o -name target \) -prune -o -name Cargo.toml -print)
git \ if [ -z "$manifests" ]; then
cargo \ if [ -n "$(find . \( -name .cargo -o -name target \) -prune -o -name '*.rs' -print -quit)" ]; then
rust echo "Rust files found but no Cargo.toml"
exit 1
- name: Cargo check fi
run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
for manifest in $(find . -name "Cargo.toml"); do
cargo check --manifest-path "$manifest" && \
echo "$manifest: check passed" || \
{ echo "$manifest: check failed"; exit 1; }
done
elif [ -n "$(find . -name "*.rs" -print -quit)" ]; then
echo "Rust files found but no Cargo.toml"
exit 1
else
echo "No Rust project found" echo "No Rust project found"
exit 0
fi fi
status=0
fmt: for manifest in $manifests; do
runs-on: alpine if (cd "$(dirname "$manifest")" && cargo clippy --all-targets --locked -- -D warnings); then
container: node:26-alpine echo "$manifest: Clippy passed"
env: else
CARGO_HOME: ${{ github.workspace }}/.cargo echo "$manifest: Clippy failed"
status=1
steps: fi
- name: Checkout done
uses: actions/checkout@v4 exit $status
- name: Cache cargo packages
uses: actions/cache@v4
env:
cache-name: cache-cargo-packages
with:
path: |
.cargo/registry
.cargo/git
target
key: rust-fmt-${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
rust-${{ runner.os }}-build-${{ env.cache-name }}-
rust-${{ runner.os }}-build-
rust-
- name: Install tools
run: |
apk add --no-cache \
git \
cargo \
rust \
rustfmt
- name: Format check - name: Format check
run: | run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then manifests=$(find . \( -name .cargo -o -name .git -o -name target \) -prune -o -name Cargo.toml -print)
status=0 if [ -z "$manifests" ]; then
for manifest in $(find . -name "Cargo.toml"); do if [ -n "$(find . \( -name .cargo -o -name target \) -prune -o -name '*.rs' -print -quit)" ]; then
cargo fmt --manifest-path "$manifest" --check && \ echo "Rust files found but no Cargo.toml"
echo "$manifest: formatting OK" || \ exit 1
{ echo "$manifest: needs formatting"; status=1; } fi
done
exit $status
elif [ -n "$(find . -name "*.rs" -print -quit)" ]; then
echo "Rust files found but no Cargo.toml"
exit 1
else
echo "No Rust project found"
fi
clippy:
runs-on: alpine
container: node:26-alpine
env:
CARGO_HOME: ${{ github.workspace }}/.cargo
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Cache cargo packages
uses: actions/cache@v4
env:
cache-name: cache-cargo-packages
with:
path: |
.cargo/registry
.cargo/git
target
key: rust-clippy-${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
rust-${{ runner.os }}-build-${{ env.cache-name }}-
rust-${{ runner.os }}-build-
rust-
- name: Install tools
run: |
apk add --no-cache \
git \
cargo \
rust \
rust-clippy
- name: Clippy
run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
status=0
for manifest in $(find . -name "Cargo.toml"); do
cargo clippy --manifest-path "$manifest" --all-targets --all-features -- -D warnings && \
echo "$manifest: Clippy passed" || \
{ echo "$manifest: Clippy failed"; status=1; }
done
exit $status
elif [ -n "$(find . -name "*.rs" -print -quit)" ]; then
echo "Rust files found but no Cargo.toml"
exit 1
else
echo "No Rust project found" echo "No Rust project found"
exit 0
fi fi
status=0
for manifest in $manifests; do
if (cd "$(dirname "$manifest")" && cargo fmt --check); then
echo "$manifest: formatting OK"
else
echo "$manifest: needs formatting"
status=1
fi
done
exit $status
+25 -10
View File
@@ -1,23 +1,38 @@
**/__pycache__/
./result/ ./result/
.pyre/ .pyre/
.cache/
.venv/
.qmlls.ini
build/
compile_commands.json
testpython
pkg/ pkg/
*.tar.* *.tar.*
uv.lock
.qtcreator/
dist/ dist/
**/target/ **/target/
**/test-plugins/ **/test-plugins/
**/Charts/ **/Charts/
network-dev/ network-dev/
**/zshell.build/
**/zshell.dist/ **/zshell.dist/
# Qml
.qtcreator/
.qmlls.ini
# CMake compile artifacts
.cache/
compile_commands.json
build/
# Cargo home (CI sets CARGO_HOME inside the workspace)
.cargo/
# Python
uv.lock
testpython
.venv/
**/__pycache__/
# Nuitka compile
**/zshell.pyi
**/zshell.cpython-*
**/zshell.build/
# Agents stuff
.opencode/ .opencode/
run-agent.sh run-agent.sh
AGENTS.md AGENTS.md
+1
View File
@@ -50,6 +50,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(QT_NO_PRIVATE_MODULE_WARNING ON)
set(ENABLE_MODULES "plugin;shell;cli;m3shapes" CACHE STRING "Modules to build/install") set(ENABLE_MODULES "plugin;shell;cli;m3shapes" CACHE STRING "Modules to build/install")
set(INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/ZShell" CACHE STRING "Library install dir") set(INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/ZShell" CACHE STRING "Library install dir")
+1 -1
View File
@@ -16,7 +16,7 @@ CustomRect {
property color activeOnColor property color activeOnColor
property bool checked property bool checked
property real checkedRadius: Tokens.rounding.medium property real checkedRadius: Tokens.rounding.medium
property real defaultRadius: Math.min(width, height) / 2 property real defaultRadius: Tokens.rounding.large
property color disabledColor: Qt.alpha(Colors.palette.m3onSurface, 0.1) property color disabledColor: Qt.alpha(Colors.palette.m3onSurface, 0.1)
property color disabledOnColor: Qt.alpha(Colors.palette.m3onSurface, 0.38) property color disabledOnColor: Qt.alpha(Colors.palette.m3onSurface, 0.38)
property bool fillWidth property bool fillWidth
-2
View File
@@ -34,7 +34,6 @@ BusyIndicator {
anchors.fill: parent anchors.fill: parent
bgColor: root.bgColor bgColor: root.bgColor
fgColor: root.fgColor fgColor: root.fgColor
hasEndIndicator: false
padding: root.padding padding: root.padding
rotation: manager.rotation rotation: manager.rotation
startAngle: manager.startFraction * 360 startAngle: manager.startFraction * 360
@@ -54,7 +53,6 @@ BusyIndicator {
Anim { Anim {
duration: manager.completeEndDuration * Tokens.anim.durations.scale duration: manager.completeEndDuration * Tokens.anim.durations.scale
properties: "opacity,internalStrokeWidth" properties: "opacity,internalStrokeWidth"
type: Anim.DefaultEffects
} }
} }
+2 -4
View File
@@ -46,8 +46,7 @@ Item {
strokeWidth: Math.min(1, remainingArc.sweepAngle) * root.strokeWidth strokeWidth: Math.min(1, remainingArc.sweepAngle) * root.strokeWidth
Behavior on strokeColor { Behavior on strokeColor {
CAnim { CAnim {}
}
} }
PathAngleArc { PathAngleArc {
@@ -84,8 +83,7 @@ Item {
} }
} }
Behavior on color { Behavior on color {
CAnim { CAnim {}
}
} }
Anim on waveProgress { Anim on waveProgress {
id: waveProgAnim id: waveProgAnim
+3 -2
View File
@@ -1,4 +1,5 @@
import QtQuick import QtQuick
import Quickshell
import ZShell.Components import ZShell.Components
Item { Item {
@@ -19,7 +20,7 @@ Item {
} }
ShaderEffectSource { ShaderEffectSource {
id: source id: src
hideSource: true hideSource: true
sourceItem: contentItem sourceItem: contentItem
@@ -27,7 +28,7 @@ Item {
RoundedMask { RoundedMask {
anchors.fill: parent anchors.fill: parent
source: source source: src
topLeftRadius: root.topLeftRadius topLeftRadius: root.topLeftRadius
topRightRadius: root.topRightRadius topRightRadius: root.topRightRadius
bottomRightRadius: root.bottomRightRadius bottomRightRadius: root.bottomRightRadius
+1 -2
View File
@@ -160,8 +160,7 @@ Item {
Behavior on color { Behavior on color {
enabled: false enabled: false
CAnim { CAnim {}
}
} }
} }
+1 -2
View File
@@ -8,7 +8,6 @@ MultiEffect {
colorization: 1 colorization: 1
Behavior on colorizationColor { Behavior on colorizationColor {
CAnim { CAnim {}
}
} }
} }
+1 -2
View File
@@ -7,7 +7,6 @@ ClippingRectangle {
color: "transparent" color: "transparent"
Behavior on color { Behavior on color {
CAnim { CAnim {}
}
} }
} }
+1 -2
View File
@@ -7,7 +7,6 @@ ClippingWrapperRectangle {
color: "transparent" color: "transparent"
Behavior on color { Behavior on color {
CAnim { CAnim {}
}
} }
} }
+2 -4
View File
@@ -124,8 +124,7 @@ ProgressBar {
anchors.left: parent.left anchors.left: parent.left
Behavior on implicitWidth { Behavior on implicitWidth {
Anim { Anim {}
}
} }
Component.onCompleted: implicitWidth = Qt.binding(() => parent.width * root.visualPosition) Component.onCompleted: implicitWidth = Qt.binding(() => parent.width * root.visualPosition)
@@ -222,8 +221,7 @@ ProgressBar {
} }
} }
Behavior on color { Behavior on color {
CAnim { CAnim {}
}
} }
Anim on waveProgress { Anim on waveProgress {
duration: root.waveDuration duration: root.waveDuration
+5 -10
View File
@@ -105,20 +105,16 @@ Switch {
width: height width: height
Behavior on end1 { Behavior on end1 {
PropAnim { PropAnim {}
}
} }
Behavior on end2 { Behavior on end2 {
PropAnim { PropAnim {}
}
} }
Behavior on start1 { Behavior on start1 {
PropAnim { PropAnim {}
}
} }
Behavior on start2 { Behavior on start2 {
PropAnim { PropAnim {}
}
} }
ShapePath { ShapePath {
@@ -134,8 +130,7 @@ Switch {
strokeWidth: 16 * 0.15 strokeWidth: 16 * 0.15
Behavior on strokeColor { Behavior on strokeColor {
CAnim { CAnim {}
}
} }
PathLine { PathLine {
+2 -4
View File
@@ -20,8 +20,7 @@ Text {
textFormat: Text.PlainText textFormat: Text.PlainText
Behavior on color { Behavior on color {
CAnim { CAnim {}
}
} }
Behavior on text { Behavior on text {
enabled: root.animate enabled: root.animate
@@ -33,8 +32,7 @@ Text {
type: Anim.FastEffects type: Anim.FastEffects
} }
PropertyAction { PropertyAction {}
}
TAnim { TAnim {
target: root target: root
-1
View File
@@ -150,7 +150,6 @@ TextFieldBase {
sourceComponent: MaterialIcon { sourceComponent: MaterialIcon {
color: Colors.palette.m3onSurfaceVariant color: Colors.palette.m3onSurfaceVariant
font.pointSize: Tokens.font.size.larger
text: root.leadingIcon text: root.leadingIcon
fontStyle: Tokens.font.icon.builders.medium.scale(0.9).build() fontStyle: Tokens.font.icon.builders.medium.scale(0.9).build()
} }
+1 -2
View File
@@ -65,8 +65,7 @@ Image {
value: root.source value: root.source
} }
PropertyAction { PropertyAction {}
}
ScriptAction { ScriptAction {
script: root.maybeStartInAnim() script: root.maybeStartInAnim()
+4 -1
View File
@@ -138,7 +138,10 @@ Item {
const t = (tlo + thi) / 2; const t = (tlo + thi) / 2;
const u = 1 - t; const u = 1 - t;
const y = u * u * u * p1.y + 3 * u * u * t * c1y + 3 * u * t * t * c2y + t * t * t * p2.y; const y = u * u * u * p1.y + 3 * u * u * t * c1y + 3 * u * t * t * c2y + t * t * t * p2.y;
return { x: targetX, y }; return {
x: targetX,
y
};
} }
// Part of the curve left/right of a cut x, including the cut point // Part of the curve left/right of a cut x, including the cut point
+1 -2
View File
@@ -64,8 +64,7 @@ MaterialShape {
to: 360 to: 360
} }
Behavior on color { Behavior on color {
CAnim { CAnim {}
}
} }
ElapsedTimer { ElapsedTimer {
+2 -4
View File
@@ -47,12 +47,10 @@ Item {
implicitWidth: vertical ? elideText.implicitHeight : elideText.implicitWidth implicitWidth: vertical ? elideText.implicitHeight : elideText.implicitWidth
Behavior on leftFadeStrength { Behavior on leftFadeStrength {
Anim { Anim {}
}
} }
Behavior on rightFadeStrength { Behavior on rightFadeStrength {
Anim { Anim {}
}
} }
onHeightChanged: resetMarquee() onHeightChanged: resetMarquee()
+3 -5
View File
@@ -34,7 +34,7 @@ MouseArea {
cursorShape: expanded ? Qt.ArrowCursor : undefined cursorShape: expanded ? Qt.ArrowCursor : undefined
enabled: expanded enabled: expanded
hoverEnabled: expanded hoverEnabled: expanded
layer.enabled: opacity < 1 layer.enabled: false
opacity: expanded ? 1 : 0 opacity: expanded ? 1 : 0
parent: { parent: {
const win = QsWindow.window; const win = QsWindow.window;
@@ -101,8 +101,7 @@ MouseArea {
yScale: root.expanded ? 1 : 0.1 yScale: root.expanded ? 1 : 0.1
Behavior on yScale { Behavior on yScale {
Anim { Anim {}
}
} }
} }
@@ -137,8 +136,7 @@ MouseArea {
radius: menu.radius - column.anchors.margins radius: menu.radius - column.anchors.margins
Behavior on radius { Behavior on radius {
Anim { Anim {}
}
} }
StateLayer { StateLayer {
+3 -6
View File
@@ -102,8 +102,7 @@ Row {
topLeftRadius: rad topLeftRadius: rad
Behavior on rad { Behavior on rad {
Anim { Anim {}
}
} }
StateLayer { StateLayer {
@@ -127,12 +126,10 @@ Row {
text: "expand_more" text: "expand_more"
Behavior on anchors.horizontalCenterOffset { Behavior on anchors.horizontalCenterOffset {
Anim { Anim {}
}
} }
Behavior on rotation { Behavior on rotation {
Anim { Anim {}
}
} }
} }
} }
+3 -6
View File
@@ -44,8 +44,7 @@ Item {
onValuesChanged: root.flagChanged() onValuesChanged: root.flagChanged()
} }
ToastWrapper { ToastWrapper {}
}
} }
component ToastWrapper: MouseArea { component ToastWrapper: MouseArea {
@@ -86,12 +85,10 @@ Item {
} }
} }
Behavior on opacity { Behavior on opacity {
Anim { Anim {}
}
} }
Behavior on scale { Behavior on scale {
Anim { Anim {}
}
} }
Component.onCompleted: modelData.lock(this) Component.onCompleted: modelData.lock(this)
+1 -2
View File
@@ -20,8 +20,7 @@ Shape {
strokeWidth: 0 strokeWidth: 0
Behavior on fillColor { Behavior on fillColor {
CAnim { CAnim {}
}
} }
PathSvg { PathSvg {
-1
View File
@@ -141,7 +141,6 @@ Singleton {
BeatTracker { BeatTracker {
id: beatTracker id: beatTracker
} }
PwObjectTracker { PwObjectTracker {
+1 -2
View File
@@ -295,8 +295,7 @@ Singleton {
Component { Component {
id: apComp id: apComp
AccessPoint { AccessPoint {}
}
} }
Process { Process {
+2 -5
View File
@@ -1081,15 +1081,13 @@ Singleton {
Component { Component {
id: commandProc id: commandProc
CommandProcess { CommandProcess {}
}
} }
Component { Component {
id: apComp id: apComp
AccessPoint { AccessPoint {}
}
} }
Timer { Timer {
@@ -1310,7 +1308,6 @@ Singleton {
} }
stdout: StdioCollector { stdout: StdioCollector {
id: stdoutCollector id: stdoutCollector
} }
onExited: code => { onExited: code => {
+1 -2
View File
@@ -164,8 +164,7 @@ Singleton {
Component { Component {
id: notifComp id: notifComp
Notif { Notif {}
}
} }
component Notif: QtObject { component Notif: QtObject {
-7
View File
@@ -43,13 +43,6 @@ Scope {
hasBar: root.geometry.barOnBottom hasBar: root.geometry.barOnBottom
} }
Timer {
interval: 5000
running: true
onTriggered: console.log("top height:", top.exclusiveZone, "left width:", left.exclusiveZone, "right width:", right.exclusiveZone, "bottom height:", bottom.exclusiveZone)
}
component ExclusionZone: CustomWindow { component ExclusionZone: CustomWindow {
property bool hasBar property bool hasBar
-1
View File
@@ -11,7 +11,6 @@ import ZShell.Blobs
import qs.Daemons import qs.Daemons
import qs.Components import qs.Components
import qs.Modules.Bar import qs.Modules.Bar
import qs.Modules.Bar.Popouts.Network as N
import ZShell.Config import ZShell.Config
import qs.Helpers import qs.Helpers
import qs.Drawers import qs.Drawers
+2 -4
View File
@@ -46,8 +46,7 @@ Item {
strokeWidth: Math.min(1, remainingArc.sweepAngle) * root.strokeWidth strokeWidth: Math.min(1, remainingArc.sweepAngle) * root.strokeWidth
Behavior on strokeColor { Behavior on strokeColor {
CAnim { CAnim {}
}
} }
PathAngleArc { PathAngleArc {
@@ -84,8 +83,7 @@ Item {
} }
} }
Behavior on color { Behavior on color {
CAnim { CAnim {}
}
} }
Anim on waveProgress { Anim on waveProgress {
id: waveProgAnim id: waveProgAnim
+1 -2
View File
@@ -7,7 +7,6 @@ ClippingRectangle {
color: "transparent" color: "transparent"
Behavior on color { Behavior on color {
CAnim { CAnim {}
}
} }
} }
+2 -4
View File
@@ -20,8 +20,7 @@ Text {
textFormat: Text.PlainText textFormat: Text.PlainText
Behavior on color { Behavior on color {
CAnim { CAnim {}
}
} }
Behavior on text { Behavior on text {
enabled: root.animate enabled: root.animate
@@ -33,8 +32,7 @@ Text {
type: Anim.FastEffects type: Anim.FastEffects
} }
PropertyAction { PropertyAction {}
}
TAnim { TAnim {
target: root target: root
+1 -2
View File
@@ -65,8 +65,7 @@ Image {
value: root.source value: root.source
} }
PropertyAction { PropertyAction {}
}
ScriptAction { ScriptAction {
script: root.maybeStartInAnim() script: root.maybeStartInAnim()
+1 -2
View File
@@ -64,8 +64,7 @@ MaterialShape {
to: 360 to: 360
} }
Behavior on color { Behavior on color {
CAnim { CAnim {}
}
} }
ElapsedTimer { ElapsedTimer {
-1
View File
@@ -200,6 +200,5 @@ Singleton {
ElapsedTimer { ElapsedTimer {
id: timer id: timer
} }
} }
-1
View File
@@ -16,7 +16,6 @@ ShellRoot {
GreeterState { GreeterState {
id: greeter id: greeter
} }
GreeterSurface { GreeterSurface {
+1 -2
View File
@@ -69,8 +69,7 @@ Singleton {
model: Quickshell.screens model: Quickshell.screens
Monitor { Monitor {}
}
} }
Process { Process {
+10
View File
@@ -77,6 +77,16 @@ Singleton {
"99": "thunderstorm" "99": "thunderstorm"
}) })
function getAppCategoryIcon(name: string, fallback: string): string {
const categories = DesktopEntries.heuristicLookup(name)?.categories;
if (categories)
for (const [key, value] of Object.entries(categoryIcons))
if (categories.includes(key))
return value;
return fallback;
}
function getAppIcon(name: string, fallback: string): string { function getAppIcon(name: string, fallback: string): string {
const icon = DesktopEntries.heuristicLookup(name)?.icon; const icon = DesktopEntries.heuristicLookup(name)?.icon;
if (fallback !== "undefined") if (fallback !== "undefined")
+1 -2
View File
@@ -33,8 +33,7 @@ Singleton {
implicitHeight: 0 implicitHeight: 0
implicitWidth: 0 implicitWidth: 0
mask: Region { mask: Region {}
}
} }
} }
-122
View File
@@ -1,122 +0,0 @@
pragma Singleton
import Quickshell
import Quickshell.Networking
import QtQuick
Singleton {
id: root
property bool active: false
readonly property list<Network> connectedNetworks: networks.filter(n => n.connected)
property Network connectingNetwork
readonly property list<NetworkDevice> devices: Networking.devices.values
property Network failedNetwork
readonly property list<Network> knownNetworks: networks.filter(n => n.known && !n.connected)
readonly property list<Network> networks: {
const list = [];
for (const d of wifiDevices) {
for (const n of d.networks.values) {
if (!list.includes(n))
list.push(n);
}
}
return list;
}
property bool scanning: false
readonly property list<Network> unknownNetworks: networks.filter(n => !n.known)
readonly property list<WifiDevice> wifiDevices: devices.filter(d => wifiDevice(d))
readonly property bool wifiEnabled: Networking.wifiEnabled
signal networkConnected
function forgetNetwork(target): void {
if (!target) {
return;
}
target.forget();
}
function isSecure(security): bool {
return (security === WifiSecurityType.WpaPsk || security === WifiSecurityType.Wpa2Psk || security === WifiSecurityType.Sae);
}
function requestConnect(network: Network, secret = undefined): void {
connectingNetwork = network;
if (secret === undefined || secret === "") {
network.connect();
} else {
(network as WifiNetwork).connectWithPsk(secret);
}
}
function setScan(value: bool): void {
for (const d of wifiDevices) {
d.scannerEnabled = value;
}
}
function setWifi(value: bool): void {
Networking.wifiEnabled = value;
}
function wifiDevice(dev): bool {
return dev.type === DeviceType.Wifi;
}
onActiveChanged: {
for (const d of wifiDevices)
d.scannerEnabled = active;
}
Timer {
id: flushFailed
interval: 5000
running: root.failedNetwork
onTriggered: {
root.failedNetwork = null;
}
}
Timer {
id: checkConnection
interval: 500
repeat: true
running: root.connectingNetwork
onTriggered: {
var completedNetwork;
switch (root.connectingNetwork.state) {
case ConnectionState.Connecting:
break;
case ConnectionState.Connected:
completedNetwork = root.connectingNetwork;
root.networkConnected();
break;
case ConnectionState.Disconnected:
root.failedNetwork = root.connectingNetwork;
completedNetwork = root.connectingNetwork;
break;
}
if (completedNetwork) {
root.connectingNetwork = null;
}
}
}
Timer {
interval: 10000
running: checkConnection.running
onTriggered: {
root.failedNetwork = root.connectingNetwork;
root.connectingNetwork = null;
}
}
}
-31
View File
@@ -1,31 +0,0 @@
import QtQuick
import ZShell.Config
import qs.Services
import qs.Modules.Settings.Common
import qs.Modules.Bar.Popouts.Network
import qs.Components
import qs.Helpers
CustomText {
required property var modelData
bottomPadding: Tokens.spacing.extraSmall
color: Colors.palette.m3onSurfaceVariant
font.pointSize: Tokens.font.size.smaller
text: {
if (modelData.type === "known") {
const count = Network.knownNetworks.filter(n => !n.connected).length;
return qsTr("%1 known network%2").arg(count).arg(count === 1 ? "" : "s");
}
if (modelData.type === "unknown") {
const count = Network.unknownNetworks.filter(n => !n.connected).length;
return qsTr("%1 network%2 available").arg(count).arg(count === 1 ? "" : "s");
}
return qsTr("object unknown");
}
topPadding: Tokens.spacing.small
}
-118
View File
@@ -1,118 +0,0 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Modules.Settings.Common
import qs.Modules.Bar.Popouts.Network
import qs.Components
import qs.Helpers
import qs.Helpers.Network
import qs.Services
ConnectedRect {
id: networkItem
readonly property int connectedCount: Network.connectedNetworks.length
readonly property bool connecting: Network.connectingNetwork === modelData
property int horizontalPadding: Tokens.padding.largeIncreased
readonly property bool inConnectedGroup: index < connectedCount
// sectioning first / last item in list reminder
// if index === network.connectedNetworks.length - 1
// if index === network.connectedNetworks.length - 1 + network.knownNetworks.length - 2
// if index === network.connectedNetworks.length - 1 + network.knownNetworks.length
// list.model.values.length - 1
required property int index
readonly property bool isSecure: Network.isSecure(modelData.security)
required property ListView list
required property var modelData
property int verticalPadding: Tokens.padding.smaller
Layout.fillWidth: true
first: !inConnectedGroup && index === connectedCount + 1
implicitHeight: rowLayout.implicitHeight + verticalPadding * 2
last: inConnectedGroup ? index === connectedCount - 1 : index === (list?.count ?? -1)
radius: Tokens.rounding.small
RowLayout {
id: rowLayout
Layout.preferredHeight: visible ? rowLayout.implicitHeight + Tokens.padding.smaller * 2 : 0
anchors.fill: parent
anchors.leftMargin: networkItem.horizontalPadding
anchors.rightMargin: networkItem.horizontalPadding
Column {
id: column
Layout.fillWidth: true
CustomText {
Layout.fillWidth: true
Layout.leftMargin: Tokens.spacing.extraSmall
Layout.rightMargin: Tokens.spacing.extraSmall
color: networkItem.modelData.active ? Colors.palette.m3primary : Colors.palette.m3onSurface
elide: Text.ElideRight
text: networkItem.modelData.name ? networkItem.modelData.name : qsTr("Unknown Network")
}
CustomText {
id: subtext
anchors.left: parent.left
anchors.right: parent.right
color: Colors.palette.m3outline
elide: Text.ElideRight
font.pointSize: Tokens.font.size.small
text: networkItem.modelData.connected ? qsTr("Connected: %1 strength").arg(Math.round(networkItem.modelData.signalStrength * 100) + "%") : qsTr("%1 strength").arg(Math.round(networkItem.modelData.signalStrength * 100) + "%")
}
}
Item {
Layout.alignment: Qt.AlignRight
implicitHeight: icon.height
implicitWidth: icon.width
MaterialIcon {
id: icon
color: networkItem.modelData.active ? Colors.palette.m3primary : Colors.palette.m3onSurfaceVariant
font.pointSize: Tokens.font.size.large
opacity: networkItem.connecting ? 0 : 1
text: Icons.getNetworkIcon(networkItem.modelData.signalStrength * 100, networkItem.isSecure)
Behavior on opacity {
Anim {
}
}
}
Loader {
active: opacity > 0
anchors.fill: parent
opacity: networkItem.connecting ? 1 : 0
Behavior on opacity {
Anim {
}
}
sourceComponent: CircularIndicator {
bgColor: "transparent"
running: true
}
}
}
}
StateLayer {
color: networkItem.modelData.active ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
onClicked: {
if (networkItem.isSecure && !networkItem.known) {
PopupManager.requestOpen(Qt.resolvedUrl("../../Modules/Bar/Popouts/Network/PSKOverlay.qml"), {
network: networkItem.modelData
});
} else {
Network.requestConnect(networkItem.modelData);
}
}
}
}
-74
View File
@@ -1,74 +0,0 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Services
import qs.Components
import qs.Modules.Settings.Common
CustomSwitch {
id: toggleItem
readonly property alias bg: bg
property alias first: bg.first
property alias last: bg.last
property string subtext
Layout.fillWidth: true
cLayer: 2
horizontalPadding: Tokens.padding.largeIncreased
implicitHeight: Math.max(implicitContentHeight, implicitIndicatorHeight) + verticalPadding * 2
implicitWidth: implicitContentWidth + implicitIndicatorWidth + horizontalPadding * 2
indicator.anchors.right: right
indicator.anchors.rightMargin: toggleItem.horizontalPadding
indicator.anchors.verticalCenter: verticalCenter
verticalPadding: Tokens.padding.normal
background: ConnectedRect {
id: bg
StateLayer {
id: stateLayer
manualPressOverride: toggleItem.pressed
}
}
contentItem: Item {
anchors.left: parent.left
anchors.leftMargin: toggleItem.horizontalPadding
anchors.right: toggleItem.indicator.left
anchors.rightMargin: Tokens.spacing.normal
implicitHeight: column.implicitHeight
implicitWidth: column.implicitWidth
ColumnLayout {
id: column
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 0
CustomText {
id: label
Layout.fillWidth: true
elide: Text.ElideRight
font.pointSize: Tokens.font.size.smaller
text: toggleItem.text
}
CustomText {
id: subtext
Layout.fillWidth: true
color: Colors.palette.m3outline
elide: Text.ElideRight
font.pointSize: Tokens.font.size.small
text: toggleItem.subtext
visible: toggleItem.subtext
}
}
}
onPressed: stateLayer.press(stateLayer.mouseX, stateLayer.mouseY)
}
+105 -43
View File
@@ -5,7 +5,7 @@ import Quickshell
import Quickshell.Io import Quickshell.Io
import QtQuick import QtQuick
import ZShell.Models import ZShell.Models
import ZShell.Internal import ZShell.Wallpaper
import ZShell.Config import ZShell.Config
import qs.Components import qs.Components
import qs.Services import qs.Services
@@ -16,24 +16,48 @@ Searcher {
id: root id: root
property string actualCurrent: WallpaperPath.currentWallpaperPath property string actualCurrent: WallpaperPath.currentWallpaperPath
property alias crops: adapter.monitorCrops
readonly property string current: showPreview ? previewPath : actualCurrent readonly property string current: showPreview ? previewPath : actualCurrent
property alias monitorCrops: monitorCrops readonly property string currentThumbnail: thumbnailFor(current)
readonly property bool currentIsVideo: isVideoPath(current)
property bool pendingPreviewClear property bool pendingPreviewClear
property bool previewColorLock property bool previewColorLock
property string previewPath property string previewPath
property bool recentlyChanged property bool recentlyChanged
property bool showPreview: false property bool showPreview: false
property int cropVersion: 0
function getCategoryFor(w: FileSystemEntry): string { function getCategoryFor(w: WallpaperEntry): string {
let category = w.parentDir.slice(Paths.wallsdir.length + 1); let category = w.parentDir.slice(Paths.wallsdir.length + 1);
if (category.includes("/")) if (category.includes("/"))
category = category.slice(0, category.indexOf("/")); category = category.slice(0, category.indexOf("/"));
return category; return category;
} }
readonly property var videoExts: ["mp4", "mkv", "webm", "avi", "mov", "wmv", "flv", "m4v", "mpg", "mpeg", "3gp", "ogv", "ts", "vob"]
function isVideoPath(path: string): bool {
if (!path)
return false;
const dot = path.lastIndexOf(".");
if (dot < 0)
return false;
return videoExts.includes(path.slice(dot + 1).toLowerCase());
}
function thumbnailFor(path: string): string {
if (!path)
return "";
for (const entry of root.list) {
if (entry.path === path)
return entry.thumbnailPath || path;
}
return path;
}
function getCrop(screen: string): var { function getCrop(screen: string): var {
return root.crops[screen]; return wallpapers.crop(root.current, screen);
} }
function preview(path: string): void { function preview(path: string): void {
@@ -50,19 +74,18 @@ Searcher {
else if (zoom > 5.0) else if (zoom > 5.0)
zoom = 5.0; zoom = 5.0;
root.crops[screen] = { wallpapers.setCrop(root.current, screen, {
x: rect.x, x: rect.x,
y: rect.y, y: rect.y,
width: rect.width, width: rect.width,
height: rect.height, height: rect.height,
zoom: zoom zoom: zoom
}; });
} }
function setWallpaper(path: string): void { function setWallpaper(path: string): void {
actualCurrent = path; actualCurrent = path;
WallpaperPath.currentWallpaperPath = path; WallpaperPath.currentWallpaperPath = path;
Quickshell.screens.forEach(n => setCrop(n.name, Qt.rect(0, 0, 1, 1), 1.0));
Quickshell.execDetached(["zshell-cli", "wallpaper", "lockscreen", "--input-image", `${root.actualCurrent}`, "--output-path", `${Paths.state}/lockscreen_bg.png`, "--blur-amount", `${Config.lock.blurAmount}`]); Quickshell.execDetached(["zshell-cli", "wallpaper", "lockscreen", "--input-image", `${root.actualCurrent}`, "--output-path", `${Paths.state}/lockscreen_bg.png`, "--blur-amount", `${Config.lock.blurAmount}`]);
if (Config.colors.schemeGen) if (Config.colors.schemeGen)
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${root.actualCurrent}`, "--scheme", `${Config.colors.schemeType}`, "--mode", `${Config.colors.mode}`]); Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${root.actualCurrent}`, "--scheme", `${Config.colors.schemeType}`, "--mode", `${Config.colors.mode}`]);
@@ -96,38 +119,13 @@ Searcher {
target: "wallpaper" target: "wallpaper"
} }
FileView { WallpaperDb {
id: monitorCrops
path: `${Paths.state}/wallpaper-crops.json`
watchChanges: true
onAdapterUpdated: cropWriteDelay.restart()
onFileChanged: reload()
JsonAdapter {
id: adapter
property var monitorCrops: ({})
}
}
Timer {
id: cropWriteDelay
interval: 100
repeat: false
running: false
onTriggered: monitorCrops.writeAdapter()
}
FileSystemModel {
id: wallpapers id: wallpapers
filter: FileSystemModel.Images dbPath: `${Paths.state}/wallpapers.sqlite`
cacheSize: wallpaper.largestBufferSize
requireHardwareCodec: true
path: Config.general.wallpaperPath path: Config.general.wallpaperPath
recursive: true
} }
Process { Process {
@@ -146,36 +144,100 @@ Searcher {
WallpaperService { WallpaperService {
id: wallpaper id: wallpaper
dbPath: wallpapers.dbPath
path: root.current path: root.current
} }
Connections {
target: wallpapers
function onCacheStored(path: string): void {
if (path === root.current)
wallpaper.invalidateCache();
}
function onCropsChanged(path: string): void {
if (path === root.current)
root.cropVersion++;
}
}
Variants { Variants {
id: screenVariants
model: Quickshell.screens model: Quickshell.screens
WallpaperObject { WallpaperObject {
readonly property var crops: root.getCrop(modelData.name) id: obj
required property var modelData required property var modelData
cropHeight: crops?.height ?? 1.0 // Crop animation only for edits, never for wallpaper swaps.
cropWidth: crops?.width ?? 1.0 property bool animateCrop: false
cropX: crops?.x ?? 0.0
cropY: crops?.y ?? 0.0
screen: modelData.name screen: modelData.name
service: wallpaper service: wallpaper
zoom: crops?.zoom ?? 1.0
// Apply the wallpaper's own crop. Driven off the service's pathChanged
// (which fires after applyPath has snapshotted the outgoing frame with
// the outgoing crop) and off crop edits, never as a reactive binding on
// the wallpaper path — so a swap applies the incoming crop atomically
// after the base is captured.
function seedCrop(): void {
// Load the current wallpaper's crop without pushing: the service
// already read it from the database. This only seeds the values so
// a later edit animates from the right starting point.
obj.syncCrop(root.getCrop(modelData.name));
}
// Apply an edited crop: assign via the setters so the Behaviors animate
// the numbers and push each intermediate value to the service.
function editCrop(): void {
const c = root.getCrop(modelData.name);
obj.cropX = c?.x ?? 0.0;
obj.cropY = c?.y ?? 0.0;
obj.cropWidth = c?.width ?? 1.0;
obj.cropHeight = c?.height ?? 1.0;
obj.zoom = c?.zoom ?? 1.0;
}
Component.onCompleted: seedCrop()
onScreenChanged: seedCrop()
Behavior on cropHeight { Behavior on cropHeight {
enabled: obj.animateCrop
Anim {} Anim {}
} }
Behavior on cropWidth { Behavior on cropWidth {
enabled: obj.animateCrop
Anim {} Anim {}
} }
Behavior on cropX { Behavior on cropX {
enabled: obj.animateCrop
Anim {} Anim {}
} }
Behavior on cropY { Behavior on cropY {
enabled: obj.animateCrop
Anim {} Anim {}
} }
} }
} }
Connections {
target: wallpaper
function onPathChanged(): void {
for (const obj of screenVariants.instances) {
obj.animateCrop = false;
obj.seedCrop();
}
}
}
Connections {
target: root
function onCropVersionChanged(): void {
for (const obj of screenVariants.instances) {
obj.animateCrop = true;
obj.editCrop();
}
}
}
} }
+8
View File
@@ -206,6 +206,14 @@ GridLayout {
} }
} }
DelegateChoice {
roleValue: "network"
delegate: EntryWrapper {
NetworkWidget {}
}
}
DelegateChoice { DelegateChoice {
roleValue: "media" roleValue: "media"
+3 -1
View File
@@ -45,7 +45,9 @@ WidgetBase {
} }
required property GridLayout loader required property GridLayout loader
required property Wrapper popouts required property Wrapper popouts
readonly property real size: horizontal ? timeText.contentWidth + Tokens.padding.medium * 2 : verticalColumn.implicitHeight + Tokens.padding.medium * 2 readonly property int roundedWidth: Math.round(timeText.implicitWidth)
readonly property int evenWidth: roundedWidth % 2 === 0 ? roundedWidth : roundedWidth + 1
readonly property real size: horizontal ? evenWidth + Tokens.padding.medium * 2 : verticalColumn.implicitHeight + Tokens.padding.medium * 2
required property ScreenState screenState required property ScreenState screenState
implicitHeight: horizontal ? baseSize : size implicitHeight: horizontal ? baseSize : size
@@ -24,7 +24,7 @@ Row {
color: Battery.getColors(root.percentage, root.devState).bg color: Battery.getColors(root.percentage, root.devState).bg
height: root.batHeight height: root.batHeight
radius: root.radius radius: root.radius
width: root.batWidth width: root.batWidth - 1
CustomText { CustomText {
color: Battery.getColors(root.percentage, root.devState).fg color: Battery.getColors(root.percentage, root.devState).fg
+1 -1
View File
@@ -13,7 +13,7 @@ WidgetBase {
readonly property int textWidth: Math.min(metrics.width, 200) readonly property int textWidth: Math.min(metrics.width, 200)
implicitHeight: horizontal ? baseSize : layout.implicitHeight + Tokens.padding.small * 2 implicitHeight: horizontal ? baseSize : layout.implicitHeight + Tokens.padding.small * 2
implicitWidth: horizontal ? layout.implicitWidth + Tokens.padding.small * 2 : baseSize implicitWidth: horizontal ? (layout.implicitWidth % 2 === 0 ? layout.implicitWidth : layout.implicitWidth + 1) + Tokens.padding.small * 2 : baseSize
radius: Tokens.rounding.full radius: Tokens.rounding.full
Behavior on implicitHeight { Behavior on implicitHeight {
+8 -12
View File
@@ -12,7 +12,6 @@ GridLayout {
property real animatedPercentage: 0 property real animatedPercentage: 0
readonly property real arcStartAngle: 0.75 * Math.PI readonly property real arcStartAngle: 0.75 * Math.PI
readonly property real arcSweep: 1.5 * Math.PI readonly property real arcSweep: 1.5 * Math.PI
readonly property real dpr: (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1
required property bool horizontal required property bool horizontal
property string icon property string icon
required property color iconColor required property color iconColor
@@ -23,17 +22,12 @@ GridLayout {
property bool warning: percentage * 100 >= warningThreshold property bool warning: percentage * 100 >= warningThreshold
property int warningThreshold: 80 property int warningThreshold: 80
function snap(v) {
return Math.round(v * dpr) / dpr;
}
columnSpacing: Tokens.spacing.medium columnSpacing: Tokens.spacing.medium
columns: horizontal ? -1 : 1 columns: horizontal ? -1 : 1
percentage: 0 percentage: 0
Behavior on animatedPercentage { Behavior on animatedPercentage {
Anim { Anim {}
}
} }
Component.onCompleted: animatedPercentage = percentage Component.onCompleted: animatedPercentage = percentage
@@ -59,7 +53,7 @@ GridLayout {
Layout.preferredHeight: root.horizontal ? icon.implicitHeight - Tokens.padding.extraSmall : 4 Layout.preferredHeight: root.horizontal ? icon.implicitHeight - Tokens.padding.extraSmall : 4
Layout.preferredWidth: root.horizontal ? 4 : icon.implicitWidth Layout.preferredWidth: root.horizontal ? 4 : icon.implicitWidth
color: Colors.layer(Colors.palette.m3surfaceContainerHigh, 2) color: Colors.layer(Colors.palette.m3surfaceContainerHigh, 2)
radius: Math.min(width, height) / 2 radius: Tokens.rounding.full
Item { Item {
id: clipper id: clipper
@@ -69,18 +63,20 @@ GridLayout {
anchors.left: root.horizontal ? undefined : parent.left anchors.left: root.horizontal ? undefined : parent.left
anchors.verticalCenter: root.horizontal ? undefined : parent.verticalCenter anchors.verticalCenter: root.horizontal ? undefined : parent.verticalCenter
clip: true clip: true
height: root.horizontal ? root.snap(root.percentage * track.height) : track.height * 2 implicitHeight: root.horizontal ? root.percentage * track.height : track.height * 2
width: root.horizontal ? track.width * 2 : root.snap(root.percentage * track.width) implicitWidth: root.horizontal ? track.width * 2 : root.percentage * track.width
CustomRect { CustomRect {
anchors.bottom: root.horizontal ? parent.bottom : undefined anchors.bottom: root.horizontal ? parent.bottom : undefined
anchors.horizontalCenter: root.horizontal ? parent.horizontalCenter : undefined anchors.horizontalCenter: root.horizontal ? parent.horizontalCenter : undefined
anchors.left: root.horizontal ? undefined : parent.left anchors.left: root.horizontal ? undefined : parent.left
anchors.verticalCenter: root.horizontal ? undefined : parent.verticalCenter anchors.verticalCenter: root.horizontal ? undefined : parent.verticalCenter
color: root.mainColor color: root.mainColor
height: track.height
radius: track.radius radius: track.radius
width: track.width
implicitHeight: track.height
implicitWidth: track.width
} }
} }
} }
+2
View File
@@ -33,6 +33,7 @@ WidgetBase {
anchors.horizontalCenterOffset: root.horizontal ? 0 : 1 anchors.horizontalCenterOffset: root.horizontal ? 0 : 1
anchors.verticalCenterOffset: root.horizontal ? 0 : -3 anchors.verticalCenterOffset: root.horizontal ? 0 : -3
columnSpacing: Tokens.spacing.small columnSpacing: Tokens.spacing.small
rowSpacing: 0
columns: root.horizontal ? -1 : 1 columns: root.horizontal ? -1 : 1
ServiceRef { ServiceRef {
@@ -51,6 +52,7 @@ WidgetBase {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
Layout.fillHeight: root.horizontal Layout.fillHeight: root.horizontal
Layout.fillWidth: !root.horizontal Layout.fillWidth: !root.horizontal
horizontal: root.horizontal horizontal: root.horizontal
icon: "memory" icon: "memory"
iconColor: root.screenState.resources ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface iconColor: root.screenState.resources ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
+8 -31
View File
@@ -31,7 +31,7 @@ WidgetBase {
return -1; return -1;
} }
readonly property real size: horizontal ? grid.implicitWidth + grid.anchors.leftMargin + grid.anchors.rightMargin : grid.implicitHeight + grid.anchors.bottomMargin + grid.anchors.topMargin readonly property real size: horizontal ? grid.implicitWidth + grid.anchors.leftMargin + grid.anchors.rightMargin : grid.implicitHeight + grid.anchors.bottomMargin + grid.anchors.topMargin
readonly property int spacing: Tokens.spacing.medium / 2 readonly property int spacing: Tokens.spacing.extraSmall
// Entries that can shrink to nothing, spacing included // Entries that can shrink to nothing, spacing included
function collapsed(entry: var): bool { function collapsed(entry: var): bool {
@@ -51,14 +51,12 @@ WidgetBase {
Behavior on implicitHeight { Behavior on implicitHeight {
enabled: !root.horizontal enabled: !root.horizontal
Anim { Anim {}
}
} }
Behavior on implicitWidth { Behavior on implicitWidth {
enabled: root.horizontal enabled: root.horizontal
Anim { Anim {}
}
} }
GridLayout { GridLayout {
@@ -70,6 +68,8 @@ WidgetBase {
anchors.rightMargin: root.horizontal ? Tokens.padding.extraSmall : 0 anchors.rightMargin: root.horizontal ? Tokens.padding.extraSmall : 0
anchors.topMargin: root.horizontal ? 0 : Tokens.padding.extraSmall anchors.topMargin: root.horizontal ? 0 : Tokens.padding.extraSmall
columns: root.horizontal ? -1 : 1 columns: root.horizontal ? -1 : 1
columnSpacing: root.spacing
rowSpacing: root.spacing
Repeater { Repeater {
model: ScriptModel { model: ScriptModel {
@@ -105,36 +105,13 @@ WidgetBase {
} }
} }
DelegateChoice {
roleValue: "bluetooth"
delegate: EntryWrapper {
name: "bluetooth"
BluetoothWidget {
}
}
}
DelegateChoice {
roleValue: "network"
delegate: EntryWrapper {
name: "network"
NetworkWidget {
}
}
}
DelegateChoice { DelegateChoice {
roleValue: "power" roleValue: "power"
delegate: EntryWrapper { delegate: EntryWrapper {
name: "upower" name: "upower"
UPowerWidget { UPowerWidget {}
}
} }
} }
} }
@@ -157,8 +134,8 @@ WidgetBase {
Layout.rightMargin: root.horizontal ? Math.round(rightGap) : 0 Layout.rightMargin: root.horizontal ? Math.round(rightGap) : 0
Layout.topMargin: root.horizontal ? 0 : Math.round(leftGap) Layout.topMargin: root.horizontal ? 0 : Math.round(leftGap)
children: item children: item
implicitHeight: item?.implicitHeight ?? 0 implicitHeight: item?.height ?? item?.implicitHeight ?? 0
implicitWidth: item?.implicitWidth ?? 0 implicitWidth: item?.width ?? item?.implicitWidth ?? 0
Behavior on leftGap { Behavior on leftGap {
Anim { Anim {
@@ -6,7 +6,7 @@ import qs.Components
import qs.Services import qs.Services
MaterialIcon { MaterialIcon {
id: speaker id: root
required property bool horizontal required property bool horizontal
@@ -15,6 +15,8 @@ MaterialIcon {
fill: 1 fill: 1
fontStyle: Tokens.font.icon.small fontStyle: Tokens.font.icon.small
text: Audio.muted ? "volume_off" : "volume_up" text: Audio.muted ? "volume_off" : "volume_up"
width: Math.round(implicitWidth)
height: Math.round(implicitHeight)
Behavior on Layout.maximumWidth { Behavior on Layout.maximumWidth {
Anim {} Anim {}
@@ -1,14 +0,0 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Services
import qs.Components
MaterialIcon {
Layout.alignment: Qt.AlignVCenter
animate: true
color: Colors.palette.m3onSurface // Network.connected ? root.textColor : Colors.palette.m3error
fill: 1
font.pointSize: Tokens.font.size.larger
text: "bluetooth"
}
@@ -6,7 +6,7 @@ import qs.Components
import qs.Services import qs.Services
MaterialIcon { MaterialIcon {
id: mic id: root
required property bool horizontal required property bool horizontal
@@ -15,6 +15,8 @@ MaterialIcon {
fill: 1 fill: 1
fontStyle: Tokens.font.icon.small fontStyle: Tokens.font.icon.small
text: Audio.sourceMuted ? "mic_off" : "mic" text: Audio.sourceMuted ? "mic_off" : "mic"
width: Math.round(implicitWidth)
height: Math.round(implicitHeight)
Behavior on Layout.maximumWidth { Behavior on Layout.maximumWidth {
Anim {} Anim {}
@@ -1,14 +0,0 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Services
import qs.Components
MaterialIcon {
Layout.alignment: Qt.AlignVCenter
animate: true
color: Colors.palette.m3onSurface // Network.connected ? root.textColor : Colors.palette.m3error
fill: 1
font.pointSize: Tokens.font.size.larger
text: "android_wifi_4_bar"
}
+1
View File
@@ -79,6 +79,7 @@ Item {
anchors.centerIn: parent anchors.centerIn: parent
antialiasing: true antialiasing: true
backer.retainWhileLoading: true
color: root.current && !Config.bar.tray.showOnHover ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface color: root.current && !Config.bar.tray.showOnHover ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
implicitSize: Config.bar.tray.trayIconSize * root.dpr implicitSize: Config.bar.tray.trayIconSize * root.dpr
layer.enabled: Config.bar.tray.recolorIcons layer.enabled: Config.bar.tray.recolorIcons
+1
View File
@@ -38,6 +38,7 @@ WidgetBase {
anchors.topMargin: root.horizontal ? 0 : Tokens.padding.extraSmall anchors.topMargin: root.horizontal ? 0 : Tokens.padding.extraSmall
columns: root.horizontal ? -1 : 1 columns: root.horizontal ? -1 : 1
rowSpacing: -2 rowSpacing: -2
columnSpacing: Tokens.spacing.extraSmall
Repeater { Repeater {
id: repeater id: repeater
+25 -27
View File
@@ -12,13 +12,14 @@ import qs.Services
Item { Item {
id: root id: root
property real activeWorkspaceMargin: Math.ceil(Tokens.padding.medium / 4) property real activeWorkspaceMargin: Tokens.padding.extraSmall / 2
readonly property int spacing: Tokens.spacing.extraSmall / 2
readonly property int effectiveActiveWorkspaceId: monitor?.activeWorkspace?.id ?? 1 readonly property int effectiveActiveWorkspaceId: monitor?.activeWorkspace?.id ?? 1
required property bool horizontal required property bool horizontal
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen) readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen)
required property ShellScreen screen required property ShellScreen screen
readonly property real shortSize: Math.max(Config.bar.height, Tokens.sizes.bar.innerSize) readonly property real shortSize: Math.max(Config.bar.height, Tokens.sizes.bar.innerSize)
readonly property real size: (workspaceButtonWidth * workspacesShown) + activeWorkspaceMargin * 2 readonly property real size: (workspaceButtonWidth * workspacesShown) + (spacing * Math.max(0, workspacesShown - 1)) + activeWorkspaceMargin * 2
property int workspaceButtonWidth: (horizontal ? bgRect.implicitHeight : bgRect.implicitWidth) - root.activeWorkspaceMargin * 2 property int workspaceButtonWidth: (horizontal ? bgRect.implicitHeight : bgRect.implicitWidth) - root.activeWorkspaceMargin * 2
property int workspaceIndexInGroup: (effectiveActiveWorkspaceId - 1) % root.workspacesShown property int workspaceIndexInGroup: (effectiveActiveWorkspaceId - 1) % root.workspacesShown
readonly property list<var> workspaces: Hyprland.workspaces.values.filter(w => w.monitor === root.monitor) readonly property list<var> workspaces: Hyprland.workspaces.values.filter(w => w.monitor === root.monitor)
@@ -59,15 +60,17 @@ Item {
anchors.fill: parent anchors.fill: parent
anchors.margins: root.activeWorkspaceMargin anchors.margins: root.activeWorkspaceMargin
columnSpacing: 0 columnSpacing: root.spacing
columns: root.horizontal ? root.workspacesShown : 1 columns: root.horizontal ? root.workspacesShown : 1
rowSpacing: 0 rowSpacing: root.spacing
rows: root.horizontal ? 1 : root.workspacesShown rows: root.horizontal ? 1 : root.workspacesShown
layer.enabled: true
layer.mipmap: true
Repeater { Repeater {
model: root.workspaces model: root.workspaces
Button { CustomRect {
id: button id: button
required property int index required property int index
@@ -75,27 +78,19 @@ Item {
implicitHeight: indicator.indicatorThickness implicitHeight: indicator.indicatorThickness
implicitWidth: indicator.indicatorThickness implicitWidth: indicator.indicatorThickness
width: root.workspaceButtonWidth radius: Tokens.rounding.full
background: Item { CustomText {
id: workspaceButtonBackground anchors.centerIn: parent
color: Colors.palette.m3onSecondaryContainer
implicitHeight: root.workspaceButtonWidth text: button.modelData?.name ?? ""
implicitWidth: root.workspaceButtonWidth
CustomText {
anchors.centerIn: parent
color: Colors.palette.m3onSecondaryContainer
elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter
text: button.modelData?.name ?? ""
verticalAlignment: Text.AlignVCenter
}
} }
onPressed: { StateLayer {
const ws = button.modelData.name; onPressed: {
Hyprland.dispatch(Hyprland.usingLua ? `hl.dsp.focus({ workspace= "${ws}"})` : `workspace ${ws}`); const ws = button.modelData.name;
Hyprland.dispatch(Hyprland.usingLua ? `hl.dsp.focus({ workspace= "${ws}"})` : `workspace ${ws}`);
}
} }
} }
} }
@@ -104,8 +99,10 @@ Item {
CustomRect { CustomRect {
id: indicator id: indicator
property real indicatorLength: (Math.abs(idxPair.idx1 - idxPair.idx2) + 1) * root.workspaceButtonWidth readonly property real step: root.workspaceButtonWidth + root.spacing
property real indicatorPosition: Math.min(idxPair.idx1, idxPair.idx2) * root.workspaceButtonWidth + root.activeWorkspaceMargin
property real indicatorLength: (Math.abs(idxPair.idx1 - idxPair.idx2) + 1) * root.workspaceButtonWidth + Math.abs(idxPair.idx1 - idxPair.idx2) * root.spacing
property real indicatorPosition: Math.min(idxPair.idx1, idxPair.idx2) * step + root.activeWorkspaceMargin
property real indicatorThickness: root.workspaceButtonWidth property real indicatorThickness: root.workspaceButtonWidth
anchors.horizontalCenter: root.horizontal ? undefined : parent.horizontalCenter anchors.horizontalCenter: root.horizontal ? undefined : parent.horizontalCenter
@@ -129,9 +126,10 @@ Item {
implicitHeight: grid.height implicitHeight: grid.height
implicitWidth: grid.width implicitWidth: grid.width
source: grid source: grid
autoPaddingEnabled: false
sourceColor: Colors.palette.m3onSurface sourceColor: Colors.palette.m3onSurface
x: root.horizontal ? -indicator.x + 3 : 0 x: root.horizontal ? -indicator.x + root.activeWorkspaceMargin : 0
y: root.horizontal ? 0 : -indicator.y + 3 y: root.horizontal ? 0 : -indicator.y + root.activeWorkspaceMargin
} }
} }
} }
-385
View File
@@ -1,385 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import QtQml.Models
import Quickshell
import Quickshell.Bluetooth
import ZShell.Components
import ZShell.Config
import qs.Services
import qs.Components
import qs.Helpers
CustomClippingRect {
id: root
readonly property list<var> btConnected: Bluetooth.devices.values.filter(d => d.connected)
implicitHeight: layout.implicitHeight + layout.anchors.margins * 2
implicitWidth: 500
ColumnLayout {
id: layout
anchors.fill: parent
anchors.margins: Tokens.padding.larger
spacing: Tokens.spacing.extraSmall / 2
CustomText {
Layout.rightMargin: Tokens.padding.extraSmall
Layout.topMargin: Tokens.padding.normal
font.pointSize: Tokens.font.size.large
text: qsTr("Bluetooth")
}
Toggle {
checked: Bluetooth.defaultAdapter?.enabled ?? false // qmllint disable unresolved-type
first: true
subtext: qsTr("Toggle bluetooth device state")
text: checked ? qsTr("Enabled") : qsTr("Disabled")
onToggled: {
const adapter = Bluetooth.defaultAdapter; // qmllint disable unresolved-type
if (adapter)
adapter.enabled = checked;
}
}
Toggle {
checked: Bluetooth.defaultAdapter?.discovering ?? false // qmllint disable unresolved-type
last: root.btConnected.length === 0
subtext: qsTr("Enable scanning for new devices")
text: qsTr("Discover")
onToggled: {
const adapter = Bluetooth.defaultAdapter; // qmllint disable unresolved-type
if (adapter)
adapter.discovering = checked;
}
}
ListView {
id: rows
Layout.fillWidth: true
Layout.preferredHeight: contentHeight
interactive: false
section.property: "connected"
spacing: Tokens.spacing.extraSmall / 2
add: Transition {
Anim {
from: 0
property: "opacity"
to: 1
}
}
delegate: DelegateChooser {
role: "isDivider"
DelegateChoice {
roleValue: true
delegate: StatusDivider {
}
}
DelegateChoice {
delegate: BluetoothItem {
list: rows
width: rows.width
}
}
}
displaced: Transition {
Anim {
easing.type: Easing.OutCubic
property: "y"
}
}
model: ScriptModel {
objectProp: "address"
values: {
const byName = (a, b) => a.name.localeCompare(b.name);
return [...[...Bluetooth.devices.values].filter(d => d.connected).sort(byName).slice(0, 5),
{
isDivider: true
},
...[...Bluetooth.devices.values].filter(d => !d.connected).sort(byName).slice(0, 5)];
}
}
move: Transition {
Anim {
easing.type: Easing.OutCubic
property: "y"
}
}
remove: Transition {
Anim {
property: "opacity"
to: 0
}
}
section.delegate: Rectangle {
required property string section
color: "transparent"
height: childrenRect.height
width: ListView.view.width
CustomText {
text: parent.section
}
}
}
}
component BluetoothItem: ConnectedRect {
id: btItem
readonly property int connectedCount: root.btConnected.length
readonly property bool connecting: Network.connectingNetwork === modelData
property int horizontalPadding: Tokens.padding.largeIncreased
readonly property bool inConnectedGroup: index < connectedCount
required property int index
required property ListView list
readonly property bool loading: modelData.state === BluetoothDeviceState.Connecting || modelData.state === BluetoothDeviceState.Disconnecting // qmllint disable unresolved-type
required property BluetoothDevice modelData
property int verticalPadding: Tokens.padding.smaller
first: !inConnectedGroup && index === connectedCount + 1
implicitHeight: rowLayout.implicitHeight + verticalPadding * 2
last: inConnectedGroup ? index === connectedCount - 1 : index === list.count - 1
radius: Tokens.rounding.small
Timer {
interval: 50
running: btItem.ListView.delayRemove
onTriggered: btItem.ListView.delayRemove = false
}
Connections {
function onConnectedChanged(): void {
if (!btItem.modelData.connected)
btItem.ListView.delayRemove = true;
}
function onPairedChanged(): void {
if (btItem.modelData.paired)
btItem.modelData.connect();
}
target: btItem.modelData
}
RowLayout {
id: rowLayout
anchors.fill: parent
anchors.leftMargin: btItem.horizontalPadding
anchors.rightMargin: btItem.horizontalPadding
Column {
id: column
Layout.fillWidth: true
CustomText {
Layout.fillWidth: true
Layout.leftMargin: Tokens.spacing.extraSmall
Layout.rightMargin: Tokens.spacing.extraSmall
elide: Text.ElideRight
text: btItem.modelData.name
}
CustomText {
id: subtext
anchors.left: parent.left
anchors.right: parent.right
color: Colors.palette.m3outline
elide: Text.ElideRight
font.pointSize: Tokens.font.size.small
text: btItem.modelData.connected ? (btItem.modelData.batteryAvailable ? qsTr("%1 battery left").arg(Math.round(btItem.modelData.battery * 100) + "%") : qsTr("Battery status unknown")) : btItem.modelData.address
}
}
Item {
Layout.alignment: Qt.AlignRight
implicitHeight: icon.height
implicitWidth: icon.width
MaterialIcon {
id: icon
font.pointSize: Tokens.font.size.large
opacity: btItem.connecting ? 0 : 1
text: btItem.modelData.batteryAvailable ? Icons.getBatteryIcon(btItem.modelData.battery) : "battery_unknown"
visible: btItem.modelData.state === BluetoothDeviceState.Connected // qmllint disable unresolved-type
Behavior on opacity {
Anim {
}
}
}
Loader {
active: opacity > 0
anchors.fill: parent
opacity: btItem.connecting ? 1 : 0
Behavior on opacity {
Anim {
}
}
sourceComponent: CircularIndicator {
bgColor: "transparent"
running: true
}
}
}
ButtonRow {
spacing: Tokens.spacing.small
IconTextButton {
font.pointSize: Tokens.font.size.small
icon: btItem.modelData.connected ? "link_off" : btItem.modelData.paired ? "link" : "add_link"
text: btItem.modelData.connected ? qsTr("Disconnect") : btItem.modelData.paired ? qsTr("Connect") : qsTr("Pair")
onClicked: {
if (btItem.modelData.connected)
btItem.modelData.disconnect();
else if (btItem.modelData.paired)
btItem.modelData.connect();
else if (!btItem.modelData.paired)
btItem.modelData.pair();
else if (btItem.modelData.pairing)
btItem.modelData.cancelPair();
}
}
}
}
}
component ConnectedRect: CustomRect {
id: bg
property bool first
property bool last
bottomLeftRadius: last ? Tokens.rounding.large : Tokens.rounding.extraSmall
bottomRightRadius: last ? Tokens.rounding.large : Tokens.rounding.extraSmall
color: Colors.tPalette.m3surfaceContainer
topLeftRadius: first ? Tokens.rounding.large : Tokens.rounding.extraSmall
topRightRadius: first ? Tokens.rounding.large : Tokens.rounding.extraSmall
Behavior on bottomLeftRadius {
Anim {
}
}
Behavior on bottomRightRadius {
Anim {
}
}
Behavior on topLeftRadius {
Anim {
}
}
Behavior on topRightRadius {
Anim {
}
}
}
component StatusDivider: CustomText {
bottomPadding: Tokens.spacing.extraSmall
color: Colors.palette.m3onSurfaceVariant
font.pointSize: Tokens.font.size.smaller
text: {
const devices = Bluetooth.devices.values; // qmllint disable unresolved-type
const connectedCount = devices.filter(d => d.connected).length;
let available = qsTr("%1 device%2 available").arg(devices.length - connectedCount).arg(devices.length === 1 ? "" : "s");
return available;
}
topPadding: Tokens.spacing.small
}
component Toggle: CustomSwitch {
id: toggleItem
readonly property alias bg: bg
property alias first: bg.first
property alias last: bg.last
property string subtext
Layout.fillWidth: true
cLayer: 2
horizontalPadding: Tokens.padding.largeIncreased
implicitHeight: Math.max(implicitContentHeight, implicitIndicatorHeight) + verticalPadding * 2
implicitWidth: implicitContentWidth + implicitIndicatorWidth + horizontalPadding * 2
indicator.anchors.right: right
indicator.anchors.rightMargin: toggleItem.horizontalPadding
indicator.anchors.verticalCenter: verticalCenter
verticalPadding: Tokens.padding.normal
background: ConnectedRect {
id: bg
StateLayer {
id: stateLayer
manualPressOverride: toggleItem.pressed
}
}
contentItem: Item {
anchors.left: parent.left
anchors.leftMargin: toggleItem.horizontalPadding
anchors.right: toggleItem.indicator.left
anchors.rightMargin: Tokens.spacing.normal
implicitHeight: column.implicitHeight
implicitWidth: column.implicitWidth
Column {
id: column
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 0
CustomText {
id: label
anchors.left: parent.left
anchors.right: parent.right
elide: Text.ElideRight
font: {
const f = Qt.font(label.font);
f.pointSize = Tokens.font.size.smaller;
return f;
}
text: toggleItem.text
}
CustomText {
id: subtext
anchors.left: parent.left
anchors.right: parent.right
color: Colors.palette.m3outline
elide: Text.ElideRight
font: {
const f = Qt.font(subtext.font);
f.pointSize = Tokens.font.size.small;
return f;
}
text: toggleItem.subtext
visible: toggleItem.subtext
}
}
}
onPressed: stateLayer.press(stateLayer.mouseX, stateLayer.mouseY)
}
}
+1 -2
View File
@@ -50,8 +50,7 @@ Item {
} }
Behavior on offsetScale { Behavior on offsetScale {
Anim { Anim {}
}
} }
Behavior on x { Behavior on x {
enabled: !root.horizontal || root.offsetScale < 1 enabled: !root.horizontal || root.offsetScale < 1
-7
View File
@@ -82,13 +82,6 @@ Item {
} }
} }
Popout {
name: "bluetooth"
sourceComponent: BluetoothPopout {
}
}
Popout { Popout {
name: "updates" name: "updates"
@@ -1,71 +0,0 @@
import QtQuick
import ZShell.Config
import qs.Services
import qs.Helpers.Network
import qs.Components
CustomRect {
id: root
readonly property bool closing: PopupManager.closed
readonly property url currentPopup: PopupManager.currentPopup
property bool shouldBeVisible: loader.status === Loader.Ready
color: Qt.alpha(Colors.palette.m3shadow, 0.3)
opacity: shouldBeVisible && !closing ? 1 : 0
visible: opacity > 0
Behavior on opacity {
Anim {
}
}
onVisibleChanged: {
if (!visible) {
PopupManager.requestClose();
PopupManager.currentPopup = "";
}
}
CustomMouseArea {
anchors.fill: parent
hoverEnabled: true
preventStealing: true
propagateComposedEvents: false
onClicked: {
const insideItemWidth = mouseX < loader.x + loader.item.width && mouseX > loader.x;
const insideItemHeight = mouseY < loader.y + loader.item.height && mouseY > loader.y;
if (insideItemHeight && insideItemWidth)
return;
PopupManager.requestClose();
}
}
Loader {
id: loader
anchors.centerIn: parent
Connections {
function onCurrentPopupChanged(): void {
if (PopupManager.currentPopup) {
loader.setSource(PopupManager.currentPopup, PopupManager.currentPopupProps);
} else {
loader.source = null;
}
}
target: PopupManager
}
}
Connections {
function onNetworkConnected(): void {
PopupManager.requestClose();
}
target: Network
}
}
-102
View File
@@ -1,102 +0,0 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import ZShell.Config
import qs.Services
import qs.Components
import qs.Helpers.Network
CustomClippingRect {
id: root
required property var network
color: Colors.palette.m3surfaceContainer
implicitHeight: layout.implicitHeight + layout.anchors.margins * 2
implicitWidth: layout.implicitWidth + layout.anchors.margins * 2
radius: Tokens.rounding.large
ColumnLayout {
id: layout
anchors.fill: parent
anchors.margins: Tokens.padding.extraLarge
spacing: Tokens.spacing.medium
MaterialIcon {
Layout.alignment: Qt.AlignHCenter
font.pointSize: Tokens.font.size.extraLarge * 2
text: "lock"
}
CustomText {
Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3onSurfaceVariant
text: qsTr("Connect to %1").arg(root.network.name)
}
CustomTextField {
id: passwordField
Layout.fillWidth: true
Layout.preferredWidth: 400
echoMode: CustomTextField.Password
errorText: qsTr("Failed to connect with given password")
isError: Network.failedNetwork
leadingIcon: "lock"
passwordMaskDelay: 5000
placeholderText: qsTr("Input password")
}
ButtonRow {
id: buttonRow
Layout.fillWidth: true
spacing: Tokens.spacing.extraSmall
IconTextButton {
fillWidth: true
font.pointSize: Tokens.font.size.normal
icon: "close"
inactiveColor: Colors.layer(Colors.palette.m3surfaceContainerHighest, 2)
inactiveOnColor: Colors.palette.m3onSurfaceVariant
isRound: true
isToggle: false
shapeMorph: true
text: "Cancel"
onClicked: PopupManager.requestClose()
}
IconTextButton {
fillWidth: true
font.pointSize: Tokens.font.size.normal
icon: Network.connectingNetwork ? "" : "check"
inactiveColor: Network.connectingNetwork ? Colors.palette.m3primaryContainer : Network.failedNetwork ? Colors.palette.m3error : Colors.palette.m3primary
inactiveOnColor: Network.connectingNetwork ? Colors.palette.m3onPrimaryContainer : Network.failedNetwork ? Colors.palette.m3onError : Colors.palette.m3onPrimary
isRound: true
isToggle: false
shapeMorph: true
text: Network.connectingNetwork ? "" : "Apply"
onClicked: Network.requestConnect(root.network, passwordField.text)
Loader {
active: opacity > 0
anchors.centerIn: parent
opacity: Network.connectingNetwork ? 1 : 0
Behavior on opacity {
Anim {
}
}
sourceComponent: CircularIndicator {
bgColor: "transparent"
implicitSize: buttonRow.height - Tokens.padding.normal
running: true
}
}
}
}
}
}
@@ -1,22 +0,0 @@
pragma Singleton
import Quickshell
import QtQuick
Singleton {
id: root
property bool closed: true
property url currentPopup: ""
property var currentPopupProps: ({})
function requestClose(): void {
closed = true;
}
function requestOpen(source: url, properties = {}): void {
currentPopupProps = properties;
currentPopup = source;
closed = false;
}
}
+16 -122
View File
@@ -1,142 +1,36 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Networking
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell
import ZShell.Components
import ZShell.Config
import qs.Services
import qs.Modules.Settings.Common
import qs.Modules.Bar.Popouts.Network
import qs.Components import qs.Components
import qs.Helpers.Network import ZShell.Config
import qs.Modules
import qs.Helpers as Helpers
CustomClippingRect { Item {
id: root id: root
required property var wrapper required property var wrapper
anchors.horizontalCenter: parent.horizontalCenter
implicitHeight: networkPopContent.height + networkPopContent.anchors.margins * 2
implicitWidth: 500 + 8 * 2
radius: (20 - Tokens.padding.small) * Tokens.rounding.scale
Component.onCompleted: Network.active = true
Component.onDestruction: Network.active = false
ColumnLayout { ColumnLayout {
id: networkPopContent id: layout
anchors.left: parent.left spacing: 8
anchors.margins: Tokens.padding.large
anchors.right: parent.right
anchors.top: parent.top
spacing: Tokens.spacing.extraSmall / 2
CustomText { Repeater {
Layout.preferredHeight: visible ? implicitHeight : 0 model: Helpers.Network.devices
Layout.rightMargin: Tokens.padding.extraSmall
font.pointSize: Tokens.font.size.large
text: qsTr("Wifi")
}
Toggle { CustomRadioButton {
Layout.preferredHeight: visible ? implicitHeight : 0 id: network
checked: Network.wifiEnabled
first: true
last: Network.connectedNetworks.length === 0
subtext: qsTr("Toggle WiFi device state")
text: checked ? qsTr("Enabled") : qsTr("Disabled")
onToggled: Network.setWifi(checked) required property NetworkDevice modelData
}
ListView { checked: Helpers.Network.activeDevice?.name === modelData.name
id: rows text: modelData.description
visible: modelData.name !== "lo"
Layout.fillWidth: true
Layout.preferredHeight: contentHeight
interactive: false
section.property: "connected"
spacing: Tokens.spacing.extraSmall / 2
add: Transition {
Anim {
from: 0
property: "opacity"
to: 1
}
}
delegate: DelegateChooser {
role: "isDivider"
DelegateChoice {
roleValue: true
delegate: NetworkDivider {
}
}
DelegateChoice {
delegate: NetworkItem {
list: rows
width: rows.width
}
}
}
displaced: Transition {
Anim {
easing.type: Easing.OutCubic
property: "y"
}
}
model: ScriptModel {
values: {
const byStrengthName = (a, b) => b.signalStrength - a.signalStrength || a.name.localeCompare(b.name);
const cNetworks = [...Network.connectedNetworks];
const kNetworks = [...Network.knownNetworks];
const uNetworks = [...Network.unknownNetworks];
return [...cNetworks.sort(byStrengthName), ...(kNetworks.length > 0 ? [
{
isDivider: true,
type: "known"
},
...kNetworks.sort(byStrengthName)] : []), ...(uNetworks.length > 0 ? [
{
isDivider: true,
type: "unknown"
},
...uNetworks.sort(byStrengthName)] : [])];
}
}
move: Transition {
Anim {
easing.type: Easing.OutCubic
property: "y"
}
}
remove: Transition {
Anim {
property: "opacity"
to: 0
}
}
section.delegate: Rectangle {
required property string section
color: "transparent"
height: childrenRect.height
width: ListView.view.width
CustomText {
text: parent.section
}
} }
} }
} }
NetworkOverlay {
anchors.fill: parent
}
} }
+7 -14
View File
@@ -26,20 +26,15 @@ StackView {
initialItem: SubMenu { initialItem: SubMenu {
handle: root.trayItem handle: root.trayItem
} }
popEnter: NoAnim { popEnter: NoAnim {}
} popExit: NoAnim {}
popExit: NoAnim { pushEnter: NoAnim {}
} pushExit: NoAnim {}
pushEnter: NoAnim {
}
pushExit: NoAnim {
}
Component { Component {
id: subMenuComp id: subMenuComp
SubMenu { SubMenu {}
}
} }
component NoAnim: Transition { component NoAnim: Transition {
@@ -59,12 +54,10 @@ StackView {
spacing: Tokens.spacing.extraSmall spacing: Tokens.spacing.extraSmall
Behavior on opacity { Behavior on opacity {
Anim { Anim {}
}
} }
Behavior on scale { Behavior on scale {
Anim { Anim {}
}
} }
Component.onCompleted: shown = true Component.onCompleted: shown = true
+4 -2
View File
@@ -54,7 +54,7 @@ Item {
} }
} }
ClipRect { CustomRect {
id: preview id: preview
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
@@ -64,6 +64,7 @@ Item {
color: Colors.tPalette.m3surfaceContainer color: Colors.tPalette.m3surfaceContainer
implicitWidth: ClipHistory.previewIsImage ? Math.max(Math.min(imagePreview.sourceSize.width + Tokens.padding.large * 2, Config.clipboard.sizes.previewWidth), Config.clipboard.sizes.minPreviewWidth) : Math.max(Math.min(textPreviewColumn.width + textPreviewColumn.anchors.margins * 2, Config.clipboard.sizes.previewWidth), Config.clipboard.sizes.minPreviewWidth) implicitWidth: ClipHistory.previewIsImage ? Math.max(Math.min(imagePreview.sourceSize.width + Tokens.padding.large * 2, Config.clipboard.sizes.previewWidth), Config.clipboard.sizes.minPreviewWidth) : Math.max(Math.min(textPreviewColumn.width + textPreviewColumn.anchors.margins * 2, Config.clipboard.sizes.previewWidth), Config.clipboard.sizes.minPreviewWidth)
radius: 25 radius: 25
clip: true
Behavior on implicitWidth { Behavior on implicitWidth {
Anim {} Anim {}
@@ -228,11 +229,12 @@ Item {
anchors.fill: parent anchors.fill: parent
spacing: Tokens.spacing.small spacing: Tokens.spacing.small
ClipRect { Item {
id: textRect id: textRect
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
clip: true
Item { Item {
id: textWrapper id: textWrapper
+2 -4
View File
@@ -307,12 +307,10 @@ CustomMouseArea {
} }
} }
Behavior on x { Behavior on x {
Anim { Anim {}
}
} }
Behavior on y { Behavior on y {
Anim { Anim {}
}
} }
onTodayItemChanged: { onTodayItemChanged: {
+1 -2
View File
@@ -77,8 +77,7 @@ Item {
implicitSize: height implicitSize: height
Behavior on clampedVal { Behavior on clampedVal {
Anim { Anim {}
}
} }
MaterialIcon { MaterialIcon {
+2 -4
View File
@@ -33,8 +33,7 @@ Item {
Repeater { Repeater {
id: shapes id: shapes
DriftingShape { DriftingShape {}
}
} }
FrameAnimation { FrameAnimation {
@@ -80,8 +79,7 @@ Item {
shape: root.shapePool[Math.floor(Math.random() * root.shapePool.length)] shape: root.shapePool[Math.floor(Math.random() * root.shapePool.length)]
Behavior on color { Behavior on color {
CAnim { CAnim {}
}
} }
Behavior on opacity { Behavior on opacity {
Anim { Anim {
+1 -2
View File
@@ -18,8 +18,7 @@ Item {
layer.enabled: true layer.enabled: true
Behavior on fallbackColor { Behavior on fallbackColor {
CAnim { CAnim {}
}
} }
layer.effect: MultiEffect { layer.effect: MultiEffect {
blurMax: 1 blurMax: 1
+5 -10
View File
@@ -54,14 +54,12 @@ Item {
Behavior on x { Behavior on x {
enabled: !mouseArea.drag.active && !root.isSnapping && !root.iconsRoot.selectedIcons.includes(root.filePath) enabled: !mouseArea.drag.active && !root.isSnapping && !root.iconsRoot.selectedIcons.includes(root.filePath)
Anim { Anim {}
}
} }
Behavior on y { Behavior on y {
enabled: !mouseArea.drag.active && !root.isSnapping && !root.iconsRoot.selectedIcons.includes(root.filePath) enabled: !mouseArea.drag.active && !root.isSnapping && !root.iconsRoot.selectedIcons.includes(root.filePath)
Anim { Anim {}
}
} }
Item { Item {
@@ -85,8 +83,7 @@ Item {
y: (root.iconsRoot.selectedIcons.includes(root.filePath) && root.iconsRoot.dragLeader !== "" && root.iconsRoot.dragLeader !== root.filePath) ? root.iconsRoot.groupDragY : 0 y: (root.iconsRoot.selectedIcons.includes(root.filePath) && root.iconsRoot.dragLeader !== "" && root.iconsRoot.dragLeader !== root.filePath) ? root.iconsRoot.groupDragY : 0
} }
transitions: Transition { transitions: Transition {
Anim { Anim {}
}
} }
onXChanged: { onXChanged: {
@@ -208,8 +205,7 @@ Item {
radius: Tokens.rounding.small radius: Tokens.rounding.small
Behavior on opacity { Behavior on opacity {
Anim { Anim {}
}
} }
} }
@@ -268,8 +264,7 @@ Item {
radius: Tokens.rounding.small radius: Tokens.rounding.small
Behavior on opacity { Behavior on opacity {
Anim { Anim {}
}
} }
} }
} }
+1 -2
View File
@@ -82,8 +82,7 @@ CustomRect {
Loader { Loader {
active: isSeparator active: isSeparator
sourceComponent: DockSeparator { sourceComponent: DockSeparator {}
}
anchors { anchors {
fill: parent fill: parent
+1 -2
View File
@@ -41,8 +41,7 @@ CarouselView {
tileHeight: focalWidth / 16 * 9 tileHeight: focalWidth / 16 * 9
delegate: Component { delegate: Component {
WallpaperTile { WallpaperTile {}
}
} }
Component.onDestruction: Wallpapers.stopPreview() Component.onDestruction: Wallpapers.stopPreview()
+60 -8
View File
@@ -1,3 +1,5 @@
pragma ComponentBehavior: Bound
import Quickshell import Quickshell
import QtQuick import QtQuick
import ZShell.Config import ZShell.Config
@@ -9,7 +11,7 @@ Item {
id: root id: root
required property bool isCurrent required property bool isCurrent
required property var modelData // FileSystemEntry-like {path, relativePath} required property var modelData
required property real tileHeight required property real tileHeight
required property real tileWidth required property real tileWidth
required property ScreenState screenState required property ScreenState screenState
@@ -29,25 +31,75 @@ Item {
radius: Tokens.rounding.small radius: Tokens.rounding.small
y: Tokens.padding.large y: Tokens.padding.large
MaterialIcon { Loader {
active: opacity > 0
anchors.centerIn: parent anchors.centerIn: parent
color: Colors.tPalette.m3outline opacity: img.status === Image.Ready ? 0 : 1
fontStyle: Tokens.font.icon.builders.extraLarge.scale(2).weight(Font.DemiBold).build()
text: "image" Behavior on opacity {
Anim {}
}
sourceComponent: CustomRect {
color: Colors.palette.m3primaryContainer
implicitHeight: loadingIndicator.implicitSize + Tokens.padding.large * 2
implicitWidth: loadingIndicator.implicitSize + Tokens.padding.large * 2
radius: Tokens.rounding.full
LoadingIndicator {
id: loadingIndicator
anchors.centerIn: parent
containsIcon: true
implicitSize: Math.min(image.width, image.height) * 0.3
}
}
} }
CachingImage { Image {
id: img
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top anchors.top: parent.top
cache: true asynchronous: true
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
height: root.tileHeight height: root.tileHeight
path: root.modelData ? root.modelData.path : "" source: root.modelData ? root.modelData.thumbnailPath : ""
smooth: root.isCurrent smooth: root.isCurrent
sourceSize.height: root.tileHeight sourceSize.height: root.tileHeight
sourceSize.width: root.tileHeight * (16 / 9) sourceSize.width: root.tileHeight * (16 / 9)
width: root.tileHeight * (16 / 9) width: root.tileHeight * (16 / 9)
opacity: status === Image.Ready ? 1 : 0
Behavior on opacity {
Anim {
type: Anim.SlowEffects
}
}
Loader {
opacity: root.modelData.isVideo && img.status === Image.Ready ? 1 : 0
active: opacity > 0
anchors.centerIn: parent
Behavior on opacity {
Anim {
type: Anim.SlowEffects
}
}
sourceComponent: CustomRect {
implicitWidth: videoIcon.implicitWidth + Tokens.padding.extraLarge * 2
implicitHeight: implicitWidth
radius: Tokens.rounding.full
color: Colors.tPalette.m3surface
MaterialIcon {
id: videoIcon
anchors.centerIn: parent
text: "video_library"
fontStyle: Tokens.font.icon.extraLarge
}
}
}
} }
StateLayer { StateLayer {
+1 -2
View File
@@ -22,8 +22,7 @@ Searcher {
model: Config.launcher.actions.values.filter(a => (a.enabled ?? true) && (Config.launcher.enableDangerousActions || !(a.dangerous ?? false))) model: Config.launcher.actions.values.filter(a => (a.enabled ?? true) && (Config.launcher.enableDangerousActions || !(a.dangerous ?? false)))
Action { Action {}
}
} }
component Action: QtObject { component Action: QtObject {
+4 -17
View File
@@ -5,14 +5,13 @@ import qs.Helpers
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
ClipRect { Item {
id: root id: root
required property var lock required property var lock
implicitHeight: layout.implicitHeight implicitHeight: layout.implicitHeight
implicitWidth: layout.implicitWidth implicitWidth: layout.implicitWidth
radius: Tokens.rounding.largeIncreased
RowLayout { RowLayout {
id: layout id: layout
@@ -50,22 +49,10 @@ ClipRect {
lock: root.lock lock: root.lock
} }
ColumnLayout { NotifDock {
Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
spacing: Tokens.spacing.medium lock: root.lock
CustomRect {
Layout.fillHeight: true
Layout.fillWidth: true
bottomRightRadius: Tokens.rounding.largeIncreased
color: Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.small
topRightRadius: Tokens.rounding.largeIncreased
NotifDock {
lock: root.lock
}
}
} }
} }
} }
+1 -1
View File
@@ -221,9 +221,9 @@ WlSessionLockSurface {
anchors.fill: parent anchors.fill: parent
color: Colors.palette.m3surface color: Colors.palette.m3surface
layer.enabled: true
opacity: Colors.transparency.enabled ? Colors.transparency.base : 1 opacity: Colors.transparency.enabled ? Colors.transparency.base : 1
radius: lockContent.radius radius: lockContent.radius
layer.enabled: true
layer.effect: MultiEffect { layer.effect: MultiEffect {
blurMax: 15 blurMax: 15
+31 -26
View File
@@ -7,39 +7,44 @@ import qs.Components
import qs.Helpers import qs.Helpers
import qs.Services import qs.Services
ClipRect { Item {
id: root id: root
required property var lock required property var lock
color: Colors.tPalette.m3surfaceContainer
implicitHeight: layout.implicitHeight + layout.anchors.margins * 2 implicitHeight: layout.implicitHeight + layout.anchors.margins * 2
radius: Tokens.rounding.small
FadeImage {
id: image
ClipRect {
anchors.fill: parent anchors.fill: parent
asynchronous: true
fillMode: Image.PreserveAspectCrop
layer.enabled: true
opacity: status === Image.Ready ? 1 : 0
source: Players.getArtUrl(Players.active)
sourceSize: {
const dpr = (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1;
return Qt.size(width * dpr, height * dpr);
}
Behavior on opacity { color: Colors.tPalette.m3surfaceContainer
Anim { radius: Tokens.rounding.small
type: Anim.StandardExtraLarge bottomLeftRadius: Tokens.rounding.largeIncreased
}
} FadeImage {
id: image
CustomRect {
anchors.fill: parent anchors.fill: parent
color: Colors.palette.m3surface asynchronous: true
opacity: 0.7 fillMode: Image.PreserveAspectCrop
opacity: status === Image.Ready ? 1 : 0
source: Players.getArtUrl(Players.active)
sourceSize: {
const dpr = (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1;
return Qt.size(width * dpr, height * dpr);
}
Behavior on opacity {
Anim {
type: Anim.StandardExtraLarge
}
}
CustomRect {
anchors.fill: parent
color: Colors.palette.m3surface
opacity: 0.7
}
} }
} }
@@ -77,7 +82,7 @@ ClipRect {
spacing: Tokens.spacing.extraSmall spacing: Tokens.spacing.extraSmall
IconButton { IconButton {
enabled: Players.active?.canGoPrevious enabled: Players.active?.canGoPrevious ?? false
icon: "skip_previous" icon: "skip_previous"
isRound: true isRound: true
shapeMorph: true shapeMorph: true
@@ -88,7 +93,7 @@ ClipRect {
IconButton { IconButton {
checked: Players.active?.isPlaying ?? false checked: Players.active?.isPlaying ?? false
enabled: Players.active?.canTogglePlaying enabled: Players.active?.canTogglePlaying ?? false
icon: Players.active?.isPlaying ? "pause" : "play_arrow" icon: Players.active?.isPlaying ? "pause" : "play_arrow"
implicitWidth: implicitHeight + Tokens.padding.largeIncreased * 2 implicitWidth: implicitHeight + Tokens.padding.largeIncreased * 2
isRound: true isRound: true
@@ -98,7 +103,7 @@ ClipRect {
} }
IconButton { IconButton {
enabled: Players.active?.canGoNext enabled: Players.active?.canGoNext ?? false
icon: "skip_next" icon: "skip_next"
isRound: true isRound: true
shapeMorph: true shapeMorph: true
+94 -104
View File
@@ -11,126 +11,116 @@ import ZShell.Config
import qs.Daemons import qs.Daemons
import qs.Services import qs.Services
ColumnLayout { CustomRect {
id: root id: root
required property var lock required property var lock
anchors.fill: parent bottomRightRadius: Tokens.rounding.largeIncreased
anchors.margins: Tokens.padding.large color: Colors.tPalette.m3surfaceContainer
spacing: Tokens.spacing.small radius: Tokens.rounding.small
topRightRadius: Tokens.rounding.largeIncreased
CustomText { ColumnLayout {
Layout.fillWidth: true anchors.fill: parent
color: Colors.palette.m3outline anchors.margins: Tokens.padding.large
elide: Text.ElideRight spacing: Tokens.spacing.small
font: Tokens.font.mono.builders.small.weight(Font.Medium).build()
text: NotifServer.list.length > 0 ? qsTr("%1 notification%2").arg(NotifServer.list.length).arg(NotifServer.list.length === 1 ? "" : "s") : qsTr("Notifications")
}
ClipRect { CustomText {
id: clipRect Layout.fillWidth: true
color: Colors.palette.m3outline
Layout.fillHeight: true elide: Text.ElideRight
Layout.fillWidth: true font: Tokens.font.mono.builders.small.weight(Font.Medium).build()
color: "transparent" text: NotifServer.list.length > 0 ? qsTr("%1 notification%2").arg(NotifServer.list.length).arg(NotifServer.list.length === 1 ? "" : "s") : qsTr("Notifications")
radius: Tokens.rounding.small
Loader {
active: opacity > 0
anchors.centerIn: parent
opacity: NotifServer.list.length > 0 ? 0 : 1
Behavior on opacity {
Anim {
duration: Tokens.anim.durations.extraLarge
}
}
sourceComponent: ColumnLayout {
spacing: Tokens.spacing.largeIncreased
Image {
asynchronous: true
fillMode: Image.PreserveAspectFit
layer.enabled: true
source: Qt.resolvedUrl(`${Quickshell.shellDir}/assets/dino.png`)
sourceSize.width: clipRect.width * 0.8
layer.effect: Coloriser {
brightness: 1
colorizationColor: Colors.palette.m3outlineVariant
}
}
CustomText {
Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant
font: Tokens.font.headline.builders.small.width(90).build()
text: qsTr("No Notifications")
}
}
} }
CustomListView { Item {
anchors.fill: parent Layout.fillHeight: true
clip: true Layout.fillWidth: true
spacing: Tokens.spacing.small
add: Transition { Loader {
Anim { active: opacity > 0
from: 0 anchors.centerIn: parent
property: "opacity" opacity: NotifServer.list.length > 0 ? 0 : 1
to: 1
Behavior on opacity {
Anim {
duration: Tokens.anim.durations.extraLarge
}
} }
sourceComponent: ColumnLayout {
spacing: Tokens.spacing.largeIncreased
Anim { CustomText {
duration: Tokens.anim.durations.expressiveDefaultSpatial Layout.alignment: Qt.AlignHCenter
easing: Tokens.anim.expressiveDefaultSpatial color: Colors.palette.m3outlineVariant
from: 0 font: Tokens.font.headline.builders.small.width(90).build()
property: "scale" text: qsTr("No Notifications")
to: 1 }
} }
} }
delegate: NotifGroup {}
displaced: Transition {
Anim {
properties: "opacity,scale"
to: 1
}
Anim { CustomListView {
duration: Tokens.anim.durations.expressiveDefaultSpatial anchors.fill: parent
easing: Tokens.anim.expressiveDefaultSpatial clip: true
property: "y" spacing: Tokens.spacing.small
}
}
model: ScriptModel {
values: {
const list = NotifServer.notClosed.map(n => [n.appName, null]);
return [...new Map(list).keys()];
}
}
move: Transition {
Anim {
properties: "opacity,scale"
to: 1
}
Anim { add: Transition {
duration: Tokens.anim.durations.expressiveDefaultSpatial Anim {
easing: Tokens.anim.expressiveDefaultSpatial from: 0
property: "y" property: "opacity"
} to: 1
} }
remove: Transition {
Anim {
property: "opacity"
to: 0
}
Anim { Anim {
property: "scale" duration: Tokens.anim.durations.expressiveDefaultSpatial
to: 0.6 easing: Tokens.anim.expressiveDefaultSpatial
from: 0
property: "scale"
to: 1
}
}
delegate: NotifGroup {}
displaced: Transition {
Anim {
properties: "opacity,scale"
to: 1
}
Anim {
duration: Tokens.anim.durations.expressiveDefaultSpatial
easing: Tokens.anim.expressiveDefaultSpatial
property: "y"
}
}
model: ScriptModel {
values: {
const list = NotifServer.notClosed.map(n => [n.appName, null]);
return [...new Map(list).keys()];
}
}
move: Transition {
Anim {
properties: "opacity,scale"
to: 1
}
Anim {
duration: Tokens.anim.durations.expressiveDefaultSpatial
easing: Tokens.anim.expressiveDefaultSpatial
property: "y"
}
}
remove: Transition {
Anim {
property: "opacity"
to: 0
}
Anim {
property: "scale"
to: 0.6
}
} }
} }
} }
+4 -3
View File
@@ -16,7 +16,7 @@ ColumnLayout {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
animate: true animate: true
color: Colors.palette.m3onSurfaceVariant color: Colors.palette.m3onSurfaceVariant
font: Tokens.font.title.large font: Tokens.font.body.large
text: Weather.description text: Weather.description
} }
@@ -29,7 +29,7 @@ ColumnLayout {
animate: true animate: true
color: Colors.palette.m3primary color: Colors.palette.m3primary
font: Tokens.font.body.large font: Tokens.font.title.large
text: Weather.temp text: Weather.temp
} }
@@ -37,6 +37,7 @@ ColumnLayout {
animate: true animate: true
color: Colors.palette.m3secondary color: Colors.palette.m3secondary
text: Weather.icon text: Weather.icon
fontStyle: Tokens.font.icon.large
} }
} }
@@ -53,7 +54,7 @@ ColumnLayout {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
animate: true animate: true
color: Colors.palette.m3onSurfaceVariant color: Colors.palette.m3onSurfaceVariant
font: Tokens.font.body.normal font: Tokens.font.body.medium
text: { text: {
const today = Weather.forecast[0]; const today = Weather.forecast[0];
return qsTr("High %1 • Low %2").arg(Weather.formatTemp(today?.maxTempC)).arg(Weather.formatTemp(today?.minTempC)); return qsTr("High %1 • Low %2").arg(Weather.formatTemp(today?.maxTempC)).arg(Weather.formatTemp(today?.minTempC));
+12 -5
View File
@@ -36,7 +36,7 @@ CustomRect {
} }
} }
VerticalFadeListView { CustomListView {
id: layout id: layout
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
@@ -45,13 +45,20 @@ CustomRect {
anchors.margins: Tokens.padding.large anchors.margins: Tokens.padding.large
anchors.right: parent.right anchors.right: parent.right
implicitHeight: contentItem.childrenRect.height implicitHeight: contentItem.childrenRect.height
model: Weather.hourlyForecast currentIndex: Weather.currentHourIndex
// highlightFollowsCurrentItem: true
// preferredHighlightEnd: currentItem.implicitWidth
// preferredHighlightBegin: 0
// highlightRangeMode: ListView.ApplyRange
clip: true
model: Weather.hourlyForecast.slice(Weather.currentHourIndex - 1)
orientation: VerticalFadeListView.Horizontal orientation: VerticalFadeListView.Horizontal
spacing: Tokens.spacing.medium spacing: Tokens.spacing.medium
delegate: ColumnLayout { delegate: ColumnLayout {
id: hour id: hour
readonly property bool current: ListView.view.currentItem === this
readonly property var cond: modelData readonly property var cond: modelData
required property int index required property int index
required property var modelData required property var modelData
@@ -60,7 +67,7 @@ CustomRect {
MaterialShape { MaterialShape {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
color: Qt.alpha(Colors.palette.m3primary, hour.index === Weather.currentHourIndex ? 1 : 0) color: Qt.alpha(Colors.palette.m3primary, hour.current ? 1 : 0)
implicitSize: temp.implicitHeight + Tokens.padding.small * 2 implicitSize: temp.implicitHeight + Tokens.padding.small * 2
shape: MaterialShape.Cookie4Sided shape: MaterialShape.Cookie4Sided
@@ -72,7 +79,7 @@ CustomRect {
id: temp id: temp
anchors.centerIn: parent anchors.centerIn: parent
color: hour.index === Weather.currentHourIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface color: hour.current ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
font: Tokens.font.body.medium font: Tokens.font.body.medium
text: Weather.formatTemp(hour.cond.tempC).slice(0, -1) // Remove C/F text: Weather.formatTemp(hour.cond.tempC).slice(0, -1) // Remove C/F
} }
@@ -96,7 +103,7 @@ CustomRect {
Layout.topMargin: Tokens.spacing.extraSmall Layout.topMargin: Tokens.spacing.extraSmall
color: Colors.palette.m3onSurfaceVariant color: Colors.palette.m3onSurfaceVariant
font: Tokens.font.body.small font: Tokens.font.body.small
text: hour.index === 0 ? qsTr("Now") : Qt.formatDateTime(new Date(hour.cond.timestamp.replace("T", " ")), Config.services.useTwelveHourClock ? "ha" : "hh:00") text: hour.current ? qsTr("Now") : Qt.formatDateTime(new Date(hour.cond.timestamp.replace("T", " ")), Config.services.useTwelveHourClock ? "ha" : "hh:00")
} }
} }
} }
+2 -2
View File
@@ -19,6 +19,7 @@ CustomRect {
return base + brief.anchors.topMargin; return base + brief.anchors.topMargin;
} }
radius: Tokens.rounding.small radius: Tokens.rounding.small
topLeftRadius: Tokens.rounding.largeIncreased
Timer { Timer {
interval: 900000 // 15 minutes interval: 900000 // 15 minutes
@@ -48,7 +49,6 @@ CustomRect {
anchors.right: parent.right anchors.right: parent.right
asynchronous: true asynchronous: true
sourceComponent: Forecast { sourceComponent: Forecast {}
}
} }
} }
+4 -1
View File
@@ -28,7 +28,10 @@ Item {
for (let i = 0; i < count; i++) for (let i = 0; i < count; i++)
height += (list.itemAtIndex(i) as NotifWrapper)?.nonAnimHeight ?? 0; height += (list.itemAtIndex(i) as NotifWrapper)?.nonAnimHeight ?? 0;
if (panels.popouts.hasCurrent && (panels.popouts.currentCenter + (panels.popouts.current?.width / 2)) > panels.notifications.x || screenState.dashboard) if (panels.popouts.hasCurrent && (panels.popouts.currentCenter + (panels.popouts.current?.width / 2)) > panels.notifications.x)
return 0;
if (screenState.dashboard && (panels.dashboard.x + panels.dashboard.width >= panels.notifications.x))
return 0; return 0;
if (screenState.osd) { if (screenState.osd) {
+1 -2
View File
@@ -131,8 +131,7 @@ Item {
} }
} }
Behavior on opacity { Behavior on opacity {
Anim { Anim {}
}
} }
} }
} }
+1 -2
View File
@@ -15,8 +15,7 @@ ClipRect {
radius: Tokens.rounding.largeIncreased radius: Tokens.rounding.largeIncreased
Behavior on animPerc { Behavior on animPerc {
Anim { Anim {}
}
} }
Contents { Contents {
+1 -2
View File
@@ -51,8 +51,7 @@ CustomRect {
value: Memory.percentage value: Memory.percentage
Behavior on clampedVal { Behavior on clampedVal {
Anim { Anim {}
}
} }
ColumnLayout { ColumnLayout {
+6 -12
View File
@@ -81,8 +81,7 @@ Item {
active: Config.dashboard.performance.showStorage active: Config.dashboard.performance.showStorage
sourceComponent: StorageCard { sourceComponent: StorageCard {}
}
} }
WrappedLoader { WrappedLoader {
@@ -90,8 +89,7 @@ Item {
active: Config.dashboard.performance.showMemory active: Config.dashboard.performance.showMemory
sourceComponent: MemoryCard { sourceComponent: MemoryCard {}
}
} }
WrappedLoader { WrappedLoader {
@@ -99,8 +97,7 @@ Item {
active: Config.dashboard.performance.showVram active: Config.dashboard.performance.showVram
sourceComponent: VramCard { sourceComponent: VramCard {}
}
} }
WrappedLoader { WrappedLoader {
@@ -108,8 +105,7 @@ Item {
active: Config.dashboard.performance.showNetwork && !vramCard.active active: Config.dashboard.performance.showNetwork && !vramCard.active
sourceComponent: NetworkCard { sourceComponent: NetworkCard {}
}
} }
} }
@@ -118,8 +114,7 @@ Item {
active: Config.dashboard.performance.showNetwork && vramCard.active active: Config.dashboard.performance.showNetwork && vramCard.active
sourceComponent: NetworkCard { sourceComponent: NetworkCard {}
}
} }
} }
@@ -127,8 +122,7 @@ Item {
Layout.fillWidth: false Layout.fillWidth: false
active: Battery.isLaptop && Config.dashboard.performance.showBattery active: Battery.isLaptop && Config.dashboard.performance.showBattery
sourceComponent: BatteryTank { sourceComponent: BatteryTank {}
}
} }
} }
+1 -2
View File
@@ -22,8 +22,7 @@ CustomRect {
topRightRadius: first ? Tokens.rounding.largeIncreased : Tokens.rounding.extraSmall topRightRadius: first ? Tokens.rounding.largeIncreased : Tokens.rounding.extraSmall
Behavior on opacity { Behavior on opacity {
Anim { Anim {}
}
} }
CustomRect { CustomRect {
+27 -3
View File
@@ -39,8 +39,7 @@ Item {
opacity: img.status === Image.Ready ? 0 : 1 opacity: img.status === Image.Ready ? 0 : 1
Behavior on opacity { Behavior on opacity {
Anim { Anim {}
}
} }
sourceComponent: CustomRect { sourceComponent: CustomRect {
color: Colors.palette.m3primaryContainer color: Colors.palette.m3primaryContainer
@@ -64,18 +63,43 @@ Item {
anchors.fill: parent anchors.fill: parent
asynchronous: true asynchronous: true
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
opacity: status === Image.Ready ? 1 : 0
retainWhileLoading: true retainWhileLoading: true
sourceSize: { sourceSize: {
const dpr = (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1; const dpr = (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1;
return Qt.size(width * dpr, height * dpr); return Qt.size(width * dpr, height * dpr);
} }
opacity: status === Image.Ready ? 1 : 0
Behavior on opacity { Behavior on opacity {
Anim { Anim {
type: Anim.SlowEffects type: Anim.SlowEffects
} }
} }
Loader {
opacity: root.modelData.isVideo && img.status === Image.Ready ? 1 : 0
active: opacity > 0
anchors.centerIn: parent
Behavior on opacity {
Anim {
type: Anim.SlowEffects
}
}
sourceComponent: CustomRect {
implicitWidth: videoIcon.implicitWidth + Tokens.padding.extraLarge * 2
implicitHeight: implicitWidth
radius: Tokens.rounding.full
color: Colors.tPalette.m3surface
MaterialIcon {
id: videoIcon
anchors.centerIn: parent
text: "video_library"
fontStyle: Tokens.font.icon.extraLarge
}
}
}
} }
} }
} }
+42 -8
View File
@@ -13,9 +13,14 @@ import qs.Services
Item { Item {
id: wrapper id: wrapper
property int buttonRowHeight: Tokens.padding.large * 3 readonly property bool isVideo: Wallpapers.currentIsVideo
readonly property string currentWallpaper: Wallpapers.current
readonly property var currentScreen: screens.length > selectedScreenIndex ? screens[selectedScreenIndex] : null
readonly property real maxZoom: 5.0
readonly property bool changesMade: { readonly property bool changesMade: {
cropRevision; cropRevision;
Wallpapers.cropVersion;
wrapper.currentWallpaper;
const r = cropRectLoader.item; const r = cropRectLoader.item;
if (!r || !currentScreen || scaledImg.status !== Image.Ready) if (!r || !currentScreen || scaledImg.status !== Image.Ready)
@@ -25,9 +30,9 @@ Item {
return Math.abs(r.x - t.x) > 0.5 || Math.abs(r.y - t.y) > 0.5 || Math.abs(r.zoom - t.zoom) > 0.001; return Math.abs(r.x - t.x) > 0.5 || Math.abs(r.y - t.y) > 0.5 || Math.abs(r.zoom - t.zoom) > 0.001;
} }
property int buttonRowHeight: Tokens.padding.large * 3
property int cropRevision: 0 property int cropRevision: 0
readonly property var currentScreen: screens.length > selectedScreenIndex ? screens[selectedScreenIndex] : null
readonly property real maxZoom: 5.0
property var screens: [] property var screens: []
property int selectedScreenIndex: 0 property int selectedScreenIndex: 0
property bool shouldBeActive: true property bool shouldBeActive: true
@@ -113,6 +118,9 @@ Item {
refreshScreens(); refreshScreens();
Qt.callLater(syncCropToScreen); Qt.callLater(syncCropToScreen);
} }
onCurrentWallpaperChanged: {
Qt.callLater(syncCropToScreen);
}
onSelectedScreenIndexChanged: { onSelectedScreenIndexChanged: {
Qt.callLater(syncCropToScreen); Qt.callLater(syncCropToScreen);
} }
@@ -127,7 +135,7 @@ Item {
anchors.rightMargin: Tokens.padding.extraLarge * 2 anchors.rightMargin: Tokens.padding.extraLarge * 2
anchors.top: parent.top anchors.top: parent.top
active: wrapper.screens.length > 1 active: wrapper.screens.length > 1 && !wrapper.isVideo
sourceComponent: ButtonRow { sourceComponent: ButtonRow {
id: screenSelector id: screenSelector
@@ -163,6 +171,7 @@ Item {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
implicitHeight: 30 implicitHeight: 30
enabled: !wrapper.isVideo
CustomSlider { CustomSlider {
id: zoomSlider id: zoomSlider
@@ -274,7 +283,7 @@ Item {
opacity: status === Image.Ready ? 1 : 0 opacity: status === Image.Ready ? 1 : 0
retainWhileLoading: true retainWhileLoading: true
smooth: true smooth: true
source: Wallpapers.current source: Wallpapers.currentThumbnail
sourceSize.height: { sourceSize.height: {
const dpr = (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1; const dpr = (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1;
return imageSlot.height * dpr; return imageSlot.height * dpr;
@@ -285,12 +294,37 @@ Item {
type: Anim.SlowEffects type: Anim.SlowEffects
} }
} }
Loader {
opacity: wrapper.isVideo && scaledImg.status === Image.Ready ? 1 : 0
active: opacity > 0
anchors.centerIn: parent
Behavior on opacity {
Anim {
type: Anim.SlowEffects
}
}
sourceComponent: CustomRect {
implicitWidth: videoIcon.implicitWidth + Tokens.padding.extraLarge * 2
implicitHeight: implicitWidth
radius: Tokens.rounding.full
color: Colors.tPalette.m3surface
MaterialIcon {
id: videoIcon
anchors.centerIn: parent
text: "video_library"
fontStyle: Tokens.font.icon.extraLarge
}
}
}
} }
Loader { Loader {
id: cropRectLoader id: cropRectLoader
active: scaledImg.status === Image.Ready active: scaledImg.status === Image.Ready && !wrapper.isVideo
sourceComponent: Component { sourceComponent: Component {
CustomRect { CustomRect {
@@ -333,7 +367,6 @@ Item {
y = t.y; y = t.y;
} }
// Where the rect should be according to saved data (same rules as a restore).
function savedTarget() { function savedTarget() {
const data = Wallpapers.getCrop(wrapper.currentScreen.name); const data = Wallpapers.getCrop(wrapper.currentScreen.name);
const hasCrop = data && (Math.abs(data.x) > 0.001 || Math.abs(data.y) > 0.001 || Math.abs(data.width - 1.0) > 0.001 || Math.abs(data.height - 1.0) > 0.001); const hasCrop = data && (Math.abs(data.x) > 0.001 || Math.abs(data.y) > 0.001 || Math.abs(data.width - 1.0) > 0.001 || Math.abs(data.height - 1.0) > 0.001);
@@ -436,9 +469,10 @@ Item {
} }
anchors.fill: parent anchors.fill: parent
cursorShape: pressed ? Qt.ClosedHandCursor : insideCropRect(mouseX, mouseY) ? Qt.OpenHandCursor : Qt.PointingHandCursor cursorShape: wrapper.isVideo ? Qt.ArrowCursor : pressed ? Qt.ClosedHandCursor : insideCropRect(mouseX, mouseY) ? Qt.OpenHandCursor : Qt.PointingHandCursor
hoverEnabled: true hoverEnabled: true
preventStealing: true preventStealing: true
enabled: !wrapper.isVideo
onPositionChanged: mouse => { onPositionChanged: mouse => {
if (pressed) { if (pressed) {
+2 -23
View File
@@ -2,8 +2,8 @@ pragma Singleton
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import ZShell.Config
import qs.Components import qs.Components
import ZShell.Config
import qs.Modules.Settings.Common import qs.Modules.Settings.Common
import qs.Modules.Settings.Pages import qs.Modules.Settings.Pages
import qs.Modules.Settings.Pages.Wallpaper import qs.Modules.Settings.Pages.Wallpaper
@@ -13,7 +13,6 @@ import qs.Modules.Settings.Pages.Panels
import qs.Modules.Settings.Pages.Panels.Bar import qs.Modules.Settings.Pages.Panels.Bar
import qs.Modules.Settings.Pages.Panels.Sidebar import qs.Modules.Settings.Pages.Panels.Sidebar
import qs.Modules.Settings.Pages.Services import qs.Modules.Settings.Pages.Services
import qs.Modules.Settings.Pages.Network
import qs.Services import qs.Services
QtObject { QtObject {
@@ -48,27 +47,7 @@ QtObject {
// Connectivity // Connectivity
Component { Component {
StackPage { PlaceholderComp {}
Component {
NetworkPage {}
}
Component {
AllNetworks {}
}
Component {
SavedNetworks {}
}
Component {
AddNetwork {}
}
Component {
OldNetwork {}
}
}
}, },
Component { Component {
PlaceholderComp {} PlaceholderComp {}
-1
View File
@@ -38,7 +38,6 @@ Item {
id: container id: container
anchors.fill: parent anchors.fill: parent
layer.enabled: opacity < 1
objectName: "PageContainer" objectName: "PageContainer"
Component.onCompleted: root.loadPage(root.sState.currentPageIdx) Component.onCompleted: root.loadPage(root.sState.currentPageIdx)
@@ -1,256 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import ZShell.Config
import ZShell.Internal
import ZShell.Blobs
import ZShell.Services
import ZShell.Components
import qs.Components
import qs.Modules.Settings.Common
PageBase {
id: root
property bool connecting: false
property bool failed: false
// Security model: index 0 = none, 1 = WPA/WPA2/WPA3 personal.
readonly property bool secured: securitySelect.active !== noneItem
property bool success: false
function submit(): void {
const ssid = ssidField.text.trim();
if (ssid.length === 0) {
ssidField.isError = true;
ssidField.forceActiveFocus();
return;
}
if (root.secured && passwordField.text.length < 8) {
passwordField.isError = true;
passwordField.forceActiveFocus();
return;
}
root.failed = false;
root.connecting = true;
Nmcli.addHiddenNetwork(ssid, root.secured ? passwordField.text : "", root.secured ? "wpa" : "none", hiddenToggle.checked, result => {
root.connecting = false;
if (result && result.success) {
root.success = true;
root.nState.closeSubPage();
} else {
root.failed = true;
if (root.secured)
passwordField.isError = true;
// Clean up the half-created profile so a retry starts fresh.
Nmcli.forgetNetwork(ssid);
}
});
}
isSubPage: true
title: Tr.tr("Add network")
ColumnLayout {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
spacing: Tokens.spacing.large
width: root.cappedWidth
Connections {
function onSubPageClosed(): void {
if (root.success)
return;
const ssid = ssidField.text.trim();
if (ssid)
Nmcli.forgetNetwork(ssid);
}
target: root.nState
}
CustomText {
Layout.fillWidth: true
Layout.leftMargin: Tokens.padding.extraSmall
color: Colours.palette.m3onSurfaceVariant
font: Tokens.font.body.small
text: Tr.tr("Enter the details below to manually connect to a network.")
wrapMode: Text.WordWrap
}
CustomTextField {
id: ssidField
Layout.fillWidth: true
Layout.topMargin: Tokens.spacing.extraSmall
errorText: Tr.tr("Network name is required")
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText
leadingIcon: "wifi"
// TRANSLATORS: SSID is a protocol term, leave it untranslated
placeholderText: Tr.tr("Network name (SSID)")
// TRANSLATORS: sample network name, translate the e.g. but the name is a placeholder
supportingText: Tr.tr("e.g. MyHiddenNetwork")
onAccepted: root.secured ? passwordField.forceActiveFocus() : root.submit()
}
ToggleRow {
id: hiddenToggle
checked: true
first: true
subtext: Tr.tr("Actively probe for a network that doesn't broadcast its name")
text: Tr.tr("Hidden network")
}
SelectRow {
id: securitySelect
Layout.topMargin: Tokens.spacing.extraSmall / 2 - parent.spacing
fallbackIcon: "lock"
// TRANSLATORS: WPA/WPA2/WPA3 are protocol names, leave them untranslated
fallbackText: Tr.tr("WPA/WPA2/WPA3 Personal")
// label: Tr.tr("Security")
last: !root.secured
Behavior on bottomLeftRadius {
Anim {
type: Anim.DefaultEffects
}
}
Behavior on bottomRightRadius {
Anim {
type: Anim.DefaultEffects
}
}
menuItems: [
MenuItem {
icon: "lock"
// TRANSLATORS: WPA/WPA2/WPA3 are protocol names, leave them untranslated
text: Tr.tr("WPA/WPA2/WPA3 Personal")
},
MenuItem {
id: noneItem
icon: "lock_open"
text: Tr.trCtx("None (open)", "wifi security type")
}
]
}
Item {
Layout.bottomMargin: root.secured ? 0 : -parent.spacing
Layout.fillWidth: true
implicitHeight: root.secured ? passwordField.implicitHeight : 0
opacity: root.secured ? 1 : 0
Behavior on Layout.bottomMargin {
Anim {
type: Anim.DefaultEffects
}
}
Behavior on implicitHeight {
Anim {
type: Anim.DefaultEffects
}
}
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
CustomTextField {
id: passwordField
anchors.left: parent.left
anchors.right: parent.right
echoMode: TextInput.Password
enabled: root.secured
errorText: root.failed ? Tr.tr("Connection failed — check the password") : Tr.tr("Password must be at least 8 characters")
leadingIcon: "key"
placeholderText: Tr.tr("Password")
// TRANSLATORS: WPA is a protocol name, leave it untranslated
supportingText: Tr.tr("WPA passwords are at least 8 characters")
onAccepted: root.submit()
}
}
RowLayout {
Layout.alignment: Qt.AlignRight
Layout.topMargin: Tokens.spacing.extraSmall - parent.spacing
spacing: Tokens.spacing.small
TextButton {
Layout.fillHeight: true
horizontalPadding: Tokens.padding.extraLarge
isRound: true
text: Tr.trCtx("Cancel", "button")
type: TextButton.Tonal
onClicked: root.nState.closeSubPage()
}
// Connect button — swaps to a loading spinner while connecting,
// matching the Wi-Fi list connect animation.
ButtonBase {
id: connectBtn
implicitHeight: connectMetrics.height + Tokens.padding.medium * 2
implicitWidth: connectMetrics.width + Tokens.padding.extraLarge * 2
// inactiveColour: Colours.palette.m3primary
// inactiveOnColour: Colours.palette.m3onPrimary
isRound: true
shapeMorph: true
// stateLayer.disabled: root.connecting || ssidField.text.trim().length === 0
onClicked: {
if (!root.connecting && ssidField.text.trim().length > 0)
root.submit();
}
TextMetrics {
id: connectMetrics
font: connectBtn.font
text: Tr.tr("Connect")
}
Anim {
id: connectContent
// anchors.centerIn: parent
// inAnimType: Anim.SlowEffects
// outAnimType: Anim.SlowEffects
// sourceComp: root.connecting ? connectLoadingComp : connectTextComp
}
Component {
id: connectLoadingComp
LoadingIndicator {
color: connectBtn.onColour
implicitSize: Math.round(Tokens.font.body.medium.pointSize * 1.4)
}
}
Component {
id: connectTextComp
CustomText {
color: connectBtn.onColour
font: connectBtn.font
text: connectMetrics.text
}
}
}
}
}
}
@@ -1,345 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Components
import qs.Helpers.Network
import qs.Services
import qs.Modules.Settings.Common
PageBase {
id: root
flickable.bottomMargin: Tokens.padding.extraExtraLarge * 2 // Extra scrolling space at the bottom
isSubPage: true
title: Tr.tr("All networks")
ColumnLayout {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
// spacing: Tokens.spacing.extraSmall / 2
width: root.cappedWidth
Timer {
interval: 300 // GlobalConfig.nexus.networkRescanInterval
repeat: true
running: root.visible && Nmcli.wifiEnabled
triggeredOnStart: true
onTriggered: Nmcli.rescanWifi()
}
ConnectedRect {
Layout.fillWidth: true
first: true
implicitHeight: headerLayout.implicitHeight + Tokens.padding.medium * 2
RowLayout {
id: headerLayout
anchors.left: parent.left
anchors.leftMargin: Tokens.padding.largeIncreased
anchors.right: parent.right
anchors.rightMargin: Tokens.padding.large
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: 1
// spacing: Tokens.spacing.extraSmall / 2
CustomText {
font: Tokens.font.title.small
text: Tr.trCtx("Filters", "network filter heading")
}
Item {
Layout.fillWidth: true
}
FilterButton {
id: savedFilter
function internalFilter(ap: Nmcli.AccessPoint): bool {
return Nmcli.hasSavedProfile(ap.ssid);
}
bottomLeftRadius: pressed ? pressedRadius : implicitHeight / 2
text: Tr.trCtx("Saved", "network filter")
topLeftRadius: pressed ? pressedRadius : implicitHeight / 2
Behavior on bottomLeftRadius {
Anim {
type: Anim.DefaultEffects
}
}
Behavior on topLeftRadius {
Anim {
type: Anim.DefaultEffects
}
}
}
FilterButton {
id: secureFilter
function internalFilter(ap: Nmcli.AccessPoint): bool {
return ap.security !== "none";
}
text: Tr.trCtx("Secured", "network filter")
}
FilterButton {
id: highFreqFilter
function internalFilter(ap: Nmcli.AccessPoint): bool {
return ap.frequency >= 4900 && ap.frequency <= 5900;
}
text: Tr.trCtx("5 GHz", "network filter")
}
FilterButton {
id: lowFreqFilter
function internalFilter(ap: Nmcli.AccessPoint): bool {
return ap.frequency >= 2400 && ap.frequency <= 2500;
}
bottomRightRadius: pressed ? pressedRadius : implicitHeight / 2
text: Tr.trCtx("2.4 GHz", "network filter")
topRightRadius: pressed ? pressedRadius : implicitHeight / 2
Behavior on bottomRightRadius {
Anim {
type: Anim.DefaultEffects
}
}
Behavior on topRightRadius {
Anim {
type: Anim.DefaultEffects
}
}
}
}
}
NetworkList {
id: networkList
function networkFilter(ap: Nmcli.AccessPoint): bool {
return savedFilter.filter(ap) && secureFilter.filter(ap) && highFreqFilter.filter(ap) && lowFreqFilter.filter(ap);
}
enableFilter: true
last: true
// nState: root.nState
}
}
component FilterButton: IconTextButton {
id: filter
property int filterState // 0 = default, 1 = on, 2 = negate
function filter(ap: Nmcli.AccessPoint): bool {
if (filterState === 0)
return true;
if (filterState === 1)
return internalFilter(ap);
return !internalFilter(ap);
}
function internalFilter(ap: Nmcli.AccessPoint): bool {
return true;
}
defaultRadius: filterState > 0 ? implicitHeight / 2 : Tokens.rounding.small
icon: ["check_indeterminate_small", "check", "close"][filterState]
// inactiveColour: [Colours.palette.m3secondaryContainer, Colours.palette.m3secondary, Colours.palette.m3tertiary][filterState]
// inactiveOnColour: [Colours.palette.m3onSecondaryContainer, Colours.palette.m3onSecondary, Colours.palette.m3onTertiary][filterState]
pressedRadius: Tokens.rounding.extraSmall
// spacing: Tokens.spacing.extraSmall
onClicked: filterState = (filterState + 1) % 3
}
component NetworkList: ItemList {
id: netList
property bool enableFilter
property int limit: 0 // 0 = show all
// required property NexusState nState
signal networkSelected(ap: Nmcli.AccessPoint)
function networkFilter(ap: Nmcli.AccessPoint): bool {
return true;
}
extraHeight: Nmcli.scanning ? Tokens.rounding.extraSmall : 0 // Inline so it isn't affected by anim
list.anchors.top: scanningIndicator.bottom
placeholderIcon: Nmcli.wifiEnabled ? "wifi_find" : "signal_wifi_off"
placeholderText: Nmcli.wifiEnabled ? Tr.tr("No networks found") : Tr.tr("Wi-Fi disabled")
showList: Nmcli.wifiEnabled
delegate: StateLayer {
id: network
property bool currentSelected
required property int index
required property var modelData
property real textOpacity: disabled ? 0.5 : 1
anchors.fill: undefined
anchors.left: netList.list.contentItem.left
anchors.right: netList.list.contentItem.right
bottomLeftRadius: netList?.last && index === netList.list.count - 1 ? Tokens.rounding.extraLarge : radius
bottomRightRadius: netList?.last && index === netList.list.count - 1 ? Tokens.rounding.extraLarge : radius
// disabled: currentSelected || Nmcli.connectingSsid() === modelData.ssid
implicitHeight: networkLayout.implicitHeight + networkLayout.anchors.margins * 2
radius: Tokens.rounding.extraSmall
Behavior on textOpacity {
Anim {
type: Anim.DefaultEffects
}
}
onClicked: {
if (!modelData.active) {
NetworkConnection.handleConnect(modelData);
currentSelected = true;
netList.networkSelected(modelData);
} else {
// Active network: open its detail/settings sub-page.
netList.nState.selectedNetworkSsid = modelData.ssid;
netList.nState.networkDetailsFromSaved = false;
netList.nState.openSubPage(3);
}
}
Connections {
function onActiveChanged(): void {
if (network.modelData.active)
network.currentSelected = false;
}
target: network.modelData
}
Connections {
function onNetworkSelected(ap: Nmcli.AccessPoint): void {
if (ap !== network.modelData)
network.currentSelected = false;
}
target: netList
}
RowLayout {
id: networkLayout
anchors.fill: parent
anchors.leftMargin: Tokens.padding.extraLarge
anchors.margins: Tokens.padding.large
anchors.rightMargin: Tokens.padding.extraLarge
// spacing: Tokens.spacing.extraSmall
MaterialIcon {
color: network.modelData.active ? Colours.palette.m3primary : Colours.palette.m3onSurfaceVariant
font.pointSize: Tokens.font.size.normal
opacity: network.textOpacity
text: Icons.getNetworkIcon(network.modelData.strength)
}
ColumnLayout {
Layout.fillWidth: true
opacity: network.textOpacity
// spacing: 0
CustomText {
Layout.fillWidth: true
elide: Text.ElideRight
font: Tokens.font.body.small
text: network.modelData.ssid
}
CustomText {
Layout.fillWidth: true
color: Colours.palette.m3outline
elide: Text.ElideRight
font: Tokens.font.label.small
text: {
const net = network.modelData;
let status = "";
if (net.active)
status = Tr.trCtx("Connected", "network connected");
else if (Nmcli.hasSavedProfile(net.ssid))
status = Tr.trCtx("Saved", "network saved");
if (status)
// TRANSLATORS: %1 = security type, %2 = connection status
return Tr.trCtx("Security: %1 • %2", "network security and status").arg(net.security).arg(status);
return Tr.trCtx("Security: %1", "network security").arg(net.security);
}
}
}
// Anim {
// // sourceComp: Nmcli.connectingSsid() === network.modelData.ssid ? loadingComp : iconComp
//
// Component {
// id: iconComp
//
// MaterialIcon {
// color: network.modelData.active ? Colours.palette.m3primary : Colours.palette.m3onSurfaceVariant
// fontStyle: Tokens.font.icon.medium
// opacity: network.textOpacity
// text: network.modelData.active ? "settings" : "lock"
// }
// }
//
// Component {
// id: loadingComp
//
// LoadingIndicator {
// implicitSize: Math.round(Tokens.font.icon.medium.pointSize * 1.3)
// }
// }
// }
}
}
// model: ScriptModel {
// values: {
// const connecting = Nmcli.connectingSsid();
// // Lower rank sorts higher in the list
// const rank = n => n.active ? 0 : n.ssid === connecting ? 1 : Nmcli.hasSavedProfile(n.ssid) ? 2 : 3;
// const sorted = [...Nmcli.networks].sort((a, b) => rank(a) - rank(b) || b.strength - a.strength);
// if (netList.limit > 0 && sorted.length > netList.limit)
// sorted.length = netList.limit;
// return netList.enableFilter ? sorted.filter(netList.networkFilter) : sorted;
// }
// }
// StyledProgressBar {
// id: scanningIndicator
//
// anchors.left: parent.left
// anchors.margins: 1
// anchors.right: parent.right
// implicitHeight: Nmcli.scanning ? Tokens.rounding.extraSmall : 0
// indeterminate: true
//
// Behavior on implicitHeight {
// Anim {
// type: Anim.DefaultEffects
// }
// }
// }
}
}

Some files were not shown because too many files have changed in this diff Show More