Author SHA1 Message Date
zach e76350fa7f Merge branch 'main' into module/wifi
C++ / fmt (pull_request) Successful in 3s
JS/TS / lint (pull_request) Successful in 16s
JS/TS / fmt (pull_request) Successful in 17s
Python / static (pull_request) Successful in 56s
Rust / fmt (pull_request) Successful in 58s
Rust / build (pull_request) Successful in 1m51s
C++ / build (pull_request) Successful in 2m32s
Rust / clippy (pull_request) Successful in 1m32s
Python / verify (pull_request) Successful in 2m39s
C++ / clang-tidy (pull_request) Successful in 4m2s
2026-08-18 19:31:42 +02:00
zach 015c1fb27d Merge branch 'main' into module/wifi
C++ / fmt (pull_request) Successful in 4s
JS/TS / fmt (pull_request) Successful in 17s
JS/TS / lint (pull_request) Successful in 20s
Python / static (pull_request) Successful in 57s
Rust / fmt (pull_request) Successful in 1m9s
C++ / build (pull_request) Successful in 2m30s
Rust / build (pull_request) Successful in 2m8s
Rust / clippy (pull_request) Successful in 1m33s
Python / verify (pull_request) Successful in 3m0s
C++ / clang-tidy (pull_request) Successful in 4m7s
2026-08-18 00:04:59 +02:00
zach f6cb7e44c2 Merge branch 'main' into module/wifi
C++ / fmt (pull_request) Successful in 8s
JS/TS / fmt (pull_request) Successful in 11s
JS/TS / lint (pull_request) Successful in 15s
Python / static (pull_request) Successful in 54s
Rust / fmt (pull_request) Successful in 30s
Rust / build (pull_request) Successful in 2m2s
C++ / build (pull_request) Successful in 4m0s
Rust / clippy (pull_request) Successful in 1m29s
Python / verify (pull_request) Successful in 3m13s
C++ / clang-tidy (pull_request) Successful in 5m7s
2026-08-17 22:05:33 +02:00
Inorishio 5a8939bc1e changed the repetaer for the networkpopout to be a listview, changed some variables in the bluetooth popout
C++ / fmt (pull_request) Successful in 4s
JS/TS / fmt (pull_request) Successful in 21s
JS/TS / lint (pull_request) Successful in 19s
Python / static (pull_request) Successful in 1m1s
Rust / fmt (pull_request) Successful in 52s
C++ / build (pull_request) Successful in 2m46s
Rust / build (pull_request) Successful in 2m25s
Rust / clippy (pull_request) Successful in 1m58s
Python / verify (pull_request) Successful in 3m16s
C++ / clang-tidy (pull_request) Successful in 6m59s
2026-08-17 22:04:49 +02:00
Inorishio 6071223009 Merge branch 'main' into module/wifi
C++ / fmt (pull_request) Successful in 4s
JS/TS / fmt (pull_request) Successful in 15s
JS/TS / lint (pull_request) Successful in 19s
Python / static (pull_request) Successful in 57s
Rust / fmt (pull_request) Successful in 1m9s
Rust / build (pull_request) Successful in 2m11s
C++ / build (pull_request) Successful in 2m34s
Rust / clippy (pull_request) Successful in 1m30s
Python / verify (pull_request) Successful in 2m55s
C++ / clang-tidy (pull_request) Successful in 3m59s
2026-08-16 22:57:54 +02:00
Inorishio 2d40ce33d1 Merge branch 'main' into module/wifi
C++ / fmt (pull_request) Successful in 5s
JS/TS / fmt (pull_request) Successful in 17s
JS/TS / lint (pull_request) Successful in 16s
Python / fmt (pull_request) Successful in 31s
Python / lint (pull_request) Successful in 41s
Python / test (pull_request) Successful in 1m5s
Python / typecheck (pull_request) Successful in 1m15s
Rust / clippy (pull_request) Failing after 12m43s
Rust / fmt (pull_request) Failing after 12m57s
Rust / build (pull_request) Failing after 13m33s
Python / buildcheck (pull_request) Failing after 13m45s
C++ / clang-tidy (pull_request) Failing after 14m23s
C++ / build (pull_request) Failing after 14m25s
2026-08-15 20:38:54 +02:00
zach 954dc10fbb bluetooth model delegate animations
C++ / fmt (pull_request) Successful in 4s
JS/TS / fmt (pull_request) Successful in 19s
JS/TS / lint (pull_request) Successful in 21s
Python / fmt (pull_request) Successful in 35s
Python / lint (pull_request) Successful in 36s
Python / test (pull_request) Successful in 1m3s
Python / typecheck (pull_request) Failing after 1m4s
C++ / build (pull_request) Successful in 1m52s
Rust / build (pull_request) Successful in 1m41s
Rust / fmt (pull_request) Successful in 1m6s
Python / buildcheck (pull_request) Successful in 2m28s
Rust / clippy (pull_request) Successful in 1m55s
C++ / clang-tidy (pull_request) Successful in 3m36s
2026-07-11 14:00:08 +02:00
zach 15ccfd24c6 Merge branch 'main' into module/wifi
C++ / fmt (pull_request) Successful in 3s
JS/TS / fmt (pull_request) Successful in 15s
JS/TS / lint (pull_request) Successful in 16s
Python / fmt (pull_request) Successful in 32s
Python / lint (pull_request) Successful in 32s
Python / test (pull_request) Successful in 1m3s
Python / typecheck (pull_request) Failing after 1m3s
C++ / build (pull_request) Successful in 1m41s
Rust / build (pull_request) Successful in 1m42s
Rust / fmt (pull_request) Successful in 1m17s
Python / buildcheck (pull_request) Successful in 2m43s
Rust / clippy (pull_request) Successful in 2m7s
C++ / clang-tidy (pull_request) Successful in 3m43s
2026-07-10 18:20:25 +02:00
zach 33b08523e9 add preliminary bluetooth widget + popout
C++ / fmt (pull_request) Successful in 3s
JS/TS / fmt (pull_request) Successful in 15s
JS/TS / lint (pull_request) Successful in 17s
Python / fmt (pull_request) Successful in 35s
Python / lint (pull_request) Successful in 34s
Python / test (pull_request) Successful in 1m0s
C++ / build (pull_request) Successful in 1m46s
Rust / fmt (pull_request) Successful in 1m19s
Rust / build (pull_request) Successful in 1m54s
Python / buildcheck (pull_request) Successful in 2m42s
Rust / clippy (pull_request) Successful in 1m54s
C++ / clang-tidy (pull_request) Successful in 3m42s
2026-07-10 16:17:47 +02:00
zach 742bece30b Merge branch 'main' into module/wifi
C++ / fmt (pull_request) Successful in 7s
JS/TS / fmt (pull_request) Successful in 14s
JS/TS / lint (pull_request) Successful in 20s
Python / fmt (pull_request) Successful in 28s
Python / lint (pull_request) Successful in 29s
Python / test (pull_request) Successful in 59s
Rust / fmt (pull_request) Successful in 57s
C++ / build (pull_request) Successful in 3m47s
Rust / build (pull_request) Successful in 2m10s
Python / buildcheck (pull_request) Successful in 2m51s
Rust / clippy (pull_request) Successful in 1m32s
C++ / clang-tidy (pull_request) Successful in 5m9s
2026-07-10 15:23:23 +02:00
zach a64eadd14a feat: add password overlay for network popout + redesign
C++ / fmt (pull_request) Successful in 4s
JS/TS / fmt (pull_request) Successful in 11s
JS/TS / lint (pull_request) Successful in 24s
Python / fmt (pull_request) Successful in 30s
Python / lint (pull_request) Successful in 34s
Python / test (pull_request) Successful in 53s
C++ / build (pull_request) Successful in 1m52s
Rust / fmt (pull_request) Successful in 1m14s
Rust / build (pull_request) Successful in 1m57s
Rust / clippy (pull_request) Successful in 2m0s
Python / buildcheck (pull_request) Successful in 2m37s
C++ / clang-tidy (pull_request) Successful in 3m43s
2026-07-09 17:33:13 +02:00
zach f9eeb793db Merge branch 'main' into module/wifi
C++ / fmt (pull_request) Successful in 5s
JS/TS / fmt (pull_request) Successful in 18s
JS/TS / lint (pull_request) Successful in 19s
Python / fmt (pull_request) Successful in 32s
Python / lint (pull_request) Successful in 36s
Python / test (pull_request) Successful in 55s
C++ / build (pull_request) Successful in 1m55s
Rust / fmt (pull_request) Successful in 1m15s
Rust / build (pull_request) Successful in 1m59s
Python / buildcheck (pull_request) Successful in 2m45s
Rust / clippy (pull_request) Successful in 1m55s
C++ / clang-tidy (pull_request) Successful in 3m41s
2026-07-09 14:22:34 +02:00
zach 6bb193c78c Merge branch 'main' into module/wifi
C++ / fmt (pull_request) Successful in 5s
JS/TS / fmt (pull_request) Successful in 19s
JS/TS / lint (pull_request) Successful in 22s
Python / lint (pull_request) Successful in 33s
Python / fmt (pull_request) Successful in 36s
Python / test (pull_request) Successful in 1m5s
C++ / build (pull_request) Successful in 1m42s
Rust / fmt (pull_request) Successful in 1m11s
Rust / build (pull_request) Successful in 1m56s
Python / buildcheck (pull_request) Successful in 2m40s
Rust / clippy (pull_request) Successful in 1m48s
C++ / clang-tidy (pull_request) Successful in 3m42s
2026-07-08 22:56:46 +02:00
Inorishio aadecbfe44 Chore: created Spacer component, moved x available networks, added connected networks
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 17s
Python / test (pull_request) Successful in 59s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m31s
C++ / build (pull_request) Successful in 2m37s
2026-07-08 22:22:03 +02:00
zach 53f0aa80ae Merge branch 'main' into module/wifi
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 30s
Python / test (pull_request) Successful in 42s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m36s
C++ / build (pull_request) Successful in 2m20s
2026-07-08 13:41:27 +02:00
Inorishio d9856c9409 Chore: created NetworkItem component, renamed network header to Wifi
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 14s
Python / test (pull_request) Successful in 46s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m51s
C++ / build (pull_request) Successful in 2m36s
2026-07-02 22:46:09 +02:00
zach 8dc9753e2b feat: don't add the same network twice
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 30s
Python / lint-format (pull_request) Successful in 48s
Python / test (pull_request) Successful in 40s
C++ / build (pull_request) Successful in 2m8s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m13s
2026-07-02 22:09:25 +02:00
zach 2a4bd3dfdd chore: remove old code
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 17s
Python / lint-format (pull_request) Successful in 31s
Python / test (pull_request) Successful in 39s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m31s
C++ / build (pull_request) Successful in 2m17s
2026-07-02 21:58:01 +02:00
zach f75f51ef50 fix: sort is now correct
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 18s
Python / lint-format (pull_request) Successful in 21s
Python / test (pull_request) Successful in 48s
C++ / build (pull_request) Successful in 2m59s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m12s
2026-07-02 21:54:58 +02:00
zach e0873304d1 add sorting by strength
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 15s
Python / lint-format (pull_request) Successful in 32s
Python / test (pull_request) Successful in 39s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m45s
C++ / build (pull_request) Successful in 2m47s
2026-07-02 21:53:42 +02:00
zach 99a0155c17 Merge branch 'main' into module/wifi
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 14s
Python / test (pull_request) Successful in 46s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m30s
C++ / build (pull_request) Successful in 2m32s
2026-07-02 21:46:15 +02:00
zach f309debea5 set scannerEnabled when popout is visible + remove scan button
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 18s
Python / lint-format (pull_request) Successful in 20s
Python / test (pull_request) Successful in 47s
C++ / build (pull_request) Successful in 3m9s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m15s
2026-07-02 16:06:52 +02:00
zach 0979fd014d Merge branch 'main' into module/wifi
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 16s
Python / lint-format (pull_request) Successful in 32s
Python / test (pull_request) Successful in 38s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m49s
C++ / build (pull_request) Successful in 3m5s
2026-07-02 15:16:22 +02:00
zach 656bd0a54d Merge branch 'main' into module/wifi
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 16s
Python / test (pull_request) Successful in 46s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m37s
C++ / build (pull_request) Successful in 2m22s
2026-06-30 22:42:51 +02:00
zach f01001dcfa merge main into module/wifi
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 9s
Python / lint-format (pull_request) Successful in 16s
Python / test (pull_request) Successful in 49s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m42s
C++ / build (pull_request) Successful in 2m28s
2026-06-30 18:15:43 +02:00
zach bc6b0d50ab Merge branch 'main' into module/wifi
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 34s
Python / lint-format (pull_request) Successful in 35s
Python / test (pull_request) Successful in 1m11s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m58s
C++ / build (pull_request) Successful in 4m31s
2026-06-29 22:10:45 +02:00
zach f27c9afc25 more methods for wifi and scanning + layout
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 15s
Python / test (pull_request) Successful in 29s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m6s
2026-06-26 15:15:03 +02:00
Inorishio 5962fc8354 Merge branch 'main' into module/wifi
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 17s
Python / lint-format (pull_request) Successful in 27s
Python / test (pull_request) Successful in 50s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m39s
2026-06-24 18:58:36 +02:00
Inorishio 1adcb9841c Added network properties, nicNames, netDevices, networkName < reminder I should use layouts.
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 16s
Python / test (pull_request) Successful in 50s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m52s
2026-06-22 23:34:58 +02:00
Inorishio 0e0f8555ed fixing singleton in helpers for network + getting output from systray 2026-06-15 20:48:01 +02:00
Inorishio 1d63e67554 removed network module and moved it to tray, have a popout window without content! everything still in wi.i.p. 2026-06-13 18:22:03 +02:00
605 changed files with 11530 additions and 51515 deletions
-3
View File
@@ -1,3 +0,0 @@
#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
-5
View File
@@ -1,5 +0,0 @@
CompileFlags:
Remove:
# GCC-only flags from the system's default spec that clang/clangd cannot
# parse. They appear in compile_commands.json but are not set by the project.
- -mno-direct-extern-access
+1
View File
@@ -38,6 +38,7 @@ body:
attributes: attributes:
label: OS label: OS
options: options:
- NixOS
- Arch - Arch
- Debian / Ubuntu - Debian / Ubuntu
- Fedora - Fedora
-1
View File
@@ -30,7 +30,6 @@ body:
- Refactor / style - Refactor / style
- CI / build / tooling - CI / build / tooling
- Documentation - Documentation
- Other (Describe in description box with header "Issue type")
validations: validations:
required: true required: true
+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 docker-cli-buildx run: apk add --no-cache docker-cli
- 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
+20 -21
View File
@@ -17,47 +17,46 @@ 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 --dry-run -Werror --style=file {} + -exec clang-format -i --style=file {} +
git diff --exit-code && echo "clang-format: passed"
build & lint: build:
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: Cache CMake build - name: Configure
uses: actions/cache@v4 run: cmake -B build -G Ninja -DENABLE_MODULES=plugin -DCMAKE_BUILD_TYPE=Release
with:
path: build
key: cpp-build-${{ hashFiles('CMakeLists.txt', '**/CMakeLists.txt') }}
restore-keys: |
cpp-build-
- name: Cache ccache - name: Build
uses: actions/cache@v4 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:
path: ~/.ccache fetch-depth: 0
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)" \ run-clang-tidy -p build -j $(nproc) -header-filter="Plugins/.*" 2>&1 | \
-source-filter="^$(pwd)/Plugins/" \ grep -v 'Suppressed\|non-user\|non-system\|unknown argument.*mno-direct\|Found compiler error\|warnings generated\.\|^Use -header-filter'
-header-filter=".*Plugins/.*" \
-removed-arg=-mno-direct-extern-access
+15 -1
View File
@@ -4,7 +4,7 @@ on:
pull_request: pull_request:
jobs: jobs:
fmt & lint: fmt:
runs-on: alpine runs-on: alpine
container: node:26-alpine container: node:26-alpine
@@ -24,6 +24,20 @@ 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
+3 -3
View File
@@ -7,7 +7,7 @@ on:
pull_request: pull_request:
jobs: jobs:
fmt & lint: static:
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 .
test & nuitka build: verify:
if: always() if: always()
runs-on: debian runs-on: debian
container: node:26-trixie-slim container: node:26-trixie-slim
@@ -64,4 +64,4 @@ jobs:
- name: Nuitka module check - name: Nuitka module check
run: | run: |
. .venv/bin/activate . .venv/bin/activate
nuitka --module --include-package=zshell cli/src/zshell/ nuitka --module --include-package=zshell cli/src/zshell/
+99 -11
View File
@@ -4,9 +4,9 @@ on:
pull_request: pull_request:
jobs: jobs:
clippy & fmt: build:
runs-on: alpine runs-on: alpine
container: node:26-alpine # keep node image for checkout to work container: node:26-alpine
env: env:
CARGO_HOME: ${{ github.workspace }}/.cargo CARGO_HOME: ${{ github.workspace }}/.cargo
@@ -16,29 +16,34 @@ jobs:
- 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 }}-cargo-${{ hashFiles('**/Cargo.lock') }} key: rust-build-${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: | restore-keys: |
rust-${{ runner.os }}-cargo- rust-${{ runner.os }}-build-${{ env.cache-name }}-
rust-${{ runner.os }}-build-
rust-
- name: Install tools - name: Install tools
run: | run: |
apk add --no-cache git cargo rust rust-clippy rustfmt apk add --no-cache \
git \
cargo \
rust
- name: Clippy (with compile check) - name: Cargo check
run: | run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
status=0
for manifest in $(find . -name "Cargo.toml"); do for manifest in $(find . -name "Cargo.toml"); do
cargo clippy --manifest-path "$manifest" -- -D warnings && \ cargo check --manifest-path "$manifest" && \
echo "$manifest: Clippy passed" || \ echo "$manifest: check passed" || \
{ echo "$manifest: Clippy failed"; status=1; } { echo "$manifest: check failed"; exit 1; }
done done
exit $status
elif [ -n "$(find . -name "*.rs" -print -quit)" ]; then elif [ -n "$(find . -name "*.rs" -print -quit)" ]; then
echo "Rust files found but no Cargo.toml" echo "Rust files found but no Cargo.toml"
exit 1 exit 1
@@ -46,6 +51,39 @@ jobs:
echo "No Rust project found" echo "No Rust project found"
fi fi
fmt:
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-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 if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
@@ -62,3 +100,53 @@ jobs:
else else
echo "No Rust project found" echo "No Rust project found"
fi 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"
fi
+10 -25
View File
@@ -1,35 +1,20 @@
**/__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.dist/
# Qml
.qtcreator/
.qmlls.ini
# CMake compile artifacts
.cache/
compile_commands.json
build/
# Python
uv.lock
testpython
.venv/
**/__pycache__/
# Nuitka compile
**/zshell.pyi
**/zshell.cpython-*
**/zshell.build/ **/zshell.build/
**/zshell.dist/
# Agents stuff
.opencode/
run-agent.sh
AGENTS.md
-1
View File
@@ -1,4 +1,3 @@
.venv/ .venv/
scripts/fzf.js scripts/fzf.js
scripts/fuzzysort.js scripts/fuzzysort.js
scripts/settings-indexer.js
+1 -2
View File
@@ -3,9 +3,8 @@ FunctionsSpacing=true
IndentWidth=4 IndentWidth=4
MaxColumnWidth=-1 MaxColumnWidth=-1
NewlineType=native NewlineType=native
GroupAttributesTogether=false NormalizeOrder=true
ObjectsSpacing=true ObjectsSpacing=true
SemicolonRule=always SemicolonRule=always
SingleLineEmptyObjects=true
SortImports=false SortImports=false
UseTabs=true UseTabs=true
+22 -32
View File
@@ -26,52 +26,49 @@ if(NOT DEFINED VERSION)
endif() endif()
endif() endif()
if(NOT DEFINED GIT_REVISION)
execute_process(COMMAND git rev-parse HEAD
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE GIT_REVISION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if("${GIT_REVISION}" STREQUAL "")
message(FATAL_ERROR "GIT_REVISION is not set and failed to get from git")
endif()
endif()
set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
project(ZShell VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) project(ZShell VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH})
message(STATUS "ZShell version: ${VERSION}") message(STATUS "ZShell version: ${VERSION}")
include(GNUInstallDirs)
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON) 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;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 "usr/lib/ZShell" CACHE STRING "Library install dir")
set(INSTALL_QMLDIR "${CMAKE_INSTALL_LIBDIR}/qt6/qml" CACHE STRING "QML install dir") set(INSTALL_QMLDIR "usr/lib/qt6/qml" CACHE STRING "QML install dir")
set(INSTALL_QSCONFDIR "${CMAKE_INSTALL_SYSCONFDIR}/xdg/quickshell/zshell" CACHE STRING "Quickshell config install dir") set(INSTALL_QSCONFDIR "etc/xdg/quickshell/zshell" CACHE STRING "Quickshell config install dir")
set(INSTALL_GREETERCONFDIR "${CMAKE_INSTALL_SYSCONFDIR}/xdg/quickshell/zshell-greeter" CACHE STRING "Quickshell greeter install dir") set(INSTALL_GREETERCONFDIR "etc/xdg/quickshell/zshell-greeter" CACHE STRING "Quickshell greeter install dir")
set(CMAKE_INSTALL_MESSAGE NEVER) set(CMAKE_INSTALL_MESSAGE NEVER)
add_compile_options( add_compile_options(
-Wall -Wextra -Wpedantic -Wshadow -Wconversion -Wall -Wextra -Wpedantic -Wshadow -Wconversion
-Wnull-dereference -Wdouble-promotion -Wold-style-cast -Wnull-dereference -Wdouble-promotion
-Wformat=2 -Wfloat-equal -Wformat=2 -Wfloat-equal -Woverloaded-virtual
-Wsign-conversion -Wredundant-decls -Wswitch -Wsign-conversion -Wredundant-decls -Wswitch
-Wunreachable-code -Wunreachable-code
$<$<COMPILE_LANGUAGE:CXX>:-Wold-style-cast>
$<$<COMPILE_LANGUAGE:CXX>:-Woverloaded-virtual>
) )
if("cli" IN_LIST ENABLE_MODULES) if("shell" IN_LIST ENABLE_MODULES)
# Build settings index
find_package(Python3 COMPONENTS Interpreter REQUIRED)
set(SETTINGS_INDEX_JSON "${CMAKE_BINARY_DIR}/settings-index.json")
execute_process(
COMMAND ${Python3_EXECUTABLE}
"${CMAKE_SOURCE_DIR}/scripts/build-settings-index.py"
"${CMAKE_SOURCE_DIR}/Modules/Settings"
"${SETTINGS_INDEX_JSON}"
RESULT_VARIABLE SETTINGS_INDEX_RESULT
)
if(NOT SETTINGS_INDEX_RESULT EQUAL 0)
message(FATAL_ERROR "Failed to build settings search index")
endif()
# Nuitka compilation # Nuitka compilation
set(ZSHELL_CLI_BUILD_DIR "${CMAKE_BINARY_DIR}/zshell-cli") set(ZSHELL_CLI_BUILD_DIR "${CMAKE_BINARY_DIR}/zshell-cli")
set(ZSHELL_CLI_DIST "${ZSHELL_CLI_BUILD_DIR}/zshell.dist") set(ZSHELL_CLI_DIST "${ZSHELL_CLI_BUILD_DIR}/zshell.dist")
@@ -191,11 +188,4 @@ if("m3shapes" IN_LIST ENABLE_MODULES)
if(TARGET m3shapesplugin) if(TARGET m3shapesplugin)
set_target_properties(m3shapesplugin PROPERTIES INSTALL_RPATH "$ORIGIN") set_target_properties(m3shapesplugin PROPERTIES INSTALL_RPATH "$ORIGIN")
endif() endif()
# Silence warnings that come from m3shapes files we do not manage.
foreach(m3shapes_target m3shapes_core m3shapes_morph m3shapes_shapes m3shapes m3shapesplugin)
if(TARGET ${m3shapes_target})
target_compile_options(${m3shapes_target} PRIVATE -Wno-shadow -Wno-float-equal)
endif()
endforeach()
endif() endif()
+9 -9
View File
@@ -27,25 +27,25 @@ AnchorAnimation {
if (type == AnchorAnim.DefaultSpatial) if (type == AnchorAnim.DefaultSpatial)
return Tokens.anim.durations.expressiveDefaultSpatial; return Tokens.anim.durations.expressiveDefaultSpatial;
if (type == AnchorAnim.SlowSpatial) if (type == AnchorAnim.SlowSpatial)
return Tokens.anim.durations.expressiveSlowSpatial; return Tokens.anim.durations.large;
const types = ["small", "normal", "large", "extraLarge"]; const types = ["small", "normal", "large", "extraLarge"];
const idx = type % 4; // 0-7 are the 4 standard types const idx = type % 4;
return Tokens.anim.durations[types[idx]]; return Tokens.anim.durations[types[idx]];
} }
easing: { easing.bezierCurve: {
if (type == AnchorAnim.FastSpatial) if (type == AnchorAnim.FastSpatial)
return Tokens.anim.expressiveFastSpatial; return Tokens.anim.curves.expressiveFastSpatial;
if (type == AnchorAnim.DefaultSpatial) if (type == AnchorAnim.DefaultSpatial)
return Tokens.anim.expressiveDefaultSpatial; return Tokens.anim.curves.expressiveDefaultSpatial;
if (type == AnchorAnim.SlowSpatial) if (type == AnchorAnim.SlowSpatial)
return Tokens.anim.expressiveSlowSpatial; return Tokens.anim.curves.expressiveSlowSpatial;
if (type >= AnchorAnim.StandardSmall && type <= AnchorAnim.StandardExtraLarge) if (type >= AnchorAnim.StandardSmall && type <= AnchorAnim.StandardExtraLarge)
return Tokens.anim.standard; return Tokens.anim.curves.standard;
if (type >= AnchorAnim.EmphasizedSmall && type <= AnchorAnim.EmphasizedExtraLarge) if (type >= AnchorAnim.EmphasizedSmall && type <= AnchorAnim.EmphasizedExtraLarge)
return Tokens.anim.emphasized; return Tokens.anim.curves.emphasized;
return Tokens.anim.expressiveDefaultSpatial; return Tokens.anim.curves.expressiveDefaultSpatial;
} }
} }
+12 -12
View File
@@ -30,34 +30,34 @@ NumberAnimation {
if (type === Anim.DefaultSpatial) if (type === Anim.DefaultSpatial)
return Tokens.anim.durations.expressiveDefaultSpatial; return Tokens.anim.durations.expressiveDefaultSpatial;
if (type === Anim.SlowSpatial) if (type === Anim.SlowSpatial)
return Tokens.anim.durations.expressiveSlowSpatial; return Tokens.anim.durations.large;
if (type === Anim.FastEffects) if (type === Anim.FastEffects)
return Tokens.anim.durations.expressiveFastEffects; return Tokens.anim.durations.expressiveFastEffects;
if (type === Anim.DefaultEffects) if (type === Anim.DefaultEffects)
return Tokens.anim.durations.expressiveDefaultEffects; return Tokens.anim.durations.expressiveEffects;
if (type === Anim.SlowEffects) if (type === Anim.SlowEffects)
return Tokens.anim.durations.expressiveSlowEffects; return Tokens.anim.durations.expressiveSlowEffects;
const types = ["small", "normal", "large", "extraLarge"]; const types = ["small", "normal", "large", "extraLarge"];
const idx = type % 4; // 0-7 are the 4 standard types const idx = type % 4;
return Tokens.anim.durations[types[idx]]; return Tokens.anim.durations[types[idx]];
} }
easing: { easing.bezierCurve: {
if (type === Anim.FastSpatial) if (type === Anim.FastSpatial)
return Tokens.anim.expressiveFastSpatial; return Tokens.anim.curves.expressiveFastSpatial;
if (type === Anim.DefaultSpatial) if (type === Anim.DefaultSpatial)
return Tokens.anim.expressiveDefaultSpatial; return Tokens.anim.curves.expressiveDefaultSpatial;
if (type === Anim.SlowSpatial) if (type === Anim.SlowSpatial)
return Tokens.anim.expressiveSlowSpatial; return Tokens.anim.curves.expressiveSlowSpatial;
if (type === Anim.FastEffects) if (type === Anim.FastEffects)
return Tokens.anim.expressiveFastEffects; return Tokens.anim.curves.expressiveFastEffects;
if (type === Anim.DefaultEffects) if (type === Anim.DefaultEffects)
return Tokens.anim.expressiveDefaultEffects; return Tokens.anim.curves.expressiveDefaultEffects;
if (type === Anim.SlowEffects) if (type === Anim.SlowEffects)
return Tokens.anim.expressiveSlowEffects; return Tokens.anim.curves.expressiveSlowEffects;
if (type >= Anim.EmphasizedSmall && type <= Anim.EmphasizedExtraLarge) if (type >= Anim.EmphasizedSmall && type <= Anim.EmphasizedExtraLarge)
return Tokens.anim.emphasized; return Tokens.anim.curves.emphasized;
return Tokens.anim.standard; return Tokens.anim.curves.standard;
} }
} }
+44 -23
View File
@@ -80,29 +80,49 @@ Slider {
property bool moving property bool moving
anchors.centerIn: parent function update(): void {
anchors.verticalCenterOffset: 1 animate = !moving;
text: moving ? Math.round(root.value * 100) : root.icon binding.when = moving;
color: Colors.palette.m3inverseOnSurface font.pointSize = moving ? Tokens.font.size.small : Tokens.font.size.larger;
font: moving ? Tokens.font.body.small : Tokens.font.icon.medium font.family = moving ? Config.appearance.font.family.sans : Config.appearance.font.family.material;
}
Behavior on moving { anchors.centerIn: parent
SequentialAnimation { color: Colors.palette.m3inverseOnSurface
Anim { text: root.icon
target: icon
property: "scale" onMovingChanged: anim.restart()
to: 0.3
duration: Tokens.anim.durations.small / 2 Binding {
easing: Tokens.anim.standardAccel id: binding
}
PropertyAction {} property: "text"
Anim { target: icon
target: icon value: Math.round(root.value * root.multiplier)
property: "scale" when: false
to: 1 }
duration: Tokens.anim.durations.normal / 2
easing: Tokens.anim.standardDecel SequentialAnimation {
} id: anim
Anim {
duration: Tokens.anim.durations.normal / 2
easing.bezierCurve: Tokens.anim.curves.standardAccel
property: "scale"
target: icon
to: 0
}
ScriptAction {
script: icon.update()
}
Anim {
duration: Tokens.anim.durations.normal / 2
easing.bezierCurve: Tokens.anim.curves.standardDecel
property: "scale"
target: icon
to: 1
} }
} }
} }
@@ -110,7 +130,7 @@ Slider {
} }
Behavior on value { Behavior on value {
Anim { Anim {
type: Anim.StandardLarge duration: Tokens.anim.durations.large
} }
} }
@@ -118,6 +138,7 @@ Slider {
onValueChanged: { onValueChanged: {
if (!initialized) { if (!initialized) {
initialized = true; initialized = true;
oldValue = value;
return; return;
} }
+8 -5
View File
@@ -1,6 +1,5 @@
import QtQuick import QtQuick
import ZShell.Config import ZShell.Config
import qs.Components
import qs.Services import qs.Services
CustomRect { CustomRect {
@@ -16,11 +15,15 @@ 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: Tokens.rounding.large property real defaultRadius: Math.min(width, height) / 2
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
property font font: Tokens.font.body.small property font font: ({
family: Config.appearance.font.family.sans,
pointSize: Tokens.font.size.larger,
bold: false
})
property real horizontalPadding: padding property real horizontalPadding: padding
readonly property alias hovered: stateLayer.containsMouse readonly property alias hovered: stateLayer.containsMouse
required implicitHeight required implicitHeight
@@ -33,7 +36,7 @@ CustomRect {
readonly property color onColor: !enabled ? disabledOnColor : internalChecked ? activeOnColor : inactiveOnColor readonly property color onColor: !enabled ? disabledOnColor : internalChecked ? activeOnColor : inactiveOnColor
property real padding property real padding
readonly property alias pressed: stateLayer.pressed readonly property alias pressed: stateLayer.pressed
property real pressedRadius: Tokens.rounding.small property real pressedRadius: Tokens.rounding.smallest
readonly property alias radiusAnim: radiusAnim readonly property alias radiusAnim: radiusAnim
property bool radiusMorph: true property bool radiusMorph: true
property alias shapeMorph: stateLayer.shapeMorph property alias shapeMorph: stateLayer.shapeMorph
@@ -51,7 +54,7 @@ CustomRect {
if (internalChecked) if (internalChecked)
return checkedRadius; return checkedRadius;
if (isRound) if (isRound)
return implicitHeight / 2 * Math.min(1, Tokens.rounding.scale); return (height || implicitHeight) / 2 * Math.min(1, Tokens.rounding.scale);
return defaultRadius; return defaultRadius;
} }
+58 -2
View File
@@ -2,6 +2,62 @@ import QtQuick
import ZShell.Config import ZShell.Config
ColorAnimation { ColorAnimation {
duration: Tokens.anim.durations.expressiveSlowEffects enum Type {
easing: Tokens.anim.expressiveSlowEffects StandardSmall = 0,
Standard,
StandardLarge,
StandardExtraLarge,
EmphasizedSmall,
Emphasized,
EmphasizedLarge,
EmphasizedExtraLarge,
FastSpatial,
DefaultSpatial,
SlowSpatial,
FastEffects,
DefaultEffects,
SlowEffects
}
property int type: CAnim.DefaultSpatial
duration: {
if (type < CAnim.StandardSmall || type > Anim.SlowEffects)
return Tokens.anim.durations.normal;
if (type === CAnim.FastSpatial)
return Tokens.anim.durations.expressiveFastSpatial;
if (type === CAnim.DefaultSpatial)
return Tokens.anim.durations.expressiveDefaultSpatial;
if (type === CAnim.SlowSpatial)
return Tokens.anim.durations.large;
if (type === CAnim.FastEffects)
return Tokens.anim.durations.expressiveFastEffects;
if (type === CAnim.DefaultEffects)
return Tokens.anim.durations.expressiveEffects;
if (type === CAnim.SlowEffects)
return Tokens.anim.durations.expressiveSlowEffects;
const types = ["small", "normal", "large", "extraLarge"];
const idx = type % 4;
return Tokens.anim.durations[types[idx]];
}
easing.bezierCurve: {
if (type === CAnim.FastSpatial)
return Tokens.anim.curves.expressiveFastSpatial;
if (type === CAnim.DefaultSpatial)
return Tokens.anim.curves.expressiveDefaultSpatial;
if (type === CAnim.SlowSpatial)
return Tokens.anim.curves.expressiveSlowSpatial;
if (type === CAnim.FastEffects)
return Tokens.anim.curves.expressiveFastEffects;
if (type === CAnim.DefaultEffects)
return Tokens.anim.curves.expressiveDefaultEffects;
if (type === CAnim.SlowEffects)
return Tokens.anim.curves.expressiveSlowEffects;
if (type >= CAnim.EmphasizedSmall && type <= Anim.EmphasizedExtraLarge)
return Tokens.anim.curves.emphasized;
return Tokens.anim.curves.standard;
}
} }
+4 -2
View File
@@ -20,10 +20,10 @@ BusyIndicator {
property int animState property int animState
property color bgColor: Colors.palette.m3secondaryContainer property color bgColor: Colors.palette.m3secondaryContainer
property color fgColor: Colors.palette.m3primary property color fgColor: Colors.palette.m3primary
property real implicitSize: Tokens.font.body.medium.pointSize * 3 property real implicitSize: Tokens.font.size.normal * 3
property real internalStrokeWidth: strokeWidth property real internalStrokeWidth: strokeWidth
readonly property alias progress: manager.progress readonly property alias progress: manager.progress
property real strokeWidth: Tokens.padding.extraSmall * 0.8 property real strokeWidth: Tokens.padding.small * 0.8
property alias type: manager.indeterminateAnimationType property alias type: manager.indeterminateAnimationType
implicitHeight: implicitSize implicitHeight: implicitSize
@@ -34,6 +34,7 @@ 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
@@ -53,6 +54,7 @@ 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
} }
} }
+5 -3
View File
@@ -21,7 +21,7 @@ Item {
readonly property real size: Math.min(width, height) readonly property real size: Math.min(width, height)
property int spacing: Tokens.spacing.small property int spacing: Tokens.spacing.small
property int startAngle: -90 property int startAngle: -90
property int strokeWidth: Tokens.padding.extraSmall property int strokeWidth: Tokens.padding.small
property int sweepAngle: 360 property int sweepAngle: 360
readonly property real thickness: strokeWidth * (1 + waveAmplitude) * 2 readonly property real thickness: strokeWidth * (1 + waveAmplitude) * 2
property real value property real value
@@ -46,7 +46,8 @@ 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 {
@@ -83,7 +84,8 @@ Item {
} }
} }
Behavior on color { Behavior on color {
CAnim {} CAnim {
}
} }
Anim on waveProgress { Anim on waveProgress {
id: waveProgAnim id: waveProgAnim
-42
View File
@@ -1,42 +0,0 @@
import QtQuick
import Quickshell
import ZShell.Components
Item {
id: root
property real radius
property real topLeftRadius: radius
property real topRightRadius: radius
property real bottomRightRadius: radius
property real bottomLeftRadius: radius
property color color: "transparent"
default property alias contentData: contentItem.data
Item {
id: contentItem
anchors.fill: parent
}
ShaderEffectSource {
id: src
hideSource: true
sourceItem: contentItem
}
RoundedMask {
anchors.fill: parent
source: src
topLeftRadius: root.topLeftRadius
topRightRadius: root.topRightRadius
bottomRightRadius: root.bottomRightRadius
bottomLeftRadius: root.bottomLeftRadius
color: root.color
}
Behavior on color {
CAnim {}
}
}
+7 -6
View File
@@ -46,10 +46,10 @@ Item {
} }
function syncFromPenColor() { function syncFromPenColor() {
if (!drawing.screenState) if (!drawing.drawingState)
return; return;
const c = drawing.screenState.penColor; const c = drawing.drawingState.penColor;
if (c.hsvSaturation > 0) { if (c.hsvSaturation > 0) {
currentHue = c.hsvHue; currentHue = c.hsvHue;
@@ -86,7 +86,7 @@ Item {
currentHue = relative / arcSweep; currentHue = relative / arcSweep;
lastChromaticHue = currentHue; lastChromaticHue = currentHue;
drawing.screenState.penColor = Qt.hsva(currentHue, drawing.screenState.penColor.hsvSaturation, drawing.screenState.penColor.hsvValue, drawing.screenState.penColor.a); drawing.drawingState.penColor = Qt.hsva(currentHue, drawing.drawingState.penColor.hsvSaturation, drawing.drawingState.penColor.hsvValue, drawing.drawingState.penColor.a);
} }
implicitHeight: 180 implicitHeight: 180
@@ -112,7 +112,7 @@ Item {
root.syncFromPenColor(); root.syncFromPenColor();
} }
target: root.drawing.screenState target: root.drawing.drawingState
} }
Canvas { Canvas {
@@ -152,7 +152,7 @@ Item {
CustomRect { CustomRect {
anchors.centerIn: parent anchors.centerIn: parent
color: root.drawing?.screenState.penColor color: root.drawing?.drawingState.penColor
implicitHeight: implicitWidth implicitHeight: implicitWidth
implicitWidth: canvas.height - root.handleSize - Tokens.padding.extraLarge * 2 implicitWidth: canvas.height - root.handleSize - Tokens.padding.extraLarge * 2
radius: Tokens.rounding.full radius: Tokens.rounding.full
@@ -160,7 +160,8 @@ Item {
Behavior on color { Behavior on color {
enabled: false enabled: false
CAnim {} CAnim {
}
} }
} }
+2 -1
View File
@@ -8,6 +8,7 @@ MultiEffect {
colorization: 1 colorization: 1
Behavior on colorizationColor { Behavior on colorizationColor {
CAnim {} CAnim {
}
} }
} }
+1 -1
View File
@@ -23,7 +23,7 @@ CheckBox {
color: Colors.palette.m3surfaceVariant color: Colors.palette.m3surfaceVariant
implicitHeight: control.checkHeight implicitHeight: control.checkHeight
implicitWidth: control.checkWidth implicitWidth: control.checkWidth
radius: Tokens.rounding.small / 2 radius: Tokens.rounding.smallest / 2
CustomRect { CustomRect {
color: Colors.palette.m3primary color: Colors.palette.m3primary
+2 -1
View File
@@ -7,6 +7,7 @@ ClippingRectangle {
color: "transparent" color: "transparent"
Behavior on color { Behavior on color {
CAnim {} CAnim {
}
} }
} }
+2 -1
View File
@@ -7,6 +7,7 @@ ClippingWrapperRectangle {
color: "transparent" color: "transparent"
Behavior on color { Behavior on color {
CAnim {} CAnim {
}
} }
} }
+3 -6
View File
@@ -1,12 +1,12 @@
import QtQuick import QtQuick
import qs.Services import qs.Helpers
Flickable { Flickable {
id: root id: root
property bool doneFakeFlick property bool doneFakeFlick
interactive: !ShellState.forActive().isDrawing interactive: !Visibilities.getForActive().isDrawing
maximumFlickVelocity: 3000 maximumFlickVelocity: 3000
rebound: Transition { rebound: Transition {
@@ -28,9 +28,6 @@ Flickable {
interval: 10 interval: 10
running: root.doneFakeFlick running: root.doneFakeFlick
onTriggered: { onTriggered: root.doneFakeFlick = false
root.doneFakeFlick = false;
root.returnToBounds();
}
} }
} }
+16 -19
View File
@@ -1,36 +1,33 @@
import QtQuick import QtQuick
import qs.Services import qs.Helpers
ListView { ListView {
id: root id: root
property bool doneFakeFlick property bool doneFakeFlick
interactive: !ShellState.forActive()?.isDrawing interactive: !Visibilities.getForActive()?.isDrawing
maximumFlickVelocity: 3000 maximumFlickVelocity: 3000
rebound: Transition { rebound: Transition {
// onRunningChanged: { onRunningChanged: {
// if (!running && !root.doneFakeFlick) { if (!running && !root.doneFakeFlick) {
// root.doneFakeFlick = true; root.doneFakeFlick = true;
// root.flick(1, 1); root.flick(1, 1);
// root.flick(-1, -1); root.flick(-1, -1);
// Qt.callLater(() => root.cancelFlick()); Qt.callLater(() => root.cancelFlick());
// } }
// } }
Anim { Anim {
properties: "x,y" properties: "x,y"
type: Anim.DefaultEffects
} }
} }
// Timer { Timer {
// interval: 10 interval: 10
// running: root.doneFakeFlick running: root.doneFakeFlick
//
// onTriggered: { onTriggered: root.doneFakeFlick = false
// root.doneFakeFlick = false; }
// }
// }
} }
-1
View File
@@ -4,7 +4,6 @@ MouseArea {
property int scrollAccumulatedY: 0 property int scrollAccumulatedY: 0
function onWheel(event: WheelEvent): void { function onWheel(event: WheelEvent): void {
event.accepted = false;
} }
onWheel: event => { onWheel: event => {
+4 -2
View File
@@ -124,7 +124,8 @@ 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)
@@ -221,7 +222,8 @@ ProgressBar {
} }
} }
Behavior on color { Behavior on color {
CAnim {} CAnim {
}
} }
Anim on waveProgress { Anim on waveProgress {
duration: root.waveDuration duration: root.waveDuration
+4 -3
View File
@@ -6,7 +6,7 @@ import qs.Services
RadioButton { RadioButton {
id: root id: root
font: Tokens.font.body.medium font.pointSize: Tokens.font.size.normal
implicitHeight: Math.max(implicitIndicatorHeight, implicitContentHeight) implicitHeight: Math.max(implicitIndicatorHeight, implicitContentHeight)
implicitWidth: implicitIndicatorWidth + implicitContentWidth + contentItem.anchors.leftMargin implicitWidth: implicitIndicatorWidth + implicitContentWidth + contentItem.anchors.leftMargin
@@ -14,7 +14,7 @@ RadioButton {
anchors.left: outerCircle.right anchors.left: outerCircle.right
anchors.leftMargin: 10 anchors.leftMargin: 10
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
font: root.font font.pointSize: root.font.pointSize
text: root.text text: root.text
} }
indicator: Rectangle { indicator: Rectangle {
@@ -29,7 +29,8 @@ RadioButton {
radius: Tokens.rounding.full radius: Tokens.rounding.full
Behavior on border.color { Behavior on border.color {
CAnim {} CAnim {
}
} }
StateLayer { StateLayer {
+2 -1
View File
@@ -6,6 +6,7 @@ Rectangle {
color: "transparent" color: "transparent"
Behavior on color { Behavior on color {
CAnim {} CAnim {
}
} }
} }
+81 -69
View File
@@ -1,41 +1,32 @@
pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Templates import QtQuick.Templates
import qs.Services import qs.Helpers
import ZShell.Config import ZShell.Config
import qs.Services
ScrollBar { ScrollBar {
id: root id: root
required property Flickable flickable
readonly property bool isHorizontal: flickable && flickable.ScrollBar.horizontal === root
readonly property real axisSize: isHorizontal ? flickable.width : flickable.height
readonly property real axisContentSize: isHorizontal ? flickable.contentWidth : flickable.contentHeight
readonly property real axisContentPos: isHorizontal ? (reversed ? flickable.contentX : (flickable.contentX - flickable.originX)) : (reversed ? flickable.contentY : (flickable.contentY - flickable.originY))
readonly property real axisLength: isHorizontal ? root.width : root.height
readonly property real effectiveSize: Math.max(nonAnimHeight, root.minimumSize) readonly property real effectiveSize: Math.max(nonAnimHeight, root.minimumSize)
readonly property real effectiveTravel: Math.max(0, 1 - root.effectiveSize) readonly property real effectiveTravel: Math.max(0, 1 - root.effectiveSize)
readonly property real nonAnimHeight: root.axisSize / root.axisContentSize required property Flickable flickable
readonly property real nonAnimY: root.axisContentPos / root.axisContentSize readonly property real nonAnimHeight: flickable.height / flickable.contentHeight
readonly property real nonAnimY: flickable.contentY / flickable.contentHeight
readonly property real rawTravel: Math.max(0, 1 - root.nonAnimHeight) readonly property real rawTravel: Math.max(0, 1 - root.nonAnimHeight)
readonly property bool reversed: isHorizontal ? (flickable instanceof ListView && flickable.layoutDirection === Qt.RightToLeft) : (flickable instanceof ListView && flickable.verticalLayoutDirection === ListView.BottomToTop) readonly property bool reversed: flickable instanceof ListView && flickable.verticalLayoutDirection === ListView.BottomToTop
property bool shouldBeActive
readonly property real travelScale: root.rawTravel > 0 ? root.effectiveTravel / root.rawTravel : 0 readonly property real travelScale: root.rawTravel > 0 ? root.effectiveTravel / root.rawTravel : 0
property bool shouldBeActive enabled: !Visibilities.getForActive().isDrawing
property bool inverted implicitWidth: Tokens.padding.extraSmall * 2
enabled: !ShellState.forActive().isDrawing contentItem: Item {
}
implicitWidth: size === 1 ? 0 : isHorizontal ? 0 : Tokens.padding.extraSmall * 2
implicitHeight: size === 1 ? 0 : isHorizontal ? Tokens.padding.extraSmall * 2 : 0
contentItem: Item {}
Behavior on position { Behavior on position {
enabled: !fullMouse.pressed enabled: !fullMouse.pressed
Anim {} Anim {
}
} }
onHoveredChanged: { onHoveredChanged: {
@@ -56,27 +47,54 @@ ScrollBar {
target: root.flickable target: root.flickable
} }
Loader { CustomClippingRect {
anchors.fill: parent anchors.bottom: parent.bottom
active: root.size < 1 anchors.right: parent.right
sourceComponent: root.isHorizontal ? horizontalTrack : verticalTrack anchors.top: parent.top
} implicitWidth: handle.implicitWidth
radius: Tokens.rounding.full
Component { CustomRect {
id: verticalTrack id: handle
VerticalScrollBarTrack { anchors.right: parent.right
scrollBar: root color: Colors.palette.m3secondary
mouseArea: fullMouse implicitHeight: root.height * root.effectiveSize
} implicitWidth: fullMouse.pressed || fullMouse.containsMouse ? Tokens.padding.extraSmall * 2 : Tokens.padding.extraSmall
} opacity: {
if (!root.enabled)
return 0;
if (root.size === 1)
return 0;
if (fullMouse.pressed)
return 1;
if (fullMouse.containsMouse)
return 0.8;
if (root.policy === ScrollBar.AlwaysOn || root.shouldBeActive)
return 0.6;
return 0;
}
radius: Tokens.rounding.full
y: root.reversed ? root.height * (1 + root.nonAnimY) * root.travelScale : root.height * root.nonAnimY * root.travelScale
Component { Behavior on implicitWidth {
id: horizontalTrack Anim {
}
}
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
HorizontalScrollBarTrack { MouseArea {
scrollBar: root id: mouse
mouseArea: fullMouse
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
}
} }
} }
@@ -93,40 +111,25 @@ ScrollBar {
property real pressOffset: 0 property real pressOffset: 0
function contentPosFromThumbStart(thumbStart) { function contentYFromThumbTop(thumbTop) {
var visualPos = root.effectiveTravel > 0 ? thumbStart / root.travelScale : 0; var visualPos = root.effectiveTravel > 0 ? thumbTop / root.travelScale : 0;
return root.reversed ? (visualPos - 1) * root.axisContentSize : visualPos * root.axisContentSize;
return root.reversed ? (visualPos - 1) * root.flickable.contentHeight : visualPos * root.flickable.contentHeight;
} }
function updateFromEvent(event) { function updateFromEvent(event) {
var eventPos = root.isHorizontal ? event.x : event.y; var posInTrack = event.y / root.height;
var posInTrack = eventPos / root.axisLength; var thumbTop = posInTrack - pressOffset;
var thumbStart = posInTrack - pressOffset; thumbTop = Math.max(0, Math.min(root.effectiveTravel, thumbTop));
thumbStart = Math.max(0, Math.min(root.effectiveTravel, thumbStart));
var newPos = contentPosFromThumbStart(thumbStart); root.flickable.contentY = contentYFromThumbTop(thumbTop);
if (root.isHorizontal)
root.flickable.contentX = newPos + (root.flickable.originX + (root.reversed ? root.flickable.contentWidth : 0));
else
root.flickable.contentY = newPos + (root.flickable.originY + (root.reversed ? root.flickable.contentHeight : 0));
} }
function visualThumbStart() { function visualThumbTop() {
const visualPos = root.reversed ? (1 + root.nonAnimY) : root.nonAnimY; const visualPos = root.reversed ? (1 + root.nonAnimY) : root.nonAnimY;
return visualPos * root.travelScale; return visualPos * root.travelScale;
} }
function onWheel(event: WheelEvent): void {
if (root.horizontal) {
event.accepted = false;
return;
}
var delta = event.angleDelta.y > 0 ? -0.1 : 0.1;
var newPos = Math.max(0, Math.min(1 - root.size, root.position + (root.inverted ? -delta : delta)));
root.position = newPos;
}
anchors.fill: parent anchors.fill: parent
cursorShape: undefined cursorShape: undefined
hoverEnabled: true hoverEnabled: true
@@ -137,15 +140,24 @@ ScrollBar {
updateFromEvent(event); updateFromEvent(event);
} }
onPressed: event => { onPressed: event => {
var currentStart = visualThumbStart(); var currentTop = visualThumbTop();
var currentEnd = currentStart + root.effectiveSize; var currentBottom = currentTop + root.effectiveSize;
var eventPos = root.isHorizontal ? event.x : event.y; var clickPos = event.y / root.height;
var clickPos = eventPos / root.axisLength;
var clickedInsideThumb = clickPos >= currentStart && clickPos <= currentEnd; var clickedInsideThumb = clickPos >= currentTop && clickPos <= currentBottom;
pressOffset = clickedInsideThumb ? (clickPos - currentStart) : root.effectiveSize / 2;
if (clickedInsideThumb) {
pressOffset = clickPos - currentTop;
} else {
pressOffset = root.effectiveSize / 2;
}
updateFromEvent(event); updateFromEvent(event);
} }
onWheel: event => {
var delta = event.angleDelta.y > 0 ? -0.1 : 0.1;
var newPos = Math.max(0, Math.min(1 - root.size, root.position + delta));
root.position = newPos;
}
} }
} }
+8 -10
View File
@@ -100,19 +100,15 @@ Slider {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.top: parent.top anchors.top: parent.top
color: root.insetColor color: root.insetColor
fontStyle: { font.pixelSize: Tokens.font.size.smaller * 2
if (root.height < 96)
return Tokens.font.icon.pixelSize(24).build();
else
return Tokens.font.icon.pixelSize(32).build();
}
text: root.insetIcon text: root.insetIcon
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
visible: !root.wavy && root.insetIcon !== "" visible: !root.wavy && root.insetIcon !== ""
x: Tokens.spacing.extraSmall + dockT * (handle.x + Tokens.spacing.small) x: Tokens.spacing.extraSmall + dockT * (handle.x + Tokens.spacing.small)
Behavior on dockT { Behavior on dockT {
Anim {} Anim {
}
} }
} }
@@ -142,9 +138,10 @@ Slider {
startX: x startX: x
Behavior on color { Behavior on color {
CAnim {} CAnim {
}
} }
Anim on waveProgress { NumberAnimation on waveProgress {
duration: root.waveDuration duration: root.waveDuration
easing.type: Easing.Linear easing.type: Easing.Linear
from: 0 from: 0
@@ -159,7 +156,8 @@ Slider {
Behavior on filledWidth { Behavior on filledWidth {
id: widthBehavior id: widthBehavior
Anim {} Anim {
}
} }
Component.onCompleted: { Component.onCompleted: {
+2 -2
View File
@@ -38,9 +38,9 @@ DoubleSpinBox {
horizontalAlignment: TextField.AlignHCenter horizontalAlignment: TextField.AlignHCenter
implicitWidth: 65 implicitWidth: 65
inputMethodHints: Qt.ImhFormattedNumbersOnly inputMethodHints: Qt.ImhFormattedNumbersOnly
leftPadding: Tokens.padding.small leftPadding: Tokens.padding.larger
readOnly: !root.editable readOnly: !root.editable
rightPadding: Tokens.padding.small rightPadding: Tokens.padding.larger
text: root.textFromValue(root.value, root.locale) text: root.textFromValue(root.value, root.locale)
validator: root.validator validator: root.validator
@@ -12,14 +12,14 @@ Row {
} }
property alias active: menu.active property alias active: menu.active
property color color: type == SplitButton.Filled ? Colors.palette.m3primary : Colors.palette.m3secondaryContainer property color color: type == CustomSplitButton.Filled ? Colors.palette.m3primary : Colors.palette.m3secondaryContainer
property color disabledColor: Qt.alpha(Colors.palette.m3onSurface, 0.1) property color disabledColor: Qt.alpha(Colors.palette.m3onSurface, 0.1)
property color disabledTextColor: Qt.alpha(Colors.palette.m3onSurface, 0.38) property color disabledTextColor: Qt.alpha(Colors.palette.m3onSurface, 0.38)
readonly property alias expandBtn: expandBtn readonly property alias expandBtn: expandBtn
property alias expanded: menu.expanded property alias expanded: menu.expanded
property string fallbackIcon property string fallbackIcon
property string fallbackText property string fallbackText
property real horizontalPadding: Tokens.padding.medium property real horizontalPadding: Tokens.padding.larger
readonly property alias iconLabel: iconLabel readonly property alias iconLabel: iconLabel
readonly property alias label: label readonly property alias label: label
readonly property alias menu: menu readonly property alias menu: menu
@@ -27,20 +27,20 @@ Row {
property bool menuOnTop property bool menuOnTop
property real minLeftWidth property real minLeftWidth
readonly property alias stateLayer: stateLayer readonly property alias stateLayer: stateLayer
property color textColor: type == SplitButton.Filled ? Colors.palette.m3onPrimary : Colors.palette.m3onSecondaryContainer property color textColor: type == CustomSplitButton.Filled ? Colors.palette.m3onPrimary : Colors.palette.m3onSecondaryContainer
readonly property alias textRow: textRow readonly property alias textRow: textRow
property int type: SplitButton.Filled property int type: CustomSplitButton.Filled
property real verticalPadding: Tokens.padding.small property real verticalPadding: Tokens.padding.small
spacing: Math.floor(Tokens.spacing.extraSmall / 2) spacing: Math.floor(Tokens.spacing.extraSmall)
CustomRect { CustomRect {
bottomRightRadius: Tokens.rounding.medium / 2 bottomRightRadius: Tokens.rounding.small / 2
color: !root.enabled ? root.disabledColor : root.color color: !root.enabled ? root.disabledColor : root.color
implicitHeight: expandBtn.implicitHeight implicitHeight: expandBtn.implicitHeight
implicitWidth: Math.max(root.minLeftWidth, textRow.implicitWidth + root.horizontalPadding * 2) implicitWidth: Math.max(root.minLeftWidth, textRow.implicitWidth + root.horizontalPadding * 2)
radius: implicitHeight / 2 * Math.min(1, Tokens.rounding.scale) radius: implicitHeight / 2 * Math.min(1, Tokens.rounding.scale)
topRightRadius: Tokens.rounding.medium / 2 topRightRadius: Tokens.rounding.small / 2
StateLayer { StateLayer {
id: stateLayer id: stateLayer
@@ -92,7 +92,7 @@ Row {
CustomRect { CustomRect {
id: expandBtn id: expandBtn
property real rad: root.expanded ? implicitHeight / 2 * Math.min(1, Tokens.rounding.scale) : Tokens.rounding.medium / 2 property real rad: root.expanded ? implicitHeight / 2 * Math.min(1, Tokens.rounding.scale) : Tokens.rounding.small / 2
bottomLeftRadius: rad bottomLeftRadius: rad
color: !root.enabled ? root.disabledColor : root.color color: !root.enabled ? root.disabledColor : root.color
@@ -102,7 +102,8 @@ Row {
topLeftRadius: rad topLeftRadius: rad
Behavior on rad { Behavior on rad {
Anim {} Anim {
}
} }
StateLayer { StateLayer {
@@ -126,10 +127,12 @@ 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 {
}
} }
} }
} }
+70
View File
@@ -0,0 +1,70 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Services
Item {
id: root
property alias active: splitButton.active
property alias buttonAlias: splitButton
property alias expanded: splitButton.expanded
property int expandedZ: 100
required property string label
property alias menuItems: splitButton.menuItems
property bool shouldBeActive: true
property alias type: splitButton.type
signal selected(item: MenuItem)
anchors.left: parent.left
anchors.right: parent.right
clip: false
implicitHeight: row.implicitHeight + Tokens.padding.smaller * 2
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
z: splitButton.menu.implicitHeight > 0 ? expandedZ : 1
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
RowLayout {
id: row
anchors.left: parent.left
anchors.margins: Tokens.padding.small
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: Tokens.spacing.normal
CustomText {
Layout.fillWidth: true
color: root.enabled ? Colors.palette.m3onSurface : Colors.palette.m3onSurfaceVariant
font.pointSize: Tokens.font.size.larger
text: root.label
}
CustomSplitButton {
id: splitButton
enabled: root.enabled
type: CustomSplitButton.Filled
z: 2
menu.onItemSelected: item => {
root.selected(item);
}
stateLayer.onClicked: {
splitButton.expanded = !splitButton.expanded;
}
}
}
}
+18 -25
View File
@@ -13,12 +13,8 @@ Switch {
implicitWidth: implicitIndicatorWidth implicitWidth: implicitIndicatorWidth
indicator: CustomRect { indicator: CustomRect {
color: { color: root.checked && root.enabled ? Colors.palette.m3primary : Colors.layer(Colors.palette.m3surfaceContainerHighest, root.cLayer)
if (!root.enabled) implicitHeight: Tokens.font.size.medium + Tokens.padding.normal * 2
return root.checked ? Qt.alpha(Colors.palette.m3onSurface, 0.12) : Qt.alpha(Colors.palette.m3surfaceContainerHighest, 0.38);
return root.checked ? Colors.palette.m3primary : Colors.layer(Colors.palette.m3surfaceContainerHighest, root.cLayer);
}
implicitHeight: 14 + Tokens.padding.small * 2
implicitWidth: implicitHeight * 1.7 implicitWidth: implicitHeight * 1.7
radius: Tokens.rounding.full radius: Tokens.rounding.full
@@ -26,11 +22,7 @@ Switch {
readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.2 : implicitHeight readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.2 : implicitHeight
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: { color: root.checked && root.enabled ? Colors.palette.m3onPrimary : Colors.layer(Colors.palette.m3outline, root.cLayer + 1)
if (!root.enabled)
return root.checked ? Qt.alpha(Colors.palette.m3surface, 0.7) : Qt.alpha(Colors.palette.m3onSurface, 0.12);
return root.checked ? Colors.palette.m3onPrimary : Colors.layer(Colors.palette.m3outline, root.cLayer + 1);
}
implicitHeight: parent.implicitHeight - Tokens.padding.extraSmall implicitHeight: parent.implicitHeight - Tokens.padding.extraSmall
implicitWidth: nonAnimWidth implicitWidth: nonAnimWidth
radius: Tokens.rounding.full radius: Tokens.rounding.full
@@ -100,37 +92,38 @@ Switch {
anchors.centerIn: parent anchors.centerIn: parent
asynchronous: true asynchronous: true
height: parent.implicitHeight - Tokens.padding.medium height: parent.implicitHeight - Tokens.padding.larger
preferredRendererType: Shape.CurveRenderer preferredRendererType: Shape.CurveRenderer
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 {
capStyle: Tokens.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap capStyle: ShapePath.RoundCap
fillColor: "transparent" fillColor: "transparent"
startX: icon.start1.x startX: icon.start1.x
startY: icon.start1.y startY: icon.start1.y
strokeColor: { strokeColor: root.checked && root.enabled ? Colors.palette.m3primary : Colors.palette.m3surfaceContainerHighest
if (!root.enabled) strokeWidth: Tokens.font.size.larger * 0.15
return root.checked ? Colors.palette.m3outline : Colors.palette.m3surfaceContainer;
return root.checked ? Colors.palette.m3primary : Colors.palette.m3surfaceContainerHighest;
}
strokeWidth: 16 * 0.15
Behavior on strokeColor { Behavior on strokeColor {
CAnim {} CAnim {
}
} }
PathLine { PathLine {
@@ -160,6 +153,6 @@ Switch {
component PropAnim: PropertyAnimation { component PropAnim: PropertyAnimation {
duration: Tokens.anim.durations.expressiveFastSpatial duration: Tokens.anim.durations.expressiveFastSpatial
easing: Tokens.anim.expressiveFastSpatial easing.bezierCurve: Tokens.anim.curves.expressiveFastSpatial
} }
} }
+7 -4
View File
@@ -14,13 +14,15 @@ Text {
property real animateTo: 1 property real animateTo: 1
color: Colors.palette.m3onSurface color: Colors.palette.m3onSurface
font: Tokens.font.body.small font.family: Config.appearance.font.family.sans
font.pointSize: Tokens.font.size.normal
linkColor: Colors.palette.m3onPrimaryFixedVariant linkColor: Colors.palette.m3onPrimaryFixedVariant
renderType: Text.QtRendering renderType: Text.NativeRendering
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
@@ -32,7 +34,8 @@ Text {
type: Anim.FastEffects type: Anim.FastEffects
} }
PropertyAction {} PropertyAction {
}
TAnim { TAnim {
target: root target: root
+28 -30
View File
@@ -15,33 +15,29 @@ TextFieldBase {
readonly property int clampedRadius: Math.min(horizontalPadding, Math.min(width, height) / 2, radius) readonly property int clampedRadius: Math.min(horizontalPadding, Math.min(width, height) / 2, radius)
readonly property string effectiveSupportingText: isError && errorText ? errorText : supportingText readonly property string effectiveSupportingText: isError && errorText ? errorText : supportingText
readonly property int filledOffset: type === CustomTextField.Filled ? Tokens.spacing.small : 0
readonly property int leadingOffset: leadingIcon ? leadingIconLoader.width + leadingIconLoader.anchors.leftMargin : 0
readonly property real smallFontScale: smallFontSize / font.pointSize
readonly property int supportingTextOffset: effectiveSupportingText ? supportingTextLoader.height + Tokens.spacing.extraSmall : 0
readonly property int trailingOffset: trailingIcon ? trailingIconLoader.width + trailingIconLoader.anchors.rightMargin : 0
readonly property bool valid: !validate || (!text && emptyIsValid) || (validate instanceof RegExp ? validate.test(text) : !!validate(text))
property int horizontalPadding: Tokens.padding.large
property int verticalPadding: Tokens.padding.large
property int radius: Tokens.rounding.small
property string supportingText
property string trailingIcon
property int smallFontSize: Tokens.font.label.small.pointSize
property int type: CustomTextField.Outlined
property var validate // Regex or function
property bool emptyIsValid: true property bool emptyIsValid: true
property string errorText property string errorText
readonly property int filledOffset: type === CustomTextField.Filled ? Tokens.spacing.small : 0
readonly property int horizontalPadding: Tokens.padding.large
property bool isError property bool isError
property string leadingIcon property string leadingIcon
readonly property int leadingOffset: leadingIcon ? leadingIconLoader.width + leadingIconLoader.anchors.leftMargin : 0
property int radius: Tokens.rounding.small
property alias sLayer: stateLayer
readonly property real smallFontScale: smallFontSize / font.pointSize
property int smallFontSize: Tokens.font.size.small
property string supportingText
readonly property int supportingTextOffset: effectiveSupportingText ? supportingTextLoader.height + Tokens.spacing.extraSmall : 0
property string trailingIcon
readonly property int trailingOffset: trailingIcon ? trailingIconLoader.width + trailingIconLoader.anchors.rightMargin : 0
property int type: CustomTextField.Outlined
readonly property bool valid: !validate || (!text && emptyIsValid) || (validate instanceof RegExp ? validate.test(text) : !!validate(text))
property var validate // Regex or function
bottomPadding: verticalPadding + supportingTextOffset - filledOffset bottomPadding: Tokens.padding.large + supportingTextOffset - filledOffset
leftPadding: horizontalPadding + leadingOffset leftPadding: horizontalPadding + leadingOffset
rightPadding: horizontalPadding + trailingOffset rightPadding: horizontalPadding + trailingOffset
topPadding: verticalPadding + filledOffset topPadding: Tokens.padding.large + filledOffset
background: Loader { background: Loader {
anchors.bottomMargin: root.supportingTextOffset anchors.bottomMargin: root.supportingTextOffset
@@ -134,8 +130,8 @@ TextFieldBase {
} }
AnchorAnim { AnchorAnim {
duration: Tokens.anim.durations.expressiveDefaultEffects duration: Tokens.anim.durations.expressiveEffects
easing: Tokens.anim.expressiveDefaultEffects easing.bezierCurve: Tokens.anim.curves.expressiveEffects
} }
} }
} }
@@ -145,13 +141,13 @@ TextFieldBase {
active: root.leadingIcon active: root.leadingIcon
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: Tokens.padding.small anchors.leftMargin: Tokens.padding.normal
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
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()
} }
} }
@@ -160,13 +156,12 @@ TextFieldBase {
active: root.trailingIcon active: root.trailingIcon
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: Tokens.padding.small anchors.rightMargin: Tokens.padding.normal
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
sourceComponent: MaterialIcon { sourceComponent: MaterialIcon {
color: Colors.palette.m3onSurfaceVariant color: Colors.palette.m3onSurfaceVariant
text: root.trailingIcon text: root.trailingIcon
fontStyle: Tokens.font.icon.builders.medium.scale(0.9).build()
} }
} }
} }
@@ -181,7 +176,7 @@ TextFieldBase {
sourceComponent: CustomText { sourceComponent: CustomText {
color: root.isError ? Colors.palette.m3error : Colors.palette.m3onSurfaceVariant color: root.isError ? Colors.palette.m3error : Colors.palette.m3onSurfaceVariant
font: Tokens.font.label.small font.pointSize: Tokens.font.size.small
text: root.effectiveSupportingText text: root.effectiveSupportingText
} }
} }
@@ -219,10 +214,12 @@ TextFieldBase {
} }
} }
Behavior on strokeColor { Behavior on strokeColor {
CAnim {} CAnim {
}
} }
Behavior on strokeWidth { Behavior on strokeWidth {
Anim {} Anim {
}
} }
PathLine { PathLine {
@@ -297,7 +294,8 @@ TextFieldBase {
implicitHeight: root.activeFocus ? 2 : 1 implicitHeight: root.activeFocus ? 2 : 1
Behavior on implicitHeight { Behavior on implicitHeight {
Anim {} Anim {
}
} }
} }
} }
+2 -1
View File
@@ -65,7 +65,8 @@ Image {
value: root.source value: root.source
} }
PropertyAction {} PropertyAction {
}
ScriptAction { ScriptAction {
script: root.maybeStartInAnim() script: root.maybeStartInAnim()
-42
View File
@@ -1,42 +0,0 @@
import QtQuick
import Quickshell
ShaderEffect {
id: root
property Item target: parent
property real glowSize: 40
property real innerGlow: 0
property real lineWidth: 2
property real intensity: 1.0
property real speed: 1.0
property int period: 6000
property bool running: true
property color color1: "#4285F4"
property color color2: "#9B72CB"
property color color3: "#D96570"
property color color4: "#F2A54A"
readonly property size itemSize: Qt.size(width, height)
readonly property size rectSize: target ? Qt.size(target.width, target.height) : Qt.size(0, 0)
readonly property real radius: (target as Rectangle).radius ?? 0
property real phase: 0
anchors.fill: target
anchors.margins: -glowSize
z: -1
enabled: false
fragmentShader: Qt.resolvedUrl(`${Quickshell.shellDir}/assets/shaders/geminiglow.frag.qsb`)
FrameAnimation {
running: root.running && root.visible
onTriggered: {
const dt = frameTime * 1000;
root.phase = (root.phase + dt * root.speed / root.period) % 1.0;
}
}
}
-58
View File
@@ -1,58 +0,0 @@
import QtQuick
import ZShell.Config
import qs.Services
Item {
id: track
required property var scrollBar
required property var mouseArea
ClipRect {
radius: Tokens.rounding.full
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
implicitHeight: handle.implicitHeight
CustomRect {
id: handle
anchors.bottom: parent.bottom
color: Colors.palette.m3secondary
implicitWidth: track.scrollBar.width * track.scrollBar.effectiveSize
implicitHeight: track.mouseArea.pressed || track.mouseArea.containsMouse ? Tokens.padding.extraSmall * 2 : Tokens.padding.extraSmall
opacity: {
if (!track.scrollBar.enabled)
return 0;
if (track.scrollBar.size === 1)
return 0;
if (track.mouseArea.pressed)
return 1;
if (track.mouseArea.containsMouse)
return 0.8;
if (track.scrollBar.policy === CustomScrollBar.AlwaysOn || track.scrollBar.shouldBeActive)
return 0.6;
return 0;
}
radius: Tokens.rounding.full
x: track.scrollBar.reversed ? track.scrollBar.width * (1 + track.scrollBar.nonAnimY) * track.scrollBar.travelScale : track.scrollBar.width * track.scrollBar.nonAnimY * track.scrollBar.travelScale
Behavior on implicitHeight {
Anim {}
}
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
MouseArea {
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
}
}
}
}
+2 -3
View File
@@ -8,7 +8,6 @@ ButtonBase {
property alias icon: label.text property alias icon: label.text
readonly property alias label: label readonly property alias label: label
font: Tokens.font.icon.medium
activeColor: type === IconButton.Filled ? Colors.palette.m3primary : Colors.palette.m3secondary activeColor: type === IconButton.Filled ? Colors.palette.m3primary : Colors.palette.m3secondary
activeOnColor: type === IconButton.Filled ? Colors.palette.m3onPrimary : type === IconButton.Tonal ? Colors.palette.m3onSecondary : Colors.palette.m3primary activeOnColor: type === IconButton.Filled ? Colors.palette.m3onPrimary : type === IconButton.Tonal ? Colors.palette.m3onSecondary : Colors.palette.m3primary
implicitHeight: { implicitHeight: {
@@ -28,7 +27,7 @@ ButtonBase {
return Colors.palette.m3onPrimary; return Colors.palette.m3onPrimary;
return type === IconButton.Tonal ? Colors.palette.m3onSecondaryContainer : Colors.palette.m3onSurfaceVariant; return type === IconButton.Tonal ? Colors.palette.m3onSecondaryContainer : Colors.palette.m3onSurfaceVariant;
} }
padding: type === IconButton.Text ? Tokens.padding.extraSmall / 2 : Tokens.padding.extraSmall padding: type === IconButton.Text ? Tokens.padding.extraSmall / 2 : Tokens.padding.small
MaterialIcon { MaterialIcon {
id: label id: label
@@ -37,7 +36,7 @@ ButtonBase {
anchors.verticalCenterOffset: 1 anchors.verticalCenterOffset: 1
color: root.onColor color: root.onColor
fill: !root.isToggle || root.internalChecked ? 1 : 0 fill: !root.isToggle || root.internalChecked ? 1 : 0
fontStyle: root.font font.pointSize: root.font.pointSize
Behavior on fill { Behavior on fill {
Anim { Anim {
+4 -3
View File
@@ -17,7 +17,7 @@ ButtonBase {
return Colors.palette.m3primary; return Colors.palette.m3primary;
return type === TextButton.Filled ? Colors.palette.m3onPrimary : Colors.palette.m3onSecondary; return type === TextButton.Filled ? Colors.palette.m3onPrimary : Colors.palette.m3onSecondary;
} }
horizontalPadding: Tokens.padding.small horizontalPadding: Tokens.padding.larger
implicitHeight: row.implicitHeight + verticalPadding * 2 implicitHeight: row.implicitHeight + verticalPadding * 2
implicitWidth: row.implicitWidth + horizontalPadding * 2 implicitWidth: row.implicitWidth + horizontalPadding * 2
inactiveColor: { inactiveColor: {
@@ -32,7 +32,7 @@ ButtonBase {
return Colors.palette.m3primary; return Colors.palette.m3primary;
return type === TextButton.Filled ? Colors.palette.m3onSurface : Colors.palette.m3onSecondaryContainer; return type === TextButton.Filled ? Colors.palette.m3onSurface : Colors.palette.m3onSecondaryContainer;
} }
verticalPadding: Tokens.padding.extraSmall verticalPadding: Tokens.padding.small
RowLayout { RowLayout {
id: row id: row
@@ -46,9 +46,10 @@ ButtonBase {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
color: root.onColor color: root.onColor
fill: root.internalChecked ? 1 : 0 fill: root.internalChecked ? 1 : 0
fontStyle: { font: {
const f = Qt.font(root.font); const f = Qt.font(root.font);
f.pointSize = Math.round(root.font.pointSize * 1.2); f.pointSize = Math.round(root.font.pointSize * 1.2);
f.family = "Material Symbols Rounded";
return f; return f;
} }
-470
View File
@@ -1,470 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
Item {
id: root
required property list<var> values
property int xSteps: 24
property real yMin: NaN
property real yMax: NaN
property real yPaddingTop: 0
property real yPaddingBottom: 0
property int verticalLineStep: 0
property int horizontalLineStep: 0
property bool firstXLine: true
property bool lastXLine: false
property int minHorizontalLines: 0
property var xLabel: null
property var yLabel: null
property real smoothing: 0
property bool curveSmoothing: true
property real markerX: -1
property real markerGap: 2 // px gap between line and marker dot edge, each side
property color lineColor: "white"
property color gridColor: "white"
property color markerColor: "white"
property color labelColor: "white"
property real gridAlpha: 0.12
property real labelAlpha: 0.6
property real fillAlpha: 0.15
property real lineWidth: 2
property real markerDotRadius: 4
property real labelFontSize: 10
Canvas {
id: canvas
anchors.fill: parent
renderStrategy: Canvas.Immediate
onWidthChanged: requestPaint()
onHeightChanged: requestPaint()
function strokeCurve(ctx, pts) {
ctx.beginPath();
ctx.moveTo(pts[0].x, pts[0].y);
if (root.curveSmoothing && pts.length > 2) {
const k = 1 / 6;
const n = pts.length;
for (let i = 0; i < n - 1; i++) {
const p0 = pts[Math.max(i - 1, 0)];
const p1 = pts[i];
const p2 = pts[i + 1];
const p3 = pts[Math.min(i + 2, n - 1)];
let c1x = p1.x + (p2.x - p0.x) * k;
let c1y = p1.y + (p2.y - p0.y) * k;
let c2x = p2.x - (p3.x - p1.x) * k;
let c2y = p2.y - (p3.y - p1.y) * k;
const minY = Math.min(p1.y, p2.y);
const maxY = Math.max(p1.y, p2.y);
c1y = Math.max(minY, Math.min(maxY, c1y));
c2y = Math.max(minY, Math.min(maxY, c2y));
ctx.bezierCurveTo(c1x, c1y, c2x, c2y, p2.x, p2.y);
}
} else {
for (let i = 0; i < pts.length - 1; i++) {
const p0 = pts[i];
const p1 = pts[i + 1];
const cx = (p0.x + p1.x) * 0.5;
ctx.bezierCurveTo(cx, p0.y, cx, p1.y, p1.x, p1.y);
}
}
}
// Control points for segment i (pts[i] -> pts[i+1]), matching strokeCurve
function segmentControls(pts, i) {
const p0 = pts[Math.max(i - 1, 0)];
const p1 = pts[i];
const p2 = pts[i + 1];
const p3 = pts[Math.min(i + 2, pts.length - 1)];
if (root.curveSmoothing && pts.length > 2) {
const k = 1 / 6;
let c1x = p1.x + (p2.x - p0.x) * k;
let c1y = p1.y + (p2.y - p0.y) * k;
let c2x = p2.x - (p3.x - p1.x) * k;
let c2y = p2.y - (p3.y - p1.y) * k;
const minY = Math.min(p1.y, p2.y);
const maxY = Math.max(p1.y, p2.y);
c1y = Math.max(minY, Math.min(maxY, c1y));
c2y = Math.max(minY, Math.min(maxY, c2y));
return [c1x, c1y, c2x, c2y];
}
const cx = (p1.x + p2.x) * 0.5;
return [cx, p1.y, cx, p2.y];
}
// Point on the curve at a given x (x is monotonic per segment)
function pointAtX(pts, targetX) {
let j = pts.length - 2;
for (let i = 0; i < pts.length - 1; i++) {
if (targetX <= pts[i + 1].x) {
j = i;
break;
}
}
const p1 = pts[j];
const p2 = pts[j + 1];
const [c1x, c1y, c2x, c2y] = segmentControls(pts, j);
let tlo = 0;
let thi = 1;
for (let it = 0; it < 24; it++) {
const tm = (tlo + thi) / 2;
const u = 1 - tm;
const x = u * u * u * p1.x + 3 * u * u * tm * c1x + 3 * u * tm * tm * c2x + tm * tm * tm * p2.x;
if (x < targetX)
tlo = tm;
else
thi = tm;
}
const t = (tlo + thi) / 2;
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;
return {
x: targetX,
y
};
}
// Part of the curve left/right of a cut x, including the cut point
function cutLeft(pts, targetX) {
if (targetX <= pts[0].x)
return [];
if (targetX >= pts[pts.length - 1].x)
return pts.slice();
let j = pts.length - 2;
for (let i = 0; i < pts.length - 1; i++) {
if (targetX <= pts[i + 1].x) {
j = i;
break;
}
}
const left = pts.slice(0, j + 1);
if (targetX > pts[j].x)
left.push(pointAtX(pts, targetX));
return left;
}
function cutRight(pts, targetX) {
if (targetX >= pts[pts.length - 1].x)
return [];
if (targetX <= pts[0].x)
return pts.slice();
let j = pts.length - 2;
for (let i = 0; i < pts.length - 1; i++) {
if (targetX <= pts[i + 1].x) {
j = i;
break;
}
}
const right = pts.slice(j + 1);
if (targetX < pts[j + 1].x)
right.unshift(pointAtX(pts, targetX));
return right;
}
onPaint: {
const ctx = getContext("2d");
ctx.reset();
const W = width;
const H = height;
if (W < 2 || H < 2)
return;
const padLeft = root.labelFontSize * 3.4;
const padRight = 2;
const padTop = 2;
const padBottom = root.labelFontSize + 8;
const plot = {
x: padLeft,
y: padTop,
w: W - padLeft - padRight,
h: H - padTop - padBottom
};
if (plot.w < 2 || plot.h < 2)
return;
// Auto-fit: NaN yMin/yMax derive from the data, snapped to the
// horizontal step grid, honoring minHorizontalLines
let lo = root.yMin;
let hi = root.yMax;
const loAuto = Number.isNaN(lo);
const hiAuto = Number.isNaN(hi);
if (loAuto || hiAuto) {
let dmin = Infinity;
let dmax = -Infinity;
for (const v of root.values) {
if (v == null)
continue;
if (v < dmin)
dmin = v;
if (v > dmax)
dmax = v;
}
if (dmin === Infinity) {
dmin = 0;
dmax = 1;
}
const step = root.horizontalLineStep;
const minSteps = root.minHorizontalLines > 1 ? root.minHorizontalLines - 1 : 0;
if (step > 0) {
if (hiAuto) {
const baseLo = loAuto ? Math.floor(dmin / step) * step : lo;
const span = Math.max(Math.ceil((dmax - baseLo) / step), minSteps, 1);
lo = baseLo;
hi = baseLo + span * step;
} else {
const span = Math.max(Math.ceil((hi - dmin) / step), minSteps, 1);
lo = hi - span * step;
}
} else {
if (loAuto)
lo = dmin;
if (hiAuto)
hi = dmax;
if (hi <= lo)
hi = lo + 1;
}
}
const scaleBottom = lo - root.yPaddingBottom;
const scaleTop = hi + root.yPaddingTop;
const range = scaleTop - scaleBottom;
if (range <= 0 || root.xSteps < 1)
return;
let series = root.values;
if (root.smoothing > 0 && series.length > 0) {
const radius = Math.round(root.smoothing);
const raw = series;
series = raw.map((v, i) => {
if (v == null)
return null;
let sum = 0;
let n = 0;
for (let j = -radius; j <= radius; j++) {
const u = raw[i + j];
if (u != null) {
sum += u;
n++;
}
}
return n > 0 ? sum / n : null;
});
}
const xAt = i => plot.x + (i / root.xSteps) * plot.w;
const yAt = v => plot.y + (1 - (v - scaleBottom) / range) * plot.h;
const crisp = v => Math.floor(v) + 0.5;
// 1. Grid lines
ctx.strokeStyle = Qt.alpha(root.gridColor, root.gridAlpha);
ctx.lineWidth = 1;
if (root.verticalLineStep > 0) {
for (let i = 0; i <= root.xSteps; i += root.verticalLineStep) {
if (i === 0 && !root.firstXLine)
continue;
if (i === root.xSteps && !root.lastXLine)
continue;
const x = crisp(xAt(i));
ctx.beginPath();
ctx.moveTo(x, plot.y);
ctx.lineTo(x, H);
ctx.stroke();
}
}
// Lines extend down into yPaddingBottom, snapped to the grid
const vStart = lo - (root.yPaddingBottom > 0 ? Math.floor(root.yPaddingBottom / root.horizontalLineStep) * root.horizontalLineStep : 0);
if (root.horizontalLineStep > 0) {
for (let v = vStart; v <= hi + 0.001; v += root.horizontalLineStep) {
const y = crisp(yAt(v));
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(plot.x + plot.w, y);
ctx.stroke();
}
}
// 2. Labels
ctx.fillStyle = Qt.alpha(root.labelColor, root.labelAlpha);
ctx.font = root.labelFontSize + "px sans-serif";
ctx.textAlign = "left";
ctx.textBaseline = "top";
if (root.verticalLineStep > 0 && root.xLabel) {
for (let i = 0; i <= root.xSteps; i += root.verticalLineStep) {
if (i === 0 && !root.firstXLine)
continue;
if (i === root.xSteps && !root.lastXLine)
continue;
const text = root.xLabel(i);
if (text)
ctx.fillText(text, xAt(i) + 4, plot.y + plot.h + 4);
}
}
ctx.textBaseline = "bottom";
if (root.horizontalLineStep > 0 && root.yLabel) {
for (let v = vStart; v <= hi + 0.001; v += root.horizontalLineStep) {
const text = root.yLabel(v);
if (text)
ctx.fillText(text, 2, yAt(v) - 2);
}
}
// 3. Curve, split into segments at null gaps
const segments = [];
let current = null;
for (let i = 0; i < series.length; i++) {
if (series[i] == null) {
if (current)
segments.push(current);
current = null;
continue;
}
const pt = {
x: xAt(i),
y: yAt(series[i])
};
if (!current)
current = [];
current.push(pt);
}
if (current)
segments.push(current);
ctx.lineCap = "round";
ctx.lineJoin = "round";
const hasMarker = root.markerX >= 0 && root.markerX <= root.xSteps;
const markerPx = hasMarker ? xAt(root.markerX) : 0;
let gapPx = 0;
if (hasMarker) {
// markerGap is measured from the dot edge, perpendicular to the
// curve; project onto x via the local curve slope
let slope = 0;
const seg = segments.find(s => s[0].x <= markerPx && markerPx <= s[s.length - 1].x);
if (seg && seg.length > 2 && markerPx - 1 >= seg[0].x && markerPx + 1 <= seg[seg.length - 1].x)
slope = (pointAtX(seg, markerPx + 1).y - pointAtX(seg, markerPx - 1).y) / 2;
gapPx = (root.markerDotRadius + root.markerGap) / Math.sqrt(1 + slope * slope);
}
for (const seg of segments) {
if (seg.length < 2)
continue;
// Fill stays continuous under the marker
ctx.fillStyle = Qt.alpha(root.lineColor, root.fillAlpha);
strokeCurve(ctx, seg);
ctx.lineTo(seg[seg.length - 1].x, plot.y + plot.h);
ctx.lineTo(seg[0].x, plot.y + plot.h);
ctx.closePath();
ctx.fill();
// Stroke has a gap around the marker
const pieces = hasMarker ? [cutLeft(seg, markerPx - gapPx), cutRight(seg, markerPx + gapPx)] : [seg];
for (const pts of pieces) {
if (pts.length < 2)
continue;
ctx.strokeStyle = root.lineColor;
ctx.lineWidth = root.lineWidth;
strokeCurve(ctx, pts);
ctx.stroke();
}
}
// 4. Marker: dashed vertical + dot
if (root.markerX >= 0 && root.markerX <= root.xSteps) {
const x = markerPx;
ctx.setLineDash([4, 4]);
ctx.strokeStyle = Qt.alpha(root.markerColor, 0.6);
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(crisp(x), plot.y);
ctx.lineTo(crisp(x), H);
ctx.stroke();
ctx.setLineDash([]);
const i0 = Math.floor(root.markerX);
const v0 = series[i0];
const v1 = series[i0 + 1];
let y = null;
if (v0 != null && v1 != null)
y = yAt(v0 + (v1 - v0) * (root.markerX - i0));
else if (v0 != null)
y = yAt(v0);
if (y != null) {
ctx.fillStyle = root.markerColor;
ctx.beginPath();
ctx.arc(x, y, root.markerDotRadius, 0, Math.PI * 2);
ctx.fill();
}
}
}
}
onValuesChanged: canvas.requestPaint()
onXStepsChanged: canvas.requestPaint()
onYMinChanged: canvas.requestPaint()
onYMaxChanged: canvas.requestPaint()
onYPaddingTopChanged: canvas.requestPaint()
onYPaddingBottomChanged: canvas.requestPaint()
onVerticalLineStepChanged: canvas.requestPaint()
onHorizontalLineStepChanged: canvas.requestPaint()
onFirstXLineChanged: canvas.requestPaint()
onLastXLineChanged: canvas.requestPaint()
onMinHorizontalLinesChanged: canvas.requestPaint()
onXLabelChanged: canvas.requestPaint()
onYLabelChanged: canvas.requestPaint()
onSmoothingChanged: canvas.requestPaint()
onCurveSmoothingChanged: canvas.requestPaint()
onMarkerXChanged: canvas.requestPaint()
onMarkerGapChanged: canvas.requestPaint()
onLineColorChanged: canvas.requestPaint()
onGridColorChanged: canvas.requestPaint()
onMarkerColorChanged: canvas.requestPaint()
onLabelColorChanged: canvas.requestPaint()
onGridAlphaChanged: canvas.requestPaint()
onLabelAlphaChanged: canvas.requestPaint()
onFillAlphaChanged: canvas.requestPaint()
onLineWidthChanged: canvas.requestPaint()
onMarkerDotRadiusChanged: canvas.requestPaint()
onLabelFontSizeChanged: canvas.requestPaint()
}
+2 -1
View File
@@ -64,7 +64,8 @@ MaterialShape {
to: 360 to: 360
} }
Behavior on color { Behavior on color {
CAnim {} CAnim {
}
} }
ElapsedTimer { ElapsedTimer {
+4 -2
View File
@@ -47,10 +47,12 @@ 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()
+17 -4
View File
@@ -1,14 +1,27 @@
import QtQuick
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
CustomText { CustomText {
id: root id: root
readonly property list<int> allowed: [20, 24, 40, 48]
property real fill: 0 property real fill: 0
property int grade: Colors.light ? 0 : -25 property int grade: Colors.light ? 0 : -25
property font fontStyle: Tokens.font.icon.small readonly property int opsz: clampOpsz(optical)
property int optical: fontInfo.pixelSize
renderType: Text.NativeRendering function clampOpsz(value): int {
font: Tokens.font.icon.size(fontStyle.pointSize).weight(fontStyle.weight).vaxes(fontStyle.variableAxes).fill(fill.toFixed(1)).grade(grade).build() return allowed.reduce((closest, current) => {
return Math.abs(current - value) < Math.abs(closest - value) ? current : closest;
});
}
font.family: "Material Symbols Rounded"
font.pointSize: Tokens.font.size.larger
font.variableAxes: ({
FILL: fill,
GRAD: grade,
opsz: opsz,
wght: fontInfo.weight
})
} }
+22 -21
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: false layer.enabled: opacity < 1
opacity: expanded ? 1 : 0 opacity: expanded ? 1 : 0
parent: { parent: {
const win = QsWindow.window; const win = QsWindow.window;
@@ -78,7 +78,7 @@ MouseArea {
implicitHeight: column.implicitHeight + column.anchors.margins * 2 implicitHeight: column.implicitHeight + column.anchors.margins * 2
implicitWidth: Math.max(200, column.implicitWidth + column.anchors.margins * 2) implicitWidth: Math.max(200, column.implicitWidth + column.anchors.margins * 2)
level: 2 level: 2
radius: Tokens.rounding.large radius: Tokens.rounding.medium
x: { x: {
watcher.transform; watcher.transform;
const item = root.attachTo; const item = root.attachTo;
@@ -101,7 +101,8 @@ MouseArea {
yScale: root.expanded ? 1 : 0.1 yScale: root.expanded ? 1 : 0.1
Behavior on yScale { Behavior on yScale {
Anim {} Anim {
}
} }
} }
@@ -123,7 +124,7 @@ MouseArea {
model: root.items model: root.items
CustomRect { CustomRect {
id: menuItem id: item
readonly property bool active: modelData === root?.active readonly property bool active: modelData === root?.active
required property int index required property int index
@@ -131,22 +132,23 @@ MouseArea {
Layout.fillWidth: true Layout.fillWidth: true
color: Qt.alpha(Colors.palette.m3tertiaryContainer, active ? 1 : 0) color: Qt.alpha(Colors.palette.m3tertiaryContainer, active ? 1 : 0)
implicitHeight: menuOptionRow.implicitHeight + Tokens.padding.medium * 2 implicitHeight: menuOptionRow.implicitHeight + Tokens.padding.larger * 2
implicitWidth: menuOptionRow.implicitWidth + Tokens.padding.medium * 2 implicitWidth: menuOptionRow.implicitWidth + Tokens.padding.larger * 2
radius: menu.radius - column.anchors.margins radius: Tokens.rounding.small
Behavior on radius { Behavior on radius {
Anim {} Anim {
}
} }
StateLayer { StateLayer {
color: menuItem.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurface color: item.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurface
enabled: root.expanded enabled: root.expanded
onClicked: { onClicked: {
root.itemSelected(menuItem.modelData); root.itemSelected(item.modelData);
root.active = menuItem.modelData; root.active = item.modelData;
menuItem.modelData.clicked(); item.modelData.clicked();
root.expanded = false; root.expanded = false;
} }
} }
@@ -155,32 +157,31 @@ MouseArea {
id: menuOptionRow id: menuOptionRow
anchors.fill: parent anchors.fill: parent
anchors.margins: Tokens.padding.medium anchors.margins: Tokens.padding.larger
spacing: Tokens.spacing.small spacing: Tokens.spacing.small
MaterialIcon { MaterialIcon {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
color: menuItem.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurfaceVariant color: item.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurfaceVariant
text: menuItem.modelData?.icon ?? "" text: item.modelData?.icon ?? ""
visible: text !== ""
} }
CustomText { CustomText {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true Layout.fillWidth: true
color: menuItem.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurface color: item.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurface
text: menuItem.modelData?.text ?? "" text: item.modelData?.text ?? ""
} }
Loader { Loader {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
active: menuItem.modelData?.trailingIcon.length > 0 active: item.modelData?.trailingIcon.length > 0
asynchronous: true asynchronous: true
visible: active visible: active
sourceComponent: MaterialIcon { sourceComponent: MaterialIcon {
color: menuItem.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurfaceVariant color: item.active ? Colors.palette.m3onTertiaryContainer : Colors.palette.m3onSurfaceVariant
text: menuItem.modelData.trailingIcon text: item.modelData.trailingIcon
} }
} }
} }
-40
View File
@@ -1,40 +0,0 @@
import QtQuick
import Quickshell
PersistentProperties {
required property ShellScreen modelData
reloadableId: "screenState"
// Drawer visibilities
property bool bar
property bool clipboard
property bool dashboard
property bool dock
property bool isDrawing
property bool launcher
property bool osd
property bool resources
property bool settings
property bool sidebar
// Dashboard state
property int dashboardTab: 0
property date dashboardDate: new Date()
// Sidebar state
property int sidebarTab: 0
property list<string> expandedNotifs: []
// Utilities state
property string recordingConfirmDelete
property bool recordingListExpanded
property string recordingMode
// Drawing state
property color penColor: "white"
property int penWidth: 4
// Screenshot state
property bool screenshot
}
+2 -2
View File
@@ -62,7 +62,7 @@ TextFieldBase {
anchors.leftMargin: Tokens.padding.large anchors.leftMargin: Tokens.padding.large
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: Colors.palette.m3onSurfaceVariant color: Colors.palette.m3onSurfaceVariant
fontStyle: Tokens.font.icon.builders.medium.scale(0.9).build() font.pointSize: Tokens.font.size.large
text: "search" text: "search"
} }
@@ -70,7 +70,7 @@ TextFieldBase {
id: clearIcon id: clearIcon
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: Tokens.padding.small anchors.rightMargin: Tokens.padding.larger
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
enabled: root.text enabled: root.text
icon: "clear" icon: "clear"
+6 -4
View File
@@ -1,6 +1,8 @@
import QtQuick import QtQuick
import QtQuick.Shapes import QtQuick.Shapes
import ZShell import ZShell
import ZShell.Components
import qs.Helpers
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
@@ -51,7 +53,7 @@ MouseArea {
anchors.fill: parent anchors.fill: parent
cursorShape: !enabled ? undefined : Qt.PointingHandCursor cursorShape: !enabled ? undefined : Qt.PointingHandCursor
enabled: parent.enabled && !ShellState.forActive()?.isDrawing enabled: parent.enabled && !Visibilities.getForActive()?.isDrawing
hoverEnabled: true hoverEnabled: true
Behavior on stateOpacity { Behavior on stateOpacity {
@@ -79,7 +81,7 @@ MouseArea {
alwaysRunToEnd: true alwaysRunToEnd: true
duration: Tokens.anim.durations.expressiveSlowEffects * 2 duration: Tokens.anim.durations.expressiveSlowEffects * 2
easing: Tokens.anim.standard easing.bezierCurve: Tokens.anim.curves.standard
property: "circleRadius" property: "circleRadius"
target: root target: root
to: root.endRadius to: root.endRadius
@@ -98,13 +100,13 @@ MouseArea {
id: base id: base
anchors.fill: parent anchors.fill: parent
bottomLeftRadius: root.parent?.bottomLeftRadius ?? radius ?? 0
bottomRightRadius: root.parent?.bottomRightRadius ?? radius ?? 0
color: Colors.palette.m3onSurface color: Colors.palette.m3onSurface
opacity: root.stateOpacity opacity: root.stateOpacity
// Pick up radius from parent if it has one (parent can be anything with radius props) // Pick up radius from parent if it has one (parent can be anything with radius props)
// qmllint disable missing-property // qmllint disable missing-property
radius: root.parent?.radius ?? 0 radius: root.parent?.radius ?? 0
bottomLeftRadius: root.parent?.bottomLeftRadius ?? radius ?? 0
bottomRightRadius: root.parent?.bottomRightRadius ?? radius ?? 0
topLeftRadius: root.parent?.topLeftRadius ?? radius ?? 0 topLeftRadius: root.parent?.topLeftRadius ?? radius ?? 0
topRightRadius: root.parent?.topRightRadius ?? radius ?? 0 topRightRadius: root.parent?.topRightRadius ?? radius ?? 0
// qmllint enable missing-property // qmllint enable missing-property
-86
View File
@@ -1,86 +0,0 @@
import QtQuick
import QtQuick.Templates
import ZShell.Config
import qs.Services
TextArea {
id: root
color: Colors.palette.m3onSurface
cursorVisible: !readOnly
font: Tokens.font.body.small
implicitHeight: contentHeight + topPadding + bottomPadding
implicitWidth: contentWidth + leftPadding + rightPadding
placeholderTextColor: Colors.palette.m3onSurfaceVariant // No anim cause placeholder is custom
renderType: TextArea.NativeRendering
selectedTextColor: color
selectionColor: Qt.alpha(Colors.palette.m3primary, 0.4)
verticalAlignment: TextInput.AlignVCenter
Behavior on color {
CAnim {}
}
Behavior on selectionColor {
CAnim {}
}
cursorDelegate: Item {}
CustomRect {
id: cursor
property bool disableBlink
color: Colors.palette.m3primary
implicitHeight: root.cursorRectangle.height
implicitWidth: 1.5
radius: Tokens.rounding.largeIncreased
x: root.cursorRectangle.x
y: root.cursorRectangle.y
Behavior on x {
Anim {
duration: Tokens.anim.durations.expressiveFastEffects
easing.bezierCurve: [0.2, 1, 0.21, 1, 1, 1]
}
}
Behavior on opacity {
Anim {
type: Anim.StandardSmall
}
}
Connections {
function onCursorPositionChanged(): void {
if (root.activeFocus && root.cursorVisible) {
cursor.opacity = 1;
cursor.disableBlink = true;
enableBlink.restart();
}
}
target: root
}
Timer {
id: enableBlink
interval: 500
onTriggered: cursor.disableBlink = false
}
Timer {
interval: 500
repeat: true
running: root.activeFocus && root.cursorVisible && !cursor.disableBlink
triggeredOnStart: true
onTriggered: parent.opacity = parent.opacity === 1 ? 0 : 1
}
Binding {
cursor.opacity: 0
when: !root.activeFocus || !root.cursorVisible
}
}
}
+2 -2
View File
@@ -14,7 +14,7 @@ ButtonBase {
return Colors.palette.m3primary; return Colors.palette.m3primary;
return type === TextButton.Filled ? Colors.palette.m3onPrimary : Colors.palette.m3onSecondary; return type === TextButton.Filled ? Colors.palette.m3onPrimary : Colors.palette.m3onSecondary;
} }
horizontalPadding: Tokens.padding.small horizontalPadding: Tokens.padding.normal
implicitHeight: label.implicitHeight + verticalPadding * 2 implicitHeight: label.implicitHeight + verticalPadding * 2
implicitWidth: label.implicitWidth + horizontalPadding * 2 implicitWidth: label.implicitWidth + horizontalPadding * 2
inactiveColor: { inactiveColor: {
@@ -29,7 +29,7 @@ ButtonBase {
return Colors.palette.m3primary; return Colors.palette.m3primary;
return type === TextButton.Filled ? Colors.palette.m3onSurface : Colors.palette.m3onSecondaryContainer; return type === TextButton.Filled ? Colors.palette.m3onSurface : Colors.palette.m3onSecondaryContainer;
} }
verticalPadding: Tokens.padding.extraSmall verticalPadding: Tokens.padding.small
CustomText { CustomText {
id: label id: label
-96
View File
@@ -1,96 +0,0 @@
import QtQuick
import QtQuick.Templates
import ZShell.Config
import qs.Services
TextEdit {
id: root
property bool animateCursor: true
property alias cursor: cursor
color: Colors.palette.m3onSurface
cursorVisible: !readOnly
font: Tokens.font.body.small
renderType: TextField.NativeRendering
selectedTextColor: color
selectionColor: Qt.alpha(Colors.palette.m3primary, 0.4)
verticalAlignment: TextInput.AlignVCenter
Behavior on color {
CAnim {}
}
cursorDelegate: Item {}
Behavior on selectionColor {
CAnim {}
}
CustomRect {
id: cursor
property bool disableBlink
color: Colors.palette.m3primary
implicitHeight: root.cursorRectangle.height
implicitWidth: 1.5
radius: Tokens.rounding.largeIncreased
x: root.cursorRectangle.x
y: root.cursorRectangle.y
Behavior on opacity {
Anim {
type: Anim.StandardSmall
}
}
Behavior on x {
enabled: root.animateCursor
Anim {
duration: Tokens.anim.durations.expressiveFastEffects
easing.bezierCurve: [0.2, 1, 0.21, 1, 1, 1]
}
}
Behavior on y {
enabled: root.animateCursor
Anim {
duration: Tokens.anim.durations.expressiveFastEffects
easing.bezierCurve: [0.2, 1, 0.21, 1, 1, 1]
}
}
Connections {
function onCursorPositionChanged(): void {
if (root.activeFocus && root.cursorVisible) {
cursor.opacity = 1;
cursor.disableBlink = true;
enableBlink.restart();
}
}
target: root
}
Timer {
id: enableBlink
interval: 500
onTriggered: cursor.disableBlink = false
}
Timer {
interval: 500
repeat: true
running: root.activeFocus && root.cursorVisible && !cursor.disableBlink
triggeredOnStart: true
onTriggered: parent.opacity = parent.opacity === 1 ? 0 : 1
}
Binding {
cursor.opacity: 0
when: !root.activeFocus || !root.cursorVisible
}
}
}
+9 -6
View File
@@ -8,21 +8,24 @@ TextField {
color: Colors.palette.m3onSurface color: Colors.palette.m3onSurface
cursorVisible: !readOnly cursorVisible: !readOnly
font: Tokens.font.body.small font.pointSize: Tokens.font.size.small
implicitHeight: contentHeight + topPadding + bottomPadding implicitHeight: contentHeight + topPadding + bottomPadding
implicitWidth: contentWidth + leftPadding + rightPadding implicitWidth: contentWidth + leftPadding + rightPadding
placeholderTextColor: Colors.palette.m3onSurfaceVariant // No anim cause placeholder is custom placeholderTextColor: Colors.palette.m3onSurfaceVariant // No anim cause placeholder is custom
renderType: TextField.QtRendering renderType: echoMode === TextField.Password ? TextField.QtRendering : TextField.NativeRendering
selectedTextColor: color selectedTextColor: color
selectionColor: Qt.alpha(Colors.palette.m3primary, 0.4) selectionColor: Qt.alpha(Colors.palette.m3primary, 0.4)
verticalAlignment: TextInput.AlignVCenter verticalAlignment: TextInput.AlignVCenter
Behavior on color { Behavior on color {
CAnim {} CAnim {
}
}
cursorDelegate: Item {
} }
cursorDelegate: Item {}
Behavior on selectionColor { Behavior on selectionColor {
CAnim {} CAnim {
}
} }
CustomRect { CustomRect {
@@ -33,7 +36,7 @@ TextField {
color: Colors.palette.m3primary color: Colors.palette.m3primary
implicitHeight: root.cursorRectangle.height implicitHeight: root.cursorRectangle.height
implicitWidth: 1.5 implicitWidth: 1.5
radius: Tokens.rounding.largeIncreased radius: Tokens.rounding.large
x: root.cursorRectangle.x x: root.cursorRectangle.x
y: root.cursorRectangle.y y: root.cursorRectangle.y
+12 -11
View File
@@ -32,11 +32,12 @@ CustomRect {
return Colors.palette.m3errorContainer; return Colors.palette.m3errorContainer;
return Colors.palette.m3surface; return Colors.palette.m3surface;
} }
implicitHeight: layout.implicitHeight + Tokens.padding.small * 2 implicitHeight: layout.implicitHeight + Tokens.padding.smaller * 2
radius: Tokens.rounding.medium radius: Tokens.rounding.normal
Behavior on border.color { Behavior on border.color {
CAnim {} CAnim {
}
} }
Elevation { Elevation {
@@ -51,10 +52,10 @@ CustomRect {
id: layout id: layout
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: Tokens.padding.small anchors.leftMargin: Tokens.padding.normal
anchors.margins: Tokens.padding.small anchors.margins: Tokens.padding.smaller
anchors.rightMargin: Tokens.padding.small anchors.rightMargin: Tokens.padding.normal
spacing: Tokens.spacing.medium spacing: Tokens.spacing.normal
CustomRect { CustomRect {
color: { color: {
@@ -66,9 +67,9 @@ CustomRect {
return Colors.palette.m3error; return Colors.palette.m3error;
return Colors.palette.m3surfaceContainerHigh; return Colors.palette.m3surfaceContainerHigh;
} }
implicitHeight: icon.implicitHeight + Tokens.padding.small * 2 implicitHeight: icon.implicitHeight + Tokens.padding.smaller * 2
implicitWidth: implicitHeight implicitWidth: implicitHeight
radius: Tokens.rounding.medium radius: Tokens.rounding.normal
MaterialIcon { MaterialIcon {
id: icon id: icon
@@ -83,7 +84,7 @@ CustomRect {
return Colors.palette.m3onError; return Colors.palette.m3onError;
return Colors.palette.m3onSurfaceVariant; return Colors.palette.m3onSurfaceVariant;
} }
fontStyle: Tokens.font.icon.builders.large.scale(1.2).build() font.pointSize: Math.round(Tokens.font.size.large * 1.2)
text: root.modelData.icon text: root.modelData.icon
} }
} }
@@ -106,7 +107,7 @@ CustomRect {
return Colors.palette.m3onSurface; return Colors.palette.m3onSurface;
} }
elide: Text.ElideRight elide: Text.ElideRight
font: Tokens.font.title.small font.pointSize: Tokens.font.size.normal
text: root.modelData.title text: root.modelData.title
} }
+9 -6
View File
@@ -21,7 +21,7 @@ Item {
} }
return h; return h;
} }
implicitWidth: Config.utilities.sizes.toastWidth - Tokens.padding.small * 2 implicitWidth: Config.utilities.sizes.toastWidth - Tokens.padding.normal * 2
Repeater { Repeater {
id: repeater id: repeater
@@ -44,7 +44,8 @@ Item {
onValuesChanged: root.flagChanged() onValuesChanged: root.flagChanged()
} }
ToastWrapper {} ToastWrapper {
}
} }
component ToastWrapper: MouseArea { component ToastWrapper: MouseArea {
@@ -81,14 +82,16 @@ Item {
Behavior on anchors.bottomMargin { Behavior on anchors.bottomMargin {
Anim { Anim {
duration: Tokens.anim.durations.expressiveDefaultSpatial duration: Tokens.anim.durations.expressiveDefaultSpatial
easing: Tokens.anim.expressiveDefaultSpatial easing.bezierCurve: Tokens.anim.curves.expressiveDefaultSpatial
} }
} }
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)
@@ -102,7 +105,7 @@ Item {
id: initAnim id: initAnim
duration: Tokens.anim.durations.expressiveDefaultSpatial duration: Tokens.anim.durations.expressiveDefaultSpatial
easing: Tokens.anim.expressiveDefaultSpatial easing.bezierCurve: Tokens.anim.curves.expressiveDefaultSpatial
from: 0 from: 0
properties: "opacity,scale" properties: "opacity,scale"
target: toast target: toast
-3
View File
@@ -1,7 +1,6 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell
import qs.Effects import qs.Effects
CustomFlickable { CustomFlickable {
@@ -10,7 +9,6 @@ CustomFlickable {
property real bottomFadeOpacity: fadeShouldBeActive(false) ? 0 : 1 property real bottomFadeOpacity: fadeShouldBeActive(false) ? 0 : 1
property real fadeAmount: 0.1 property real fadeAmount: 0.1
property real topFadeOpacity: fadeShouldBeActive(true) ? 0 : 1 property real topFadeOpacity: fadeShouldBeActive(true) ? 0 : 1
readonly property real dpr: Math.ceil((QsWindow.window as QsWindow)?.devicePixelRatio ?? 1)
function fadeShouldBeActive(isStart: bool): bool { function fadeShouldBeActive(isStart: bool): bool {
// When content is smaller than flickable size, hide fade when rebound starts // When content is smaller than flickable size, hide fade when rebound starts
@@ -30,7 +28,6 @@ CustomFlickable {
type: Anim.SlowEffects type: Anim.SlowEffects
} }
} }
layer.textureSize: Qt.size(width * dpr, height * dpr)
layer.effect: Mask { layer.effect: Mask {
maskSource: mask maskSource: mask
+3 -7
View File
@@ -7,9 +7,7 @@ CustomListView {
id: root id: root
property real endFadeOpacity: fadeShouldBeActive(false) ? 0 : 1 property real endFadeOpacity: fadeShouldBeActive(false) ? 0 : 1
property bool fadeEnabled: true
property real fadeAmount: 0.1 property real fadeAmount: 0.1
property real fadeThreshold: 0.0
readonly property bool horizontal: orientation === ListView.Horizontal readonly property bool horizontal: orientation === ListView.Horizontal
property real startFadeOpacity: fadeShouldBeActive(true) ? 0 : 1 property real startFadeOpacity: fadeShouldBeActive(true) ? 0 : 1
@@ -18,9 +16,7 @@ CustomListView {
} }
function fadeShouldBeActive(isStart: bool): bool { function fadeShouldBeActive(isStart: bool): bool {
if (!fadeEnabled) // When content is smaller than flickable size, hide fade when rebound starts.
return false;
if (contentSize() + marginStart() + marginEnd() < viewportSize() && rebound.running && ((isStart ? overshootStart() > 0 : overshootStart() < 0))) { if (contentSize() + marginStart() + marginEnd() < viewportSize() && rebound.running && ((isStart ? overshootStart() > 0 : overshootStart() < 0))) {
return false; return false;
} }
@@ -32,11 +28,11 @@ CustomListView {
} }
function marginEnd(): real { function marginEnd(): real {
return horizontal ? rightMargin - fadeThreshold : bottomMargin - fadeThreshold; return horizontal ? rightMargin : bottomMargin;
} }
function marginStart(): real { function marginStart(): real {
return horizontal ? leftMargin - fadeThreshold : topMargin - fadeThreshold; return horizontal ? leftMargin : topMargin;
} }
function overshootStart(): real { function overshootStart(): real {
-58
View File
@@ -1,58 +0,0 @@
import QtQuick
import ZShell.Config
import qs.Services
Item {
id: track
required property var scrollBar
required property var mouseArea
ClipRect {
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.top: parent.top
radius: Tokens.rounding.full
implicitWidth: handle.implicitWidth
CustomRect {
id: handle
anchors.right: parent.right
color: Colors.palette.m3secondary
implicitHeight: track.scrollBar.height * track.scrollBar.effectiveSize
implicitWidth: track.mouseArea.pressed || track.mouseArea.containsMouse ? Tokens.padding.extraSmall * 2 : Tokens.padding.extraSmall
opacity: {
if (!track.scrollBar.enabled)
return 0;
if (track.scrollBar.size === 1)
return 0;
if (track.mouseArea.pressed)
return 1;
if (track.mouseArea.containsMouse)
return 0.8;
if (track.scrollBar.policy === CustomScrollBar.AlwaysOn || track.scrollBar.shouldBeActive)
return 0.6;
return 0;
}
radius: Tokens.rounding.full
y: track.scrollBar.reversed ? track.scrollBar.height * (1 + track.scrollBar.nonAnimY) * track.scrollBar.travelScale : track.scrollBar.height * track.scrollBar.nonAnimY * track.scrollBar.travelScale
Behavior on implicitWidth {
Anim {}
}
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
MouseArea {
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
}
}
}
}
+2 -1
View File
@@ -20,7 +20,8 @@ Shape {
strokeWidth: 0 strokeWidth: 0
Behavior on fillColor { Behavior on fillColor {
CAnim {} CAnim {
}
} }
PathSvg { PathSvg {
+1
View File
@@ -141,6 +141,7 @@ Singleton {
BeatTracker { BeatTracker {
id: beatTracker id: beatTracker
} }
PwObjectTracker { PwObjectTracker {
+2 -1
View File
@@ -295,7 +295,8 @@ Singleton {
Component { Component {
id: apComp id: apComp
AccessPoint {} AccessPoint {
}
} }
Process { Process {
+5 -2
View File
@@ -1081,13 +1081,15 @@ Singleton {
Component { Component {
id: commandProc id: commandProc
CommandProcess {} CommandProcess {
}
} }
Component { Component {
id: apComp id: apComp
AccessPoint {} AccessPoint {
}
} }
Timer { Timer {
@@ -1308,6 +1310,7 @@ Singleton {
} }
stdout: StdioCollector { stdout: StdioCollector {
id: stdoutCollector id: stdoutCollector
} }
onExited: code => { onExited: code => {
+3 -3
View File
@@ -10,7 +10,6 @@ import ZShell
import qs.Components import qs.Components
import qs.Modules import qs.Modules
import qs.Helpers import qs.Helpers
import qs.Services
import qs.Paths import qs.Paths
import ZShell.Config import ZShell.Config
@@ -26,7 +25,7 @@ Singleton {
property alias server: server property alias server: server
function shouldThrottle(appName: string): bool { function shouldThrottle(appName: string): bool {
if (props.dnd || ShellState.anySidebarOpen()) if (props.dnd || [...Visibilities.screens.values()].some(v => v.sidebar))
return false; return false;
const key = (appName || "unknown").trim().toLowerCase(); const key = (appName || "unknown").trim().toLowerCase();
@@ -164,7 +163,8 @@ Singleton {
Component { Component {
id: notifComp id: notifComp
Notif {} Notif {
}
} }
component Notif: QtObject { component Notif: QtObject {
+10 -5
View File
@@ -3,23 +3,28 @@ pragma ComponentBehavior: Bound
import Quickshell import Quickshell
import QtQuick import QtQuick
import ZShell.Internal import ZShell.Internal
import qs.Components
Item { Item {
id: root id: root
readonly property alias content: contentLoader.item readonly property alias content: contentLoader.item
required property ScreenState screenState readonly property PersistentProperties drawingState: PersistentProperties {
property color penColor: "white"
property int penWidth: 4
reloadableId: "drawingState"
}
required property PersistentProperties visibilities
Loader { Loader {
id: contentLoader id: contentLoader
active: root.screenState.isDrawing active: root.visibilities.isDrawing
anchors.fill: parent anchors.fill: parent
sourceComponent: StrokeCanvas { sourceComponent: StrokeCanvas {
penColor: root.screenState.penColor penColor: root.drawingState.penColor
penWidth: root.screenState.penWidth penWidth: root.drawingState.penWidth
} }
} }
} }
+9 -1
View File
@@ -43,6 +43,13 @@ 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
@@ -52,6 +59,7 @@ Scope {
name: "Bar-Exclusion" name: "Bar-Exclusion"
screen: root.screen screen: root.screen
mask: Region {} mask: Region {
}
} }
} }
+52 -76
View File
@@ -1,7 +1,7 @@
import Quickshell import Quickshell
import QtQuick import QtQuick
import ZShell.Config
import qs.Components import qs.Components
import ZShell.Config
import qs.Helpers import qs.Helpers
import qs.Modules.Bar.Popouts import qs.Modules.Bar.Popouts
@@ -19,7 +19,7 @@ Item {
required property ShellScreen screen required property ShellScreen screen
property bool singleGestureTriggered: false property bool singleGestureTriggered: false
property bool utilitiesShortcutActive property bool utilitiesShortcutActive
required property ScreenState screenState required property PersistentProperties visibilities
function inBottomPanel(panel: Item, x: real, y: real): bool { function inBottomPanel(panel: Item, x: real, y: real): bool {
const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0)); const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0));
@@ -63,7 +63,7 @@ Item {
cursorShape: (active && centroid.pressPosition.y < root.bar.implicitHeight) ? Qt.ClosedHandCursor : undefined cursorShape: (active && centroid.pressPosition.y < root.bar.implicitHeight) ? Qt.ClosedHandCursor : undefined
dragThreshold: 0 dragThreshold: 0
enabled: !root.screenState.isDrawing enabled: !root.visibilities.isDrawing
grabPermissions: PointerHandler.CanTakeOverFromHandlersOfSameType | PointerHandler.ApprovesTakeOverByAnything grabPermissions: PointerHandler.CanTakeOverFromHandlersOfSameType | PointerHandler.ApprovesTakeOverByAnything
maximumPointCount: 1 maximumPointCount: 1
minimumPointCount: 1 minimumPointCount: 1
@@ -76,59 +76,45 @@ Item {
onCentroidChanged: { onCentroidChanged: {
const x = centroid.position.x; const x = centroid.position.x;
const y = centroid.position.y; const y = centroid.position.y;
const pressX = centroid.pressPosition.x; const dragX = x - centroid.pressPosition.x;
const pressY = centroid.pressPosition.y; const dragY = y - centroid.pressPosition.y;
const dragX = x - pressX;
const dragY = y - pressY;
if (pressY >= root.screen.height - Config.bar.border && pressX > root.screen.width / 5 && dragY < -Config.launcher.dragThreshold) if (centroid.pressPosition.y >= root.screen.height - Config.bar.border && centroid.pressPosition.x > root.screen.width / 5 && dragY < -Config.launcher.dragThreshold)
root.screenState.launcher = true; root.visibilities.launcher = true;
if (root.singleGestureTriggered) if (root.singleGestureTriggered)
return; return;
const inDashboard = root.inTopPanel(root.panels.dashboardWrapper, pressX, pressY); if (root.geometry.insetTop(root.borderThickness) > centroid.pressPosition.y) {
if (inDashboard && root.withinPanelWidth(root.panels.dashboardWrapper, x, y)) {
if (dragY > Config.dashboard.dragThreshold) {
root.screenState.dashboard = true;
root.singleGestureTriggered = true;
} else if (dragY < -Config.dashboard.dragThreshold) {
root.screenState.dashboard = false;
root.singleGestureTriggered = true;
}
}
if (root.geometry.insetTop(root.borderThickness) > pressY && (Config.dashboard.showOnHover || !inDashboard)) {
if (dragY > 20) { if (dragY > 20) {
root.screenState.settings = true; root.visibilities.settings = true;
root.singleGestureTriggered = true; root.singleGestureTriggered = true;
} else if (dragY < -20) { } else if (dragY < -20) {
root.screenState.settings = false; root.visibilities.settings = false;
root.singleGestureTriggered = true; root.singleGestureTriggered = true;
} }
} }
if (pressY > root.screen.height - root.geometry.insetBottom(root.borderThickness) && pressX < root.screen.width / 5 && dragY < -50) if (centroid.pressPosition.y > root.screen.height - root.geometry.insetBottom(root.borderThickness) && centroid.pressPosition.x < root.screen.width / 5 && dragY < -50)
root.screenState.clipboard = true; root.visibilities.clipboard = true;
if (!Config.dock.hoverToReveal && pressY > root.screen.height - root.geometry.insetTop(root.borderThickness) && pressX > root.screen.width / 5 && !root.screenState.launcher) if (!Config.dock.hoverToReveal && centroid.pressPosition.y > root.screen.height - root.geometry.insetTop(root.borderThickness) && centroid.pressPosition.x > root.screen.width / 5 && !root.visibilities.launcher)
if (dragY < -10) { if (dragY < -10) {
root.screenState.dock = true; root.visibilities.dock = true;
root.singleGestureTriggered = true; root.singleGestureTriggered = true;
} }
if (pressX > root.screen.width - Config.bar.border && pressY < (root.screen.height / 2) && dragX < -Config.sidebar.dragThreshold) { if (centroid.pressPosition.x > root.screen.width - Config.bar.border && centroid.pressPosition.y < (root.screen.height / 2) && dragX < -Config.sidebar.dragThreshold) {
root.screenState.sidebar = true; root.visibilities.sidebar = true;
root.singleGestureTriggered = true; root.singleGestureTriggered = true;
} }
if (pressX >= root.screen.width - Config.bar.border && pressY > (root.screen.height / 2) && dragX < -20) { if (centroid.pressPosition.x >= root.screen.width - Config.bar.border && centroid.pressPosition.y > (root.screen.height / 2) && dragX < -20) {
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r+1', on_current_monitor = true })`); Hypr.dispatch(`hl.dsp.focus({ workspace = 'r+1', on_current_monitor = true })`);
root.singleGestureTriggered = true; root.singleGestureTriggered = true;
} }
if (pressX <= Config.bar.border && dragX > 20) { if (centroid.pressPosition.x <= Config.bar.border && dragX > 20) {
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r-1', on_current_monitor = true })`); Hypr.dispatch(`hl.dsp.focus({ workspace = 'r-1', on_current_monitor = true })`);
root.singleGestureTriggered = true; root.singleGestureTriggered = true;
} }
@@ -141,7 +127,7 @@ Item {
property bool setInitialPoint: false property bool setInitialPoint: false
acceptedButtons: Qt.LeftButton | Qt.RightButton acceptedButtons: Qt.LeftButton | Qt.RightButton
enabled: root.screenState.isDrawing && (!root.inLeftPanel(root.panels.drawing, hoverHandler.point.position.x, hoverHandler.point.position.y) || !root.panels.drawing.expanded) enabled: root.visibilities.isDrawing && (!root.inLeftPanel(root.panels.drawing, hoverHandler.point.position.x, hoverHandler.point.position.y) || !root.panels.drawing.expanded)
onActiveChanged: { onActiveChanged: {
if (!active) { if (!active) {
@@ -192,12 +178,12 @@ Item {
HoverHandler { HoverHandler {
id: hoverHandler id: hoverHandler
cursorShape: root.screenState.isDrawing && !root.inLeftPanel(root.panels.drawing, point.position.x, point.position.y) ? Qt.BlankCursor : undefined cursorShape: root.visibilities.isDrawing && !root.inLeftPanel(root.panels.drawing, point.position.x, point.position.y) ? Qt.BlankCursor : undefined
onHoveredChanged: { onHoveredChanged: {
if (!hovered) { if (!hovered) {
if (!root.osdShortcutActive) { if (!root.osdShortcutActive) {
root.screenState.osd = false; root.visibilities.osd = false;
root.panels.osd.hovered = false; root.panels.osd.hovered = false;
} }
@@ -207,16 +193,13 @@ Item {
if (Config.bar.autoHide || root.bar.fullscreen) if (Config.bar.autoHide || root.bar.fullscreen)
root.bar.isHovered = false; root.bar.isHovered = false;
if (root.screenState.dashboard && Config.dashboard.showOnHover)
root.screenState.dashboard = false;
} }
} }
onPointChanged: { onPointChanged: {
const x = point.position.x; const x = point.position.x;
const y = point.position.y; const y = point.position.y;
if (root.screenState.isDrawing) { if (root.visibilities.isDrawing) {
if (root.inLeftPanel(root.panels.drawing, x, y) && !(drawingHandler.point.pressedButtons & Qt.LeftButton)) { if (root.inLeftPanel(root.panels.drawing, x, y) && !(drawingHandler.point.pressedButtons & Qt.LeftButton)) {
root.panels.drawing.expand(); root.panels.drawing.expand();
root.drawing.content.hideHover(); root.drawing.content.hideHover();
@@ -227,7 +210,7 @@ Item {
return; return;
} }
if (!root.screenState.bar && Config.bar.autoHide && root.geometry.barContains(x, y, true)) if (!root.visibilities.bar && Config.bar.autoHide && root.geometry.barContains(x, y, true))
root.bar.isHovered = true; root.bar.isHovered = true;
if (root.bar.fullscreen && !root.bar.isHovered) if (root.bar.fullscreen && !root.bar.isHovered)
@@ -245,7 +228,7 @@ Item {
const showOsd = outOfSidebar && root.inRightPanel(root.panels.osdWrapper, x, y); const showOsd = outOfSidebar && root.inRightPanel(root.panels.osdWrapper, x, y);
if (!root.osdShortcutActive) { if (!root.osdShortcutActive) {
root.screenState.osd = showOsd; root.visibilities.osd = showOsd;
root.panels.osd.hovered = showOsd; root.panels.osd.hovered = showOsd;
} else if (showOsd) { } else if (showOsd) {
root.osdShortcutActive = false; root.osdShortcutActive = false;
@@ -253,13 +236,8 @@ Item {
} }
} }
if (Config.dock.enable && Config.dock.hoverToReveal && !root.screenState.dock && !root.screenState.launcher && root.inBottomPanel(root.panels.dock, x, y)) if (Config.dock.enabled && Config.dock.hoverToReveal && !root.visibilities.dock && !root.visibilities.launcher && root.inBottomPanel(root.panels.dock, x, y))
root.screenState.dock = true; root.visibilities.dock = true;
const inDashboard = root.inTopPanel(root.panels.dashboardWrapper, x, y);
if (Config.dashboard.showOnHover)
root.screenState.dashboard = inDashboard;
if (root.geometry.barContains(x, y)) if (root.geometry.barContains(x, y))
root.bar.checkPopout(root.geometry.axisPos(x, y)); root.bar.checkPopout(root.geometry.axisPos(x, y));
@@ -268,17 +246,16 @@ Item {
Connections { Connections {
function onDashboardChanged() { function onDashboardChanged() {
if (root.screenState.dashboard) { if (root.visibilities.dashboard) {
const inDashboardArea = root.inTopPanel(root.panels.dashboard, root.mouseX, root.mouseY); const inDashboardArea = root.inTopPanel(root.panels.dashboard, root.mouseX, root.mouseY);
if (!inDashboardArea) { if (!inDashboardArea) {
root.dashboardShortcutActive = true; root.dashboardShortcutActive = true;
} }
if (root.panels.launcher.x + root.panels.launcher.width > root.panels.dashboardWrapper.x) if (root.panels.launcher.x + root.panels.launcher.width > root.panels.dashboardWrapper.x)
root.screenState.launcher = false; root.visibilities.launcher = false;
root.screenState.settings = false; root.visibilities.settings = false;
root.screenState.sidebar = false; root.visibilities.sidebar = false;
root.screenState.resources = false;
root.popouts.hasCurrent = false; root.popouts.hasCurrent = false;
} else { } else {
root.dashboardShortcutActive = false; root.dashboardShortcutActive = false;
@@ -286,12 +263,12 @@ Item {
} }
function onIsDrawingChanged() { function onIsDrawingChanged() {
if (!root.screenState.isDrawing) if (!root.visibilities.isDrawing)
root.drawing.clear(); root.drawing.clear();
} }
function onLauncherChanged() { function onLauncherChanged() {
if (!root.screenState.launcher) { if (!root.visibilities.launcher) {
root.dashboardShortcutActive = false; root.dashboardShortcutActive = false;
root.osdShortcutActive = false; root.osdShortcutActive = false;
root.utilitiesShortcutActive = false; root.utilitiesShortcutActive = false;
@@ -299,23 +276,23 @@ Item {
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY); const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
if (!inOsdArea) { if (!inOsdArea) {
root.screenState.osd = false; root.visibilities.osd = false;
root.panels.osd.hovered = false; root.panels.osd.hovered = false;
} }
} }
if (root.screenState.launcher) { if (root.visibilities.launcher) {
if (root.panels.dashboardWrapper.x < root.panels.launcher.x + root.panels.launcher.width) { if (root.panels.dashboardWrapper.x < root.panels.launcher.x + root.panels.launcher.width) {
root.screenState.dashboard = false; root.visibilities.dashboard = false;
} }
root.screenState.dock = false; root.visibilities.dock = false;
root.screenState.settings = false; root.visibilities.settings = false;
} }
} }
function onOsdChanged() { function onOsdChanged() {
if (root.screenState.osd) { if (root.visibilities.osd) {
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY); const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
if (!inOsdArea) { if (!inOsdArea) {
root.osdShortcutActive = true; root.osdShortcutActive = true;
@@ -326,33 +303,32 @@ Item {
} }
function onResourcesChanged() { function onResourcesChanged() {
if (root.screenState.resources) { if (root.visibilities.resources && (root.popouts.currentName.startsWith("audio") || root.popouts.currentName.startsWith("updates"))) {
if (root.popouts.currentName.startsWith("audio") || root.popouts.currentName.startsWith("updates")) root.popouts.hasCurrent = false;
root.popouts.hasCurrent = false;
root.screenState.settings = false;
root.screenState.dashboard = false;
} }
if (root.visibilities.resources)
root.visibilities.settings = false;
} }
function onSettingsChanged() { function onSettingsChanged() {
if (root.screenState.settings) { if (root.visibilities.settings) {
root.screenState.resources = false; root.visibilities.resources = false;
root.screenState.dashboard = false; root.visibilities.dashboard = false;
root.screenState.sidebar = false; root.visibilities.sidebar = false;
root.panels.popouts.hasCurrent = false; root.panels.popouts.hasCurrent = false;
root.screenState.launcher = false; root.visibilities.launcher = false;
} }
} }
function onSidebarChanged() { function onSidebarChanged() {
if (root.screenState.sidebar) { if (root.visibilities.sidebar) {
root.screenState.dashboard = false; root.visibilities.dashboard = false;
root.screenState.settings = false; root.visibilities.settings = false;
root.popouts.hasCurrent = false; root.popouts.hasCurrent = false;
} }
} }
target: root.screenState target: root.visibilities
} }
} }
-315
View File
@@ -1,315 +0,0 @@
import Quickshell
import QtQuick
import ZShell.Config
import ZShell.Internal
import qs.Components
import qs.Helpers
import qs.Modules.Bar.Popouts
CustomMouseArea {
id: root
required property Item bar
required property real borderThickness
property bool dashboardShortcutActive
property point dragStart
required property Drawing drawing
required property EdgeGeometry geometry
property bool osdShortcutActive
required property Panels panels
required property Wrapper popouts
required property ShellScreen screen
required property ScreenState screenState
property bool setInitialPoint: false
property bool singleGestureTriggered: false
property bool utilitiesShortcutActive
function inBottomPanel(panel: Item, x: real, y: real): bool {
const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0));
return y > root.height - (panelHeight + geometry.insetBottom(borderThickness)) && withinPanelWidth(panel, x, y);
}
function inLeftPanel(panel: Item, x: real, y: real): bool {
return x < panel.x + panel.width + geometry.insetLeft(borderThickness) && withinPanelHeight(panel, x, y);
}
function inPopoutArea(x: real, y: real): bool {
const panel = panels.popoutsWrapper;
if (!geometry.horizontal)
return inLeftPanel(panel, x, y);
return withinPanelWidth(panel, x, y) && withinPanelHeight(panel, x, y);
}
function inRightPanel(panel: Item, x: real, y: real): bool {
return x > panel.x + geometry.insetLeft(borderThickness) && withinPanelHeight(panel, x, y);
}
function inTopPanel(panel: Item, x: real, y: real): bool {
const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0));
return y < geometry.insetTop(borderThickness) + panelHeight && withinPanelWidth(panel, x, y);
}
function withinPanelHeight(panel: Item, x: real, y: real): bool {
const panelY = panel.y + geometry.insetTop(borderThickness);
return y >= panelY && y <= panelY + panel.height;
}
function withinPanelWidth(panel: Item, x: real, y: real): bool {
const panelX = panel.x + geometry.insetLeft(borderThickness);
return x >= panelX && x <= panelX + panel.width;
}
acceptedButtons: bar.fullscreen ? Qt.NoButton : Qt.AllButtons
anchors.fill: parent
hoverEnabled: true
onContainsMouseChanged: {
if (!containsMouse) {
if (!osdShortcutActive) {
screenState.osd = false;
panels.osd.hovered = false;
}
if (!popouts.currentName.startsWith("traymenu") || Config.bar.tray.showOnHover) {
popouts.hasCurrent = false;
}
if (Config.bar.autoHide || bar.fullscreen)
bar.isHovered = false;
if (screenState.dashboard && Config.dashboard.showOnHover)
screenState.dashboard = false;
}
}
onPositionChanged: event => {
const x = event.x;
const y = event.y;
const dragX = x - dragStart.x;
const dragY = y - dragStart.y;
if (screenState.isDrawing) {
if (pressed) {
drawing.content.appendPoint(x, y);
} else {
if (inLeftPanel(panels.drawing, x, y)) {
if (!(pressedButtons & Qt.LeftButton)) {
panels.drawing.expand();
drawing.content.hideHover();
Cursor.clearOverride();
}
} else {
Cursor.setOverride(Qt.BlankCursor);
drawing.content.showHover(x, y);
}
}
if (drawing.content.hoverVisible)
drawing.content.showHover(x, y);
return;
}
if (root.bar.fullscreen && !root.bar.isHovered)
unhideTimer.start();
if (pressed && dragStart.y >= screen.height - Config.bar.border && dragStart.x > screen.width / 5 && dragY < -Config.launcher.dragThreshold)
screenState.launcher = true;
const entryAt = bar.entryAt(geometry.axisPos(x, y));
const showDashboard = Config.dashboard.showOnHover && inTopPanel(panels.dashboardWrapper, x, y) && (!entryAt || entryAt === "activeWindow");
if (!dashboardShortcutActive) {
screenState.dashboard = showDashboard;
} else if (showDashboard) {
dashboardShortcutActive = false;
}
if (pressed && geometry.insetTop(borderThickness) > dragStart.y && withinPanelWidth(panels.dashboardWrapper, x, y)) {
if (dragY > Config.dashboard.dragThreshold)
screenState.dashboard = true;
else if (dragY < -Config.dashboard.dragThreshold)
screenState.dashboard = false;
}
if (pressed && geometry.insetTop(borderThickness) > dragStart.y && !inTopPanel(panels.dashboardWrapper, dragStart.x, dragStart.y)) {
if (dragY > 20) {
screenState.settings = true;
} else if (dragY < -20) {
screenState.settings = false;
}
}
if (pressed && dragStart.y > screen.height - geometry.insetBottom(borderThickness) && dragStart.x < screen.width / 5 && dragY < -50)
screenState.clipboard = true;
if (pressed && !Config.dock.hoverToReveal && dragStart.y > screen.height - geometry.insetTop(borderThickness) && dragStart.x > screen.width / 5 && !screenState.launcher)
if (dragY < -10)
screenState.dock = true;
if (pressed && dragStart.x > screen.width - Config.bar.border && dragStart.y < (screen.height / 2) && dragX < -Config.sidebar.dragThreshold)
screenState.sidebar = true;
if (pressed && dragStart.x >= screen.width - Config.bar.border && dragStart.y > (screen.height / 2) && dragX < -20)
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r+1', on_current_monitor = true })`);
if (pressed && dragStart.x <= Config.bar.border && dragX > 20)
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r-1', on_current_monitor = true })`);
if (!screenState.bar && Config.bar.autoHide && geometry.barContains(x, y, true))
bar.isHovered = true;
if (panels.sidebar.offsetScale === 1) {
const showOsd = inRightPanel(panels.osdWrapper, x, y);
if (showOsd) {
osdShortcutActive = false;
panels.osd.hovered = true;
}
} else {
const outOfSidebar = x < width - panels.sidebar.width * (1 - panels.sidebar.offsetScale);
const showOsd = outOfSidebar && inRightPanel(panels.osdWrapper, x, y);
if (!osdShortcutActive) {
screenState.osd = showOsd;
panels.osd.hovered = showOsd;
} else if (showOsd) {
osdShortcutActive = false;
panels.osd.hovered = true;
}
}
if (Config.dock.enable && Config.dock.hoverToReveal && !screenState.dock && !screenState.launcher && inBottomPanel(panels.dock, x, y))
screenState.dock = true;
if (geometry.barContains(x, y))
bar.checkPopout(geometry.axisPos(x, y));
}
onPressed: event => {
dragStart = Qt.point(event.x, event.y);
if (screenState.isDrawing) {
if (event.button & Qt.RightButton) {
drawing.content.clear();
return;
}
if (!inLeftPanel(panels.drawing, mouseX, mouseY))
drawing.content.beginStroke(event.x, event.y);
}
}
onPressedChanged: {
if (!pressed) {
if (screenState.isDrawing) {
setInitialPoint = false;
drawing.content.endStroke();
if (!inLeftPanel(panels.drawing, mouseX, mouseY))
drawing.content.showHover(mouseX, mouseY);
}
} else {
if (screenState.isDrawing) {
panels.drawing.collapse();
}
}
}
Timer {
id: unhideTimer
interval: Config.bar.revealDelay
repeat: false
running: false
onTriggered: {
if (root.containsMouse)
root.bar.isHovered = true;
}
}
Connections {
function onDashboardChanged(): void {
if (root.screenState.dashboard) {
const inDashboardArea = root.inTopPanel(root.panels.dashboardWrapper, root.mouseX, root.mouseY);
if (!inDashboardArea) {
root.dashboardShortcutActive = true;
}
root.screenState.launcher = false;
root.screenState.settings = false;
root.screenState.sidebar = false;
root.screenState.resources = false;
root.popouts.hasCurrent = false;
} else {
root.dashboardShortcutActive = false;
}
}
function onIsDrawingChanged(): void {
if (root.screenState.isDrawing) {
Cursor.setOverride(Qt.BlankCursor);
} else {
Cursor.clearOverride();
}
}
function onLauncherChanged(): void {
if (!root.screenState.launcher) {
root.dashboardShortcutActive = false;
root.osdShortcutActive = false;
root.utilitiesShortcutActive = false;
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
if (!inOsdArea) {
root.screenState.osd = false;
root.panels.osd.hovered = false;
}
}
if (root.screenState.launcher) {
root.screenState.dashboard = false;
root.screenState.dock = false;
root.screenState.settings = false;
}
}
function onOsdChanged(): void {
if (root.screenState.osd) {
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
if (!inOsdArea) {
root.osdShortcutActive = true;
}
} else {
root.osdShortcutActive = false;
}
}
function onResourcesChanged(): void {
if (root.screenState.resources) {
if (root.popouts.currentName.startsWith("audio") || root.popouts.currentName.startsWith("updates"))
root.popouts.hasCurrent = false;
root.screenState.settings = false;
root.screenState.dashboard = false;
}
}
function onSettingsChanged(): void {
if (root.screenState.settings) {
root.screenState.resources = false;
root.screenState.dashboard = false;
root.screenState.sidebar = false;
root.panels.popouts.hasCurrent = false;
root.screenState.launcher = false;
}
}
function onSidebarChanged(): void {
if (root.screenState.sidebar) {
if (root.panels.dashboardWrapper.x + root.panels.dashboardWrapper.width > root.panels.sidebar.x)
root.screenState.dashboard = false;
root.screenState.settings = false;
root.popouts.hasCurrent = false;
}
}
target: root.screenState
}
}
+30 -27
View File
@@ -3,8 +3,8 @@ import QtQuick
import qs.Components import qs.Components
import qs.Modules.Bar.Popouts as Popouts import qs.Modules.Bar.Popouts as Popouts
import qs.Modules.Notifications as Notifications import qs.Modules.Notifications as Notifications
import qs.Modules.Sidebar as Sidebar import qs.Modules.Notifications.Sidebar as Sidebar
import qs.Modules.Sidebar.Utils as Utils import qs.Modules.Notifications.Sidebar.Utils as Utils
import qs.Modules.Dashboard as Dashboard import qs.Modules.Dashboard as Dashboard
import qs.Modules.Osd as Osd import qs.Modules.Osd as Osd
import qs.Components.Toast as Toasts import qs.Components.Toast as Toasts
@@ -19,11 +19,15 @@ import ZShell.Config
Item { Item {
id: root id: root
required property Item bar
required property real borderThickness
readonly property alias clipboard: clipboard readonly property alias clipboard: clipboard
readonly property alias dashboard: dashboard readonly property alias dashboard: dashboard
readonly property alias dashboardWrapper: dashboardWrapper readonly property alias dashboardWrapper: dashboardWrapper
readonly property alias dock: dock readonly property alias dock: dock
readonly property alias drawing: drawing readonly property alias drawing: drawing
required property var drawingItem
required property EdgeGeometry geometry
readonly property alias launcher: launcher readonly property alias launcher: launcher
readonly property alias notifications: notifications readonly property alias notifications: notifications
readonly property alias osd: osd readonly property alias osd: osd
@@ -32,18 +36,13 @@ Item {
readonly property alias popoutsWrapper: popouts readonly property alias popoutsWrapper: popouts
readonly property alias resources: resources readonly property alias resources: resources
readonly property alias resourcesWrapper: resourcesWrapper readonly property alias resourcesWrapper: resourcesWrapper
required property ShellScreen screen
readonly property alias settings: settings readonly property alias settings: settings
readonly property alias settingsWrapper: settingsWrapper readonly property alias settingsWrapper: settingsWrapper
readonly property alias sidebar: sidebar readonly property alias sidebar: sidebar
readonly property alias toasts: toasts readonly property alias toasts: toasts
readonly property alias utilities: utilities readonly property alias utilities: utilities
required property PersistentProperties visibilities
required property Item bar
required property real borderThickness
required property var drawingItem
required property EdgeGeometry geometry
required property ShellScreen screen
required property ScreenState screenState
anchors.bottomMargin: geometry.insetBottom(borderThickness) anchors.bottomMargin: geometry.insetBottom(borderThickness)
anchors.fill: parent anchors.fill: parent
@@ -65,7 +64,7 @@ Item {
id: resources id: resources
position: root.geometry.position position: root.geometry.position
screenState: root.screenState visibilities: root.visibilities
} }
} }
@@ -85,8 +84,8 @@ Item {
anchors.right: parent.right anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
screen: root.screen screen: root.screen
screenState: root.screenState
sidebarOrSessionVisible: sidebar.visible sidebarOrSessionVisible: sidebar.visible
visibilities: root.visibilities
} }
} }
@@ -97,7 +96,7 @@ Item {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
drawing: root.drawingItem drawing: root.drawingItem
screen: root.screen screen: root.screen
screenState: root.screenState visibilities: root.visibilities
} }
Popouts.ClipWrapper { Popouts.ClipWrapper {
@@ -112,7 +111,7 @@ Item {
id: toasts id: toasts
anchors.bottom: sidebar.visible ? parent.bottom : utilities.top anchors.bottom: sidebar.visible ? parent.bottom : utilities.top
anchors.margins: Tokens.padding.small anchors.margins: Tokens.padding.normal
anchors.right: sidebar.left anchors.right: sidebar.left
} }
@@ -122,8 +121,8 @@ Item {
anchors.right: parent.right anchors.right: parent.right
anchors.top: parent.top anchors.top: parent.top
panels: root panels: root
screenState: root.screenState
sidebarPanel: sidebar sidebarPanel: sidebar
visibilities: root.visibilities
} }
Launcher.Wrapper { Launcher.Wrapper {
@@ -133,7 +132,7 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
panels: root panels: root
screen: root.screen screen: root.screen
screenState: root.screenState visibilities: root.visibilities
} }
Utils.Wrapper { Utils.Wrapper {
@@ -141,8 +140,9 @@ Item {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right anchors.right: parent.right
screenState: root.screenState popouts: popouts
sidebar: sidebar sidebar: sidebar
visibilities: root.visibilities
} }
Item { Item {
@@ -150,16 +150,18 @@ Item {
property real offsetScale: dashboard.shouldBeActive ? 0 : 1 property real offsetScale: dashboard.shouldBeActive ? 0 : 1
anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: root.geometry.barOnLeft || root.geometry.barOnBottom ? parent.bottom : undefined
anchors.top: parent.top anchors.left: root.geometry.barOnLeft ? parent.left : undefined
clip: offsetScale > 0 anchors.right: root.geometry.barOnLeft ? undefined : parent.right
implicitHeight: dashboard.implicitHeight * (1 - offsetScale) anchors.top: root.geometry.barOnBottom || root.geometry.barOnLeft ? undefined : parent.top
implicitWidth: dashboard.implicitWidth clip: true
implicitHeight: root.geometry.horizontal ? dashboard.implicitHeight * (1 - offsetScale) : dashboard.implicitHeight
implicitWidth: root.geometry.horizontal ? dashboard.implicitWidth : dashboard.implicitWidth * (1 - offsetScale)
Behavior on offsetScale { Behavior on offsetScale {
Anim { Anim {
duration: Tokens.anim.durations.expressiveDefaultSpatial duration: Tokens.anim.durations.expressiveDefaultSpatial
easing: Tokens.anim.expressiveDefaultSpatial easing.bezierCurve: Tokens.anim.curves.expressiveDefaultSpatial
} }
} }
@@ -167,7 +169,8 @@ Item {
id: dashboard id: dashboard
offsetScale: dashboardWrapper.offsetScale offsetScale: dashboardWrapper.offsetScale
screenState: root.screenState position: root.geometry.position
visibilities: root.visibilities
} }
} }
@@ -178,7 +181,7 @@ Item {
anchors.right: parent.right anchors.right: parent.right
anchors.top: notifications.bottom anchors.top: notifications.bottom
panels: root panels: root
screenState: root.screenState visibilities: root.visibilities
} }
Item { Item {
@@ -194,7 +197,7 @@ Item {
anchors.verticalCenterOffset: (-implicitHeight - 5 - ((root.height - implicitHeight) / 2)) * offsetScale anchors.verticalCenterOffset: (-implicitHeight - 5 - ((root.height - implicitHeight) / 2)) * offsetScale
panels: root panels: root
screen: root.screen screen: root.screen
screenState: root.screenState visibilities: root.visibilities
} }
} }
@@ -205,7 +208,7 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
panels: root panels: root
screen: root.screen screen: root.screen
screenState: root.screenState visibilities: root.visibilities
} }
Clipboard.Wrapper { Clipboard.Wrapper {
@@ -214,6 +217,6 @@ Item {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.left: parent.left anchors.left: parent.left
screen: root.screen screen: root.screen
screenState: root.screenState visibilities: root.visibilities
} }
} }
+116 -98
View File
@@ -11,6 +11,7 @@ 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
@@ -50,47 +51,49 @@ CustomWindow {
readonly property alias menuRegion: regions.menuPopoutRegion readonly property alias menuRegion: regions.menuPopoutRegion
readonly property HyprlandMonitor monitor: Hypr.monitorFor(screen) readonly property HyprlandMonitor monitor: Hypr.monitorFor(screen)
property var root: Quickshell.shellDir property var root: Quickshell.shellDir
readonly property ScreenState screenState: ShellState.forScreen(screen)
readonly property real sdfBorderOffset: 2 * fsTransitionProg readonly property real sdfBorderOffset: 2 * fsTransitionProg
readonly property real shadowOpacity: 0.5 * (1 - (bar.isHovered ? 0 : fsTransitionProg)) readonly property real shadowOpacity: 0.7 * (1 - (bar.isHovered ? 0 : fsTransitionProg))
property color surfaceColor: Colors.tPalette.m3surface property color surfaceColor: Colors.tPalette.m3surface
WlrLayershell.exclusionMode: ExclusionMode.Ignore WlrLayershell.exclusionMode: ExclusionMode.Ignore
WlrLayershell.keyboardFocus: screenState.screenshot ? WlrKeyboardFocus.Exclusive : screenState.launcher || screenState.settings || screenState.sidebar ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None WlrLayershell.keyboardFocus: visibilities.launcher || visibilities.settings ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
WlrLayershell.layer: (fsTransitionProg > 0 && Config.general.showOverFullscreen) || (hasSpecialWorkspace && hasFullscreenOnNormalWs) ? WlrLayer.Overlay : WlrLayer.Top WlrLayershell.layer: (fsTransitionProg > 0 && Config.general.showOverFullscreen) || (hasSpecialWorkspace && hasFullscreenOnNormalWs) ? WlrLayer.Overlay : WlrLayer.Top
color: "transparent" color: "transparent"
mask: screenState.isDrawing || screenState.screenshot ? null : (hasFullscreen && !bar.isHovered ? emptyRegion : regions) contentItem.focus: true
mask: visibilities.isDrawing ? null : (hasFullscreen && !bar.isHovered ? emptyRegion : regions)
name: "Bar" name: "Bar"
Behavior on fsTransitionProg { Behavior on fsTransitionProg {
Anim {} Anim {
}
} }
Behavior on surfaceColor { Behavior on surfaceColor {
CAnim {} CAnim {
}
} }
contentItem.Keys.onEscapePressed: { contentItem.Keys.onEscapePressed: {
if (Config.bar.autoHide) if (Config.bar.autoHide)
screenState.bar = false; visibilities.bar = false;
screenState.launcher = false; visibilities.launcher = false;
screenState.sidebar = false; visibilities.sidebar = false;
screenState.dashboard = false; visibilities.dashboard = false;
screenState.osd = false; visibilities.osd = false;
screenState.settings = false; visibilities.settings = false;
screenState.resources = false; visibilities.resources = false;
screenState.dock = false; visibilities.dock = false;
screenState.clipboard = false; visibilities.clipboard = false;
panels.popouts.hasCurrent = false; panels.popouts.hasCurrent = false;
} }
onHasFullscreenChanged: { onHasFullscreenChanged: {
screenState.launcher = false; visibilities.launcher = false;
screenState.sidebar = false; visibilities.sidebar = false;
screenState.dashboard = false; visibilities.dashboard = false;
screenState.osd = false; visibilities.osd = false;
screenState.settings = false; visibilities.settings = false;
screenState.resources = false; visibilities.resources = false;
screenState.clipboard = false; visibilities.clipboard = false;
screenState.dock = false; visibilities.dock = false;
panels.popouts.hasCurrent = false; panels.popouts.hasCurrent = false;
} }
@@ -143,40 +146,53 @@ CustomWindow {
id: focusGrab id: focusGrab
active: { active: {
const s = root.screenState; const v = visibilities;
const c = Config; const conf = Config;
if (!N.PopupManager.closed && panels.popouts.hasCurrent)
if ((s.launcher && c.launcher.enabled) || (s.sidebar && c.sidebar.enabled) || (s.resources && c.dashboard.performance.enabled) || (s.clipboard && c.clipboard.enabled))
return true; return true;
if (s.dashboard && !c.dashboard.showOnHover && c.dashboard.enabled) if ((v.launcher && conf.launcher.enabled) || (v.sidebar && conf.sidebar.enabled) || (v.resources && conf.dashboard.performance.enabled) || (v.dashboard && conf.dashboard.enabled) || v.settings || (v.clipboard && conf.clipboard.enabled) || (v.dock && conf.dock.enabled))
return true; return true;
if (!c.dock.hoverToReveal && s.dock && c.dock.enable) if (panels.popouts.hasCurrent && panels.popouts.currentName.startsWith("traymenu") && (!conf.bar.tray.showOnHover || (panels.popouts.current as StackView)?.depth > 1))
return true;
if (panels.popouts.currentName.startsWith("traymenu") && (panels.popouts.current as StackView)?.depth > 1)
return true;
if (s.settings)
return true; return true;
return false; return false;
} }
windows: [root] windows: [root]
onCleared: { onCleared: {
root.screenState.launcher = false; visibilities.launcher = false;
root.screenState.sidebar = false; visibilities.sidebar = false;
root.screenState.dashboard = false; visibilities.dashboard = false;
root.screenState.osd = false; visibilities.osd = false;
root.screenState.settings = false; visibilities.settings = false;
root.screenState.resources = false; visibilities.resources = false;
root.screenState.clipboard = false; visibilities.clipboard = false;
root.screenState.dock = false; visibilities.dock = false;
panels.popouts.hasCurrent = false; panels.popouts.hasCurrent = false;
} }
} }
PersistentProperties {
id: visibilities
property bool bar
property bool clipboard
property bool dashboard
property bool dock
property bool isDrawing
property bool launcher
property bool notif: NotifServer.popups.length > 0
property bool osd
property bool resources
property bool settings
property bool sidebar
Component.onCompleted: Visibilities.load(root.screen, this)
}
Binding { Binding {
property: "bar" property: "bar"
target: root.screenState target: visibilities
value: root.screenState.sidebar || root.screenState.dashboard || root.screenState.osd || (!Config.bar.hideWhenNotif && NotifServer.popups.length > 0) || root.screenState.resources || root.screenState.settings || bar.isHovered value: visibilities.sidebar || visibilities.dashboard || visibilities.osd || (!Config.bar.hideWhenNotif && visibilities.notif) || visibilities.resources || visibilities.settings || bar.isHovered
when: Config.bar.autoHide when: Config.bar.autoHide
} }
@@ -184,19 +200,23 @@ CustomWindow {
id: surface id: surface
anchors.fill: parent anchors.fill: parent
layer.enabled: true
opacity: root.surfaceColor.a opacity: root.surfaceColor.a
BlobGroup2 { layer.effect: MultiEffect {
blurMax: 32
shadowColor: Qt.alpha(Colors.palette.m3shadow, Math.max(0, root.shadowOpacity))
shadowEnabled: true
}
BlobGroup {
id: blobGroup id: blobGroup
anchors.fill: parent
color: root.surfaceColor color: root.surfaceColor
shadowColor: Qt.alpha(Colors.palette.m3shadow, Math.max(0, root.shadowOpacity))
shadowRadius: 18
smoothing: Config.bar.smoothing smoothing: Config.bar.smoothing
} }
BlobInvertedRect2 { BlobInvertedRect {
anchors.fill: parent anchors.fill: parent
anchors.margins: -50 anchors.margins: -50
borderBottom: geometry.insetBottom(root.borderThickness) - anchors.margins - root.sdfBorderOffset borderBottom: geometry.insetBottom(root.borderThickness) - anchors.margins - root.sdfBorderOffset
@@ -216,7 +236,7 @@ CustomWindow {
implicitHeight: panels.dashboard.height * (geometry.horizontal ? 1 + extraExtent : 1) implicitHeight: panels.dashboard.height * (geometry.horizontal ? 1 + extraExtent : 1)
implicitWidth: panels.dashboard.width * (geometry.horizontal ? 1 : 1 + extraExtent) implicitWidth: panels.dashboard.width * (geometry.horizontal ? 1 : 1 + extraExtent)
panel: panels.dashboardWrapper panel: panels.dashboardWrapper
radius: Tokens.rounding.extraLarge radius: Tokens.rounding.normal
x: panels.dashboardWrapper.x + panels.dashboard.x + geometry.insetLeft(root.borderThickness) - (geometry.horizontal ? 0 : panels.dashboard.width * extraExtent) x: panels.dashboardWrapper.x + panels.dashboard.x + geometry.insetLeft(root.borderThickness) - (geometry.horizontal ? 0 : panels.dashboard.width * extraExtent)
y: panels.dashboardWrapper.y + panels.dashboard.y + geometry.insetTop(root.borderThickness) - (geometry.horizontal && geometry.position !== "bottom" ? panels.dashboard.height * extraExtent : 0) y: panels.dashboardWrapper.y + panels.dashboard.y + geometry.insetTop(root.borderThickness) - (geometry.horizontal && geometry.position !== "bottom" ? panels.dashboard.height * extraExtent : 0)
} }
@@ -229,7 +249,7 @@ CustomWindow {
deformAmount: 0.06 deformAmount: 0.06
implicitHeight: panels.launcher.height * (1 + extraHeight) implicitHeight: panels.launcher.height * (1 + extraHeight)
panel: panels.launcher panel: panels.launcher
radius: Tokens.rounding.medium + (Tokens.padding.largeIncreased / 4) radius: Tokens.rounding.smallest + 5
y: panels.launcher.y + geometry.insetTop(root.borderThickness) y: panels.launcher.y + geometry.insetTop(root.borderThickness)
} }
@@ -246,7 +266,6 @@ CustomWindow {
PanelBg { PanelBg {
id: osdBg id: osdBg
atRestOverride: panels.osd.offsetScale < 0.99
deformAmount: 0.1 deformAmount: 0.1
implicitHeight: panels.osd.height implicitHeight: panels.osd.height
implicitWidth: panels.osd.width implicitWidth: panels.osd.width
@@ -258,9 +277,8 @@ CustomWindow {
PanelBg { PanelBg {
id: notifsBg id: notifsBg
atRestOverride: panel.implicitHeight > 0
panel: panels.notifications panel: panels.notifications
radius: Tokens.rounding.large radius: Tokens.rounding.normal
} }
PanelBg { PanelBg {
@@ -281,28 +299,26 @@ CustomWindow {
implicitHeight: panels.popouts.height * (geometry.horizontal ? 1 + extraExtent : 1) implicitHeight: panels.popouts.height * (geometry.horizontal ? 1 + extraExtent : 1)
implicitWidth: panels.popouts.width * (geometry.horizontal ? 1 : 1 + extraExtent) implicitWidth: panels.popouts.width * (geometry.horizontal ? 1 : 1 + extraExtent)
panel: panels.popoutsWrapper panel: panels.popoutsWrapper
radius: panels.popouts.current?.panelRadius ?? Tokens.rounding.large radius: panels.popouts.current?.panelRadius ?? Tokens.rounding.normal
x: panels.popoutsWrapper.x + panels.popouts.x + geometry.insetLeft(root.borderThickness) - (geometry.horizontal ? 0 : panels.popouts.width * extraExtent) x: panels.popoutsWrapper.x + panels.popouts.x + geometry.insetLeft(root.borderThickness) - (geometry.horizontal ? 0 : panels.popouts.width * extraExtent)
y: panels.popoutsWrapper.y + panels.popouts.y + geometry.insetTop(root.borderThickness) - (geometry.barOnTop ? panels.popouts.height * extraExtent : 0) y: panels.popoutsWrapper.y + panels.popouts.y + geometry.insetTop(root.borderThickness) - (geometry.barOnTop ? panels.popouts.height * extraExtent : 0)
Behavior on extraExtent { Behavior on extraExtent {
Anim {} Anim {
}
} }
} }
PanelBg { PanelBg {
id: resourcesBg id: resourcesBg
property real extraExtent: 0.2
atRestOverride: panels.resources.offsetScale < 0.99
deformAmount: 0.05 deformAmount: 0.05
implicitHeight: panels.resources.height * (1 + extraExtent) implicitHeight: panels.resources.height
implicitWidth: panels.resources.width * (1 + extraExtent) implicitWidth: panels.resources.width
panel: panels.resourcesWrapper panel: panels.resourcesWrapper
radius: Tokens.rounding.largeIncreased + Tokens.padding.small radius: Tokens.rounding.large
x: panels.resourcesWrapper.x + panels.resources.x + geometry.insetLeft(root.borderThickness) - panels.resources.width * extraExtent x: panels.resourcesWrapper.x + panels.resources.x + geometry.insetLeft(root.borderThickness)
y: panels.resourcesWrapper.y + panels.resources.y + geometry.insetTop(root.borderThickness) - panels.resources.height * extraExtent y: panels.resourcesWrapper.y + panels.resources.y + geometry.insetTop(root.borderThickness)
} }
PanelBg { PanelBg {
@@ -310,7 +326,7 @@ CustomWindow {
deformAmount: 0.03 deformAmount: 0.03
panel: panels.settings panel: panels.settings
radius: Tokens.rounding.largeIncreased + Tokens.padding.small radius: Tokens.rounding.large + Tokens.padding.normal
} }
PanelBg { PanelBg {
@@ -318,7 +334,7 @@ CustomWindow {
deformAmount: 0.08 deformAmount: 0.08
panel: panels.dock panel: panels.dock
radius: Tokens.rounding.medium radius: Tokens.rounding.normal
} }
PanelBg { PanelBg {
@@ -326,21 +342,15 @@ CustomWindow {
deformAmount: 0.08 deformAmount: 0.08
panel: panels.drawing panel: panels.drawing
radius: Tokens.rounding.medium radius: Tokens.rounding.normal
} }
PanelBg { PanelBg {
id: clipboardBg id: clipboardBg
property real extraExtent: 0.2
deformAmount: 0.03 deformAmount: 0.03
implicitHeight: panels.clipboard.height * (1 + extraExtent)
implicitWidth: panels.clipboard.width * (1 + extraExtent)
panel: panels.clipboard panel: panels.clipboard
radius: 29 radius: 29
x: panels.clipboard.x + geometry.insetLeft(root.borderThickness) - panels.clipboard.width * extraExtent
y: panels.clipboard.y + geometry.insetTop(root.borderThickness)
} }
} }
@@ -348,11 +358,37 @@ CustomWindow {
id: drawing id: drawing
anchors.fill: parent anchors.fill: parent
screenState: root.screenState layer.enabled: true
visibilities: visibilities
z: 2 z: 2
layer.effect: MultiEffect {
maskEnabled: true
maskInverted: true
maskSource: maskSource
}
} }
Interactions2 { Item {
id: maskSource
anchors.fill: parent
layer.enabled: true
visible: false
CustomRect {
readonly property int extraWidth: radius
color: "white"
implicitHeight: panels.drawing.height
implicitWidth: panels.drawing.width + extraWidth
radius: drawingBg.radius
x: -extraWidth + root.borderThickness + panels.drawing.x
y: panels.drawing.y + bar.implicitHeight
}
}
Interactions {
id: interactions id: interactions
anchors.fill: parent anchors.fill: parent
@@ -364,7 +400,7 @@ CustomWindow {
panels: panels panels: panels
popouts: panels.popouts popouts: panels.popouts
screen: root.screen screen: root.screen
screenState: root.screenState visibilities: visibilities
Panels { Panels {
id: panels id: panels
@@ -374,7 +410,7 @@ CustomWindow {
drawingItem: drawing drawingItem: drawing
geometry: geometry geometry: geometry
screen: root.screen screen: root.screen
screenState: root.screenState visibilities: visibilities
clipboard.transform: Matrix4x4 { clipboard.transform: Matrix4x4 {
matrix: clipboardBg.deformMatrix matrix: clipboardBg.deformMatrix
@@ -417,45 +453,27 @@ CustomWindow {
anchors.bottom: geometry.barOnBottom ? parent.bottom : undefined anchors.bottom: geometry.barOnBottom ? parent.bottom : undefined
anchors.left: parent.left anchors.left: parent.left
anchors.top: geometry.barOnBottom ? undefined : parent.top anchors.top: geometry.barOnBottom ? undefined : parent.top
enabled: !screenState.isDrawing enabled: !visibilities.isDrawing
fullscreen: root.hasFullscreen fullscreen: root.hasFullscreen
height: geometry.horizontal ? implicitHeight : parent.height height: geometry.horizontal ? implicitHeight : parent.height
popouts: panels.popouts popouts: panels.popouts
popoutsWrapper: panels.popoutsWrapper popoutsWrapper: panels.popoutsWrapper
position: geometry.position position: geometry.position
screen: root.screen screen: root.screen
screenState: root.screenState visibilities: visibilities
width: geometry.horizontal ? parent.width : implicitWidth width: geometry.horizontal ? parent.width : implicitWidth
} }
} }
LazyLoader { component PanelBg: BlobRect {
id: loader
property bool closing: !root.screenState.screenshot
onClosingChanged: if (!closing)
activeAsync = true
Picker {
loader: loader
parent: root.contentItem
screen: root.screen
screenState: root.screenState
}
}
component PanelBg: BlobRect2 {
property real deformAmount: 0.15 property real deformAmount: 0.15
required property Item panel required property Item panel
atRestOverride: panel?.offsetScale < 0.99 ?? false
damping: 28.3
deformScale: (deformAmount * Config.appearance.deform.scale) / 10000 deformScale: (deformAmount * Config.appearance.deform.scale) / 10000
group: blobGroup group: blobGroup
implicitHeight: panel.height implicitHeight: panel.height
implicitWidth: panel.width implicitWidth: panel.width
radius: Tokens.rounding.small radius: Tokens.rounding.smallest
x: panel.x + geometry.insetLeft(root.borderThickness) x: panel.x + geometry.insetLeft(root.borderThickness)
y: panel.y + geometry.insetTop(root.borderThickness) y: panel.y + geometry.insetTop(root.borderThickness)
} }
+33
View File
@@ -0,0 +1,33 @@
import QtQuick
import QtQuick.Effects
Item {
id: root
property real radius
Rectangle {
id: shadowRect
anchors.fill: root
color: "black"
layer.enabled: true
radius: root.radius
visible: false
}
MultiEffect {
id: effects
anchors.fill: shadowRect
autoPaddingEnabled: true
maskEnabled: true
maskInverted: true
maskSource: shadowRect
shadowBlur: 2.0
shadowColor: "black"
shadowEnabled: true
shadowOpacity: 1
source: shadowRect
}
}
+36 -20
View File
@@ -18,7 +18,7 @@ ColumnLayout {
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: false Layout.fillWidth: false
Layout.preferredWidth: centerWidth Layout.preferredWidth: centerWidth
spacing: Tokens.spacing.largeIncreased * 2 spacing: Tokens.spacing.large * 2
RowLayout { RowLayout {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
@@ -27,21 +27,27 @@ ColumnLayout {
CustomText { CustomText {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
color: Colors.palette.m3secondary color: Colors.palette.m3secondary
font: Tokens.font.headline.builders.large.scale(3 * root.centerScale).weight(Font.Bold).width(30).build() font.bold: true
font.family: Config.appearance.font.family.clock
font.pointSize: Math.floor(Tokens.font.size.extraLarge * 3 * root.centerScale)
text: Time.hourStr text: Time.hourStr
} }
CustomText { CustomText {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
color: Colors.palette.m3primary color: Colors.palette.m3primary
font: Tokens.font.headline.builders.large.scale(3 * root.centerScale).weight(Font.Bold).width(30).build() font.bold: true
font.family: Config.appearance.font.family.clock
font.pointSize: Math.floor(Tokens.font.size.extraLarge * 3 * root.centerScale)
text: ":" text: ":"
} }
CustomText { CustomText {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
color: Colors.palette.m3secondary color: Colors.palette.m3secondary
font: Tokens.font.headline.builders.large.scale(3 * root.centerScale).weight(Font.Bold).width(30).build() font.bold: true
font.family: Config.appearance.font.family.clock
font.pointSize: Math.floor(Tokens.font.size.extraLarge * 3 * root.centerScale)
text: Time.minuteStr text: Time.minuteStr
} }
} }
@@ -50,13 +56,15 @@ ColumnLayout {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.topMargin: -Tokens.padding.large * 2 Layout.topMargin: -Tokens.padding.large * 2
color: Colors.palette.m3tertiary color: Colors.palette.m3tertiary
font: Tokens.font.title.builders.medium.weight(Font.DemiBold).build() font.bold: true
font.family: Config.appearance.font.family.mono
font.pointSize: Math.floor(Tokens.font.size.extraLarge * root.centerScale)
text: Time.format("dddd, d MMMM yyyy") text: Time.format("dddd, d MMMM yyyy")
} }
ClipRect { CustomClippingRect {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Tokens.spacing.largeIncreased * 2 Layout.topMargin: Tokens.spacing.large * 2
color: Colors.tPalette.m3surfaceContainer color: Colors.tPalette.m3surfaceContainer
implicitHeight: root.centerWidth / 2 implicitHeight: root.centerWidth / 2
implicitWidth: root.centerWidth / 2 implicitWidth: root.centerWidth / 2
@@ -65,7 +73,7 @@ ColumnLayout {
MaterialIcon { MaterialIcon {
anchors.centerIn: parent anchors.centerIn: parent
color: Colors.palette.m3onSurfaceVariant color: Colors.palette.m3onSurfaceVariant
fontStyle: Tokens.font.icon.size(root.centerWidth / 4).build() font.pointSize: Math.floor(root.centerWidth / 4)
text: "person" text: "person"
} }
@@ -80,7 +88,9 @@ ColumnLayout {
CustomText { CustomText {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3onSurfaceVariant color: Colors.palette.m3onSurfaceVariant
font: Tokens.font.body.large font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.normal
font.weight: 600
text: root.greeter.username text: root.greeter.username
visible: text.length > 0 visible: text.length > 0
} }
@@ -89,7 +99,7 @@ ColumnLayout {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
color: Colors.tPalette.m3surfaceContainer color: Colors.tPalette.m3surfaceContainer
focus: true focus: true
implicitHeight: input.implicitHeight + Tokens.padding.extraSmall * 2 implicitHeight: input.implicitHeight + Tokens.padding.small * 2
implicitWidth: root.centerWidth * 0.8 implicitWidth: root.centerWidth * 0.8
radius: Tokens.rounding.full radius: Tokens.rounding.full
@@ -120,11 +130,11 @@ ColumnLayout {
id: input id: input
anchors.fill: parent anchors.fill: parent
anchors.margins: Tokens.padding.extraSmall anchors.margins: Tokens.padding.small
spacing: Tokens.spacing.medium spacing: Tokens.spacing.normal
Item { Item {
implicitHeight: statusIcon.implicitHeight + Tokens.padding.extraSmall * 2 implicitHeight: statusIcon.implicitHeight + Tokens.padding.small * 2
implicitWidth: implicitHeight implicitWidth: implicitHeight
MaterialIcon { MaterialIcon {
@@ -145,7 +155,8 @@ ColumnLayout {
} }
Behavior on opacity { Behavior on opacity {
Anim {} Anim {
}
} }
} }
@@ -163,7 +174,7 @@ ColumnLayout {
CustomRect { CustomRect {
color: root.greeter.buffer && !root.greeter.launching ? Colors.palette.m3primary : Colors.layer(Colors.palette.m3surfaceContainerHigh, 2) color: root.greeter.buffer && !root.greeter.launching ? Colors.palette.m3primary : Colors.layer(Colors.palette.m3surfaceContainerHigh, 2)
implicitHeight: enterIcon.implicitHeight + Tokens.padding.extraSmall * 2 implicitHeight: enterIcon.implicitHeight + Tokens.padding.small * 2
implicitWidth: implicitHeight implicitWidth: implicitHeight
radius: Tokens.rounding.full radius: Tokens.rounding.full
@@ -180,6 +191,7 @@ ColumnLayout {
anchors.centerIn: parent anchors.centerIn: parent
color: root.greeter.buffer && !root.greeter.launching ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface color: root.greeter.buffer && !root.greeter.launching ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
font.weight: 500
text: root.greeter.launching ? "hourglass_top" : "arrow_forward" text: root.greeter.launching ? "hourglass_top" : "arrow_forward"
} }
} }
@@ -188,11 +200,12 @@ ColumnLayout {
Item { Item {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: -Tokens.spacing.largeIncreased Layout.topMargin: -Tokens.spacing.large
implicitHeight: Math.max(message.implicitHeight, stateMessage.implicitHeight) implicitHeight: Math.max(message.implicitHeight, stateMessage.implicitHeight)
Behavior on implicitHeight { Behavior on implicitHeight {
Anim {} Anim {
}
} }
CustomText { CustomText {
@@ -232,10 +245,12 @@ ColumnLayout {
wrapMode: Text.WrapAtWordBoundaryOrAnywhere wrapMode: Text.WrapAtWordBoundaryOrAnywhere
Behavior on opacity { Behavior on opacity {
Anim {} Anim {
}
} }
Behavior on scale { Behavior on scale {
Anim {} Anim {
}
} }
onMsgChanged: { onMsgChanged: {
@@ -277,7 +292,8 @@ ColumnLayout {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
color: isError ? Colors.palette.m3error : Colors.palette.m3primary color: isError ? Colors.palette.m3error : Colors.palette.m3primary
font: Tokens.font.mono.medium font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.small
horizontalAlignment: Qt.AlignHCenter horizontalAlignment: Qt.AlignHCenter
opacity: 0 opacity: 0
scale: 0.7 scale: 0.7
+11 -11
View File
@@ -30,11 +30,11 @@ NumberAnimation {
if (type === Anim.DefaultSpatial) if (type === Anim.DefaultSpatial)
return Tokens.anim.durations.expressiveDefaultSpatial; return Tokens.anim.durations.expressiveDefaultSpatial;
if (type === Anim.SlowSpatial) if (type === Anim.SlowSpatial)
return Tokens.anim.durations.expressiveSlowSpatial; return Tokens.anim.durations.large;
if (type === Anim.FastEffects) if (type === Anim.FastEffects)
return Tokens.anim.durations.expressiveFastEffects; return Tokens.anim.durations.expressiveFastEffects;
if (type === Anim.DefaultEffects) if (type === Anim.DefaultEffects)
return Tokens.anim.durations.expressiveDefaultEffects; return Tokens.anim.durations.expressiveEffects;
if (type === Anim.SlowEffects) if (type === Anim.SlowEffects)
return Tokens.anim.durations.expressiveSlowEffects; return Tokens.anim.durations.expressiveSlowEffects;
@@ -42,22 +42,22 @@ NumberAnimation {
const idx = type % 4; // 0-7 are the 4 standard types const idx = type % 4; // 0-7 are the 4 standard types
return Tokens.anim.durations[types[idx]]; return Tokens.anim.durations[types[idx]];
} }
easing: { easing.bezierCurve: {
if (type === Anim.FastSpatial) if (type === Anim.FastSpatial)
return Tokens.anim.expressiveFastSpatial; return Tokens.anim.curves.expressiveFastSpatial;
if (type === Anim.DefaultSpatial) if (type === Anim.DefaultSpatial)
return Tokens.anim.expressiveDefaultSpatial; return Tokens.anim.curves.expressiveDefaultSpatial;
if (type === Anim.SlowSpatial) if (type === Anim.SlowSpatial)
return Tokens.anim.expressiveSlowSpatial; return Tokens.anim.curves.expressiveSlowSpatial;
if (type === Anim.FastEffects) if (type === Anim.FastEffects)
return Tokens.anim.expressiveFastEffects; return Tokens.anim.curves.expressiveFastEffects;
if (type === Anim.DefaultEffects) if (type === Anim.DefaultEffects)
return Tokens.anim.expressiveDefaultEffects; return Tokens.anim.curves.expressiveDefaultEffects;
if (type === Anim.SlowEffects) if (type === Anim.SlowEffects)
return Tokens.anim.expressiveSlowEffects; return Tokens.anim.curves.expressiveSlowEffects;
if (type >= Anim.EmphasizedSmall && type <= Anim.EmphasizedExtraLarge) if (type >= Anim.EmphasizedSmall && type <= Anim.EmphasizedExtraLarge)
return Tokens.anim.emphasized; return Tokens.anim.curves.emphasized;
return Tokens.anim.standard; return Tokens.anim.curves.standard;
} }
} }
+58 -2
View File
@@ -2,6 +2,62 @@ import QtQuick
import ZShell.Config import ZShell.Config
ColorAnimation { ColorAnimation {
duration: Tokens.anim.durations.expressiveSlowEffects enum Type {
easing: Tokens.anim.expressiveSlowEffects StandardSmall = 0,
Standard,
StandardLarge,
StandardExtraLarge,
EmphasizedSmall,
Emphasized,
EmphasizedLarge,
EmphasizedExtraLarge,
FastSpatial,
DefaultSpatial,
SlowSpatial,
FastEffects,
DefaultEffects,
SlowEffects
}
property int type: CAnim.DefaultSpatial
duration: {
if (type < CAnim.StandardSmall || type > Anim.SlowEffects)
return Tokens.anim.durations.normal;
if (type === CAnim.FastSpatial)
return Tokens.anim.durations.expressiveFastSpatial;
if (type === CAnim.DefaultSpatial)
return Tokens.anim.durations.expressiveDefaultSpatial;
if (type === CAnim.SlowSpatial)
return Tokens.anim.durations.large;
if (type === CAnim.FastEffects)
return Tokens.anim.durations.expressiveFastEffects;
if (type === CAnim.DefaultEffects)
return Tokens.anim.durations.expressiveEffects;
if (type === CAnim.SlowEffects)
return Tokens.anim.durations.expressiveSlowEffects;
const types = ["small", "normal", "large", "extraLarge"];
const idx = type % 4;
return Tokens.anim.durations[types[idx]];
}
easing.bezierCurve: {
if (type === CAnim.FastSpatial)
return Tokens.anim.curves.expressiveFastSpatial;
if (type === CAnim.DefaultSpatial)
return Tokens.anim.curves.expressiveDefaultSpatial;
if (type === CAnim.SlowSpatial)
return Tokens.anim.curves.expressiveSlowSpatial;
if (type === CAnim.FastEffects)
return Tokens.anim.curves.expressiveFastEffects;
if (type === CAnim.DefaultEffects)
return Tokens.anim.curves.expressiveDefaultEffects;
if (type === CAnim.SlowEffects)
return Tokens.anim.curves.expressiveSlowEffects;
if (type >= CAnim.EmphasizedSmall && type <= Anim.EmphasizedExtraLarge)
return Tokens.anim.curves.emphasized;
return Tokens.anim.curves.standard;
}
} }
+2 -2
View File
@@ -20,10 +20,10 @@ BusyIndicator {
property int animState property int animState
property color bgColor: Colors.palette.m3secondaryContainer property color bgColor: Colors.palette.m3secondaryContainer
property color fgColor: Colors.palette.m3primary property color fgColor: Colors.palette.m3primary
property real implicitSize: Tokens.font.body.medium.pointSize * 3 property real implicitSize: Tokens.font.size.normal * 3
property real internalStrokeWidth: strokeWidth property real internalStrokeWidth: strokeWidth
readonly property alias progress: manager.progress readonly property alias progress: manager.progress
property real strokeWidth: Tokens.padding.extraSmall * 0.8 property real strokeWidth: Tokens.padding.small * 0.8
property alias type: manager.indeterminateAnimationType property alias type: manager.indeterminateAnimationType
implicitHeight: implicitSize implicitHeight: implicitSize
+5 -3
View File
@@ -21,7 +21,7 @@ Item {
readonly property real size: Math.min(width, height) readonly property real size: Math.min(width, height)
property int spacing: Tokens.spacing.small property int spacing: Tokens.spacing.small
property int startAngle: -90 property int startAngle: -90
property int strokeWidth: Tokens.padding.extraSmall property int strokeWidth: Tokens.padding.small
property int sweepAngle: 360 property int sweepAngle: 360
readonly property real thickness: strokeWidth * (1 + waveAmplitude) * 2 readonly property real thickness: strokeWidth * (1 + waveAmplitude) * 2
property real value property real value
@@ -46,7 +46,8 @@ 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 {
@@ -83,7 +84,8 @@ Item {
} }
} }
Behavior on color { Behavior on color {
CAnim {} CAnim {
}
} }
Anim on waveProgress { Anim on waveProgress {
id: waveProgAnim id: waveProgAnim
-41
View File
@@ -1,41 +0,0 @@
import QtQuick
import ZShell.Components
Item {
id: root
property real radius
property real topLeftRadius: radius
property real topRightRadius: radius
property real bottomRightRadius: radius
property real bottomLeftRadius: radius
property color color: "transparent"
default property alias contentData: contentItem.data
Item {
id: contentItem
anchors.fill: parent
}
ShaderEffectSource {
id: source
hideSource: true
sourceItem: contentItem
}
RoundedMask {
anchors.fill: parent
source: source
topLeftRadius: root.topLeftRadius
topRightRadius: root.topRightRadius
bottomRightRadius: root.bottomRightRadius
bottomLeftRadius: root.bottomLeftRadius
color: root.color
}
Behavior on color {
CAnim {}
}
}
+2 -1
View File
@@ -7,6 +7,7 @@ ClippingRectangle {
color: "transparent" color: "transparent"
Behavior on color { Behavior on color {
CAnim {} CAnim {
}
} }
} }
-23
View File
@@ -1,36 +1,13 @@
import QtQuick import QtQuick
import qs.Services
Flickable { Flickable {
id: root id: root
property bool doneFakeFlick
interactive: !ShellState.forActive().isDrawing
maximumFlickVelocity: 3000 maximumFlickVelocity: 3000
rebound: Transition { rebound: Transition {
onRunningChanged: {
if (!running && !root.doneFakeFlick) {
root.doneFakeFlick = true;
root.flick(1, 1);
root.flick(-1, -1);
Qt.callLater(() => root.cancelFlick());
}
}
Anim { Anim {
properties: "x,y" properties: "x,y"
} }
} }
Timer {
interval: 10
running: root.doneFakeFlick
onTriggered: {
root.doneFakeFlick = false;
root.returnToBounds();
}
}
} }
-23
View File
@@ -1,36 +1,13 @@
import QtQuick import QtQuick
import qs.Services
ListView { ListView {
id: root id: root
property bool doneFakeFlick
interactive: !ShellState.forActive()?.isDrawing
maximumFlickVelocity: 3000 maximumFlickVelocity: 3000
rebound: Transition { rebound: Transition {
// onRunningChanged: {
// if (!running && !root.doneFakeFlick) {
// root.doneFakeFlick = true;
// root.flick(1, 1);
// root.flick(-1, -1);
// Qt.callLater(() => root.cancelFlick());
// }
// }
Anim { Anim {
properties: "x,y" properties: "x,y"
type: Anim.DefaultEffects
} }
} }
// Timer {
// interval: 10
// running: root.doneFakeFlick
//
// onTriggered: {
// root.doneFakeFlick = false;
// }
// }
} }
-1
View File
@@ -4,7 +4,6 @@ MouseArea {
property int scrollAccumulatedY: 0 property int scrollAccumulatedY: 0
function onWheel(event: WheelEvent): void { function onWheel(event: WheelEvent): void {
event.accepted = false;
} }
onWheel: event => { onWheel: event => {
+2 -1
View File
@@ -6,6 +6,7 @@ Rectangle {
color: "transparent" color: "transparent"
Behavior on color { Behavior on color {
CAnim {} CAnim {
}
} }
} }
+11 -11
View File
@@ -14,36 +14,36 @@ Text {
property real animateTo: 1 property real animateTo: 1
color: Colors.palette.m3onSurface color: Colors.palette.m3onSurface
font: Tokens.font.body.small font.family: Config.appearance.font.family.sans
font.pointSize: Tokens.font.size.normal
linkColor: Colors.palette.m3onPrimaryFixedVariant linkColor: Colors.palette.m3onPrimaryFixedVariant
renderType: Text.QtRendering renderType: Text.NativeRendering
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
SequentialAnimation { SequentialAnimation {
TAnim { Anim {
target: root
to: root.animateFrom to: root.animateFrom
type: Anim.FastEffects
} }
PropertyAction {} PropertyAction {
}
TAnim { Anim {
target: root
to: root.animateTo to: root.animateTo
type: Anim.DefaultEffects
} }
} }
} }
component TAnim: Anim { component Anim: NumberAnimation {
duration: root.animateDuration / 2 duration: root.animateDuration / 2
easing.type: Easing.BezierSpline
properties: root.animateProp.split(",").length > 1 ? root.animateProp : "" properties: root.animateProp.split(",").length > 1 ? root.animateProp : ""
property: root.animateProp.split(",").length === 1 ? root.animateProp : "" property: root.animateProp.split(",").length === 1 ? root.animateProp : ""
target: root target: root
+52 -281
View File
@@ -1,305 +1,76 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Shapes import QtQuick.Controls
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
TextFieldBase { TextField {
id: root id: root
enum TextFieldType { background: null
Outlined, color: Colors.palette.m3onSurface
Filled cursorVisible: !readOnly
} font.family: Config.appearance.font.family.sans
font.pointSize: Tokens.font.size.smaller
placeholderTextColor: Colors.palette.m3outline
renderType: echoMode === TextField.Password ? TextField.QtRendering : TextField.NativeRendering
readonly property int clampedRadius: Math.min(horizontalPadding, Math.min(width, height) / 2, radius) Behavior on color {
readonly property string effectiveSupportingText: isError && errorText ? errorText : supportingText CAnim {
readonly property int filledOffset: type === CustomTextField.Filled ? Tokens.spacing.small : 0
readonly property int leadingOffset: leadingIcon ? leadingIconLoader.width + leadingIconLoader.anchors.leftMargin : 0
readonly property real smallFontScale: smallFontSize / font.pointSize
readonly property int supportingTextOffset: effectiveSupportingText ? supportingTextLoader.height + Tokens.spacing.extraSmall : 0
readonly property int trailingOffset: trailingIcon ? trailingIconLoader.width + trailingIconLoader.anchors.rightMargin : 0
readonly property bool valid: !validate || (!text && emptyIsValid) || (validate instanceof RegExp ? validate.test(text) : !!validate(text))
property int horizontalPadding: Tokens.padding.large
property int verticalPadding: Tokens.padding.large
property int radius: Tokens.rounding.small
property string supportingText
property string trailingIcon
property int smallFontSize: Tokens.font.label.small.pointSize
property int type: CustomTextField.Outlined
property var validate // Regex or function
property bool emptyIsValid: true
property string errorText
property bool isError
property string leadingIcon
bottomPadding: verticalPadding + supportingTextOffset - filledOffset
leftPadding: horizontalPadding + leadingOffset
rightPadding: horizontalPadding + trailingOffset
topPadding: verticalPadding + filledOffset
background: Loader {
anchors.bottomMargin: root.supportingTextOffset
anchors.fill: parent
sourceComponent: root.type === CustomTextField.Filled ? filledComp : outlineComp
StateLayer {
id: stateLayer
cursorShape: Qt.IBeamCursor
enabled: !root.activeFocus
manualPressOverride: tapHandler.pressed
radius: root.type === CustomTextField.Outlined ? root.clampedRadius : 0
topLeftRadius: root.clampedRadius
topRightRadius: root.clampedRadius
onClicked: root.focus = true
} }
} }
cursorDelegate: CustomRect {
id: cursor
onEditingFinished: { property bool disableBlink
if (!valid)
isError = true;
}
onPressed: {
if (stateLayer.enabled)
stateLayer.press(stateLayer.mouseX, stateLayer.mouseY);
}
onTextEdited: {
if (isError)
isError = false;
}
Item { color: Colors.palette.m3primary
id: contentWrapper implicitWidth: 2
radius: Tokens.rounding.normal
anchors.bottomMargin: root.supportingTextOffset Behavior on opacity {
anchors.fill: parent Anim {
duration: Tokens.anim.durations.small
CustomText {
id: placeholder
anchors.left: parent.left
anchors.leftMargin: root.leftPadding
anchors.topMargin: Tokens.padding.extraSmall
anchors.verticalCenter: parent.verticalCenter
color: root.isError ? Colors.palette.m3error : (root.activeFocus ? Colors.palette.m3primary : root.text ? Colors.palette.m3outline : root.placeholderTextColor)
font.family: root.font.family
font.pointSize: root.font.pointSize
font.variableAxes: root.font.variableAxes
font.weight: root.font.weight
renderType: Text.QtRendering
text: root.placeholderText
states: [
State {
name: "smallOutlined"
when: root.type === CustomTextField.Outlined && (root.activeFocus || root.text)
PropertyChanges {
placeholder.anchors.leftMargin: -(1 - root.smallFontScale) * placeholder.width / 2 + root.horizontalPadding
placeholder.scale: root.smallFontScale
}
AnchorChanges {
anchors.verticalCenter: contentWrapper.top
target: placeholder
}
},
State {
name: "smallFilled"
when: root.type === CustomTextField.Filled && (root.activeFocus || root.text)
PropertyChanges {
placeholder.anchors.leftMargin: -(1 - root.smallFontScale) * placeholder.width / 2 + root.horizontalPadding + root.leadingOffset
placeholder.scale: root.smallFontScale
}
AnchorChanges {
anchors.top: contentWrapper.top
anchors.verticalCenter: undefined
target: placeholder
}
}
]
transitions: Transition {
Anim {
properties: "scale,leftMargin"
type: Anim.DefaultEffects
}
AnchorAnim {
duration: Tokens.anim.durations.expressiveDefaultEffects
easing: Tokens.anim.expressiveDefaultEffects
}
} }
} }
Loader { Connections {
id: leadingIconLoader function onCursorPositionChanged(): void {
if (root.activeFocus && root.cursorVisible) {
active: root.leadingIcon cursor.opacity = 1;
anchors.left: parent.left cursor.disableBlink = true;
anchors.leftMargin: Tokens.padding.small enableBlink.restart();
anchors.verticalCenter: parent.verticalCenter
sourceComponent: MaterialIcon {
color: Colors.palette.m3onSurfaceVariant
text: root.leadingIcon
fontStyle: Tokens.font.icon.builders.medium.scale(0.9).build()
}
}
Loader {
id: trailingIconLoader
active: root.trailingIcon
anchors.right: parent.right
anchors.rightMargin: Tokens.padding.small
anchors.verticalCenter: parent.verticalCenter
sourceComponent: MaterialIcon {
color: Colors.palette.m3onSurfaceVariant
text: root.trailingIcon
fontStyle: Tokens.font.icon.builders.medium.scale(0.9).build()
}
}
}
Loader {
id: supportingTextLoader
active: root.effectiveSupportingText
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.leftMargin: root.horizontalPadding
sourceComponent: CustomText {
color: root.isError ? Colors.palette.m3error : Colors.palette.m3onSurfaceVariant
font: Tokens.font.label.small
text: root.effectiveSupportingText
}
}
TapHandler {
id: tapHandler
}
Component {
id: outlineComp
Shape {
id: bg
asynchronous: true
preferredRendererType: Shape.CurveRenderer
ShapePath {
id: path
readonly property real inset: strokeWidth / 2
readonly property real outlineGap: placeholder.width * root.smallFontScale + Tokens.spacing.small * 2
property real outlineGapScale: root.activeFocus || root.text ? 1 : 0
capStyle: ShapePath.RoundCap
fillColor: "transparent"
startX: path.inset + root.horizontalPadding - root.clampedRadius + path.outlineGap * (1 - path.outlineGapScale) / 2 + path.outlineGap * path.outlineGapScale
startY: path.inset
strokeColor: root.isError ? Colors.palette.m3error : (root.activeFocus ? Colors.palette.m3primary : Colors.palette.m3outline)
strokeWidth: root.activeFocus ? 2 : 1
Behavior on outlineGapScale {
Anim {
type: Anim.DefaultEffects
}
}
Behavior on strokeColor {
CAnim {}
}
Behavior on strokeWidth {
Anim {}
}
PathLine {
x: bg.width - path.inset - root.clampedRadius
y: path.inset
}
PathArc {
radiusX: root.clampedRadius
radiusY: root.clampedRadius
x: bg.width - path.inset
y: path.inset + root.clampedRadius
}
PathLine {
x: bg.width - path.inset
y: bg.height - path.inset - root.clampedRadius
}
PathArc {
radiusX: root.clampedRadius
radiusY: root.clampedRadius
x: bg.width - path.inset - root.clampedRadius
y: bg.height - path.inset
}
PathLine {
x: path.inset + root.clampedRadius
y: bg.height - path.inset
}
PathArc {
radiusX: root.clampedRadius
radiusY: root.clampedRadius
x: path.inset
y: bg.height - path.inset - root.clampedRadius
}
PathLine {
x: path.inset
y: path.inset + root.clampedRadius
}
PathArc {
radiusX: root.clampedRadius
radiusY: root.clampedRadius
x: path.inset + root.clampedRadius
y: path.inset
}
PathLine {
x: path.inset + root.horizontalPadding - root.clampedRadius / 2 + path.outlineGap * (1 - path.outlineGapScale) / 2
y: path.inset
} }
} }
target: root
}
Timer {
id: enableBlink
interval: 100
onTriggered: cursor.disableBlink = false
}
Timer {
interval: 500
repeat: true
running: root.activeFocus && root.cursorVisible && !cursor.disableBlink
triggeredOnStart: true
onTriggered: parent.opacity = parent.opacity === 1 ? 0 : 1
}
Binding {
cursor.opacity: 0
when: !root.activeFocus || !root.cursorVisible
} }
} }
Behavior on placeholderTextColor {
Component { CAnim {
id: filledComp
CustomRect {
color: root.activeFocus ? Colors.tPalette.m3surfaceContainerHighest : Colors.tPalette.m3surfaceContainerHigh
topLeftRadius: root.clampedRadius
topRightRadius: root.clampedRadius
CustomRect {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
color: root.isError ? Colors.palette.m3error : (root.activeFocus ? Colors.palette.m3primary : Colors.palette.m3outline)
implicitHeight: root.activeFocus ? 2 : 1
Behavior on implicitHeight {
Anim {}
}
}
} }
} }
} }
+2 -3
View File
@@ -1,16 +1,15 @@
import QtQuick import QtQuick
import QtQuick.Controls
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
TextInput { TextInput {
renderType: Text.NativeRendering renderType: Text.NativeRendering
selectedTextColor: Colors.palette.m3onSecondaryContainer selectedTextColor: Colors.palette.m3onSecondaryContainer
selectionColor: Colors.tPalette.colSecondaryContainer selectionColor: Colors.tPalette.m3onSecondaryContainer
font { font {
family: Config.appearance?.font.family.sans ?? "sans-serif" family: Config.appearance?.font.family.sans ?? "sans-serif"
hintingPreference: Font.PreferFullHinting hintingPreference: Font.PreferFullHinting
pixelSize: Config.appearance?.font.size.normal ?? 15 pixelSize: Tokens?.font.size.normal ?? 15
} }
} }
-78
View File
@@ -1,78 +0,0 @@
import QtQuick
import Quickshell
import ZShell.Config
Image {
id: root
property int fadeInAnim: Anim.DefaultEffects
property int fadeInLargeAnim: Anim.StandardLarge
property int fadeOutAnim: Anim.FastEffects
property bool fadingOut
property bool hadPrevious
property bool preventInit
function maybeStartInAnim(): void {
if (!preventInit && !opacityInAnim.running && status === Image.Ready) {
opacityInAnim.type = hadPrevious ? fadeInAnim : fadeInLargeAnim;
opacityInAnim.start();
}
}
asynchronous: true
fillMode: Image.PreserveAspectCrop
opacity: 0
retainWhileLoading: true
sourceSize: {
const dpr = (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1;
return Qt.size(width * dpr, height * dpr);
}
Anim on opacity {
id: opacityInAnim
running: false
to: 1
}
Behavior on source {
SequentialAnimation {
ScriptAction {
script: opacityInAnim.stop()
}
PropertyAction {
property: "fadingOut"
target: root
value: true
}
Anim {
property: "opacity"
target: root
to: 0
type: root.fadeOutAnim
}
PropertyAction {
property: "fadingOut"
target: root
value: false
}
PropertyAction {
property: "hadPrevious"
target: root
value: root.source
}
PropertyAction {}
ScriptAction {
script: root.maybeStartInAnim()
}
}
}
onPreventInitChanged: maybeStartInAnim()
onStatusChanged: maybeStartInAnim()
}
-114
View File
@@ -1,114 +0,0 @@
import QtQuick
import Quickshell
import M3Shapes
import ZShell.Config
import qs.Services
MaterialShape {
id: root
property bool animated: true
property real cRotation
property bool containsIcon
property real dampingRatio: 0.6
property real lRotation
property int morphAnimRotation: 60
property real morphScale: 0.14
property alias rotateAnimDuration: rotateAnim.duration
property int shapeIndex
property list<int> shapes: {
if (containsIcon)
return [MaterialShape.SoftBurst, MaterialShape.Cookie9Sided, MaterialShape.Pill, MaterialShape.Sunny, MaterialShape.Cookie4Sided, MaterialShape.Oval];
return [MaterialShape.SoftBurst, MaterialShape.Cookie9Sided, MaterialShape.Pentagon, MaterialShape.Pill, MaterialShape.Sunny, MaterialShape.Cookie4Sided, MaterialShape.Oval];
}
readonly property real springDuration: {
const wn = Math.sqrt(stiffness);
const r = -dampingRatio * wn;
const c = 1 / Math.sqrt(1 - dampingRatio * dampingRatio);
return Math.log(visibilityThreshold / c) / r;
}
readonly property real springMaxVelocity: {
const wn = Math.sqrt(stiffness);
const factor = Math.exp(-z * Math.acos(z) / Math.sqrt(1 - z * z));
return wn * factor;
}
property bool springSettled: true
property real stiffness: 180
property real thisLRotation
property real visibilityThreshold: 0.075
function spring(t: real): var {
const wn = Math.sqrt(stiffness);
const za = dampingRatio * wn;
const wd = wn * Math.sqrt(1 - dampingRatio * dampingRatio);
const r = za / wd;
const pos = 1 - Math.exp(-za * t) * (Math.cos(wd * t) + r * Math.sin(wd * t));
const vel = Math.exp(-za * t) * (wn * wn / wd) * Math.sin(wd * t);
return [pos, vel];
}
color: Colors.palette.m3primary
implicitSize: 38
toShape: shapes[0]
RotationAnimation on cRotation {
id: rotateAnim
duration: 4666
easing.type: Easing.Linear
from: 0
loops: Animation.Infinite
running: root.animated
to: 360
}
Behavior on color {
CAnim {}
}
ElapsedTimer {
id: timer
}
FrameAnimation {
running: root.animated && !root.springSettled
onTriggered: {
const t = timer.elapsed();
if (t >= root.springDuration) {
root.springSettled = true;
} else {
const [pos, vel] = root.spring(t);
root.morphProgress = Math.min(1, pos); // Overshooting the morph looks weird
root.thisLRotation = pos * root.morphAnimRotation;
root.scale = 1 + vel * root.morphScale / root.springMaxVelocity;
}
}
}
Timer {
interval: 650
repeat: true
running: root.animated
triggeredOnStart: true
onTriggered: {
root.beginBatchUpdate();
root.fromShape = root.toShape;
root.shapeIndex = (root.shapeIndex + 1) % root.shapes.length;
root.toShape = root.shapes[root.shapeIndex];
root.morphProgress = 0;
root.rotation = root.rotation;
root.lRotation = (root.lRotation + root.thisLRotation) % 360;
root.thisLRotation = 0;
root.rotation = Qt.binding(() => root.cRotation + root.lRotation + root.thisLRotation);
root.springSettled = false;
timer.restart();
root.endBatchUpdate();
}
}
}
+9 -7
View File
@@ -1,14 +1,16 @@
import QtQuick
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
CustomText { CustomText {
id: root property real fill
property real fill: 0
property int grade: Colors.light ? 0 : -25 property int grade: Colors.light ? 0 : -25
property font fontStyle: Tokens.font.icon.small
renderType: Text.NativeRendering font.family: "Material Symbols Rounded"
font: Tokens.font.icon.size(fontStyle.pointSize).weight(fontStyle.weight).vaxes(fontStyle.variableAxes).fill(fill.toFixed(1)).grade(grade).build() font.pointSize: Tokens.font.size.larger
font.variableAxes: ({
FILL: fill.toFixed(1),
GRAD: grade,
opsz: fontInfo.pixelSize,
wght: fontInfo.weight
})
} }
+9 -7
View File
@@ -1,6 +1,8 @@
import QtQuick import QtQuick
import QtQuick.Shapes import QtQuick.Shapes
import ZShell import ZShell
import ZShell.Components
import qs.Helpers
import ZShell.Config import ZShell.Config
import qs.Services import qs.Services
@@ -11,6 +13,7 @@ MouseArea {
property alias bottomRightRadius: base.bottomRightRadius property alias bottomRightRadius: base.bottomRightRadius
property real circleRadius property real circleRadius
property alias color: base.color property alias color: base.color
property bool disabled
readonly property real endRadius: { readonly property real endRadius: {
const d1 = distSq(0, 0); const d1 = distSq(0, 0);
const d2 = distSq(width, 0); const d2 = distSq(width, 0);
@@ -19,7 +22,6 @@ MouseArea {
return (Math.sqrt(Math.max(d1, d2, d3, d4)) + (shapeMorph ? 24 : 0)) * 1.3; return (Math.sqrt(Math.max(d1, d2, d3, d4)) + (shapeMorph ? 24 : 0)) * 1.3;
} }
property real endRadiusAtPress property real endRadiusAtPress
property bool manualHoverOverride
property bool manualPressOverride property bool manualPressOverride
property real pressX: width / 2 property real pressX: width / 2
property real pressY: height / 2 property real pressY: height / 2
@@ -27,7 +29,7 @@ MouseArea {
readonly property alias rect: base readonly property alias rect: base
property bool shapeMorph property bool shapeMorph
property bool showHoverBackground: true property bool showHoverBackground: true
property real stateOpacity: containsMouse || manualHoverOverride ? 0.08 : 0 property real stateOpacity: containsMouse ? 0.08 : 0
property alias topLeftRadius: base.topLeftRadius property alias topLeftRadius: base.topLeftRadius
property alias topRightRadius: base.topRightRadius property alias topRightRadius: base.topRightRadius
@@ -50,8 +52,8 @@ MouseArea {
} }
anchors.fill: parent anchors.fill: parent
cursorShape: !enabled ? undefined : Qt.PointingHandCursor cursorShape: disabled ? undefined : Qt.PointingHandCursor
enabled: parent.enabled enabled: !disabled
hoverEnabled: true hoverEnabled: true
Behavior on stateOpacity { Behavior on stateOpacity {
@@ -79,7 +81,7 @@ MouseArea {
alwaysRunToEnd: true alwaysRunToEnd: true
duration: Tokens.anim.durations.expressiveSlowEffects * 2 duration: Tokens.anim.durations.expressiveSlowEffects * 2
easing: Tokens.anim.standard easing.bezierCurve: Tokens.anim.curves.standard
property: "circleRadius" property: "circleRadius"
target: root target: root
to: root.endRadius to: root.endRadius
@@ -98,13 +100,13 @@ MouseArea {
id: base id: base
anchors.fill: parent anchors.fill: parent
bottomLeftRadius: root.parent?.bottomLeftRadius ?? radius ?? 0
bottomRightRadius: root.parent?.bottomRightRadius ?? radius ?? 0
color: Colors.palette.m3onSurface color: Colors.palette.m3onSurface
opacity: root.stateOpacity opacity: root.stateOpacity
// Pick up radius from parent if it has one (parent can be anything with radius props) // Pick up radius from parent if it has one (parent can be anything with radius props)
// qmllint disable missing-property // qmllint disable missing-property
radius: root.parent?.radius ?? 0 radius: root.parent?.radius ?? 0
bottomLeftRadius: root.parent?.bottomLeftRadius ?? radius ?? 0
bottomRightRadius: root.parent?.bottomRightRadius ?? radius ?? 0
topLeftRadius: root.parent?.topLeftRadius ?? radius ?? 0 topLeftRadius: root.parent?.topLeftRadius ?? radius ?? 0
topRightRadius: root.parent?.topRightRadius ?? radius ?? 0 topRightRadius: root.parent?.topRightRadius ?? radius ?? 0
// qmllint enable missing-property // qmllint enable missing-property
+40 -17
View File
@@ -11,18 +11,36 @@ RowLayout {
required property var greeter required property var greeter
required property real screenHeight required property real screenHeight
spacing: Tokens.spacing.largeIncreased * 2 spacing: Tokens.spacing.large * 2
CustomRect { ColumnLayout {
Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
topLeftRadius: Tokens.rounding.largeIncreased spacing: Tokens.spacing.normal
bottomLeftRadius: Tokens.rounding.largeIncreased
color: Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.small
UserDock { CustomRect {
greeter: root.greeter Layout.fillWidth: true
color: Colors.tPalette.m3surfaceContainer
implicitHeight: weather.implicitHeight
radius: Tokens.rounding.small
topLeftRadius: Tokens.rounding.large
WeatherInfo {
id: weather
rootHeight: root.height
}
}
CustomClippingRect {
Layout.fillHeight: true
Layout.fillWidth: true
bottomLeftRadius: Tokens.rounding.large
color: Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.small
UserDock {
greeter: root.greeter
}
} }
} }
@@ -31,16 +49,21 @@ RowLayout {
screenHeight: root.screenHeight screenHeight: root.screenHeight
} }
CustomRect { ColumnLayout {
Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
bottomRightRadius: Tokens.rounding.largeIncreased spacing: Tokens.spacing.normal
color: Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.small
topRightRadius: Tokens.rounding.largeIncreased
SessionDock { CustomRect {
greeter: root.greeter Layout.fillHeight: true
Layout.fillWidth: true
bottomRightRadius: Tokens.rounding.large
color: Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.small
topRightRadius: Tokens.rounding.large
SessionDock {
greeter: root.greeter
}
} }
} }
} }
+7 -6
View File
@@ -43,7 +43,7 @@ Scope {
ParallelAnimation { ParallelAnimation {
Anim { Anim {
duration: Tokens.anim.durations.expressiveFastSpatial duration: Tokens.anim.durations.expressiveFastSpatial
easing: Tokens.anim.expressiveFastSpatial easing.bezierCurve: Tokens.anim.curves.expressiveFastSpatial
property: "scale" property: "scale"
target: lockContent target: lockContent
to: 1 to: 1
@@ -65,7 +65,7 @@ Scope {
Anim { Anim {
duration: Tokens.anim.durations.expressiveDefaultSpatial duration: Tokens.anim.durations.expressiveDefaultSpatial
easing: Tokens.anim.expressiveDefaultSpatial easing.bezierCurve: Tokens.anim.curves.expressiveDefaultSpatial
property: "scale" property: "scale"
target: content target: content
to: 1 to: 1
@@ -74,12 +74,12 @@ Scope {
Anim { Anim {
property: "radius" property: "radius"
target: lockBg target: lockBg
to: Tokens.rounding.largeIncreased * 1.5 to: Tokens.rounding.large * 1.5
} }
Anim { Anim {
duration: Tokens.anim.durations.expressiveDefaultSpatial duration: Tokens.anim.durations.expressiveDefaultSpatial
easing: Tokens.anim.expressiveDefaultSpatial easing.bezierCurve: Tokens.anim.curves.expressiveDefaultSpatial
property: "implicitWidth" property: "implicitWidth"
target: lockContent target: lockContent
to: (win.screen?.height ?? 0) * Config.lock.sizes.heightMult * Config.lock.sizes.ratio to: (win.screen?.height ?? 0) * Config.lock.sizes.heightMult * Config.lock.sizes.ratio
@@ -87,7 +87,7 @@ Scope {
Anim { Anim {
duration: Tokens.anim.durations.expressiveDefaultSpatial duration: Tokens.anim.durations.expressiveDefaultSpatial
easing: Tokens.anim.expressiveDefaultSpatial easing.bezierCurve: Tokens.anim.curves.expressiveDefaultSpatial
property: "implicitHeight" property: "implicitHeight"
target: lockContent target: lockContent
to: (win.screen?.height ?? 0) * Config.lock.sizes.heightMult to: (win.screen?.height ?? 0) * Config.lock.sizes.heightMult
@@ -134,7 +134,8 @@ Scope {
id: lockIcon id: lockIcon
anchors.centerIn: parent anchors.centerIn: parent
fontStyle: Tokens.font.icon.builders.extraLarge.size(Tokens.font.icon.extraLarge.pointSize * 3).build() font.bold: true
font.pointSize: Tokens.font.size.extraLarge * 4
text: "lock" text: "lock"
} }
+1
View File
@@ -200,5 +200,6 @@ Singleton {
ElapsedTimer { ElapsedTimer {
id: timer id: timer
} }
} }
+16 -9
View File
@@ -39,7 +39,8 @@ Item {
anchors.centerIn: parent anchors.centerIn: parent
animate: true animate: true
color: root.greeter.launching ? Colors.palette.m3secondary : Colors.palette.m3outline color: root.greeter.launching ? Colors.palette.m3secondary : Colors.palette.m3outline
font: Tokens.font.mono.medium font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.normal
opacity: root.buffer ? 0 : 1 opacity: root.buffer ? 0 : 1
text: { text: {
if (root.greeter.launching) if (root.greeter.launching)
@@ -50,7 +51,8 @@ Item {
} }
Behavior on opacity { Behavior on opacity {
Anim {} Anim {
}
} }
} }
@@ -62,13 +64,15 @@ Item {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: Colors.palette.m3onSurface color: Colors.palette.m3onSurface
elide: Text.ElideLeft elide: Text.ElideLeft
font: Tokens.font.mono.medium font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.normal
horizontalAlignment: Qt.AlignHCenter horizontalAlignment: Qt.AlignHCenter
opacity: root.greeter.echoResponse && root.buffer ? 1 : 0 opacity: root.greeter.echoResponse && root.buffer ? 1 : 0
text: root.buffer text: root.buffer
Behavior on opacity { Behavior on opacity {
Anim {} Anim {
}
} }
} }
@@ -85,7 +89,7 @@ Item {
anchors.centerIn: parent anchors.centerIn: parent
anchors.horizontalCenterOffset: implicitWidth > root.width ? -(implicitWidth - root.width) / 2 : 0 anchors.horizontalCenterOffset: implicitWidth > root.width ? -(implicitWidth - root.width) / 2 : 0
implicitHeight: Tokens.font.body.medium implicitHeight: Tokens.font.size.normal
implicitWidth: fullWidth implicitWidth: fullWidth
interactive: false interactive: false
opacity: root.greeter.echoResponse ? 0 : 1 opacity: root.greeter.echoResponse ? 0 : 1
@@ -103,12 +107,13 @@ Item {
scale: 0 scale: 0
Behavior on opacity { Behavior on opacity {
Anim {} Anim {
}
} }
Behavior on scale { Behavior on scale {
Anim { Anim {
duration: Tokens.anim.durations.expressiveFastSpatial duration: Tokens.anim.durations.expressiveFastSpatial
easing: Tokens.anim.expressiveFastSpatial easing.bezierCurve: Tokens.anim.curves.expressiveFastSpatial
} }
} }
@@ -151,13 +156,15 @@ Item {
Behavior on implicitWidth { Behavior on implicitWidth {
id: imWidthBehavior id: imWidthBehavior
Anim {} Anim {
}
} }
model: ScriptModel { model: ScriptModel {
values: root.buffer.split("") values: root.buffer.split("")
} }
Behavior on opacity { Behavior on opacity {
Anim {} Anim {
}
} }
} }
} }
+10 -5
View File
@@ -13,17 +13,21 @@ import qs.Paths
Singleton { Singleton {
id: root id: root
readonly property M3Palette current: M3Palette {} readonly property M3Palette current: M3Palette {
}
property bool currentLight property bool currentLight
property string flavour property string flavour
readonly property bool light: showPreview ? previewLight : currentLight readonly property bool light: showPreview ? previewLight : currentLight
readonly property M3Palette palette: showPreview ? preview : current readonly property M3Palette palette: showPreview ? preview : current
readonly property M3Palette preview: M3Palette {} readonly property M3Palette preview: M3Palette {
}
property bool previewLight property bool previewLight
property string scheme property string scheme
property bool showPreview property bool showPreview
readonly property M3TPalette tPalette: M3TPalette {} readonly property M3TPalette tPalette: M3TPalette {
readonly property Transparency transparency: Transparency {} }
readonly property Transparency transparency: Transparency {
}
readonly property alias wallLuminance: analyser.luminance readonly property alias wallLuminance: analyser.luminance
function alterColor(c: color, a: real, layer: int): color { function alterColor(c: color, a: real, layer: int): color {
@@ -108,7 +112,8 @@ Singleton {
function setMode(mode: string): void { function setMode(mode: string): void {
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--mode", mode]); Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--mode", mode]);
Config.colors.mode = mode; Config.general.color.mode = mode;
Config.save();
} }
function swapRG(c: color): color { function swapRG(c: color): color {
+22 -16
View File
@@ -13,19 +13,22 @@ ColumnLayout {
anchors.fill: parent anchors.fill: parent
anchors.margins: Tokens.padding.large anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.large spacing: Tokens.spacing.smaller
CustomText { CustomText {
Layout.fillWidth: true Layout.fillWidth: true
color: Colors.palette.m3outline color: Colors.palette.m3outline
elide: Text.ElideRight elide: Text.ElideRight
font: Tokens.font.mono.builders.small.weight(Font.Medium).build() font.family: Config.appearance.font.family.mono
font.weight: 500
text: root.greeter.sessions.length > 0 ? qsTr("%1 session%2").arg(root.greeter.sessions.length).arg(root.greeter.sessions.length === 1 ? "" : "s") : qsTr("Sessions") text: root.greeter.sessions.length > 0 ? qsTr("%1 session%2").arg(root.greeter.sessions.length).arg(root.greeter.sessions.length === 1 ? "" : "s") : qsTr("Sessions")
} }
Item { CustomClippingRect {
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
color: "transparent"
radius: Tokens.rounding.small
Loader { Loader {
active: opacity > 0 active: opacity > 0
@@ -38,19 +41,21 @@ ColumnLayout {
} }
} }
sourceComponent: ColumnLayout { sourceComponent: ColumnLayout {
spacing: Tokens.spacing.largeIncreased spacing: Tokens.spacing.large
MaterialIcon { MaterialIcon {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant color: Colors.palette.m3outlineVariant
fontStyle: Tokens.font.icon.large font.pointSize: Tokens.font.size.extraLarge * 2
text: "desktop_windows" text: "desktop_windows"
} }
CustomText { CustomText {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant color: Colors.palette.m3outlineVariant
font: Tokens.font.headline.builders.small.width(90).build() font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.large
font.weight: 500
text: qsTr("No Sessions Found") text: qsTr("No Sessions Found")
} }
} }
@@ -63,7 +68,6 @@ ColumnLayout {
clip: true clip: true
currentIndex: root.greeter.sessionIndex currentIndex: root.greeter.sessionIndex
highlightFollowsCurrentItem: false highlightFollowsCurrentItem: false
boundsBehavior: ListView.StopAtBounds
model: root.greeter.sessions model: root.greeter.sessions
spacing: Tokens.spacing.small spacing: Tokens.spacing.small
@@ -75,8 +79,8 @@ ColumnLayout {
anchors.left: parent?.left anchors.left: parent?.left
anchors.right: parent?.right anchors.right: parent?.right
implicitHeight: row.implicitHeight + Tokens.padding.small * 2 implicitHeight: row.implicitHeight + Tokens.padding.normal * 2
radius: Tokens.rounding.large radius: Tokens.rounding.normal - Tokens.padding.smaller
StateLayer { StateLayer {
onClicked: { onClicked: {
@@ -88,12 +92,12 @@ ColumnLayout {
id: row id: row
anchors.fill: parent anchors.fill: parent
anchors.margins: Tokens.padding.small anchors.margins: Tokens.padding.normal
spacing: Tokens.spacing.medium spacing: Tokens.spacing.normal
MaterialIcon { MaterialIcon {
color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurfaceVariant color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurfaceVariant
fontStyle: Tokens.font.icon.extraLarge font.pointSize: Tokens.font.size.extraLarge
text: modelData.kind === "x11" ? "tv" : "desktop_windows" text: modelData.kind === "x11" ? "tv" : "desktop_windows"
} }
@@ -105,7 +109,8 @@ ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
elide: Text.ElideRight elide: Text.ElideRight
font: Tokens.font.body.builders.medium.weight(Font.Bold).build() font.pointSize: Tokens.font.size.normal
font.weight: 600
text: modelData.name text: modelData.name
} }
@@ -113,7 +118,8 @@ ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimaryFixedVariant : Colors.palette.m3onSurfaceVariant color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimaryFixedVariant : Colors.palette.m3onSurfaceVariant
elide: Text.ElideRight elide: Text.ElideRight
font: Tokens.font.mono.small font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.small
text: modelData.kind text: modelData.kind
} }
} }
@@ -123,13 +129,13 @@ ColumnLayout {
color: Colors.palette.m3primary color: Colors.palette.m3primary
implicitHeight: sessions.currentItem?.implicitHeight ?? 0 implicitHeight: sessions.currentItem?.implicitHeight ?? 0
implicitWidth: sessions.width implicitWidth: sessions.width
radius: Tokens.rounding.large radius: Tokens.rounding.normal - Tokens.padding.smaller
y: sessions.currentItem?.y ?? 0 y: sessions.currentItem?.y ?? 0
Behavior on y { Behavior on y {
Anim { Anim {
duration: Tokens.anim.durations.small duration: Tokens.anim.durations.small
easing: Tokens.anim.expressiveDefaultEffects easing.bezierCurve: Tokens.anim.curves.expressiveEffects
} }
} }
} }
+22 -17
View File
@@ -14,19 +14,22 @@ ColumnLayout {
anchors.fill: parent anchors.fill: parent
anchors.margins: Tokens.padding.large anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.large spacing: Tokens.spacing.smaller
CustomText { CustomText {
Layout.fillWidth: true Layout.fillWidth: true
color: Colors.palette.m3outline color: Colors.palette.m3outline
elide: Text.ElideRight elide: Text.ElideRight
font: Tokens.font.mono.builders.small.weight(Font.Medium).build() font.family: Config.appearance.font.family.mono
font.weight: 500
text: root.greeter.users.length > 0 ? qsTr("%1 user%2").arg(root.greeter.users.length).arg(root.greeter.users.length === 1 ? "" : "s") : qsTr("Users") text: root.greeter.users.length > 0 ? qsTr("%1 user%2").arg(root.greeter.users.length).arg(root.greeter.users.length === 1 ? "" : "s") : qsTr("Users")
} }
Item { CustomClippingRect {
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
color: "transparent"
radius: Tokens.rounding.small
Loader { Loader {
active: opacity > 0 active: opacity > 0
@@ -39,20 +42,22 @@ ColumnLayout {
} }
} }
sourceComponent: ColumnLayout { sourceComponent: ColumnLayout {
spacing: Tokens.spacing.largeIncreased spacing: Tokens.spacing.large
MaterialIcon { MaterialIcon {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant color: Colors.palette.m3outlineVariant
fill: 1 fill: 1
fontStyle: Tokens.font.icon.large font.pointSize: Tokens.font.size.extraLarge * 2
text: "account_circle" text: "account_circle"
} }
CustomText { CustomText {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant color: Colors.palette.m3outlineVariant
font: Tokens.font.headline.builders.small.width(90).build() font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.large
font.weight: 500
text: qsTr("No Users Found") text: qsTr("No Users Found")
} }
} }
@@ -65,7 +70,6 @@ ColumnLayout {
clip: true clip: true
currentIndex: root.greeter.selectedUser ? root.greeter.users.indexOf(root.greeter.selectedUser) : -1 currentIndex: root.greeter.selectedUser ? root.greeter.users.indexOf(root.greeter.selectedUser) : -1
highlightFollowsCurrentItem: false highlightFollowsCurrentItem: false
boundsBehavior: ListView.StopAtBounds
model: root.greeter.users model: root.greeter.users
spacing: Tokens.spacing.small spacing: Tokens.spacing.small
@@ -77,8 +81,8 @@ ColumnLayout {
anchors.left: parent?.left anchors.left: parent?.left
anchors.right: parent?.right anchors.right: parent?.right
implicitHeight: row.implicitHeight + Tokens.padding.small * 2 implicitHeight: row.implicitHeight + Tokens.padding.normal * 2
radius: Tokens.rounding.large radius: Tokens.rounding.normal - Tokens.padding.smaller
StateLayer { StateLayer {
onClicked: { onClicked: {
@@ -90,10 +94,10 @@ ColumnLayout {
id: row id: row
anchors.fill: parent anchors.fill: parent
anchors.margins: Tokens.padding.small anchors.margins: Tokens.padding.normal
spacing: Tokens.spacing.medium spacing: Tokens.spacing.normal
ClipRect { CustomClippingRect {
Layout.preferredHeight: 50 Layout.preferredHeight: 50
Layout.preferredWidth: 50 Layout.preferredWidth: 50
radius: Tokens.rounding.full radius: Tokens.rounding.full
@@ -102,7 +106,7 @@ ColumnLayout {
anchors.centerIn: parent anchors.centerIn: parent
color: user.index === users.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurfaceVariant color: user.index === users.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurfaceVariant
fill: 1 fill: 1
fontStyle: Tokens.font.icon.extraLarge font.pointSize: Tokens.font.size.extraLarge
text: modelData.kind === "x11" ? "tv" : "account_circle" text: modelData.kind === "x11" ? "tv" : "account_circle"
} }
@@ -120,7 +124,8 @@ ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
color: user.index === users.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface color: user.index === users.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
elide: Text.ElideRight elide: Text.ElideRight
font: Tokens.font.body.builders.medium.weight(Font.Bold).build() font.pointSize: Tokens.font.size.normal
font.weight: 600
text: modelData.username text: modelData.username
} }
} }
@@ -130,13 +135,13 @@ ColumnLayout {
color: Colors.palette.m3primary color: Colors.palette.m3primary
implicitHeight: users.currentItem?.implicitHeight ?? 0 implicitHeight: users.currentItem?.implicitHeight ?? 0
implicitWidth: users.width implicitWidth: users.width
radius: Tokens.rounding.large radius: Tokens.rounding.normal - Tokens.padding.smaller
y: users.currentItem?.y ?? 0 y: users.currentItem?.y ?? 0
Behavior on y { Behavior on y {
Anim { Anim {
duration: Tokens.anim.durations.expressiveDefaultEffects duration: Tokens.anim.durations.small
easing: Tokens.anim.expressiveDefaultEffects easing.bezierCurve: Tokens.anim.curves.expressiveEffects
} }
} }
} }
+170
View File
@@ -0,0 +1,170 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Components
import qs.Helpers
import qs.Services
ColumnLayout {
id: root
required property int rootHeight
anchors.left: parent.left
anchors.margins: Tokens.padding.large * 2
anchors.right: parent.right
spacing: Tokens.spacing.small
Loader {
Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: -Tokens.padding.large
Layout.topMargin: Tokens.padding.large * 2
active: root.rootHeight > 610
visible: active
sourceComponent: CustomText {
color: Colors.palette.m3primary
font.pointSize: Tokens.font.size.extraLarge
font.weight: 500
text: qsTr("Weather")
}
}
RowLayout {
Layout.fillWidth: true
spacing: Tokens.spacing.large
MaterialIcon {
animate: true
color: Colors.palette.m3secondary
font.pointSize: Tokens.font.size.extraLarge * 2.5
text: Weather.icon
}
ColumnLayout {
spacing: Tokens.spacing.small
CustomText {
Layout.fillWidth: true
animate: true
color: Colors.palette.m3secondary
elide: Text.ElideRight
font.pointSize: Tokens.font.size.large
font.weight: 500
text: Weather.description
}
CustomText {
Layout.fillWidth: true
animate: true
color: Colors.palette.m3onSurfaceVariant
elide: Text.ElideRight
font.pointSize: Tokens.font.size.normal
text: qsTr("Humidity: %1%").arg(Weather.humidity)
}
}
Loader {
Layout.rightMargin: Tokens.padding.smaller
active: root.width > 400
visible: active
sourceComponent: ColumnLayout {
spacing: Tokens.spacing.small
CustomText {
Layout.fillWidth: true
animate: true
color: Colors.palette.m3primary
elide: Text.ElideLeft
font.pointSize: Tokens.font.size.extraLarge
font.weight: 500
horizontalAlignment: Text.AlignRight
text: Weather.temp
}
CustomText {
Layout.fillWidth: true
animate: true
color: Colors.palette.m3outline
elide: Text.ElideLeft
font.pointSize: Tokens.font.size.smaller
horizontalAlignment: Text.AlignRight
text: qsTr("Feels like: %1").arg(Weather.feelsLike)
}
}
}
}
Loader {
id: forecastLoader
Layout.bottomMargin: Tokens.padding.large * 2
Layout.fillWidth: true
Layout.topMargin: Tokens.spacing.smaller
active: root.rootHeight > 820
visible: active
sourceComponent: RowLayout {
spacing: Tokens.spacing.large
Repeater {
model: {
const forecast = Weather.hourlyForecast;
const count = root.width < 320 ? 3 : root.width < 400 ? 4 : 5;
if (!forecast)
return Array.from({
length: count
}, () => null);
return forecast.slice(0, count);
}
ColumnLayout {
id: forecastHour
required property var modelData
Layout.fillWidth: true
spacing: Tokens.spacing.small
CustomText {
Layout.fillWidth: true
color: Colors.palette.m3outline
font.pointSize: Tokens.font.size.larger
horizontalAlignment: Text.AlignHCenter
text: {
const hour = forecastHour.modelData?.hour ?? 0;
return hour > 12 ? `${(hour - 12).toString().padStart(2, "0")} PM` : `${hour.toString().padStart(2, "0")} AM`;
}
}
MaterialIcon {
Layout.alignment: Qt.AlignHCenter
font.pointSize: Tokens.font.size.extraLarge * 1.5
font.weight: 500
text: forecastHour.modelData?.icon ?? "cloud_alert"
}
CustomText {
Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3secondary
font.pointSize: Tokens.font.size.larger
text: Config.services.useFahrenheit ? `${forecastHour.modelData?.tempF ?? 0}°F` : `${forecastHour.modelData?.tempC ?? 0}°C`
}
}
}
}
}
Timer {
interval: 900000 // 15 minutes
repeat: true
running: true
triggeredOnStart: true
onTriggered: Weather.reload()
}
}

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