84 Commits

Author SHA1 Message Date
zach 04fb534a05 scroll deceleration env
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 20s
Python / test (pull_request) Successful in 48s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m8s
2026-06-06 21:21:32 +02:00
zach 50e99501de switches and popouts
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 25s
Python / test (pull_request) Successful in 51s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m11s
2026-06-06 21:13:44 +02:00
zach 65c56bc598 updated components
Python / lint-format (pull_request) Successful in 41s
Python / test (pull_request) Successful in 1m9s
Lint & Format (Rust) / lint-format (pull_request) Successful in 2m42s
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 20s
2026-06-06 00:49:19 +02:00
zach 82518006c3 revert toaster enum 2026-06-05 19:45:43 +02:00
zach 9cefdf509c cmake build hotfix 2026-06-05 12:31:37 +02:00
zach 27924aca37 disable incompatible-type for qmllint in clock 2026-06-04 23:22:28 +02:00
zach b4716d25c0 nodiscard 2026-06-04 22:57:45 +02:00
zach d8f047dbc9 nodiscard 2026-06-04 22:38:17 +02:00
zach 91b50b312d open launcher with gesture from bottom border 2026-06-04 19:01:02 +02:00
zach 3e933a8b78 remove logging 2026-06-04 18:56:42 +02:00
zach e127928126 Merge pull request 'Rewrite the manager responsible for handling automatic hyprsunset temperature activation as a qml plugin' (#117) from hyprsunset-manager-rewrite into main
Reviewed-on: #117
2026-06-04 15:05:45 +02:00
zach 94f2cf076c fix applying end() on init
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 23s
Python / test (pull_request) Successful in 49s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m3s
2026-06-04 15:05:18 +02:00
zach 9168b6e893 Merge branch 'main' into hyprsunset-manager-rewrite
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 24s
Python / test (pull_request) Successful in 42s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m3s
2026-06-04 14:56:53 +02:00
zach a477fb2e22 fix namespaces and logging names 2026-06-04 14:56:20 +02:00
zach 6586cc2788 do not use apply() in initializer
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 17s
Python / test (pull_request) Successful in 48s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-06-03 23:21:07 +02:00
zach 0be98a64ac Merge branch 'main' into hyprsunset-manager-rewrite
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 15s
Python / lint-format (pull_request) Successful in 23s
Python / test (pull_request) Successful in 54s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-06-03 18:02:27 +02:00
zach b6e7ee7a54 Merge pull request 'lid behavior watcher to lock session' (#115) from lid-switch-behavior into main
Reviewed-on: #115
2026-06-03 18:02:21 +02:00
AramJonghu 59789ab8d3 unused imports in shell.qml
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 21s
Python / test (pull_request) Successful in 51s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-06-03 18:01:10 +02:00
AramJonghu a128c0fa40 removal reduntant config option and settings, unused lines in Lock.qml
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 21s
Python / test (pull_request) Successful in 52s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-06-03 17:59:03 +02:00
AramJonghu 6f856e2162 fix typo
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 18s
Python / test (pull_request) Successful in 44s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m5s
2026-06-03 17:47:12 +02:00
AramJonghu a19701222b forgotton import and removal LidService
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 26s
Python / test (pull_request) Successful in 41s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-06-03 17:43:58 +02:00
AramJonghu 0d8f558f66 Took caelestia lid logic (prepare to sleep) instead
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 19s
Python / test (pull_request) Successful in 45s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m3s
2026-06-03 17:41:29 +02:00
AramJonghu ed28d8b56a Took caelestia lid logic (prepare to sleep) instead 2026-06-03 17:41:17 +02:00
AramJonghu deef85d10c troubleshooting dbus
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 15s
Python / lint-format (pull_request) Successful in 19s
Python / test (pull_request) Successful in 49s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m5s
2026-06-03 17:02:46 +02:00
AramJonghu f9ab1e2a10 inability to connect to DBus.Properties resolved
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 13s
Python / lint-format (pull_request) Successful in 20s
Python / test (pull_request) Successful in 51s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m3s
2026-06-03 16:51:15 +02:00
AramJonghu 7dbce0bf8c qmlls lied to me - import ZShell returned
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 23s
Python / test (pull_request) Successful in 45s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-06-03 16:29:11 +02:00
AramJonghu 2920c57163 removes direct extern access, receives signal properly now, removed declaration unused method
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 14s
Python / lint-format (pull_request) Successful in 21s
Python / test (pull_request) Successful in 44s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m6s
2026-06-03 16:27:31 +02:00
AramJonghu 0584cd618e missing changes
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 31s
Python / test (pull_request) Successful in 50s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m3s
2026-06-03 02:30:13 +02:00
AramJonghu 896e5e520d Added clang-format/tidy for additional rules. Adjusted inbranch cpp files.
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 15s
Python / lint-format (pull_request) Successful in 37s
Python / test (pull_request) Successful in 59s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m20s
2026-06-03 02:05:18 +02:00
AramJonghu 45f36ce71c added lock. in shell.qml, wrong scope
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 20s
Python / test (pull_request) Successful in 47s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m5s
2026-06-03 01:14:37 +02:00
AramJonghu 3bcdbbabbb build fix
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 16s
Python / lint-format (pull_request) Successful in 23s
Python / test (pull_request) Successful in 48s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m9s
2026-06-03 01:02:50 +02:00
AramJonghu 016dcc008f Cpp changes, minor refactor plus separate signal logic, typo.
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 26s
Python / test (pull_request) Successful in 1m3s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m9s
- typo in searchindex
- Cpp plugin changed to use enum lidstate
- minor refactor
- Using signals instead of direct property access.
- Using states instead
- checking lidclosed instead of preparetosleep
2026-06-03 00:54:44 +02:00
zach d246ba1800 rewrite the manager responsible for handling automatic hyprsunset temperature activation as a qml plugin
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 13s
Python / lint-format (pull_request) Successful in 19s
Python / test (pull_request) Successful in 50s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m2s
2026-06-02 16:09:48 +02:00
AramJonghu c91b53fbaa QTQuick
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 24s
Python / test (pull_request) Successful in 50s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m7s
2026-06-02 00:03:30 +02:00
AramJonghu ad2ee99d9c format using qmlformat
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 20s
Python / test (pull_request) Successful in 51s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m5s
2026-06-01 23:59:07 +02:00
AramJonghu 7da2c7827a refactor: using more oop
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 14s
Python / lint-format (pull_request) Successful in 20s
Python / test (pull_request) Successful in 52s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m6s
2026-06-01 23:53:26 +02:00
AramJonghu db9c98b322 set default enabled for laptops, disabled for non battery devices
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 15s
Python / lint-format (pull_request) Successful in 22s
Python / test (pull_request) Successful in 46s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m5s
2026-06-01 21:03:59 +02:00
AramJonghu 1c1c6275df move lid watch toggle into lock settings 2026-06-01 20:45:04 +02:00
AramJonghu 83cf008a19 setting option to disable lidwatcher, also in settingswindow 2026-06-01 20:28:55 +02:00
AramJonghu c514e98687 nonexistant locked should be resolved 2026-06-01 20:12:17 +02:00
AramJonghu 0d097524c3 shell.qml could not load Connections, now fixed 2026-06-01 20:07:09 +02:00
AramJonghu 7de8cc3104 lidwatcher plugin initial setup 2026-06-01 20:02:37 +02:00
AramJonghu 82aa7c415f initial commit 2026-06-01 19:50:40 +02:00
zach 807b2525b7 Merge pull request 'initial refactor of Interactions.qml to add better support for touch screen gestures' (#114) from feat/improved-gestures into main
Reviewed-on: #114
Reviewed-by: AramJonghu <2+aramjonghu@noreply.git.zach-dev.cc>
Reviewed-by: Inorishio <inorishio@gmail.com>
2026-06-01 19:18:53 +02:00
zach 9e3cad6dbd Merge branch 'main' into feat/improved-gestures
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 13s
Python / lint-format (pull_request) Successful in 25s
Python / test (pull_request) Successful in 51s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m3s
2026-06-01 19:01:12 +02:00
zach 9436acd626 change charging color 2026-06-01 17:12:28 +02:00
AramJonghu 17e600e78e Merge branch 'main' into feat/improved-gestures
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 18s
Python / test (pull_request) Successful in 47s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m6s
2026-06-01 15:05:04 +02:00
zach 7eba84c8be Better battery indicator and dedicated Battery singleton in helpers 2026-06-01 15:04:48 +02:00
zach 0820c8e023 Merge branch 'main' into feat/improved-gestures
Python / lint-format (pull_request) Successful in 22s
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 27s
Python / test (pull_request) Successful in 50s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m2s
2026-06-01 12:13:33 +02:00
zach ebedf4b6fe increase slider visibility and remove debug logging 2026-06-01 12:12:00 +02:00
zach 6926880074 remove multi-touch handlers, single point for sidebar
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 21s
Python / test (pull_request) Successful in 50s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m1s
2026-05-31 23:05:31 +02:00
zach 1e1c90a0c5 remove redundant logging
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 22s
Python / test (pull_request) Successful in 43s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m2s
2026-05-31 22:45:32 +02:00
zach db6051457f fix drag gestures firing more than once from one drag
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 20s
Python / test (pull_request) Successful in 47s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m2s
2026-05-31 22:30:20 +02:00
zach 8f381fa8f0 Merge branch 'main' into feat/improved-gestures
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 14s
Python / lint-format (pull_request) Successful in 20s
Python / test (pull_request) Successful in 51s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-05-31 22:08:09 +02:00
zach 2a7cd66f40 Actually fix background desync, blobs didn't follow final settle after deform shader 2026-05-31 19:06:17 +02:00
zach cf22b41f07 allow pointer takeover by anything, restrict takeover from items
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 23s
Python / test (pull_request) Successful in 41s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m2s
2026-05-31 14:32:39 +02:00
zach c267dfb0c2 remove leftover test timer and import
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 19s
Python / test (pull_request) Successful in 58s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-05-31 14:16:36 +02:00
zach e06fd71f11 initial refactor of Interactions.qml to add better support for touch screen gestures
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 19s
Python / test (pull_request) Successful in 51s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m5s
2026-05-31 13:58:21 +02:00
zach d041ce2471 Merge pull request 'zshell-img-tools' (#104) from zshell-img-tools into main
Reviewed-on: #104
2026-05-31 00:30:12 +02:00
Inorishio 7f3397e27c changes comments
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 19s
Python / test (pull_request) Successful in 42s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-05-31 00:23:13 +02:00
Inorishio 0d7c5d199a Update zshell-img-tools/src/main.rs
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 19s
Python / test (pull_request) Successful in 47s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m2s
Lint had issues with --help so for now I'm just removing it.
2026-05-30 20:59:30 +02:00
Inorishio 8ea295d1ce Some prework for the binary, linter issue is resolved, unnecessary comments removed
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 19s
Python / test (pull_request) Successful in 50s
Lint & Format (Rust) / lint-format (pull_request) Failing after 49s
2026-05-30 20:40:14 +02:00
zach 164c0a18c2 disable screenshot options in gui when disabling effects
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 52s
Lint & Format (Rust) / lint-format (pull_request) Failing after 1m7s
2026-05-29 23:44:36 +02:00
zach 1d0fc63177 Merge branch 'main' into zshell-img-tools
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 33s
Python / test (pull_request) Successful in 44s
Lint & Format (Rust) / lint-format (pull_request) Failing after 1m9s
2026-05-29 23:25:07 +02:00
zach 79998a36f0 Merge pull request 'either fail ? then check fail else pass' (#112) from format-check-rust-fix into main
Reviewed-on: #112
Reviewed-by: zach <zach@brohn.se>
2026-05-29 23:24:49 +02:00
AramJonghu a747f21af5 Merge branch 'main' into format-check-rust-fix
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 23s
Python / lint-format (pull_request) Successful in 23s
Python / test (pull_request) Successful in 43s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m43s
2026-05-29 20:14:06 +02:00
AramJonghu 21c6940fb1 either fail ? then check fail else pass
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Lint & Format (Rust) / lint-format (pull_request) Has been cancelled
Python / lint-format (pull_request) Successful in 29s
Python / test (pull_request) Successful in 45s
2026-05-29 20:12:36 +02:00
zach c4cca73fd9 Merge branch 'main' into zshell-img-tools
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 24s
Python / test (pull_request) Successful in 45s
Lint & Format (Rust) / lint-format (pull_request) Successful in 50s
2026-05-29 12:28:13 +02:00
zach d6102d9ebe fix battery popup thresholds firing more than once per charging period 2026-05-29 12:25:28 +02:00
zach 84db7f41af Merge branch 'main' into zshell-img-tools
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 32s
Python / test (pull_request) Successful in 47s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m3s
2026-05-29 11:50:27 +02:00
zach 0819e8e2d1 Merge pull request 'format check shows green check, not anymore when format shows fail' (#110) from format-check-rust-fix into main
Reviewed-on: #110
Reviewed-by: zach <zach@brohn.se>
2026-05-29 11:50:10 +02:00
AramJonghu 0c47ba805f format check shows green check, not anymore when format shows fail
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 21s
Python / test (pull_request) Successful in 51s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m43s
2026-05-29 00:19:25 +02:00
zach 84ecd1481b Merge branch 'main' into zshell-img-tools
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 25s
Python / test (pull_request) Successful in 50s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m8s
2026-05-28 23:24:26 +02:00
zach 428809fee8 Merge pull request 'hotfix zshell-cli autocompletion failing' (#108) from hotfix-zshell-autocompletion into main
Reviewed-on: #108
Reviewed-by: zach <zach@brohn.se>
2026-05-28 23:24:17 +02:00
zach 1d667bedfd incorrect color channel assignment fixed
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 17s
Python / lint-format (pull_request) Successful in 24s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m11s
Python / test (pull_request) Successful in 1m16s
2026-05-28 23:19:57 +02:00
zach 760b8bfb93 pass hyprland options when auto is enabled
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 21s
Python / test (pull_request) Successful in 53s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-05-28 23:16:04 +02:00
AramJonghu 52be099914 removed exception in favor of sys.exit() to exit silently with print
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 20s
Python / test (pull_request) Successful in 44s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m46s
2026-05-28 22:43:25 +02:00
Inorishio 1d0ee72498 Merge branch 'zshell-img-tools' of git.zach-dev.cc:zach/z-bar-qt into zshell-img-tools
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 15s
Python / lint-format (pull_request) Successful in 19s
Python / test (pull_request) Successful in 43s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m3s
2026-05-28 20:23:38 +02:00
Inorishio d0a3a0a269 updated 2026-05-28 20:23:28 +02:00
Inorishio 0305f5a2be --corners > --corner 2026-05-28 19:11:47 +02:00
zach 494653e029 Merge branch 'main' into hotfix-zshell-autocompletion
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 18s
Python / test (pull_request) Successful in 1m20s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m57s
2026-05-28 16:55:42 +02:00
AramJonghu e9aa8268be ctx is unused
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 13s
Python / lint-format (pull_request) Successful in 19s
Python / test (pull_request) Successful in 47s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m46s
2026-05-28 14:25:43 +02:00
AramJonghu 0ad28ac017 now a check if cli completion is installed 2026-05-28 14:08:02 +02:00
AramJonghu fda3712855 attempt hotfix 2026-05-28 13:49:34 +02:00
77 changed files with 2917 additions and 1447 deletions
+58
View File
@@ -0,0 +1,58 @@
---
BasedOnStyle: LLVM
AccessModifierOffset: 0
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignOperands: Align
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: Never
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false
BinPackParameters: false
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeComma
ColumnLimit: 80
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
FixNamespaceComments: true
IndentCaseLabels: false
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: false
Language: Cpp
MaxEmptyLinesToKeep: 2
NamespaceIndentation: None
PenaltyBreakAssignment: 10
PenaltyBreakBeforeFirstCallParameter: 100
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 100
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
ReflowComments: false
SortIncludes: false
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: c++20
TabWidth: 4
UseTab: Always
+31
View File
@@ -0,0 +1,31 @@
---
Checks: >
-*,
bugprone-*,
clang-analyzer-*,
modernize-*,
-modernize-use-trailing-return-type,
performance-*,
readability-braces-around-statements,
readability-else-after-return,
readability-identifier-naming,
readability-redundant-*,
readability-simplify-*,
CheckOptions:
readability-identifier-naming.ClassCase: CamelCase
readability-identifier-naming.EnumCase: CamelCase
readability-identifier-naming.FunctionCase: camelBack
readability-identifier-naming.MemberCase: camelBack
readability-identifier-naming.MemberPrefix: m_
readability-identifier-naming.MethodCase: camelBack
readability-identifier-naming.NamespaceCase: CamelCase
readability-identifier-naming.ParameterCase: camelBack
readability-identifier-naming.PrivateMemberPrefix: m_
readability-identifier-naming.StaticConstantCase: UPPER_CASE
readability-identifier-naming.StaticConstantPrefix: k
readability-identifier-naming.VariableCase: camelBack
WarningsAsErrors: "*"
HeaderFilterRegex: ".*"
FormatStyle: file
...
+16 -3
View File
@@ -38,15 +38,18 @@ jobs:
rustfmt \
rust-clippy
- name: Format check
- id: format-check
name: Format check
continue-on-error: true
run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
status=0
for manifest in $(find . -name "Cargo.toml"); do
cargo fmt --manifest-path "$manifest" --check && \
echo "$manifest: formatting OK" || \
echo "$manifest: needs formatting"
{ echo "$manifest: needs formatting"; status=1; }
done
exit $status
elif [ -n "$(find . -name "*.rs" -print -quit)" ]; then
echo "Rust files found but no Cargo.toml"
exit 1
@@ -54,7 +57,8 @@ jobs:
echo "No Rust project found"
fi
- name: Clippy
- id: clippy
name: Clippy
run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
status=0
@@ -70,3 +74,12 @@ jobs:
else
echo "No Rust project found"
fi
- name: Check results
if: always()
run: |
if [ "${{ steps.format-check.outcome }}" = "failure" ] || [ "${{ steps.clippy.outcome }}" = "failure" ]; then
echo "One or more checks failed"
exit 1
fi
echo "All checks passed"
+1
View File
@@ -25,6 +25,7 @@ add_compile_options(
if("plugin" IN_LIST ENABLE_MODULES)
add_subdirectory(Plugins)
endif()
if("shell" IN_LIST ENABLE_MODULES)
+58 -3
View File
@@ -2,7 +2,62 @@ import QtQuick
import qs.Config
NumberAnimation {
duration: Appearance.anim.durations.normal
easing.bezierCurve: Appearance.anim.curves.standard
easing.type: Easing.BezierSpline
enum Type {
StandardSmall = 0,
Standard,
StandardLarge,
StandardExtraLarge,
EmphasizedSmall,
Emphasized,
EmphasizedLarge,
EmphasizedExtraLarge,
FastSpatial,
DefaultSpatial,
SlowSpatial,
FastEffects,
DefaultEffects,
SlowEffects
}
property int type: Anim.DefaultSpatial
duration: {
if (type < Anim.StandardSmall || type > Anim.SlowEffects)
return Appearance.anim.durations.normal;
if (type === Anim.FastSpatial)
return Appearance.anim.durations.expressiveFastSpatial;
if (type === Anim.DefaultSpatial)
return Appearance.anim.durations.expressiveDefaultSpatial;
if (type === Anim.SlowSpatial)
return Appearance.anim.durations.large;
if (type === Anim.FastEffects)
return Appearance.anim.durations.expressiveFastEffects;
if (type === Anim.DefaultEffects)
return Appearance.anim.durations.expressiveEffects;
if (type === Anim.SlowEffects)
return Appearance.anim.durations.expressiveSlowEffects;
const types = ["small", "normal", "large", "extraLarge"];
const idx = type % 4; // 0-7 are the 4 standard types
return Appearance.anim.durations[types[idx]];
}
easing.bezierCurve: {
if (type === Anim.FastSpatial)
return Appearance.anim.curves.expressiveFastSpatial;
if (type === Anim.DefaultSpatial)
return Appearance.anim.curves.expressiveDefaultSpatial;
if (type === Anim.SlowSpatial)
return Appearance.anim.curves.expressiveSlowSpatial;
if (type === Anim.FastEffects)
return Appearance.anim.curves.expressiveFastEffects;
if (type === Anim.DefaultEffects)
return Appearance.anim.curves.expressiveDefaultEffects;
if (type === Anim.SlowEffects)
return Appearance.anim.curves.expressiveSlowEffects;
if (type >= Anim.EmphasizedSmall && type <= Anim.EmphasizedExtraLarge)
return Appearance.anim.curves.emphasized;
return Appearance.anim.curves.standard;
}
}
+156 -29
View File
@@ -1,47 +1,174 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Templates
import ZShell.Components
import ZShell
import qs.Components
import qs.Config
Slider {
id: root
property color color: DynamicColors.palette.m3primary
property bool animateWave
property color bgColor: enabled ? DynamicColors.palette.m3secondaryContainer : Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
property color fgColor: enabled ? DynamicColors.palette.m3primary : Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
property real filledWidth
property real pos: visualPosition
property int waveDuration: 1000
property real waveFrequency: 6
property bool wavy
background: Item {
CustomRect {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: parent.top
bottomRightRadius: root.implicitHeight / 6
color: root.color
implicitWidth: root.handle.x - root.implicitHeight / 2
radius: Appearance.rounding.full
topRightRadius: root.implicitHeight / 6
}
signal interaction(v: real)
implicitHeight: 12
implicitWidth: 200
contentItem: Item {
anchors.fill: parent
CustomRect {
anchors.bottom: parent.bottom
id: remaining
anchors.left: handle.right
anchors.leftMargin: Appearance.spacing.extraSmall
anchors.right: parent.right
anchors.top: parent.top
bottomLeftRadius: root.implicitHeight / 6
color: DynamicColors.tPalette.m3surfaceContainer
implicitWidth: parent.width - root.handle.x - root.handle.implicitWidth - root.implicitHeight / 2
radius: Appearance.rounding.full
topLeftRadius: root.implicitHeight / 6
}
}
handle: CustomRect {
anchors.verticalCenter: parent.verticalCenter
color: root.color
implicitHeight: 15
implicitWidth: 5
bottomLeftRadius: Appearance.rounding.extraSmall / 2
color: root.bgColor
implicitHeight: parent.height * (parent.height <= 12 ? opacity : Math.min(opacity * 2, 1))
opacity: Math.min(width, 12) / 12
radius: Appearance.rounding.small
topLeftRadius: Appearance.rounding.extraSmall / 2
}
CustomRect {
anchors.right: parent.right
anchors.rightMargin: 4 * remaining.opacity
anchors.verticalCenter: parent.verticalCenter
color: root.fgColor
implicitHeight: 4 * remaining.opacity
implicitWidth: implicitHeight
opacity: remaining.opacity
radius: Appearance.rounding.full
x: root.visualPosition * root.availableWidth - implicitWidth / 2
}
CustomRect {
id: handle
anchors.left: filled.right
anchors.leftMargin: Appearance.spacing.extraSmall
anchors.verticalCenter: parent.verticalCenter
color: root.fgColor
implicitHeight: {
const mult = parent.height <= 12 ? 3 : 1.2;
const pressMult = parent.height <= 12 ? 4 : 1.5;
return parent.height * (mouse.pressed ? pressMult : mult);
}
implicitWidth: 4
radius: Appearance.rounding.full
Behavior on implicitHeight {
Anim {
type: Anim.FastSpatial
}
}
}
Loader {
id: filled
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
asynchronous: true
sourceComponent: root.wavy ? waveComp : lineComp
}
Component {
id: lineComp
CustomRect {
bottomRightRadius: Appearance.rounding.extraSmall / 2
color: root.fgColor
implicitHeight: root.height
implicitWidth: root.filledWidth
radius: Appearance.rounding.small
topRightRadius: Appearance.rounding.extraSmall / 2
}
}
Component {
id: waveComp
WavyLine {
color: root.fgColor
frequency: root.waveFrequency
fullLength: root.width - handle.implicitWidth - handle.anchors.leftMargin
implicitHeight: lineWidth * amplitudeMultiplier * 2 + lineWidth
implicitWidth: root.filledWidth
lineWidth: root.height * 0.7
startX: x
Behavior on color {
CAnim {
}
}
Anim on waveProgress {
duration: root.waveDuration
easing.type: Easing.Linear
from: 0
loops: Animation.Infinite
paused: !root.animateWave
running: true
to: 1
}
}
}
}
Behavior on filledWidth {
id: widthBehavior
Anim {
}
}
Component.onCompleted: filledWidth = Qt.binding(() => (width - handle.implicitWidth - handle.anchors.leftMargin) * pos)
Binding {
id: posBinding
property: "pos"
target: root
value: ZUtils.clamp(mouse.pressStartPos + mouse.dragMovement, 0, 1)
when: mouse.pressed
}
MouseArea {
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
id: mouse
property real dragMovement
property real pressStartPos
property real pressStartX
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
implicitHeight: handle.implicitHeight
preventStealing: true
onPositionChanged: e => {
dragMovement = (e.x - pressStartX) / width;
root.interaction(posBinding.value);
}
onPressed: e => {
widthBehavior.enabled = false;
pressStartX = e.x;
pressStartPos = root.visualPosition;
}
onReleased: e => {
root.interaction(posBinding.value);
widthBehavior.enabled = true;
dragMovement = 0;
}
}
}
+6 -5
View File
@@ -28,6 +28,7 @@ RowLayout {
CustomTextField {
id: textField
color: root.enabled ? DynamicColors.palette.m3onSurface : Qt.alpha(DynamicColors.palette.m3onSurface, 0.5)
implicitHeight: upButton.implicitHeight
inputMethodHints: Qt.ImhFormattedNumbersOnly
leftPadding: Appearance.padding.normal
@@ -36,7 +37,7 @@ RowLayout {
text: root.isEditing ? text : root.displayText
background: CustomRect {
color: DynamicColors.tPalette.m3surfaceContainerHigh
color: root.enabled ? DynamicColors.tPalette.m3surfaceContainerHigh : DynamicColors.tPalette.m3surfaceContainerLow
implicitWidth: 100
radius: Appearance.rounding.full
}
@@ -85,7 +86,7 @@ RowLayout {
CustomRect {
id: upButton
color: DynamicColors.palette.m3primary
color: root.enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 1)
implicitHeight: upIcon.implicitHeight + Appearance.padding.small * 2
implicitWidth: implicitHeight
radius: Appearance.rounding.full
@@ -113,13 +114,13 @@ RowLayout {
id: upIcon
anchors.centerIn: parent
color: DynamicColors.palette.m3onPrimary
color: root.enabled ? DynamicColors.palette.m3onPrimary : Qt.alpha(DynamicColors.palette.m3onSurface, 0.5)
text: "keyboard_arrow_up"
}
}
CustomRect {
color: DynamicColors.palette.m3primary
color: root.enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 1)
implicitHeight: downIcon.implicitHeight + Appearance.padding.small * 2
implicitWidth: implicitHeight
radius: Appearance.rounding.full
@@ -147,7 +148,7 @@ RowLayout {
id: downIcon
anchors.centerIn: parent
color: DynamicColors.palette.m3onPrimary
color: root.enabled ? DynamicColors.palette.m3onPrimary : Qt.alpha(DynamicColors.palette.m3onSurface, 0.5)
text: "keyboard_arrow_down"
}
}
+34 -67
View File
@@ -1,7 +1,6 @@
import QtQuick
import QtQuick.Layouts
import qs.Config
import qs.Helpers
Row {
id: root
@@ -12,66 +11,46 @@ Row {
}
property alias active: menu.active
property color color: type == CustomSplitButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondaryContainer
property color colour: type == CustomSplitButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondaryContainer
property bool disabled
property color disabledColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
property color disabledTextColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
property color disabledColour: Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
property color disabledTextColour: Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
readonly property alias expandBtn: expandBtn
property alias expanded: menu.expanded
property string fallbackIcon
property string fallbackText
property real horizontalPadding: Appearance.padding.normal
property alias iconLabel: iconLabel
property alias label: label
property alias menu: menu
property real horizontalPadding: Appearance.padding.larger
readonly property alias iconLabel: iconLabel
readonly property alias label: label
readonly property alias menu: menu
property alias menuItems: menu.items
property bool menuOnTop
property alias stateLayer: stateLayer
property color textColor: type == CustomSplitButton.Filled ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSecondaryContainer
property real minLeftWidth
readonly property alias stateLayer: stateLayer
property color textColour: type == CustomSplitButton.Filled ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSecondaryContainer
readonly property alias textRow: textRow
property int type: CustomSplitButton.Filled
property real verticalPadding: Appearance.padding.smaller
property real verticalPadding: Appearance.padding.small
function closeDropdown(): void {
SettingsDropdowns.close(menu);
}
function openDropdown(): void {
if (root.disabled)
return;
SettingsDropdowns.open(menu, root);
}
function toggleDropdown(): void {
if (root.disabled)
return;
SettingsDropdowns.toggle(menu, root);
}
spacing: Math.floor(Appearance.spacing.small / 2)
onExpandedChanged: {
if (!expanded)
SettingsDropdowns.forget(menu);
}
spacing: Math.floor(Appearance.spacing.extraSmall)
CustomRect {
bottomRightRadius: Appearance.rounding.small / 2
color: root.disabled ? root.disabledColor : root.color
color: root.disabled ? root.disabledColour : root.colour
implicitHeight: expandBtn.implicitHeight
implicitWidth: textRow.implicitWidth + root.horizontalPadding * 2
implicitWidth: Math.max(root.minLeftWidth, textRow.implicitWidth + root.horizontalPadding * 2)
radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale)
topRightRadius: Appearance.rounding.small / 2
StateLayer {
id: stateLayer
function onClicked(): void {
root.active?.clicked();
}
color: root.textColor
bottomRightRadius: parent.bottomRightRadius
color: root.textColour
disabled: root.disabled
rect.bottomRightRadius: parent.bottomRightRadius
rect.topRightRadius: parent.topRightRadius
topRightRadius: parent.topRightRadius
onClicked: root.active?.clicked()
}
RowLayout {
@@ -86,7 +65,7 @@ Row {
Layout.alignment: Qt.AlignVCenter
animate: true
color: root.disabled ? root.disabledTextColor : root.textColor
color: root.disabled ? root.disabledTextColour : root.textColour
fill: 1
text: root.active?.activeIcon ?? root.fallbackIcon
}
@@ -98,12 +77,12 @@ Row {
Layout.preferredWidth: implicitWidth
animate: true
clip: true
color: root.disabled ? root.disabledTextColor : root.textColor
color: root.disabled ? root.disabledTextColour : root.textColour
text: root.active?.activeText ?? root.fallbackText
Behavior on Layout.preferredWidth {
Anim {
easing.bezierCurve: Appearance.anim.curves.emphasized
type: Anim.Emphasized
}
}
}
@@ -116,7 +95,7 @@ Row {
property real rad: root.expanded ? implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) : Appearance.rounding.small / 2
bottomLeftRadius: rad
color: root.disabled ? root.disabledColor : root.color
color: root.disabled ? root.disabledColour : root.colour
implicitHeight: expandIcon.implicitHeight + root.verticalPadding * 2
implicitWidth: implicitHeight
radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale)
@@ -130,14 +109,12 @@ Row {
StateLayer {
id: expandStateLayer
function onClicked(): void {
root.toggleDropdown();
}
color: root.textColor
color: root.textColour
disabled: root.disabled
rect.bottomLeftRadius: parent.bottomLeftRadius
rect.topLeftRadius: parent.topLeftRadius
onClicked: root.expanded = !root.expanded
}
MaterialIcon {
@@ -145,7 +122,7 @@ Row {
anchors.centerIn: parent
anchors.horizontalCenterOffset: root.expanded ? 0 : -Math.floor(root.verticalPadding / 4)
color: root.disabled ? root.disabledTextColor : root.textColor
color: root.disabled ? root.disabledTextColour : root.textColour
rotation: root.expanded ? 180 : 0
text: "expand_more"
@@ -158,24 +135,14 @@ Row {
}
}
}
}
Menu {
id: menu
anchors.bottomMargin: Appearance.spacing.small
anchors.right: parent.right
anchors.top: parent.bottom
anchors.topMargin: Appearance.spacing.small
states: State {
when: root.menuOnTop
AnchorChanges {
anchors.bottom: expandBtn.top
anchors.top: undefined
target: menu
}
}
}
attachSideY: root.menuOnTop ? Menu.Top : Menu.Bottom
attachTo: expandBtn
marginY: Appearance.spacing.small * (root.menuOnTop ? -1 : 1)
thisSideY: root.menuOnTop ? Menu.Bottom : Menu.Top
}
}
+6 -6
View File
@@ -9,7 +9,6 @@ Item {
property alias active: splitButton.active
property alias buttonAlias: splitButton
property bool enabled: true
property alias expanded: splitButton.expanded
property int expandedZ: 100
required property string label
@@ -25,7 +24,7 @@ Item {
implicitHeight: row.implicitHeight + Appearance.padding.smaller * 2
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
z: root.expanded ? expandedZ : -1
z: splitButton.menu.implicitHeight > 0 ? expandedZ : 1
Behavior on opacity {
Anim {
@@ -50,7 +49,6 @@ Item {
color: root.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.larger
text: root.label
z: root.expanded ? root.expandedZ : -1
}
CustomSplitButton {
@@ -58,14 +56,16 @@ Item {
enabled: root.enabled
type: CustomSplitButton.Filled
z: root.expanded ? root.expandedZ : -1
z: 2
menu.onItemSelected: item => {
root.selected(item);
splitButton.closeDropdown();
// splitButton.closeDropdown();
}
stateLayer.onClicked: {
splitButton.toggleDropdown();
// splitButton.toggleDropdown();
splitButton.expanded = !splitButton.expanded;
console.log(root.z);
}
}
}
+18 -16
View File
@@ -1,6 +1,6 @@
import QtQuick
import QtQuick.Templates
import QtQuick.Shapes
import QtQuick.Templates
import qs.Config
Switch {
@@ -12,38 +12,41 @@ Switch {
implicitWidth: implicitIndicatorWidth
indicator: CustomRect {
color: root.checked ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer)
implicitHeight: 13 + 7 * 2
color: root.checked && root.enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer)
implicitHeight: Appearance.font.size.medium + Appearance.padding.normal * 2
implicitWidth: implicitHeight * 1.7
radius: Appearance.rounding.full
CustomRect {
readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.3 : implicitHeight
readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.2 : implicitHeight
anchors.verticalCenter: parent.verticalCenter
color: root.checked ? DynamicColors.palette.m3onPrimary : DynamicColors.layer(DynamicColors.palette.m3outline, root.cLayer + 1)
implicitHeight: parent.implicitHeight - 10
color: root.checked && root.enabled ? DynamicColors.palette.m3onPrimary : DynamicColors.layer(DynamicColors.palette.m3outline, root.cLayer + 1)
implicitHeight: parent.implicitHeight - Appearance.padding.extraSmall
implicitWidth: nonAnimWidth
radius: Appearance.rounding.full
x: root.checked ? parent.implicitWidth - nonAnimWidth - 10 / 2 : 10 / 2
x: root.checked ? parent.implicitWidth - nonAnimWidth - Appearance.padding.extraSmall / 2 : Appearance.padding.extraSmall / 2
Behavior on implicitWidth {
Anim {
type: Anim.FastSpatial
}
}
Behavior on x {
Anim {
type: Anim.FastSpatial
}
}
CustomRect {
anchors.fill: parent
color: root.checked ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface
color: root.checked && root.enabled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface
opacity: root.pressed ? 0.1 : root.hovered ? 0.08 : 0
radius: parent.radius
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
}
@@ -63,14 +66,14 @@ Switch {
}
property point end2: {
if (root.pressed)
return Qt.point(width, height / 2);
return Qt.point(width * 0.8, height / 2);
if (root.checked)
return Qt.point(width * 0.85, height * 0.2);
return Qt.point(width * 0.85, height * 0.15);
}
property point start1: {
if (root.pressed)
return Qt.point(width * 0.1, height / 2);
return Qt.point(width * 0.2, height / 2);
if (root.checked)
return Qt.point(width * 0.15, height / 2);
return Qt.point(width * 0.15, height * 0.15);
@@ -88,7 +91,7 @@ Switch {
anchors.centerIn: parent
asynchronous: true
height: parent.implicitHeight - Appearance.padding.small * 2
height: parent.implicitHeight - Appearance.padding.larger
preferredRendererType: Shape.CurveRenderer
width: height
@@ -110,11 +113,11 @@ Switch {
}
ShapePath {
capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
capStyle: ShapePath.RoundCap
fillColor: "transparent"
startX: icon.start1.x
startY: icon.start1.y
strokeColor: root.checked ? DynamicColors.palette.m3primary : DynamicColors.palette.m3surfaceContainerHighest
strokeColor: root.checked && root.enabled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3surfaceContainerHighest
strokeWidth: Appearance.font.size.larger * 0.15
Behavior on strokeColor {
@@ -148,8 +151,7 @@ Switch {
}
component PropAnim: PropertyAnimation {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
easing.type: Easing.BezierSpline
duration: Appearance.anim.durations.expressiveFastSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
}
}
+2 -1
View File
@@ -1,6 +1,6 @@
import qs.Config
import QtQuick
import QtQuick.Effects
import qs.Config
RectangularShadow {
property real dp: [0, 1, 3, 6, 8, 12][level]
@@ -13,6 +13,7 @@ RectangularShadow {
Behavior on dp {
Anim {
type: Anim.SlowEffects
}
}
}
+98 -38
View File
@@ -2,49 +2,107 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.Config
import qs.Drawers
Elevation {
MouseArea {
id: root
enum Side {
Top,
Bottom,
Left,
Right
}
property MenuItem active: items[0] ?? null
property int attachSideX: Menu.Right
property int attachSideY: Menu.Bottom
required property Item attachTo
property bool expanded
property list<MenuItem> items
property real marginX
property real marginY
property int thisSideX: Menu.Right
property int thisSideY: Menu.Top
signal itemSelected(item: MenuItem)
implicitHeight: root.expanded ? column.implicitHeight + Appearance.padding.small * 2 : 0
implicitWidth: Math.max(200, column.implicitWidth)
level: 2
opacity: root.expanded ? 1 : 0
radius: Appearance.rounding.normal
anchors.fill: parent
enabled: expanded
layer.enabled: opacity < 1
opacity: expanded ? 1 : 0
parent: {
const win = QsWindow.window;
const contentWin = win as Windows;
return contentWin ? contentWin.interactionWrapper : (win as QsWindow).contentItem;
}
Behavior on implicitHeight {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
}
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
type: Anim.DefaultEffects
}
}
CustomClippingRect {
onClicked: expanded = false
TransformWatcher {
id: watcher
a: root.parent
b: root.attachTo
}
Elevation {
id: menu
implicitHeight: column.implicitHeight + column.anchors.margins * 2
implicitWidth: Math.max(200, column.implicitWidth + column.anchors.margins * 2)
level: 2
radius: Appearance.rounding.medium
x: {
watcher.transform;
const item = root.attachTo;
let off = root.attachSideX === Menu.Left ? 0 : item.width;
if (root.thisSideX === Menu.Right)
off -= width;
return item.mapToItem(root.parent, off, 0).x + root.marginX;
}
y: {
watcher.transform;
const item = root.attachTo;
let off = root.attachSideY === Menu.Top ? 0 : item.height;
if (root.thisSideY === Menu.Bottom)
off -= height;
return item.mapToItem(root.parent, 0, off).y + root.marginY;
}
transform: Scale {
origin.y: root.thisSideY === Menu.Bottom ? menu.height : 0
yScale: root.expanded ? 1 : 0.1
Behavior on yScale {
Anim {
}
}
}
CustomRect {
anchors.fill: parent
color: DynamicColors.palette.m3surfaceContainer
color: DynamicColors.palette.m3surfaceContainerLow
radius: parent.radius
ColumnLayout {
id: column
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 5
anchors.fill: parent
anchors.margins: Appearance.padding.extraSmall
spacing: Appearance.spacing.extraSmall
Repeater {
id: repeater
model: root.items
CustomRect {
@@ -55,25 +113,25 @@ Elevation {
required property MenuItem modelData
Layout.fillWidth: true
implicitHeight: menuOptionRow.implicitHeight + Appearance.padding.normal * 2
implicitWidth: menuOptionRow.implicitWidth + Appearance.padding.normal * 2
color: Qt.alpha(DynamicColors.palette.m3tertiaryContainer, active ? 1 : 0)
implicitHeight: menuOptionRow.implicitHeight + Appearance.padding.larger * 2
implicitWidth: menuOptionRow.implicitWidth + Appearance.padding.larger * 2
radius: Appearance.rounding.small
CustomRect {
anchors.fill: parent
anchors.leftMargin: Appearance.padding.small
anchors.rightMargin: Appearance.padding.small
color: Qt.alpha(DynamicColors.palette.m3secondaryContainer, active ? 1 : 0)
radius: Appearance.rounding.normal - Appearance.padding.small
StateLayer {
function onClicked(): void {
root.itemSelected(item.modelData);
root.active = item.modelData;
root.expanded = false;
Behavior on radius {
Anim {
}
}
color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
StateLayer {
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurface
disabled: !root.expanded
onClicked: {
root.itemSelected(item.modelData);
root.active = item.modelData;
item.modelData.clicked();
root.expanded = false;
}
}
@@ -81,29 +139,30 @@ Elevation {
id: menuOptionRow
anchors.fill: parent
anchors.margins: Appearance.padding.normal
anchors.margins: Appearance.padding.larger
spacing: Appearance.spacing.small
MaterialIcon {
Layout.alignment: Qt.AlignVCenter
color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurfaceVariant
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurfaceVariant
text: item.modelData.icon
}
CustomText {
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurface
text: item.modelData.text
}
Loader {
Layout.alignment: Qt.AlignVCenter
active: item.modelData.trailingIcon.length > 0
asynchronous: true
visible: active
sourceComponent: MaterialIcon {
color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurfaceVariant
text: item.modelData.trailingIcon
}
}
@@ -112,4 +171,5 @@ Elevation {
}
}
}
}
}
+167 -62
View File
@@ -1,15 +1,53 @@
import qs.Config
import QtQuick
import QtQuick.Shapes
import ZShell
import ZShell.Components
import qs.Helpers
import qs.Config
MouseArea {
id: root
property color color: DynamicColors.palette.m3onSurface
property alias bottomLeftRadius: base.bottomLeftRadius
property alias bottomRightRadius: base.bottomRightRadius
property real circleRadius
property alias color: base.color
property bool disabled
property real radius: parent?.radius ?? 0
property alias rect: hoverLayer
readonly property real endRadius: {
const d1 = distSq(0, 0);
const d2 = distSq(width, 0);
const d3 = distSq(0, height);
const d4 = distSq(width, height);
return (Math.sqrt(Math.max(d1, d2, d3, d4)) + (shapeMorph ? 24 : 0)) * 1.3;
}
property real endRadiusAtPress
property bool manualPressOverride
property real pressX: width / 2
property real pressY: height / 2
property alias radius: base.radius
readonly property alias rect: base
property bool shapeMorph
property bool showHoverBackground: true
property real stateOpacity: containsMouse ? 0.08 : 0
property alias topLeftRadius: base.topLeftRadius
property alias topRightRadius: base.topRightRadius
function onClicked(): void {
function clamp(r: real): real {
return Math.max(0, Math.min(r, width / 2, height / 2));
}
function distSq(x: real, y: real): real {
return (pressX - x) ** 2 + (pressY - y) ** 2;
}
function press(x: real, y: real): void {
pressX = x;
pressY = y;
fadeAnim.complete();
circleRadius = 0;
circle.opacity = 0.1;
rippleAnim.restart();
endRadiusAtPress = endRadius;
}
anchors.fill: parent
@@ -17,79 +55,146 @@ MouseArea {
enabled: !disabled
hoverEnabled: true
onClicked: event => !disabled && onClicked(event)
onPressed: event => {
if (disabled)
return;
rippleAnim.x = event.x;
rippleAnim.y = event.y;
const dist = (ox, oy) => ox * ox + oy * oy;
rippleAnim.radius = Math.sqrt(Math.max(dist(event.x, event.y), dist(event.x, height - event.y), dist(width - event.x, event.y), dist(width - event.x, height - event.y)));
rippleAnim.restart();
Behavior on stateOpacity {
Anim {
type: Anim.DefaultEffects
}
}
SequentialAnimation {
onCircleRadiusChanged: {
if (!(pressed || manualPressOverride) && circleRadius > endRadiusAtPress * 0.99 && !fadeAnim.running)
fadeAnim.start();
}
onClicked: event => !disabled && onClicked(event)
onManualPressOverrideChanged: {
if (!(pressed || manualPressOverride) && circleRadius > endRadiusAtPress * 0.99 && !fadeAnim.running)
fadeAnim.start();
}
onPressed: e => press(e.x, e.y)
onPressedChanged: {
if (!(pressed || manualPressOverride) && !rippleAnim.running && circle.opacity > 0)
fadeAnim.start();
}
Anim {
id: rippleAnim
property real radius
property real x
property real y
PropertyAction {
property: "x"
target: ripple
value: rippleAnim.x
}
PropertyAction {
property: "y"
target: ripple
value: rippleAnim.y
}
PropertyAction {
property: "opacity"
target: ripple
value: 0.08
alwaysRunToEnd: true
duration: Appearance.anim.durations.expressiveSlowEffects * 2
easing.bezierCurve: Appearance.anim.curves.standard
property: "circleRadius"
target: root
to: root.endRadius
}
Anim {
easing.bezierCurve: MaterialEasing.standardDecel
from: 0
properties: "implicitWidth,implicitHeight"
target: ripple
to: rippleAnim.radius * 2
}
id: fadeAnim
Anim {
property: "opacity"
target: ripple
target: circle
to: 0
type: Anim.SlowEffects
}
}
CustomClippingRect {
id: hoverLayer
anchors.fill: parent
border.pixelAligned: false
color: Qt.alpha(root.color, root.disabled ? 0 : root.pressed ? 0.1 : root.containsMouse ? 0.08 : 0)
radius: root.radius
CustomRect {
id: ripple
id: base
border.pixelAligned: false
color: root.color
anchors.fill: parent
bottomLeftRadius: root.parent?.bottomLeftRadius ?? radius ?? 0
bottomRightRadius: root.parent?.bottomRightRadius ?? radius ?? 0
color: DynamicColors.palette.m3onSurface
opacity: root.stateOpacity
// Pick up radius from parent if it has one (parent can be anything with radius props)
// qmllint disable missing-property
radius: root.parent?.radius ?? 0
topLeftRadius: root.parent?.topLeftRadius ?? radius ?? 0
topRightRadius: root.parent?.topRightRadius ?? radius ?? 0
// qmllint enable missing-property
}
Shape {
id: circle
anchors.fill: parent
opacity: 0
radius: Appearance.rounding.full
preferredRendererType: Shape.CurveRenderer
transform: Translate {
x: -ripple.width / 2
y: -ripple.height / 2
ShapePath {
fillColor: base.color
startX: root.clamp(base.topLeftRadius)
startY: 0
strokeColor: "transparent"
strokeWidth: 0
fillGradient: RadialGradient {
centerRadius: root.circleRadius
centerX: root.pressX
centerY: root.pressY
focalX: centerX
focalY: centerY
GradientStop {
color: Qt.alpha(base.color, 1)
position: 0
}
GradientStop {
color: Qt.alpha(base.color, 1)
position: ZUtils.clamp(1 - 0.2 * root.endRadius / root.circleRadius, 0.01, 0.99)
}
GradientStop {
color: Qt.alpha(base.color, ZUtils.clamp((root.circleRadius / root.endRadius - 0.9) / 0.1, 0, 1))
position: 1
}
}
PathLine {
x: root.width - root.clamp(base.topRightRadius)
y: 0
}
PathArc {
radiusX: root.clamp(base.topRightRadius)
radiusY: root.clamp(base.topRightRadius)
relativeX: root.clamp(base.topRightRadius)
relativeY: root.clamp(base.topRightRadius)
}
PathLine {
x: root.width
y: root.height - root.clamp(base.bottomRightRadius)
}
PathArc {
radiusX: root.clamp(base.bottomRightRadius)
radiusY: root.clamp(base.bottomRightRadius)
relativeX: -root.clamp(base.bottomRightRadius)
relativeY: root.clamp(base.bottomRightRadius)
}
PathLine {
x: root.clamp(base.bottomLeftRadius)
y: root.height
}
PathArc {
radiusX: root.clamp(base.bottomLeftRadius)
radiusY: root.clamp(base.bottomLeftRadius)
relativeX: -root.clamp(base.bottomLeftRadius)
relativeY: -root.clamp(base.bottomLeftRadius)
}
PathLine {
x: 0
y: root.clamp(base.topLeftRadius)
}
PathArc {
radiusX: root.clamp(base.topLeftRadius)
radiusY: root.clamp(base.topLeftRadius)
x: root.clamp(base.topLeftRadius)
y: 0
}
}
}
-2
View File
@@ -7,8 +7,6 @@ Singleton {
readonly property AppearanceConf.Deform deform: Config.appearance.deform
readonly property AppearanceConf.FontStuff font: Config.appearance.font
readonly property AppearanceConf.Padding padding: Config.appearance.padding
// Literally just here to shorten accessing stuff :woe:
// Also kinda so I can keep accessing it with `Appearance.xxx` instead of `Conf.appearance.xxx`
readonly property AppearanceConf.Rounding rounding: Config.appearance.rounding
readonly property AppearanceConf.Spacing spacing: Config.appearance.spacing
readonly property AppearanceConf.Transparency transparency: Config.appearance.transparency
+16 -4
View File
@@ -28,9 +28,13 @@ JsonObject {
property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1]
property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1]
property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1]
property list<real> expressiveDefaultEffects: [0.34, 0.8, 0.34, 1, 1, 1]
property list<real> expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1, 1, 1]
property list<real> expressiveEffects: [0.34, 0.8, 0.34, 1, 1, 1]
property list<real> expressiveFastEffects: [0.31, 0.94, 0.34, 1, 1, 1]
property list<real> expressiveFastSpatial: [0.42, 1.67, 0.21, 0.9, 1, 1]
property list<real> expressiveSlowEffects: [0.34, 0.88, 0.34, 1, 1, 1]
property list<real> expressiveSlowSpatial: [0.39, 1.29, 0.35, 0.98, 1, 1]
property list<real> standard: [0.2, 0, 0, 1, 1, 1]
property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1]
property list<real> standardDecel: [0, 0, 0, 1, 1, 1]
@@ -38,7 +42,9 @@ JsonObject {
component AnimDurations: JsonObject {
property int expressiveDefaultSpatial: 500 * scale
property int expressiveEffects: 200 * scale
property int expressiveFastEffects: 150 * scale
property int expressiveFastSpatial: 350 * scale
property int expressiveSlowEffects: 300 * scale
property int extraLarge: 1000 * scale
property int large: 600 * scale
property int normal: 400 * scale
@@ -57,7 +63,8 @@ JsonObject {
component FontSize: JsonObject {
property int extraLarge: 28 * scale
property int large: 18 * scale
property int larger: 15 * scale
property int larger: 16 * scale
property int medium: 14 * scale
property int normal: 13 * scale
property real scale: 1
property int small: 11 * scale
@@ -70,23 +77,28 @@ JsonObject {
}
}
component Padding: JsonObject {
property int large: 15 * scale
property int larger: 13 * scale
property int normal: 9 * scale
property int extraLargeIncreased: 32 * scale
property int extraSmall: 4 * scale
property int large: 16 * scale
property int larger: 12 * scale
property int normal: 8 * scale
property real scale: 1
property int small: 5 * scale
property int smaller: 7 * scale
property int smallest: 2 * scale
}
component Rounding: JsonObject {
property int extraSmall: 4 * scale
property int full: 1000 * scale
property int large: 24 * scale
property int medium: 16 * scale
property int normal: 18 * scale
property real scale: 1
property int small: 12 * scale
property int smallest: 8 * scale
}
component Spacing: JsonObject {
property int extraSmall: 4 * scale
property int large: 20 * scale
property int larger: 16 * scale
property int normal: 12 * scale
+4
View File
@@ -115,6 +115,10 @@ Singleton {
Config.save();
}
function swapRG(c: color): color {
return Qt.rgba(c.g, c.r, c.b, c.a);
}
Component.onCompleted: debounceTimer.triggered()
Connections {
-47
View File
@@ -1,47 +0,0 @@
import Quickshell
import Quickshell.Services.UPower
import QtQuick
import ZShell
import qs.Config
import qs.Components.Toast
Scope {
id: root
readonly property real currentPerc: UPower.displayDevice.percentage
readonly property list<var> popupThresholds: [...Config.general.battery.popupThresholds].sort((a, b) => b.perc - a.perc)
Connections {
function onOnBatteryChanged(): void {
if (UPower.onBattery) {
if (Config.utilities.toasts.chargingChanged)
Toaster.toast(qsTr("Charger unplugged"), qsTr("Battery is discharging"), "power_off");
} else {
if (Config.utilities.toasts.chargingChanged)
Toaster.toast(qsTr("Charger plugged in"), qsTr("Battery is charging"), "power");
for (const level of root.popupThresholds)
level.warned = false;
}
}
target: UPower
}
Connections {
function onPercentageChanged(): void {
if (!UPower.onBattery)
return;
const p = UPower.displayDevice.percentage * 100;
for (const perc of root.popupThresholds) {
if (p <= perc.perc && !perc.warned) {
perc.warned = true;
console.log(perc.warned + "\n" + [...Config.general.battery.popupThresholds][0].warned);
Toaster.toast(perc.title ?? qsTr("Battery warning"), perc.message ?? qsTr("Battery perc is low"), perc.icon ?? "battery_android_alert", perc.critical ? Toast.Error : Toast.Warning);
}
}
}
target: UPower.displayDevice
}
}
+70
View File
@@ -0,0 +1,70 @@
import Quickshell
import Quickshell.Services.UPower
import QtQuick
import ZShell
import qs.Config
import qs.Components.Toast
import qs.Helpers
Scope {
id: root
readonly property list<var> popupThresholds: [...Config.general.battery.popupThresholds].sort((a, b) => b.perc - a.perc)
function nearestThresholdAbove(p: real): var {
const thresholds = [...root.popupThresholds];
for (const perc of thresholds) {
if (p < perc.perc)
return perc;
}
return null;
}
Connections {
function onOnBatteryChanged(): void {
if (!Battery.ready)
return;
if (Battery.onBattery) {
if (Config.utilities.toasts.chargingChanged)
Toaster.toast(qsTr("Charger unplugged"), qsTr("Battery is discharging"), "power_off");
const p = Battery.currentPerc * 100;
const perc = root.nearestThresholdAbove(p);
if (perc)
Toaster.toast(perc.title ?? qsTr("Battery warning"), perc.message ?? qsTr("Battery is low"), perc.icon ?? "battery_android_alert", perc.critical ? Toast.Error : Toast.Warning);
} else {
if (Config.utilities.toasts.chargingChanged)
Toaster.toast(qsTr("Charger plugged in"), qsTr("Battery is charging"), "power");
}
}
target: Battery
}
Connections {
function onCurrentPercChanged(): void {
if (!Battery.onBattery)
return;
const p = Battery.currentPerc * 100;
for (const perc of root.popupThresholds) {
if (p == perc.perc) {
Toaster.toast(perc.title ?? qsTr("Battery warning"), perc.message ?? qsTr("Battery perc is low"), perc.icon ?? "battery_android_alert", perc.critical ? Toast.Error : Toast.Warning);
}
}
}
function onReadyChanged(): void {
if (!Battery.ready)
return;
const p = Battery.currentPerc * 100;
const perc = root.nearestThresholdAbove(p);
if (perc)
Toaster.toast(perc.title ?? qsTr("Battery warning"), perc.message ?? qsTr("Battery is low"), perc.icon ?? "battery_android_alert", perc.critical ? Toast.Error : Toast.Warning);
}
target: Battery
}
}
+25
View File
@@ -0,0 +1,25 @@
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
Variants {
model: Quickshell.screens
Scope {
id: scope
required property ShellScreen modelData
Exclusions {
bar: content.bar
screen: scope.modelData
}
Windows {
id: content
screen: scope.modelData
}
}
}
+88 -43
View File
@@ -2,20 +2,21 @@ import Quickshell
import QtQuick
import qs.Components
import qs.Config
import qs.Helpers
import qs.Modules as BarPopouts
CustomMouseArea {
Item {
id: root
required property Item bar
property bool dashboardShortcutActive
property point dragStart
required property Drawing drawing
required property DrawingInput input
property bool osdShortcutActive
required property Panels panels
required property BarPopouts.Wrapper popouts
required property ShellScreen screen
property bool singleGestureTriggered: false
property bool utilitiesShortcutActive
required property PersistentProperties visibilities
@@ -52,78 +53,123 @@ CustomMouseArea {
}
anchors.fill: parent
cursorShape: (pressed && dragStart.y < bar.implicitHeight) ? Qt.ClosedHandCursor : undefined
hoverEnabled: true
propagateComposedEvents: false
onContainsMouseChanged: {
if (!containsMouse) {
if (!osdShortcutActive) {
visibilities.osd = false;
DragHandler {
id: singleHandler
cursorShape: (active && centroid.pressPosition.y < root.bar.implicitHeight) ? Qt.ClosedHandCursor : undefined
dragThreshold: 0
grabPermissions: PointerHandler.CanTakeOverFromHandlersOfSameType | PointerHandler.ApprovesTakeOverByAnything
maximumPointCount: 1
minimumPointCount: 1
target: null
onActiveChanged: {
if (!active)
root.singleGestureTriggered = false;
}
onCentroidChanged: {
const x = centroid.position.x;
const y = centroid.position.y;
const dragX = x - centroid.pressPosition.x;
const dragY = y - centroid.pressPosition.y;
if (centroid.pressPosition.y >= root.screen.height - Config.barConfig.border && dragY < -200)
root.visibilities.launcher = true;
if (root.singleGestureTriggered)
return;
if (centroid.pressPosition.y < root.bar.implicitHeight) {
if (dragY > 20) {
root.visibilities.settings = true;
root.singleGestureTriggered = true;
} else if (dragY < -20) {
root.visibilities.settings = false;
root.singleGestureTriggered = true;
}
}
if (!Config.dock.hoverToReveal && centroid.pressPosition.y > root.screen.height - root.bar.implicitHeight)
if (dragY < -10) {
root.visibilities.dock = true;
root.singleGestureTriggered = true;
}
if (centroid.pressPosition.x > root.screen.width - Config.barConfig.border && centroid.pressPosition.y < (root.screen.height / 2) && dragX < -20) {
root.visibilities.sidebar = true;
root.singleGestureTriggered = true;
}
if (centroid.pressPosition.x >= root.screen.width - Config.barConfig.border && centroid.pressPosition.y > (root.screen.height / 2) && dragX < -20) {
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r+1', on_current_monitor = true })`);
root.singleGestureTriggered = true;
}
if (centroid.pressPosition.x <= Config.barConfig.border && dragX > 20) {
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r-1', on_current_monitor = true })`);
root.singleGestureTriggered = true;
}
}
}
HoverHandler {
id: hoverHandler
onHoveredChanged: {
if (!hovered) {
if (!root.osdShortcutActive) {
root.visibilities.osd = false;
root.panels.osd.hovered = false;
}
if (!popouts.currentName.startsWith("traymenu")) {
popouts.hasCurrent = false;
if (!root.popouts.currentName.startsWith("traymenu")) {
root.popouts.hasCurrent = false;
}
if (Config.barConfig.autoHide)
bar.isHovered = false;
root.bar.isHovered = false;
}
}
onPositionChanged: event => {
const x = event.x;
const y = event.y;
const dragX = x - dragStart.x;
const dragY = y - dragStart.y;
onPointChanged: {
const x = point.position.x;
const y = point.position.y;
if (root.visibilities.isDrawing && !root.inLeftPanel(root.panels.drawing, x, y)) {
root.input.z = 2;
root.panels.drawing.expanded = false;
}
if (!visibilities.bar && Config.barConfig.autoHide && y < bar.implicitHeight)
bar.isHovered = true;
if (!root.visibilities.bar && Config.barConfig.autoHide && y < root.bar.implicitHeight)
root.bar.isHovered = true;
if (pressed && dragStart.y < bar.implicitHeight) {
if (dragY > 20)
visibilities.settings = true;
else if (dragY < -20)
visibilities.settings = false;
}
if (Config.dock.hoverToReveal && pressed && dragStart.y > root.screen.height - root.bar.implicitHeight)
if (dragY < -10)
visibilities.dock = true;
if (panels.sidebar.width === 0) {
const showOsd = inRightPanel(panels.osdWrapper, x, y);
if (root.panels.sidebar.width === 0) {
const showOsd = root.inRightPanel(root.panels.osdWrapper, x, y);
if (showOsd) {
osdShortcutActive = false;
root.osdShortcutActive = false;
root.panels.osd.hovered = true;
}
} else {
const outOfSidebar = x < width - panels.sidebar.width;
const showOsd = outOfSidebar && inRightPanel(panels.osdWrapper, x, y);
const outOfSidebar = x < root.width - root.panels.sidebar.width;
const showOsd = outOfSidebar && root.inRightPanel(root.panels.osdWrapper, x, y);
if (!osdShortcutActive) {
visibilities.osd = showOsd;
if (!root.osdShortcutActive) {
root.visibilities.osd = showOsd;
root.panels.osd.hovered = showOsd;
} else if (showOsd) {
osdShortcutActive = false;
root.osdShortcutActive = false;
root.panels.osd.hovered = true;
}
}
if (Config.dock.enable && !Config.dock.hoverToReveal && !visibilities.dock && !visibilities.launcher && inBottomPanel(panels.dock, x, y))
visibilities.dock = true;
if (Config.dock.enable && Config.dock.hoverToReveal && !root.visibilities.dock && !root.visibilities.launcher && root.inBottomPanel(root.panels.dock, x, y))
root.visibilities.dock = true;
if (y < root.bar.implicitHeight) {
if (y < root.bar.implicitHeight)
root.bar.checkPopout(x);
}
}
onPressed: event => dragStart = Qt.point(event.x, event.y)
Connections {
function onDashboardChanged() {
@@ -164,7 +210,6 @@ CustomMouseArea {
if (root.visibilities.launcher) {
if (root.panels.dashboardWrapper.x < root.panels.launcher.x + root.panels.launcher.width) {
console.log("true");
root.visibilities.dashboard = false;
}
+13 -27
View File
@@ -9,29 +9,17 @@ import Quickshell.Hyprland
import ZShell.Blobs
import qs.Daemons
import qs.Components
import qs.Modules
import qs.Modules.Bar
import qs.Config
import qs.Helpers
import qs.Drawers
Variants {
model: Quickshell.screens
Scope {
id: scope
required property var modelData
Exclusions {
bar: bar
screen: scope.modelData
}
CustomWindow {
id: win
CustomWindow {
id: root
readonly property alias bar: bar
readonly property bool hasFullscreen: Hypr.monitorFor(screen)?.activeWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen === 2)
readonly property alias interactionWrapper: interactions
property var root: Quickshell.shellDir
WlrLayershell.exclusionMode: ExclusionMode.Ignore
@@ -40,7 +28,6 @@ Variants {
contentItem.focus: true
mask: visibilities.isDrawing ? null : region
name: "Bar"
screen: scope.modelData
contentItem.Keys.onEscapePressed: {
if (Config.barConfig.autoHide)
@@ -62,10 +49,10 @@ Variants {
Region {
id: region
height: win.height - bar.implicitHeight - Config.barConfig.border
height: root.height - bar.implicitHeight - Config.barConfig.border
intersection: Intersection.Xor
regions: popoutRegions.instances
width: win.width - Config.barConfig.border * 2
width: root.width - Config.barConfig.border * 2
x: Config.barConfig.border
y: bar.implicitHeight
}
@@ -97,7 +84,7 @@ Variants {
id: focusGrab
active: visibilities.dock || visibilities.resources || visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || (panels.popouts.hasCurrent && panels.popouts.currentName.startsWith("traymenu"))
windows: [win]
windows: [root]
onCleared: {
visibilities.launcher = false;
@@ -125,7 +112,7 @@ Variants {
property bool settings
property bool sidebar
Component.onCompleted: Visibilities.load(scope.modelData, this)
Component.onCompleted: Visibilities.load(root.screen, this)
}
IpcHandler {
@@ -336,15 +323,16 @@ Variants {
}
Interactions {
id: mouseArea
id: interactions
anchors.fill: parent
bar: bar
drawing: drawingLoader.item
enabled: true
input: inputLoader.item
panels: panels
popouts: panels.popouts
screen: scope.modelData
screen: root.screen
visibilities: visibilities
z: 1
@@ -353,7 +341,7 @@ Variants {
bar: bar
drawingItem: drawingLoader.item
screen: scope.modelData
screen: root.screen
visibilities: visibilities
dashboard.transform: Matrix4x4 {
@@ -395,12 +383,10 @@ Variants {
anchors.right: parent.right
popouts: panels.popouts
popoutsWrapper: panels.popoutsWrapper
screen: scope.modelData
screen: root.screen
visibilities: visibilities
}
}
}
}
component PanelBg: BlobRect {
property real deformAmount: 0.15
+32
View File
@@ -0,0 +1,32 @@
pragma Singleton
import Quickshell
import Quickshell.Services.UPower
import qs.Config
Singleton {
id: root
readonly property var colors: {
if (deviceState === UPowerDeviceState.Charging || deviceState === UPowerDeviceState.FullyCharged)
return {
fg: DynamicColors.swapRG(DynamicColors.palette.m3error),
bg: DynamicColors.swapRG(DynamicColors.palette.m3onError)
};
else if (currentPerc <= 0.2)
return {
fg: DynamicColors.palette.m3error,
bg: DynamicColors.palette.m3onError
};
else
return {
fg: DynamicColors.palette.m3onSurface,
bg: DynamicColors.palette.m3surface
};
}
readonly property real currentPerc: UPower.displayDevice.percentage
readonly property var deviceState: UPower.displayDevice.state
readonly property bool isLaptop: UPower.displayDevice.isLaptopBattery
readonly property bool onBattery: UPower.onBattery
readonly property bool ready: UPower.displayDevice.ready
}
+11 -59
View File
@@ -2,12 +2,13 @@ pragma Singleton
import Quickshell
import QtQuick
import ZShell.Services
import qs.Config
Singleton {
id: root
property bool enabled
readonly property bool enabled: service.enabled
readonly property int end: Config.general.color.scheduleHyprsunsetEnd
property bool manualToggle: false
readonly property int start: Config.general.color.scheduleHyprsunsetStart
@@ -17,69 +18,20 @@ Singleton {
if (!Config.general.color.scheduleHyprsunset)
return;
var now = new Date();
if (now.getHours() >= root.start || now.getHours() < root.end) {
root.startNightLight(root.temp);
} else {
root.stopNightLight();
}
}
function startNightLight(temp: int): void {
Quickshell.execDetached(["hyprctl", "hyprsunset", "temperature", `${temp}`]);
root.enabled = true;
}
function stopNightLight(): void {
Quickshell.execDetached(["hyprctl", "hyprsunset", "identity"]);
root.enabled = false;
service.apply();
}
function toggleNightLight(): void {
if (enabled)
stopNightLight();
else
startNightLight(temp);
service.manualToggle = true;
service.toggle();
}
onManualToggleChanged: {
if (root.manualToggle)
manualTimer.start();
}
HyprsunsetManager {
id: service
Timer {
id: manualTimer
interval: 60000 * 60
repeat: false
running: false
onTriggered: {
root.manualToggle = false;
}
}
Timer {
interval: 5000
repeat: true
running: true
triggeredOnStart: true
onTriggered: {
if (!Config.general.color.scheduleHyprsunset)
return;
if (root.manualToggle)
return;
var now = new Date();
if (now.getHours() >= root.start || now.getHours() < root.end) {
if (!root.enabled)
root.startNightLight(root.temp);
} else {
if (root.enabled)
root.stopNightLight();
}
}
activeAuto: Config.general.color.scheduleHyprsunset
endTime: root.end
startTime: root.start
temp: root.temp
}
}
+10 -1
View File
@@ -26,6 +26,7 @@ MouseArea {
return (bc.pinned - ac.pinned) || ((bc.fullscreen !== 0) - (ac.fullscreen !== 0)) || (bc.floating - ac.floating);
});
}
readonly property int cornerRadius: Hypr.options.decoration.rounding
property real ex: screen.width
property real ey: screen.height
required property LazyLoader loader
@@ -38,6 +39,7 @@ MouseArea {
required property ShellScreen screen
property real sh: Math.abs(sy - ey)
readonly property color shadowColor: Hypr.options.decoration.shadow.color
readonly property bool shadowEnabled: Hypr.options.decoration.shadow.enabled
readonly property var shadowOffset: Hypr.options.decoration.shadow.offset
readonly property int shadowRange: Hypr.options.decoration.shadow.range
readonly property int shadowRenderPower: Hypr.options.decoration.shadow.render_power
@@ -71,7 +73,14 @@ MouseArea {
function save(): void {
const tmpfile = Qt.resolvedUrl(`/tmp/zshell-picker-${Quickshell.processId}-${Date.now()}.png`);
const cmd = Config.screenshot.enable_pp ? ["zshell-img-tools", "--scale", root.scaleRatio, "--shadow-blur-radius", root.shadowRange, "--image"] : ["swappy", "-f"];
const rounding = root.cornerRadius > 0;
const shadow_blur = root.shadowRange / root.shadowRenderPower;
const r = Math.floor(root.shadowColor.r * 256);
const g = Math.floor(root.shadowColor.g * 256);
const b = Math.floor(root.shadowColor.b * 256);
const a = Math.floor(root.shadowColor.a * 256);
const args = Config.screenshot.mode === "auto" ? ["--rounding", `${rounding}`, "--radius", root.cornerRadius, "--shadow", root.shadowEnabled, "--shadow-blur", `${shadow_blur}`, "--shadow-color", `${r},${g},${b},${a}`, "--shadow-offset-x", root.shadowOffset[0], "--shadow-offset-y", root.shadowOffset[1]] : [];
const cmd = Config.screenshot.enable_pp ? ["zshell-img-tools", "--scale", root.scaleRatio, ...args, "--image"] : ["swappy", "-f"];
ZShellIo.saveItem(screencopy, tmpfile, Qt.rect(Math.ceil(rsx), Math.ceil(rsy), Math.floor(sw), Math.floor(sh)), path => Quickshell.execDetached([...cmd, path]));
closeAnim.start();
}
-26
View File
@@ -1,26 +0,0 @@
pragma Singleton
import Quickshell
import Quickshell.Services.UPower
Singleton {
id: root
readonly property real batteryPercent: UPower.displayDevice.percentage
readonly property list<UPowerDevice> devices: UPower.devices.values
readonly property UPowerDevice displayDevice: UPower.displayDevice
readonly property bool onBattery: UPower.onBattery
// property bool toastShown
//
// Connections {
// target: UPower
//
// function onPercentageChanged(): {
// if (root.batteryPercent >= 0.2 && toastShown)
// return;
//
// root.toastShown = true;
// Toaster.toast(qsTr("Battery "))
// }
// }
}
+1 -1
View File
@@ -23,7 +23,7 @@ CustomRect {
anchors.centerIn: parent
color: root.visibilities.dashboard ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
font: Appearance.font.family.mono
font: Appearance.font.family.mono // qmllint disable incompatible-type
text: Time.dateStr
Behavior on color {
+5
View File
@@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Wayland
import ZShell.Internal
import qs.Config
import qs.Helpers
@@ -29,6 +30,10 @@ Scope {
Quickshell.execDetached(action);
}
LidWatcher {
onAboutToSleep: root.lock.lock.locked = true
}
Variants {
model: Config.general.idle.timeouts
+3 -4
View File
@@ -62,6 +62,7 @@ SettingsPage {
SettingsSection {
sectionId: "Color"
z: 1
SettingsHeader {
name: "Color"
@@ -69,7 +70,7 @@ SettingsPage {
CustomSplitButtonRow {
active: Config.general.color.mode === "light" ? menuItems[0] : menuItems[1]
buttonAlias.disabled: !Config.general.color.schemeGeneration
enabled: Config.general.color.schemeGeneration
label: qsTr("Scheme mode")
menuItems: [
@@ -103,9 +104,8 @@ SettingsPage {
id: schemeType
active: root.schemeTypeItem(menuItems, Config.colors.schemeType)
buttonAlias.disabled: !Config.general.color.schemeGeneration
enabled: Config.general.color.schemeGeneration
label: qsTr("Scheme type")
z: 2
menuItems: [
MenuItem {
@@ -250,7 +250,6 @@ SettingsPage {
SettingsSection {
sectionId: "Default Apps"
z: -1
SettingsHeader {
name: "Default Apps"
@@ -1,5 +1,6 @@
import qs.Modules.Settings.Categories.Lockscreen
import qs.Modules.Settings.Controls
import qs.Helpers
import qs.Config
SettingsPage {
+39 -32
View File
@@ -20,7 +20,8 @@ SettingsPage {
}
CustomSplitButtonRow {
// active: true
active: Config.screenshot.mode === "manual" ? menuItems[0] : menuItems[1]
enabled: Config.screenshot.enable_pp
label: qsTr("Effects mode")
menuItems: [
@@ -46,31 +47,8 @@ SettingsPage {
shouldBeActive: Config.screenshot.mode === "manual"
}
SettingSpinBox {
min: 0
name: "Corner radius"
object: Config.screenshot
setting: "radius"
shouldBeActive: Config.screenshot.mode === "manual"
step: 1
}
Separator {
shouldBeActive: Config.screenshot.mode === "manual"
}
SettingSwitch {
name: "Enable shadow"
object: Config.screenshot
setting: "shadow"
shouldBeActive: Config.screenshot.mode === "manual"
}
Separator {
shouldBeActive: Config.screenshot.mode === "manual"
}
SettingSwitch {
enabled: Config.screenshot.enable_pp
name: "Enable rounded corners"
object: Config.screenshot
setting: "rounding"
@@ -78,15 +56,16 @@ SettingsPage {
}
Separator {
shouldBeActive: Config.screenshot.mode === "manual"
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.rounding
}
SettingSpinBox {
enabled: Config.screenshot.enable_pp
min: 0
name: "Shadow blur amount"
name: "Corner radius"
object: Config.screenshot
setting: "shadow_blur"
shouldBeActive: Config.screenshot.mode === "manual"
setting: "radius"
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.rounding
step: 1
}
@@ -94,6 +73,32 @@ SettingsPage {
shouldBeActive: Config.screenshot.mode === "manual"
}
SettingSwitch {
enabled: Config.screenshot.enable_pp
name: "Enable shadow"
object: Config.screenshot
setting: "shadow"
shouldBeActive: Config.screenshot.mode === "manual"
}
Separator {
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow
}
SettingSpinBox {
enabled: Config.screenshot.enable_pp
min: 0
name: "Shadow blur amount"
object: Config.screenshot
setting: "shadow_blur"
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow
step: 1
}
Separator {
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow
}
// SettingSwitch {
// name: "Shadow color broken atm"
// object: Config.Screenshot
@@ -119,24 +124,26 @@ SettingsPage {
// }
SettingSpinBox {
enabled: Config.screenshot.enable_pp
min: 0
name: "Shadow offset X"
object: Config.screenshot
setting: "shadow_offset_x"
shouldBeActive: Config.screenshot.mode === "manual"
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow
step: 1
}
Separator {
shouldBeActive: Config.screenshot.mode === "manual"
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow
}
SettingSpinBox {
enabled: Config.screenshot.enable_pp
min: 0
name: "Shadow offset Y"
object: Config.screenshot
setting: "shadow_offset_y"
shouldBeActive: Config.screenshot.mode === "manual"
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow
step: 1
}
}
+11 -6
View File
@@ -80,7 +80,8 @@ Item {
required property ShellScreen modelData
function applyCrop(): void {
if (!cropRectLoader.item) return;
if (!cropRectLoader.item)
return;
const cropRect = cropRectLoader.item;
// We need to calculate the exact percentage coordinates that map perfectly
@@ -97,7 +98,8 @@ Item {
}
function zoomClipRect(zoom: real): void {
if (!cropRectLoader.item) return;
if (!cropRectLoader.item)
return;
const cropRect = cropRectLoader.item;
let oldCenterX = cropRect.x + cropRect.width * 0.5;
@@ -139,13 +141,14 @@ Item {
id: zoomSlider
Layout.fillWidth: true
Layout.preferredHeight: 10
Layout.preferredHeight: Appearance.padding.larger * 3
from: 1.0
implicitHeight: Appearance.padding.larger * 3
to: 5.0
value: cropRectLoader.item ? cropRectLoader.item.zoom : 1.0
onMoved: {
delegate.zoomClipRect(value);
onInteraction: value => {
delegate.zoomClipRect(1 + (value * 4));
wrapper.changesMade = true;
}
}
@@ -198,6 +201,7 @@ Item {
Loader {
id: cropRectLoader
active: scaledImg.paintedWidth > 0 && scaledImg.status == Image.Ready
sourceComponent: Component {
@@ -272,7 +276,8 @@ Item {
id: mouse
function updateCrop(mouseX, mouseY) {
if (!cropRectLoader.item) return;
if (!cropRectLoader.item)
return;
const cropRect = cropRectLoader.item;
let nx = mouseX - cropRect.width * 0.5;
+80 -60
View File
@@ -19,7 +19,7 @@ Item {
required property var wrapper
implicitHeight: vol.implicitHeight + Appearance.padding.small * 2
implicitWidth: 400 + Appearance.padding.small * 2
implicitWidth: 600 + Appearance.padding.small * 2
CustomRect {
anchors.left: parent.left
@@ -52,25 +52,25 @@ Item {
CustomRect {
Layout.fillWidth: true
Layout.preferredHeight: 42 + Appearance.spacing.smaller * 2
Layout.preferredHeight: 65 + Appearance.spacing.smaller * 2
Layout.topMargin: root.topMargin
color: DynamicColors.tPalette.m3surfaceContainer
radius: root.rounding
RowLayout {
id: outputVolume
Item {
id: sinkIcon
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.margins: Appearance.spacing.smaller
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 15
anchors.leftMargin: Appearance.padding.larger
anchors.top: parent.top
implicitWidth: childrenRect.width
CustomRect {
Layout.alignment: Qt.AlignVCenter
Layout.preferredHeight: 40
Layout.preferredWidth: 40
anchors.centerIn: parent
color: Audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary
implicitHeight: 54
implicitWidth: 54
radius: Appearance.rounding.full
MaterialIcon {
@@ -78,7 +78,7 @@ Item {
anchors.centerIn: parent
animate: true
color: Audio.muted ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onPrimary
font.pointSize: 22
font.pointSize: Appearance.font.size.extraLarge
text: Audio.muted ? "volume_off" : "volume_up"
}
@@ -92,11 +92,20 @@ Item {
}
}
}
}
ColumnLayout {
Layout.fillWidth: true
anchors.bottom: parent.bottom
anchors.bottomMargin: Appearance.padding.smallest
anchors.left: sinkIcon.right
anchors.leftMargin: Appearance.spacing.larger
anchors.right: parent.right
anchors.rightMargin: Appearance.padding.large
anchors.top: parent.top
anchors.topMargin: Appearance.padding.smaller
RowLayout {
Layout.fillHeight: true
Layout.fillWidth: true
CustomText {
@@ -113,15 +122,15 @@ Item {
}
CustomMouseArea {
Layout.bottomMargin: 5
Layout.bottomMargin: Appearance.padding.normal
Layout.fillWidth: true
Layout.preferredHeight: 10
Layout.preferredHeight: Appearance.padding.larger * 3
CustomSlider {
anchors.left: parent.left
anchors.right: parent.right
color: Audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary
implicitHeight: 10
fgColor: Audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary
implicitHeight: parent.height
value: Audio.volume
Behavior on value {
@@ -129,8 +138,7 @@ Item {
}
}
onMoved: Audio.setVolume(value)
}
onInteraction: value => Audio.setVolume(value)
}
}
}
@@ -138,7 +146,7 @@ Item {
CustomClippingRect {
Layout.fillWidth: true
Layout.preferredHeight: 42 + Appearance.spacing.smaller * 2
Layout.preferredHeight: 65 + Appearance.spacing.smaller * 2
Layout.topMargin: root.topMargin
color: DynamicColors.tPalette.m3surfaceContainer
radius: root.rounding
@@ -165,20 +173,20 @@ Item {
}
}
RowLayout {
id: inputVolume
Item {
id: sourceIcon
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.margins: Appearance.spacing.smaller
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 15
anchors.leftMargin: Appearance.padding.larger
anchors.top: parent.top
implicitWidth: childrenRect.width
CustomRect {
Layout.alignment: Qt.AlignVCenter
Layout.preferredHeight: 40
Layout.preferredWidth: 40
anchors.centerIn: parent
color: Audio.sourceMuted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary
implicitHeight: 54
implicitWidth: 54
radius: Appearance.rounding.full
MaterialIcon {
@@ -186,7 +194,7 @@ Item {
anchors.centerIn: parent
animate: true
color: Audio.sourceMuted ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onPrimary
font.pointSize: 22
font.pointSize: Appearance.font.size.extraLarge
text: Audio.sourceMuted ? "mic_off" : "mic"
}
@@ -200,9 +208,17 @@ Item {
}
}
}
}
ColumnLayout {
Layout.fillWidth: true
anchors.bottom: parent.bottom
anchors.bottomMargin: Appearance.padding.smallest
anchors.left: sourceIcon.right
anchors.leftMargin: Appearance.spacing.larger
anchors.right: parent.right
anchors.rightMargin: Appearance.padding.large
anchors.top: parent.top
anchors.topMargin: Appearance.padding.smaller
RowLayout {
Layout.fillHeight: true
@@ -222,15 +238,15 @@ Item {
}
CustomMouseArea {
Layout.bottomMargin: 5
Layout.bottomMargin: Appearance.padding.normal
Layout.fillWidth: true
implicitHeight: 10
Layout.preferredHeight: Appearance.padding.larger * 3
CustomSlider {
anchors.left: parent.left
anchors.right: parent.right
color: Audio.sourceMuted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary
implicitHeight: 10
fgColor: Audio.sourceMuted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary
implicitHeight: parent.height
value: Audio.sourceVolume
Behavior on value {
@@ -238,8 +254,7 @@ Item {
}
}
onMoved: Audio.setSourceVolume(value)
}
onInteraction: value => Audio.setSourceVolume(value)
}
}
}
@@ -265,7 +280,7 @@ Item {
required property var modelData
Layout.fillWidth: true
Layout.preferredHeight: 42 + Appearance.spacing.smaller * 2
Layout.preferredHeight: 65 + Appearance.spacing.smaller * 2
Layout.topMargin: root.topMargin
color: DynamicColors.tPalette.m3surfaceContainer
radius: root.rounding
@@ -292,18 +307,20 @@ Item {
}
}
RowLayout {
id: layoutVolume
Item {
id: appBoxIcon
anchors.fill: parent
anchors.margins: Appearance.spacing.smaller
spacing: 15
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.leftMargin: Appearance.padding.larger
anchors.top: parent.top
implicitWidth: childrenRect.width
CustomRect {
Layout.alignment: Qt.AlignVCenter
Layout.preferredHeight: 40
Layout.preferredWidth: 40
anchors.centerIn: parent
color: appBox.modelData.audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary
implicitHeight: 54
implicitWidth: 54
radius: Appearance.rounding.full
MaterialIcon {
@@ -312,7 +329,7 @@ Item {
anchors.centerIn: parent
animate: true
color: appBox.modelData.audio.muted ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onPrimary
font.pointSize: 22
font.pointSize: Appearance.font.size.extraLarge
text: appBox.modelData.audio.muted ? "volume_off" : "volume_up"
}
@@ -325,10 +342,7 @@ Item {
}
}
}
ColumnLayout {
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Layout.fillHeight: true
}
TextMetrics {
id: metrics
@@ -338,13 +352,22 @@ Item {
text: Audio.getStreamName(appBox.modelData)
}
ColumnLayout {
anchors.bottom: parent.bottom
anchors.bottomMargin: Appearance.padding.smallest
anchors.left: appBoxIcon.right
anchors.leftMargin: Appearance.spacing.larger
anchors.right: parent.right
anchors.rightMargin: Appearance.padding.large
anchors.top: parent.top
anchors.topMargin: Appearance.padding.smaller
RowLayout {
Layout.fillHeight: true
Layout.fillWidth: true
CustomText {
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
Layout.fillHeight: true
Layout.fillWidth: true
elide: Text.ElideRight
text: metrics.elidedText
@@ -352,26 +375,24 @@ Item {
CustomText {
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
Layout.fillHeight: true
font.bold: true
text: qsTr("%1").arg(appBox.modelData.audio.muted ? qsTr("Muted") : `${Math.round(appBox.modelData.audio.volume * 100)}%`)
}
}
CustomMouseArea {
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
Layout.fillHeight: true
Layout.bottomMargin: Appearance.padding.normal
Layout.fillWidth: true
implicitHeight: 10
Layout.preferredHeight: Appearance.padding.larger * 3
CustomSlider {
anchors.left: parent.left
anchors.right: parent.right
color: appBox.modelData.audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary
implicitHeight: 10
fgColor: appBox.modelData.audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary
implicitHeight: parent.height
value: appBox.modelData.audio.volume
onMoved: {
onInteraction: value => {
Audio.setStreamVolume(appBox.modelData, value);
}
}
@@ -380,5 +401,4 @@ Item {
}
}
}
}
}
+100 -13
View File
@@ -3,33 +3,120 @@ import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Config
import qs.Helpers as Helpers
import qs.Helpers
RowLayout {
Item {
id: root
implicitHeight: Battery.isLaptop ? batteryIconLoader.item.implicitHeight : upowerIconLoader.item.implicitHeight
implicitWidth: Battery.isLaptop ? batteryIconLoader.item.implicitWidth : upowerIconLoader.item.implicitWidth
Loader {
id: batteryIconLoader
active: Battery.isLaptop
anchors.centerIn: parent
sourceComponent: Row {
id: batteryIcon
property real batHeight: 16
property real batWidth: 30
property real nubHeight: 6
property real nubWidth: 2
property real radius: Appearance.rounding.smallest / 2
spacing: 1
CustomRect {
id: track
anchors.verticalCenter: parent.verticalCenter
color: Battery.colors.bg
height: batteryIcon.batHeight
radius: batteryIcon.radius
width: batteryIcon.batWidth
CustomText {
color: Battery.colors.fg
font.pointSize: Appearance.font.size.larger / 1.5
font.weight: 800
height: track.height
horizontalAlignment: Text.AlignHCenter
text: Math.round(Battery.currentPerc * 100)
verticalAlignment: Text.AlignVCenter
width: track.width
}
Item {
clip: true
width: parent.width * Battery.currentPerc
anchors {
bottom: parent.bottom
left: parent.left
top: parent.top
}
CustomRect {
id: fill
color: Battery.colors.fg
height: track.height
radius: track.radius
width: track.width
CustomText {
id: batteryLabel
clip: true
color: Battery.colors.bg
font.pointSize: 7.5
font.weight: 800
height: track.height
horizontalAlignment: Text.AlignHCenter
text: Math.round(Battery.currentPerc * 100)
verticalAlignment: Text.AlignVCenter
width: track.width
}
}
}
}
CustomRect {
id: nub
anchors.verticalCenter: parent.verticalCenter
bottomRightRadius: 20
color: Battery.currentPerc < 0.99 ? track.color : fill.color
height: batteryIcon.nubHeight
topRightRadius: 20
width: batteryIcon.nubWidth
}
}
}
Loader {
id: upowerIconLoader
active: !Battery.isLaptop
anchors.centerIn: parent
sourceComponent: RowLayout {
id: upowerIcon
MaterialIcon {
Layout.alignment: Qt.AlignVCenter
animate: true
color: !Helpers.UPower.onBattery || UPower.displayDevice.percentage > 0.2 ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3error
fill: 1
text: {
if (!Helpers.UPower.displayDevice.isLaptopBattery) {
if (PowerProfiles.profile === PowerProfile.PowerSaver)
return "nest_eco_leaf";
if (PowerProfiles.profile === PowerProfile.Performance)
return "bolt";
return "power_settings_new";
}
const perc = Helpers.UPower.displayDevice.percentage;
const charging = [UPowerDeviceState.Charging, UPowerDeviceState.FullyCharged, UPowerDeviceState.PendingCharge].includes(Helpers.UPower.displayDevice.state);
if (perc === 1)
return charging ? "battery_charging_full" : "battery_full";
let level = Math.floor(perc * 7);
if (charging && (level === 4 || level === 1))
level--;
return charging ? `battery_charging_${(level + 3) * 10}` : `battery_${level}_bar`;
}
}
}
}
+4 -4
View File
@@ -18,13 +18,13 @@ public:
explicit BlobGroup(QObject* parent = nullptr);
~BlobGroup() override;
qreal smoothing() const {
[[nodiscard]] qreal smoothing() const {
return m_smoothing;
}
void setSmoothing(qreal s);
QColor color() const {
[[nodiscard]] QColor color() const {
return m_color;
}
@@ -36,11 +36,11 @@ void removeShape(BlobShape* shape);
void setInvertedRect(BlobInvertedRect* rect);
void clearInvertedRect(BlobInvertedRect* rect);
const QList<BlobShape*>& shapes() const {
[[nodiscard]] const QList<BlobShape*>& shapes() const {
return m_shapes;
}
BlobInvertedRect* invertedRect() const {
[[nodiscard]] BlobInvertedRect* invertedRect() const {
return m_invertedRect;
}
+5 -5
View File
@@ -16,25 +16,25 @@ public:
explicit BlobInvertedRect(QQuickItem* parent = nullptr);
~BlobInvertedRect() override;
qreal borderLeft() const {
[[nodiscard]] qreal borderLeft() const {
return m_borderLeft;
}
void setBorderLeft(qreal v);
qreal borderRight() const {
[[nodiscard]] qreal borderRight() const {
return m_borderRight;
}
void setBorderRight(qreal v);
qreal borderTop() const {
[[nodiscard]] qreal borderTop() const {
return m_borderTop;
}
void setBorderTop(qreal v);
qreal borderBottom() const {
[[nodiscard]] qreal borderBottom() const {
return m_borderBottom;
}
@@ -47,7 +47,7 @@ void borderTopChanged();
void borderBottomChanged();
protected:
bool isInvertedRect() const override {
[[nodiscard]] bool isInvertedRect() const override {
return true;
}
+2 -2
View File
@@ -21,8 +21,8 @@ struct BlobRectData {
class BlobMaterial : public QSGMaterial {
public:
QSGMaterialType* type() const override;
QSGMaterialShader* createShader(QSGRendererInterface::RenderMode) const override;
[[nodiscard]] QSGMaterialType* type() const override;
[[nodiscard]] QSGMaterialShader* createShader(QSGRendererInterface::RenderMode) const override;
int compare(const QSGMaterial* other) const override;
float m_paddedX = 0;
+23 -9
View File
@@ -14,15 +14,11 @@ BlobRect::~BlobRect() {
}
void BlobRect::updatePolish() {
BlobShape::updatePolish();
if (m_physicsActive) {
// Check if deformation is visually imperceptible
float totalDelta = std::abs(m_dm00 - 1.0f) + std::abs(m_dm01) + std::abs(m_dm11 - 1.0f);
float totalVel = std::abs(m_dmVel00) + std::abs(m_dmVel01) + std::abs(m_dmVel11);
if (totalDelta < 0.004f && totalVel < 0.05f) {
// Snap to rest, no visible deformation
m_dm00 = 1.0f;
m_dm01 = 0.0f;
m_dm11 = 1.0f;
@@ -31,6 +27,16 @@ void BlobRect::updatePolish() {
emit rawDeformMatrixChanged();
updateCenteredDeformMatrix();
m_physicsActive = false;
if (m_group) {
QMetaObject::invokeMethod(
this,
[this]() {
if (m_group)
m_group->markDirty();
},
Qt::QueuedConnection);
}
} else {
QMetaObject::invokeMethod(
this,
@@ -41,6 +47,8 @@ void BlobRect::updatePolish() {
Qt::QueuedConnection);
}
}
BlobShape::updatePolish();
}
void BlobRect::updatePhysics() {
@@ -56,7 +64,6 @@ void BlobRect::updatePhysics() {
const float dt = static_cast<float>(m_elapsed.restart()) / 1000.0f;
if (dt > 0.1f || dt < 0.001f) {
m_prevScenePos = scenePos;
// Still check atRest on skipped frames to avoid getting stuck
if (m_physicsActive)
checkAtRest(0.0f);
return;
@@ -74,8 +81,6 @@ void BlobRect::updatePhysics() {
m_physicsActive = true;
}
// Compute target deformation matrix from velocity
// R(θ) * diag(stretch, compress) * R(θ)^T
const float kStretchFactor = static_cast<float>(m_deformScale);
constexpr float kMaxStretch = 0.35f;
@@ -98,7 +103,6 @@ void BlobRect::updatePhysics() {
target11 = targetStretch * sin2 + targetCompress * cos2;
}
// Underdamped spring on each matrix component
const float kStiffness = static_cast<float>(m_stiffness);
const float kDamping = static_cast<float>(m_damping);
@@ -238,9 +242,19 @@ void BlobRect::checkAtRest(float speed) {
m_dmVel00 = 0.0f;
m_dmVel01 = 0.0f;
m_dmVel11 = 0.0f;
m_deformMatrix = QMatrix4x4(); // identity
m_deformMatrix = QMatrix4x4();
emit rawDeformMatrixChanged();
updateCenteredDeformMatrix();
m_physicsActive = false;
if (m_group) {
QMetaObject::invokeMethod(
this,
[this]() {
if (m_group)
m_group->markDirty();
},
Qt::QueuedConnection);
}
}
}
+7 -7
View File
@@ -24,7 +24,7 @@ public:
explicit BlobRect(QQuickItem* parent = nullptr);
~BlobRect() override;
qreal stiffness() const {
[[nodiscard]] qreal stiffness() const {
return m_stiffness;
}
@@ -35,7 +35,7 @@ void setStiffness(qreal s) {
}
}
qreal damping() const {
[[nodiscard]] qreal damping() const {
return m_damping;
}
@@ -46,7 +46,7 @@ void setDamping(qreal d) {
}
}
qreal deformScale() const {
[[nodiscard]] qreal deformScale() const {
return m_deformScale;
}
@@ -62,25 +62,25 @@ QQmlListProperty<BlobRect> exclude();
bool isExcluded(const BlobShape* other) const override;
void cornerRadii(float out[4]) const override;
qreal topLeftRadius() const {
[[nodiscard]] qreal topLeftRadius() const {
return m_topLeftRadius;
}
void setTopLeftRadius(qreal r);
qreal topRightRadius() const {
[[nodiscard]] qreal topRightRadius() const {
return m_topRightRadius;
}
void setTopRightRadius(qreal r);
qreal bottomLeftRadius() const {
[[nodiscard]] qreal bottomLeftRadius() const {
return m_bottomLeftRadius;
}
void setBottomLeftRadius(qreal r);
qreal bottomRightRadius() const {
[[nodiscard]] qreal bottomRightRadius() const {
return m_bottomRightRadius;
}
+7 -23
View File
@@ -9,7 +9,6 @@
#include <cmath>
static float deformPadding(const QMatrix4x4& dm, float hw, float hh) {
// Bounding box of the deformed shape: |M * corners|
const float dm00 = dm(0, 0), dm01 = dm(0, 1);
const float dm10 = dm(1, 0), dm11 = dm(1, 1);
const float boundX = std::abs(dm00) * hw + std::abs(dm01) * hh;
@@ -69,16 +68,17 @@ void BlobShape::geometryChange(const QRectF& newGeometry, const QRectF& oldGeome
QQuickItem::geometryChange(newGeometry, oldGeometry);
updateCenteredDeformMatrix();
if (m_group) {
// Accumulate sub-pixel drift so slow movements don't desync the shader
m_pendingDx += static_cast<float>(newGeometry.x() - oldGeometry.x());
m_pendingDy += static_cast<float>(newGeometry.y() - oldGeometry.y());
// Accumulate size delta across multiple frames so incremental size
// changes that are each below the threshold still trigger a dirty
// mark once their accumulated delta exceeds it.
m_pendingDw += static_cast<float>(newGeometry.width() - oldGeometry.width());
m_pendingDh += static_cast<float>(newGeometry.height() - oldGeometry.height());
if (std::abs(m_pendingDx) > 0.5f || std::abs(m_pendingDy) > 0.5f ||
std::abs(m_pendingDw) > 0.5f || std::abs(m_pendingDh) > 0.5f) {
const float deformMag = std::abs(m_deformMatrix(0, 0) - 1.0f) + std::abs(m_deformMatrix(0, 1)) +
std::abs(m_deformMatrix(1, 0)) + std::abs(m_deformMatrix(1, 1) - 1.0f);
const float syncThreshold = deformMag > 0.001f ? 0.05f : 0.5f;
if (std::abs(m_pendingDx) > syncThreshold || std::abs(m_pendingDy) > syncThreshold ||
std::abs(m_pendingDw) > syncThreshold || std::abs(m_pendingDh) > syncThreshold) {
m_pendingDx = 0;
m_pendingDy = 0;
m_pendingDw = 0;
@@ -124,7 +124,6 @@ void BlobShape::updatePolish() {
if (!m_group)
return;
// Ensure all shapes have up-to-date physics (only once per frame)
m_group->ensurePhysicsUpdated();
const QPointF scenePos = mapToScene(QPointF(0, 0));
@@ -149,13 +148,11 @@ void BlobShape::updatePolish() {
width() + 2.0 * static_cast<double>(totalPad), height() + 2.0 * static_cast<double>(totalPad));
}
// Filter nearby normal rects
m_cachedRects.clear();
m_cachedMyIndex = -2;
const QRectF myPadded(static_cast<double>(m_cachedPaddedX), static_cast<double>(m_cachedPaddedY),
static_cast<double>(m_cachedPaddedW), static_cast<double>(m_cachedPaddedH));
// Track shape pointers parallel to m_cachedRects for pairwise exclusion lookups
QVector<BlobShape*> rectShapes;
rectShapes.reserve(m_group->shapes().size());
@@ -163,7 +160,6 @@ void BlobShape::updatePolish() {
if (other->isInvertedRect())
continue;
// Skip zero-size rects
if (other->width() <= 0 || other->height() <= 0)
continue;
@@ -202,7 +198,6 @@ void BlobShape::updatePolish() {
r.offsetX = dm(0, 3);
r.offsetY = dm(1, 3);
// Pre-compute inverse deformation matrix
const float det = a * d - c * b;
const float invDet = std::abs(det) > 1e-6f ? 1.0f / det : 1.0f;
r.invDeform[0] = d * invDet;
@@ -210,12 +205,10 @@ void BlobShape::updatePolish() {
r.invDeform[2] = -c * invDet;
r.invDeform[3] = a * invDet;
// Pre-compute minimum eigenvalue (avoids per-pixel sqrt)
const float halfTr = 0.5f * (a + d);
const float halfDiff = 0.5f * (a - d);
r.minEig = halfTr - std::sqrt(halfDiff * halfDiff + c * c);
// Pre-compute screen-space AABB half-extents
r.screenHalfX = std::abs(a) * r.hw + std::abs(c) * r.hh;
r.screenHalfY = std::abs(b) * r.hw + std::abs(d) * r.hh;
@@ -227,8 +220,6 @@ void BlobShape::updatePolish() {
if (isInvertedRect())
m_cachedMyIndex = -1;
// Compute pairwise exclude masks. Bit j in entry i is set iff rect i excludes rect j
// or rect j excludes rect i. The shader uses this to avoid smin between excluded pairs.
const auto cachedCount = m_cachedRects.size();
for (qsizetype i = 0; i < cachedCount; ++i) {
int mask = 0;
@@ -243,7 +234,6 @@ void BlobShape::updatePolish() {
m_cachedRects[i].excludeMask = mask;
}
// Cache inverted rect data
m_cachedHasInverted = false;
m_cachedInvertedRadius = 0;
memset(m_cachedInvertedOuter, 0, sizeof(m_cachedInvertedOuter));
@@ -262,7 +252,6 @@ void BlobShape::updatePolish() {
const float innerHW = outerHW - static_cast<float>((inv->borderLeft() + inv->borderRight()) / 2.0);
const float innerHH = outerHH - static_cast<float>((inv->borderTop() + inv->borderBottom()) / 2.0);
// Check if this rect is near the border (within 2x smoothing of inner edge)
bool nearBorder = isInvertedRect();
if (!nearBorder) {
const float margin = pad * 2.0f;
@@ -270,7 +259,6 @@ void BlobShape::updatePolish() {
const float myCY = m_cachedPaddedY + m_cachedPaddedH * 0.5f;
const float myHW = m_cachedPaddedW * 0.5f;
const float myHH = m_cachedPaddedH * 0.5f;
// Near border if any edge of padded rect is within margin of inner edge
nearBorder = (myCX - myHW < innerCX - innerHW + margin) || (myCX + myHW > innerCX + innerHW - margin) ||
(myCY - myHH < innerCY - innerHH + margin) || (myCY + myHH > innerCY + innerHH - margin);
}
@@ -291,7 +279,6 @@ void BlobShape::updatePolish() {
}
}
// Pre-compute effective per-corner radii (moves O(N²) work from GPU to CPU)
const float smoothFactor = pad;
constexpr float minR = 2.0f;
const auto rectCount = m_cachedRects.size();
@@ -328,7 +315,6 @@ void BlobShape::updatePolish() {
fTl = std::min(fTl, cpuSmoothstep(0.0f, smoothFactor, -cpuSdBox(cTlX, cTlY, icx, icy, ihw, ihh)));
}
// Combine base radii with fill factors into effective per-corner radii
ri.radius[0] = std::max(ri.radius[0] * fTr, minR);
ri.radius[1] = std::max(ri.radius[1] * fBr, minR);
ri.radius[2] = std::max(ri.radius[2] * fBl, minR);
@@ -357,7 +343,6 @@ QSGNode* BlobShape::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) {
node->setFlag(QSGNode::OwnsMaterial);
}
// Update geometry
auto* geometry = node->geometry();
auto* v = geometry->vertexDataAsTexturedPoint2D();
@@ -373,7 +358,6 @@ QSGNode* BlobShape::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) {
node->markDirty(QSGNode::DirtyGeometry);
// Update material
auto* material = static_cast<BlobMaterial*>(node->material());
material->m_paddedX = m_cachedPaddedX;
material->m_paddedY = m_cachedPaddedY;
+10 -11
View File
@@ -21,23 +21,23 @@ public:
explicit BlobShape(QQuickItem* parent = nullptr);
~BlobShape() override = default;
BlobGroup* group() const {
[[nodiscard]] BlobGroup* group() const {
return m_group;
}
void setGroup(BlobGroup* g);
qreal radius() const {
[[nodiscard]] qreal radius() const {
return m_radius;
}
void setRadius(qreal r);
QMatrix4x4 deformMatrix() const {
[[nodiscard]] QMatrix4x4 deformMatrix() const {
return m_centeredDeformMatrix;
}
QMatrix4x4 rawDeformMatrix() const {
[[nodiscard]] QMatrix4x4 rawDeformMatrix() const {
return m_deformMatrix;
}
@@ -53,7 +53,7 @@ void geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) overri
void updatePolish() override;
QSGNode* updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) override;
virtual bool isInvertedRect() const {
[[nodiscard]] virtual bool isInvertedRect() const {
return false;
}
@@ -72,10 +72,9 @@ void updateCenteredDeformMatrix();
BlobGroup* m_group = nullptr;
qreal m_radius = 0;
QMatrix4x4 m_deformMatrix; // identity by default
QMatrix4x4 m_deformMatrix;
QMatrix4x4 m_centeredDeformMatrix;
// Cached data from updatePolish
float m_cachedPaddedX = 0;
float m_cachedPaddedY = 0;
float m_cachedPaddedW = 0;
@@ -84,10 +83,10 @@ QRectF m_localPaddedRect;
QVector<BlobRectData> m_cachedRects;
int m_cachedMyIndex = -2;
float m_pendingDx = 0;
float m_pendingDy = 0;
float m_pendingDw = 0;
float m_pendingDh = 0;
bool m_cachedHasInverted = false;
float m_pendingDy = 0;
float m_pendingDw = 0;
float m_pendingDh = 0;
bool m_cachedHasInverted = false;
float m_cachedInvertedRadius = 0;
float m_cachedInvertedOuter[4] = {};
float m_cachedInvertedInner[4] = {};
+2
View File
@@ -45,11 +45,13 @@ qml_module(ZShell
requests.hpp requests.cpp
toaster.hpp toaster.cpp
qalculator.hpp qalculator.cpp
zutils.hpp zutils.cpp
LIBRARIES
Qt::Gui
Qt::Quick
Qt::Concurrent
Qt::Sql
Qt::DBus
PkgConfig::Qalculate
)
+1
View File
@@ -2,6 +2,7 @@ qml_module(ZShell-components
URI ZShell.Components
SOURCES
lazylistview.hpp lazylistview.cpp
wavyline.hpp wavyline.cpp
LIBRARIES
Qt::Quick
)
+255
View File
@@ -0,0 +1,255 @@
#include "wavyline.hpp"
#include <qpainter.h>
#include <qpainterpath.h>
namespace ZShell::controls {
WavyLine::WavyLine(QQuickItem* parent)
: QQuickPaintedItem(parent)
, m_lineWidth(4)
, m_amplitudeMultiplier(0.5)
, m_frequency(6)
, m_startX(0)
, m_fullLength(0)
, m_color(Qt::white)
, m_waveProgress(0)
, m_pathType(Linear)
, m_startAngle(0)
, m_fullAngle(360)
, m_radius(-1)
, m_value(1)
, m_startAngleRad(0)
, m_fullAngleRad(2 * M_PI) {
setAntialiasing(true);
}
int WavyLine::lineWidth() const {
return m_lineWidth;
}
void WavyLine::setLineWidth(int lineWidth) {
if (m_lineWidth != lineWidth) {
m_lineWidth = lineWidth;
emit lineWidthChanged();
update();
}
}
qreal WavyLine::amplitudeMultiplier() const {
return m_amplitudeMultiplier;
}
void WavyLine::setAmplitudeMultiplier(qreal amplitudeMultiplier) {
if (!qFuzzyCompare(m_amplitudeMultiplier + 1.0, amplitudeMultiplier + 1.0)) {
m_amplitudeMultiplier = amplitudeMultiplier;
emit amplitudeMultiplierChanged();
update();
}
}
int WavyLine::frequency() const {
return m_frequency;
}
void WavyLine::setFrequency(int frequency) {
if (m_frequency != frequency) {
m_frequency = frequency;
emit frequencyChanged();
update();
}
}
qreal WavyLine::startX() const {
return m_startX;
}
void WavyLine::setStartX(qreal startX) {
if (!qFuzzyCompare(m_startX + 1.0, startX + 1.0)) {
m_startX = startX;
emit startXChanged();
update();
}
}
qreal WavyLine::fullLength() const {
return m_fullLength;
}
void WavyLine::setFullLength(qreal fullLength) {
if (!qFuzzyCompare(m_fullLength + 1.0, fullLength + 1.0)) {
m_fullLength = fullLength;
emit fullLengthChanged();
update();
}
}
QColor WavyLine::color() const {
return m_color;
}
void WavyLine::setColor(const QColor& color) {
if (m_color != color) {
m_color = color;
emit colorChanged();
update();
}
}
qreal WavyLine::waveProgress() const {
return m_waveProgress;
}
void WavyLine::setWaveProgress(qreal progress) {
if (!qFuzzyCompare(m_waveProgress + 1.0, progress + 1.0)) {
m_waveProgress = progress;
emit waveProgressChanged();
update();
}
}
WavyLine::PathType WavyLine::pathType() const {
return m_pathType;
}
void WavyLine::setPathType(PathType pathType) {
if (m_pathType != pathType) {
m_pathType = pathType;
emit pathTypeChanged();
update();
}
}
qreal WavyLine::startAngle() const {
return m_startAngle;
}
void WavyLine::setStartAngle(qreal startAngle) {
if (!qFuzzyCompare(m_startAngle + 1.0, startAngle + 1.0)) {
m_startAngle = startAngle;
m_startAngleRad = startAngle * M_PI / 180.0;
emit startAngleChanged();
update();
}
}
qreal WavyLine::fullAngle() const {
return m_fullAngle;
}
void WavyLine::setFullAngle(qreal fullAngle) {
if (!qFuzzyCompare(m_fullAngle + 1.0, fullAngle + 1.0)) {
m_fullAngle = fullAngle;
m_fullAngleRad = fullAngle * M_PI / 180.0;
emit fullAngleChanged();
update();
}
}
qreal WavyLine::radius() const {
return m_radius;
}
void WavyLine::setRadius(qreal radius) {
if (!qFuzzyCompare(m_radius + 1.0, radius + 1.0)) {
m_radius = radius;
emit radiusChanged();
update();
}
}
qreal WavyLine::value() const {
return m_value;
}
void WavyLine::setValue(qreal value) {
if (!qFuzzyCompare(m_value + 1.0, value + 1.0)) {
m_value = value;
emit valueChanged();
update();
}
}
void WavyLine::paint(QPainter* painter) {
painter->setRenderHint(QPainter::Antialiasing);
painter->setPen(QPen(m_color, m_lineWidth, Qt::SolidLine, Qt::RoundCap));
if (m_pathType == Arc) {
paintArc(painter);
} else {
paintLinear(painter);
}
}
void WavyLine::paintLinear(QPainter* painter) {
const auto amplitude = m_lineWidth * m_amplitudeMultiplier;
const auto phase = m_waveProgress * 2 * M_PI;
const auto centerY = height() / 2;
const auto len = m_fullLength > 0 ? m_fullLength : 1;
const auto start = m_lineWidth / 2.0;
const auto fullEnd = width() - m_lineWidth / 2.0;
const auto drawEnd = start + (fullEnd - start) * m_value;
QPainterPath path;
bool first = true;
for (int x = m_lineWidth / 2; x <= drawEnd; ++x) {
const auto theta = m_frequency * 2 * M_PI * (x + m_startX) / len + phase;
const auto waveY = centerY + amplitude * qSin(theta);
if (first) {
path.moveTo(x, waveY);
first = false;
} else {
path.lineTo(x, waveY);
}
}
painter->drawPath(path);
}
void WavyLine::paintArc(QPainter* painter) {
if (m_fullAngleRad <= 0) {
return;
}
const auto amplitude = m_lineWidth * m_amplitudeMultiplier;
const auto cx = width() / 2.0;
const auto cy = height() / 2.0;
const auto radius = m_radius > 0 ? m_radius : (qMin(width(), height()) - m_lineWidth - 2 * amplitude) / 2.0;
if (radius <= 0) {
return;
}
const auto phase = m_waveProgress * 2 * M_PI;
const auto arcLen = radius * m_fullAngleRad;
const auto len = m_fullLength > 0 ? m_fullLength : arcLen;
const auto drawAngleRad = m_fullAngleRad * m_value;
if (drawAngleRad <= 0) {
return;
}
const auto N = qMax(64, qCeil(radius * drawAngleRad));
const auto dTheta = drawAngleRad / N;
QPainterPath path;
for (int i = 0; i <= N; ++i) {
const auto theta = m_startAngleRad + i * dTheta;
const auto s = i * dTheta * radius;
const auto phi = m_frequency * 2 * M_PI * (s + m_startX) / len + phase;
const auto r = radius + amplitude * qSin(phi);
const auto px = cx + r * qCos(theta);
const auto py = cy + r * qSin(theta);
if (i == 0) {
path.moveTo(px, py);
} else {
path.lineTo(px, py);
}
}
painter->drawPath(path);
}
} // namespace ZShell::controls
+107
View File
@@ -0,0 +1,107 @@
#pragma once
#include <qqmlintegration.h>
#include <qquickpainteditem.h>
namespace ZShell::controls {
class WavyLine : public QQuickPaintedItem {
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(int lineWidth READ lineWidth WRITE setLineWidth NOTIFY lineWidthChanged FINAL)
Q_PROPERTY(qreal amplitudeMultiplier READ amplitudeMultiplier WRITE setAmplitudeMultiplier NOTIFY
amplitudeMultiplierChanged FINAL)
Q_PROPERTY(int frequency READ frequency WRITE setFrequency NOTIFY frequencyChanged FINAL)
Q_PROPERTY(qreal startX READ startX WRITE setStartX NOTIFY startXChanged FINAL)
Q_PROPERTY(qreal fullLength READ fullLength WRITE setFullLength NOTIFY fullLengthChanged FINAL)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged FINAL)
Q_PROPERTY(qreal waveProgress READ waveProgress WRITE setWaveProgress NOTIFY waveProgressChanged FINAL)
Q_PROPERTY(PathType pathType READ pathType WRITE setPathType NOTIFY pathTypeChanged FINAL)
Q_PROPERTY(qreal startAngle READ startAngle WRITE setStartAngle NOTIFY startAngleChanged FINAL)
Q_PROPERTY(qreal fullAngle READ fullAngle WRITE setFullAngle NOTIFY fullAngleChanged FINAL)
Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged FINAL)
Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged FINAL)
public:
enum PathType {
Linear,
Arc
};
Q_ENUM(PathType)
explicit WavyLine(QQuickItem* parent = nullptr);
[[nodiscard]] int lineWidth() const;
void setLineWidth(int lineWidth);
[[nodiscard]] qreal amplitudeMultiplier() const;
void setAmplitudeMultiplier(qreal amplitudeMultiplier);
[[nodiscard]] int frequency() const;
void setFrequency(int frequency);
[[nodiscard]] qreal startX() const;
void setStartX(qreal startX);
[[nodiscard]] qreal fullLength() const;
void setFullLength(qreal fullLength);
[[nodiscard]] QColor color() const;
void setColor(const QColor& color);
[[nodiscard]] qreal waveProgress() const;
void setWaveProgress(qreal progress);
[[nodiscard]] PathType pathType() const;
void setPathType(PathType pathType);
[[nodiscard]] qreal startAngle() const;
void setStartAngle(qreal startAngle);
[[nodiscard]] qreal fullAngle() const;
void setFullAngle(qreal fullAngle);
[[nodiscard]] qreal radius() const;
void setRadius(qreal radius);
[[nodiscard]] qreal value() const;
void setValue(qreal value);
void paint(QPainter* painter) override;
signals:
void lineWidthChanged();
void amplitudeMultiplierChanged();
void frequencyChanged();
void startXChanged();
void fullLengthChanged();
void colorChanged();
void waveProgressChanged();
void pathTypeChanged();
void startAngleChanged();
void fullAngleChanged();
void radiusChanged();
void valueChanged();
private:
void paintLinear(QPainter* painter);
void paintArc(QPainter* painter);
int m_lineWidth;
qreal m_amplitudeMultiplier;
int m_frequency;
qreal m_startX;
qreal m_fullLength;
QColor m_color;
qreal m_waveProgress;
PathType m_pathType;
qreal m_startAngle;
qreal m_fullAngle;
qreal m_radius;
qreal m_value;
qreal m_startAngleRad;
qreal m_fullAngleRad;
};
} // namespace ZShell::controls
+1
View File
@@ -9,6 +9,7 @@ qml_module(ZShell-internal
sparklineitem.hpp sparklineitem.cpp
arcgauge.hpp arcgauge.cpp
wallpaperimage.hpp wallpaperimage.cpp
lidwatcher.hpp lidwatcher.cpp
LIBRARIES
Qt::Gui
Qt::Quick
+1 -1
View File
@@ -58,4 +58,4 @@ qreal m_sweepAngle = 1.5 * M_PI;
qreal m_lineWidth = 10.0;
};
} // namespace ZShell::internal
} // namespace ZShell::Internal
+34 -33
View File
@@ -7,59 +7,60 @@
namespace ZShell::internal {
class CachingImageManager : public QObject {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QQuickItem* item READ item WRITE setItem NOTIFY itemChanged REQUIRED)
Q_PROPERTY(QUrl cacheDir READ cacheDir WRITE setCacheDir NOTIFY cacheDirChanged REQUIRED)
Q_PROPERTY(QQuickItem* item READ item WRITE setItem NOTIFY itemChanged REQUIRED)
Q_PROPERTY(QUrl cacheDir READ cacheDir WRITE setCacheDir NOTIFY cacheDirChanged REQUIRED)
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
Q_PROPERTY(QUrl cachePath READ cachePath NOTIFY cachePathChanged)
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
Q_PROPERTY(QUrl cachePath READ cachePath NOTIFY cachePathChanged)
public:
explicit CachingImageManager(QObject* parent = nullptr)
explicit CachingImageManager(QObject* parent = nullptr)
: QObject(parent)
, m_item(nullptr) {}
, m_item(nullptr) {
}
[[nodiscard]] QQuickItem* item() const;
void setItem(QQuickItem* item);
[[nodiscard]] QQuickItem* item() const;
void setItem(QQuickItem* item);
[[nodiscard]] QUrl cacheDir() const;
void setCacheDir(const QUrl& cacheDir);
[[nodiscard]] QUrl cacheDir() const;
void setCacheDir(const QUrl& cacheDir);
[[nodiscard]] QString path() const;
void setPath(const QString& path);
[[nodiscard]] QString path() const;
void setPath(const QString& path);
[[nodiscard]] QUrl cachePath() const;
[[nodiscard]] QUrl cachePath() const;
Q_INVOKABLE void updateSource();
Q_INVOKABLE void updateSource(const QString& path);
Q_INVOKABLE void updateSource();
Q_INVOKABLE void updateSource(const QString& path);
signals:
void itemChanged();
void cacheDirChanged();
void itemChanged();
void cacheDirChanged();
void pathChanged();
void cachePathChanged();
void usingCacheChanged();
void pathChanged();
void cachePathChanged();
void usingCacheChanged();
private:
QString m_shaPath;
QString m_shaPath;
QQuickItem* m_item;
QUrl m_cacheDir;
QQuickItem* m_item;
QUrl m_cacheDir;
QString m_path;
QUrl m_cachePath;
QString m_path;
QUrl m_cachePath;
QMetaObject::Connection m_widthConn;
QMetaObject::Connection m_heightConn;
QMetaObject::Connection m_widthConn;
QMetaObject::Connection m_heightConn;
[[nodiscard]] qreal effectiveScale() const;
[[nodiscard]] QSize effectiveSize() const;
[[nodiscard]] qreal effectiveScale() const;
[[nodiscard]] QSize effectiveSize() const;
void createCache(const QString& path, const QString& cache, const QString& fillMode, const QSize& size) const;
[[nodiscard]] static QString sha256sum(const QString& path);
void createCache(const QString& path, const QString& cache, const QString& fillMode, const QSize& size) const;
[[nodiscard]] static QString sha256sum(const QString& path);
};
} // namespace ZShell::internal
+2 -2
View File
@@ -2,7 +2,7 @@
#include <algorithm>
namespace caelestia::internal {
namespace ZShell::internal {
CircularBuffer::CircularBuffer(QObject* parent)
: QObject(parent) {
@@ -92,4 +92,4 @@ qreal CircularBuffer::at(int index) const {
return m_data[actualIndex];
}
} // namespace caelestia::internal
} // namespace ZShell::internal
+2 -2
View File
@@ -4,7 +4,7 @@
#include <qqmlintegration.h>
#include <qvector.h>
namespace caelestia::internal {
namespace ZShell::internal {
class CircularBuffer : public QObject {
Q_OBJECT
@@ -41,4 +41,4 @@ int m_count = 0;
int m_capacity = 0;
};
} // namespace caelestia::internal
} // namespace ZShell::internal
@@ -1,5 +1,6 @@
#pragma once
#include <cstdint>
#include <qeasingcurve.h>
#include <qobject.h>
#include <qqmlintegration.h>
+37 -37
View File
@@ -8,67 +8,67 @@
namespace ZShell::internal::hypr {
class HyprKeyboard : public QObject {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("HyprKeyboard instances can only be retrieved from a HyprDevices")
Q_PROPERTY(QVariantHash lastIpcObject READ lastIpcObject NOTIFY lastIpcObjectChanged)
Q_PROPERTY(QString address READ address NOTIFY addressChanged)
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
Q_PROPERTY(QString layout READ layout NOTIFY layoutChanged)
Q_PROPERTY(QString activeKeymap READ activeKeymap NOTIFY activeKeymapChanged)
Q_PROPERTY(bool capsLock READ capsLock NOTIFY capsLockChanged)
Q_PROPERTY(bool numLock READ numLock NOTIFY numLockChanged)
Q_PROPERTY(bool main READ main NOTIFY mainChanged)
Q_PROPERTY(QVariantHash lastIpcObject READ lastIpcObject NOTIFY lastIpcObjectChanged)
Q_PROPERTY(QString address READ address NOTIFY addressChanged)
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
Q_PROPERTY(QString layout READ layout NOTIFY layoutChanged)
Q_PROPERTY(QString activeKeymap READ activeKeymap NOTIFY activeKeymapChanged)
Q_PROPERTY(bool capsLock READ capsLock NOTIFY capsLockChanged)
Q_PROPERTY(bool numLock READ numLock NOTIFY numLockChanged)
Q_PROPERTY(bool main READ main NOTIFY mainChanged)
public:
explicit HyprKeyboard(QJsonObject ipcObject, QObject* parent = nullptr);
explicit HyprKeyboard(QJsonObject ipcObject, QObject* parent = nullptr);
[[nodiscard]] QVariantHash lastIpcObject() const;
[[nodiscard]] QString address() const;
[[nodiscard]] QString name() const;
[[nodiscard]] QString layout() const;
[[nodiscard]] QString activeKeymap() const;
[[nodiscard]] bool capsLock() const;
[[nodiscard]] bool numLock() const;
[[nodiscard]] bool main() const;
[[nodiscard]] QVariantHash lastIpcObject() const;
[[nodiscard]] QString address() const;
[[nodiscard]] QString name() const;
[[nodiscard]] QString layout() const;
[[nodiscard]] QString activeKeymap() const;
[[nodiscard]] bool capsLock() const;
[[nodiscard]] bool numLock() const;
[[nodiscard]] bool main() const;
bool updateLastIpcObject(QJsonObject object);
bool updateLastIpcObject(QJsonObject object);
signals:
void lastIpcObjectChanged();
void addressChanged();
void nameChanged();
void layoutChanged();
void activeKeymapChanged();
void capsLockChanged();
void numLockChanged();
void mainChanged();
void lastIpcObjectChanged();
void addressChanged();
void nameChanged();
void layoutChanged();
void activeKeymapChanged();
void capsLockChanged();
void numLockChanged();
void mainChanged();
private:
QJsonObject m_lastIpcObject;
QJsonObject m_lastIpcObject;
};
class HyprDevices : public QObject {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("HyprDevices instances can only be retrieved from a HyprExtras")
Q_PROPERTY(
Q_PROPERTY(
QQmlListProperty<ZShell::internal::hypr::HyprKeyboard> keyboards READ keyboards NOTIFY keyboardsChanged)
public:
explicit HyprDevices(QObject* parent = nullptr);
explicit HyprDevices(QObject* parent = nullptr);
[[nodiscard]] QQmlListProperty<HyprKeyboard> keyboards();
[[nodiscard]] QQmlListProperty<HyprKeyboard> keyboards();
bool updateLastIpcObject(QJsonObject object);
bool updateLastIpcObject(QJsonObject object);
signals:
void keyboardsChanged();
void keyboardsChanged();
private:
QList<HyprKeyboard*> m_keyboards;
QList<HyprKeyboard*> m_keyboards;
};
} // namespace ZShell::internal::hypr
+86
View File
@@ -0,0 +1,86 @@
#include "lidwatcher.hpp"
#include <QtDBus/qdbusconnection.h>
#include <QtDBus/qdbuserror.h>
#include <QtDBus/qdbusinterface.h>
#include <QtDBus/qdbusreply.h>
#include <qloggingcategory.h>
Q_LOGGING_CATEGORY(lcLidWatcher, "ZShell.internal.logindmanager", QtInfoMsg)
namespace ZShell::internal {
LidWatcher::LidWatcher(QObject* parent) : QObject(parent) {
auto bus = QDBusConnection::systemBus();
if (!bus.isConnected()) {
qCWarning(lcLidWatcher)
<< "Failed to connect to system bus:" << bus.lastError().message();
return;
}
bool ok = bus.connect("org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"PrepareForSleep",
this,
SLOT(handlePrepareForSleep(bool)));
if (!ok) {
qCWarning(lcLidWatcher)
<< "Failed to connect to PrepareForSleep signal:"
<< bus.lastError().message();
}
QDBusInterface login1("org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
bus);
const QDBusReply<QDBusObjectPath> reply = login1.call("GetSession", "auto");
if (!reply.isValid()) {
qCWarning(lcLidWatcher) << "Failed to get session path";
return;
}
const auto sessionPath = reply.value().path();
ok = bus.connect("org.freedesktop.login1",
sessionPath,
"org.freedesktop.login1.Session",
"Lock",
this,
SLOT(handleLockRequested()));
if (!ok) {
qCWarning(lcLidWatcher)
<< "Failed to connect to Lock signal:" << bus.lastError().message();
}
ok = bus.connect("org.freedesktop.login1",
sessionPath,
"org.freedesktop.login1.Session",
"Unlock",
this,
SLOT(handleUnlockRequested()));
if (!ok) {
qCWarning(lcLidWatcher) << "Failed to connect to Unlock signal:"
<< bus.lastError().message();
}
}
void LidWatcher::handlePrepareForSleep(bool sleep) {
if (sleep) {
emit aboutToSleep();
} else {
emit resumed();
}
}
void LidWatcher::handleLockRequested() {
emit lockRequested();
}
void LidWatcher::handleUnlockRequested() {
emit unlockRequested();
}
} // namespace ZShell::internal
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include <qobject.h>
#include <qqmlintegration.h>
namespace ZShell::internal {
class LidWatcher : public QObject {
Q_OBJECT
QML_ELEMENT
public:
explicit LidWatcher(QObject* parent = nullptr);
signals:
void aboutToSleep();
void resumed();
void lockRequested();
void unlockRequested();
private slots:
void handlePrepareForSleep(bool sleep);
void handleLockRequested();
void handleUnlockRequested();
};
} // namespace ZShell::internal
+2 -2
View File
@@ -4,7 +4,7 @@
#include <qpainterpath.h>
#include <qpen.h>
namespace caelestia::internal {
namespace ZShell::internal {
SparklineItem::SparklineItem(QQuickItem* parent)
: QQuickPaintedItem(parent) {
@@ -212,4 +212,4 @@ void SparklineItem::setLineWidth(qreal width) {
update();
}
} // namespace caelestia::internal
} // namespace ZShell::internal
+2 -2
View File
@@ -7,7 +7,7 @@
#include "circularbuffer.hpp"
namespace caelestia::internal {
namespace ZShell::internal {
class SparklineItem : public QQuickPaintedItem {
Q_OBJECT
@@ -87,4 +87,4 @@ int m_historyLength = 30;
qreal m_lineWidth = 2.0;
};
} // namespace caelestia::internal
} // namespace ZShell::internal
+91 -90
View File
@@ -1,5 +1,6 @@
#pragma once
#include <cstdint>
#include <qabstractitemmodel.h>
#include <qdir.h>
#include <qfilesystemwatcher.h>
@@ -13,136 +14,136 @@
namespace ZShell::models {
class FileSystemEntry : public QObject {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("FileSystemEntry instances can only be retrieved from a FileSystemModel")
Q_PROPERTY(QString path READ path CONSTANT)
Q_PROPERTY(QString relativePath READ relativePath NOTIFY relativePathChanged)
Q_PROPERTY(QString name READ name CONSTANT)
Q_PROPERTY(QString baseName READ baseName CONSTANT)
Q_PROPERTY(QString parentDir READ parentDir CONSTANT)
Q_PROPERTY(QString suffix READ suffix CONSTANT)
Q_PROPERTY(qint64 size READ size CONSTANT)
Q_PROPERTY(bool isDir READ isDir CONSTANT)
Q_PROPERTY(bool isImage READ isImage CONSTANT)
Q_PROPERTY(QString mimeType READ mimeType CONSTANT)
Q_PROPERTY(QString path READ path CONSTANT)
Q_PROPERTY(QString relativePath READ relativePath NOTIFY relativePathChanged)
Q_PROPERTY(QString name READ name CONSTANT)
Q_PROPERTY(QString baseName READ baseName CONSTANT)
Q_PROPERTY(QString parentDir READ parentDir CONSTANT)
Q_PROPERTY(QString suffix READ suffix CONSTANT)
Q_PROPERTY(qint64 size READ size CONSTANT)
Q_PROPERTY(bool isDir READ isDir CONSTANT)
Q_PROPERTY(bool isImage READ isImage CONSTANT)
Q_PROPERTY(QString mimeType READ mimeType CONSTANT)
public:
explicit FileSystemEntry(const QString& path, const QString& relativePath, QObject* parent = nullptr);
explicit FileSystemEntry(const QString& path, const QString& relativePath, QObject* parent = nullptr);
[[nodiscard]] QString path() const;
[[nodiscard]] QString relativePath() const;
[[nodiscard]] QString name() const;
[[nodiscard]] QString baseName() const;
[[nodiscard]] QString parentDir() const;
[[nodiscard]] QString suffix() const;
[[nodiscard]] qint64 size() const;
[[nodiscard]] bool isDir() const;
[[nodiscard]] bool isImage() const;
[[nodiscard]] QString mimeType() const;
[[nodiscard]] QString path() const;
[[nodiscard]] QString relativePath() const;
[[nodiscard]] QString name() const;
[[nodiscard]] QString baseName() const;
[[nodiscard]] QString parentDir() const;
[[nodiscard]] QString suffix() const;
[[nodiscard]] qint64 size() const;
[[nodiscard]] bool isDir() const;
[[nodiscard]] bool isImage() const;
[[nodiscard]] QString mimeType() const;
void updateRelativePath(const QDir& dir);
void updateRelativePath(const QDir& dir);
signals:
void relativePathChanged();
void relativePathChanged();
private:
const QFileInfo m_fileInfo;
const QFileInfo m_fileInfo;
const QString m_path;
QString m_relativePath;
const QString m_path;
QString m_relativePath;
mutable bool m_isImage;
mutable bool m_isImageInitialised;
mutable bool m_isImage;
mutable bool m_isImageInitialised;
mutable QString m_mimeType;
mutable bool m_mimeTypeInitialised;
mutable QString m_mimeType;
mutable bool m_mimeTypeInitialised;
};
class FileSystemModel : public QAbstractListModel {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
Q_PROPERTY(bool recursive READ recursive WRITE setRecursive NOTIFY recursiveChanged)
Q_PROPERTY(bool watchChanges READ watchChanges WRITE setWatchChanges NOTIFY watchChangesChanged)
Q_PROPERTY(bool showHidden READ showHidden WRITE setShowHidden NOTIFY showHiddenChanged)
Q_PROPERTY(bool sortReverse READ sortReverse WRITE setSortReverse NOTIFY sortReverseChanged)
Q_PROPERTY(Filter filter READ filter WRITE setFilter NOTIFY filterChanged)
Q_PROPERTY(QStringList nameFilters READ nameFilters WRITE setNameFilters NOTIFY nameFiltersChanged)
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
Q_PROPERTY(bool recursive READ recursive WRITE setRecursive NOTIFY recursiveChanged)
Q_PROPERTY(bool watchChanges READ watchChanges WRITE setWatchChanges NOTIFY watchChangesChanged)
Q_PROPERTY(bool showHidden READ showHidden WRITE setShowHidden NOTIFY showHiddenChanged)
Q_PROPERTY(bool sortReverse READ sortReverse WRITE setSortReverse NOTIFY sortReverseChanged)
Q_PROPERTY(Filter filter READ filter WRITE setFilter NOTIFY filterChanged)
Q_PROPERTY(QStringList nameFilters READ nameFilters WRITE setNameFilters NOTIFY nameFiltersChanged)
Q_PROPERTY(QQmlListProperty<ZShell::models::FileSystemEntry> entries READ entries NOTIFY entriesChanged)
Q_PROPERTY(QQmlListProperty<ZShell::models::FileSystemEntry> entries READ entries NOTIFY entriesChanged)
public:
enum Filter {
enum Filter {
NoFilter,
Images,
Files,
Dirs
};
Q_ENUM(Filter)
};
Q_ENUM(Filter)
explicit FileSystemModel(QObject* parent = nullptr);
explicit FileSystemModel(QObject* parent = nullptr);
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
[[nodiscard]] int rowCount(const QModelIndex& parent = QModelIndex()) const override;
[[nodiscard]] QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
[[nodiscard]] QString path() const;
void setPath(const QString& path);
[[nodiscard]] QString path() const;
void setPath(const QString& path);
[[nodiscard]] bool recursive() const;
void setRecursive(bool recursive);
[[nodiscard]] bool recursive() const;
void setRecursive(bool recursive);
[[nodiscard]] bool watchChanges() const;
void setWatchChanges(bool watchChanges);
[[nodiscard]] bool watchChanges() const;
void setWatchChanges(bool watchChanges);
[[nodiscard]] bool showHidden() const;
void setShowHidden(bool showHidden);
[[nodiscard]] bool showHidden() const;
void setShowHidden(bool showHidden);
[[nodiscard]] bool sortReverse() const;
void setSortReverse(bool sortReverse);
[[nodiscard]] bool sortReverse() const;
void setSortReverse(bool sortReverse);
[[nodiscard]] Filter filter() const;
void setFilter(Filter filter);
[[nodiscard]] Filter filter() const;
void setFilter(Filter filter);
[[nodiscard]] QStringList nameFilters() const;
void setNameFilters(const QStringList& nameFilters);
[[nodiscard]] QStringList nameFilters() const;
void setNameFilters(const QStringList& nameFilters);
[[nodiscard]] QQmlListProperty<FileSystemEntry> entries();
[[nodiscard]] QQmlListProperty<FileSystemEntry> entries();
signals:
void pathChanged();
void recursiveChanged();
void watchChangesChanged();
void showHiddenChanged();
void sortReverseChanged();
void filterChanged();
void nameFiltersChanged();
void entriesChanged();
void pathChanged();
void recursiveChanged();
void watchChangesChanged();
void showHiddenChanged();
void sortReverseChanged();
void filterChanged();
void nameFiltersChanged();
void entriesChanged();
private:
QDir m_dir;
QFileSystemWatcher m_watcher;
QList<FileSystemEntry*> m_entries;
QHash<QString, QFuture<QPair<QSet<QString>, QSet<QString>>>> m_futures;
QDir m_dir;
QFileSystemWatcher m_watcher;
QList<FileSystemEntry*> m_entries;
QHash<QString, QFuture<QPair<QSet<QString>, QSet<QString> > > > m_futures;
QString m_path;
bool m_recursive;
bool m_watchChanges;
bool m_showHidden;
bool m_sortReverse;
Filter m_filter;
QStringList m_nameFilters;
QString m_path;
bool m_recursive;
bool m_watchChanges;
bool m_showHidden;
bool m_sortReverse;
Filter m_filter;
QStringList m_nameFilters;
void watchDirIfRecursive(const QString& path);
void update();
void updateWatcher();
void updateEntries();
void updateEntriesForDir(const QString& dir);
void applyChanges(const QSet<QString>& removedPaths, const QSet<QString>& addedPaths);
[[nodiscard]] bool compareEntries(const FileSystemEntry* a, const FileSystemEntry* b) const;
void watchDirIfRecursive(const QString& path);
void update();
void updateWatcher();
void updateEntries();
void updateEntriesForDir(const QString& dir);
void applyChanges(const QSet<QString>& removedPaths, const QSet<QString>& addedPaths);
[[nodiscard]] bool compareEntries(const FileSystemEntry* a, const FileSystemEntry* b) const;
};
} // namespace ZShell::models
+1
View File
@@ -9,6 +9,7 @@ qml_module(ZShell-services
cavaprovider.hpp cavaprovider.cpp
desktopmodel.hpp desktopmodel.cpp
desktopstatemanager.hpp desktopstatemanager.cpp
hyprsunsetmanager.hpp hyprsunsetmanager.cpp
LIBRARIES
Qt6::Core
Qt6::Qml
+1 -1
View File
@@ -59,7 +59,7 @@ quint32 readChunk(double* out, quint32 count = 0);
private:
explicit AudioCollector(QObject* parent = nullptr);
~AudioCollector();
~AudioCollector() override;
std::jthread m_thread;
std::vector<float> m_buffer1;
+2 -3
View File
@@ -11,11 +11,10 @@ Q_OBJECT
public:
explicit AudioProcessor(QObject* parent = nullptr);
~AudioProcessor();
~AudioProcessor() override;
void init();
public slots:
void start();
void stop();
@@ -31,7 +30,7 @@ Q_OBJECT
public:
explicit AudioProvider(QObject* parent = nullptr);
~AudioProvider();
~AudioProvider() override;
protected:
AudioProcessor* m_processor;
+1 -1
View File
@@ -11,7 +11,7 @@ Q_OBJECT
public:
explicit BeatProcessor(QObject* parent = nullptr);
~BeatProcessor();
~BeatProcessor() override;
signals:
void beat(smpl_t bpm);
+1 -1
View File
@@ -11,7 +11,7 @@ Q_OBJECT
public:
explicit CavaProcessor(QObject* parent = nullptr);
~CavaProcessor();
~CavaProcessor() override;
void setBars(int bars);
+5 -4
View File
@@ -4,6 +4,7 @@
#include <QList>
#include <QString>
#include <QQmlEngine>
#include <cstdint>
namespace ZShell::services {
@@ -30,9 +31,9 @@ enum DesktopRoles {
explicit DesktopModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
Q_INVOKABLE void loadDirectory(const QString &path);
Q_INVOKABLE void moveIcon(int index, int newX, int newY);
@@ -43,4 +44,4 @@ QList<DesktopItem> m_items;
void saveCurrentLayout();
};
} // namespace ZShell::services
} // namespace ZShell::Services
@@ -18,7 +18,7 @@ Q_INVOKABLE void saveLayout(const QVariantMap& layout);
Q_INVOKABLE QVariantMap getLayout();
private:
QString getConfigFilePath() const;
[[nodiscard]] QString getConfigFilePath() const;
};
} // namespace ZShell::services
@@ -0,0 +1,147 @@
#include "hyprsunsetmanager.hpp"
#include <qlogging.h>
#include <qobject.h>
#include <qprocess.h>
#include <qtimer.h>
namespace ZShell::services {
HyprsunsetManager::HyprsunsetManager(QObject* parent) : QObject(parent) {
connect(&m_timer, &QTimer::timeout, this, &HyprsunsetManager::apply);
connect(&m_manualTimer, &QTimer::timeout, this, [this] {
m_manualToggle = false;
emit manualToggleChanged();
apply();
});
connect(&m_startCooldown, &QTimer::timeout, this, [this] {
m_startAllowed = true;
apply();
});
m_startCooldown.start(2000);
m_manualTimer.setSingleShot(true);
m_timer.start(60000);
m_process.setStandardInputFile(QProcess::nullDevice());
m_process.setStandardOutputFile(QProcess::nullDevice());
}
int HyprsunsetManager::startTime() const {
return m_startTime;
}
int HyprsunsetManager::endTime() const {
return m_endTime;
}
bool HyprsunsetManager::manualToggle() const {
return m_manualToggle;
}
bool HyprsunsetManager::enabled() const {
return m_enabled;
}
bool HyprsunsetManager::activeAuto() const {
return m_activeAuto;
}
int HyprsunsetManager::temp() const {
return m_temp;
}
void HyprsunsetManager::setActiveAuto(bool activeAuto) {
if (activeAuto == m_activeAuto)
return;
m_activeAuto = activeAuto;
emit activeAutoChanged();
}
void HyprsunsetManager::setManualToggle(bool toggle) {
if (toggle == m_manualToggle)
return;
m_manualToggle = toggle;
emit manualToggleChanged();
m_manualTimer.start(60 * 60 * 1000);
}
void HyprsunsetManager::setEndTime(const int& time) {
if (time == m_endTime)
return;
m_endTime = time;
emit endTimeChanged();
apply();
}
void HyprsunsetManager::setStartTime(const int& time) {
if (time == m_startTime)
return;
m_startTime = time;
emit startTimeChanged();
apply();
}
void HyprsunsetManager::setTemp(const int& temp) {
if (temp == m_temp)
return;
m_temp = temp;
emit tempChanged();
apply();
}
void HyprsunsetManager::toggle() {
if (m_enabled) {
end();
} else {
start();
}
}
void HyprsunsetManager::start() {
if (m_enabled && m_initialized)
return;
m_initialized = true;
m_enabled = true;
emit enabledChanged();
m_process.setProgram("hyprctl");
m_process.setArguments({"hyprsunset", "temperature", QString::number(m_temp)});
m_process.startDetached();
}
void HyprsunsetManager::end() {
if (!m_enabled && m_initialized)
return;
m_initialized = true;
m_enabled = false;
emit enabledChanged();
m_process.setProgram("hyprctl");
m_process.setArguments({"hyprsunset", "identity"});
m_process.startDetached();
}
void HyprsunsetManager::apply() {
if (m_manualToggle || !m_activeAuto || !m_startAllowed)
return;
const auto current = QTime::currentTime().hour();
if (current >= m_startTime || current < m_endTime) {
start();
} else {
end();
}
}
};
@@ -0,0 +1,66 @@
#pragma once
#include <QObject>
#include <QTime>
#include <QProcess>
#include <QTimer>
#include <qqmlintegration.h>
#include <qtmetamacros.h>
namespace ZShell::services {
class HyprsunsetManager : public QObject {
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(bool enabled READ enabled NOTIFY enabledChanged)
Q_PROPERTY(int startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged)
Q_PROPERTY(int endTime READ endTime WRITE setEndTime NOTIFY endTimeChanged)
Q_PROPERTY(int temp READ temp WRITE setTemp NOTIFY tempChanged)
Q_PROPERTY(bool activeAuto READ activeAuto WRITE setActiveAuto NOTIFY activeAutoChanged)
Q_PROPERTY(bool manualToggle READ manualToggle WRITE setManualToggle NOTIFY manualToggleChanged)
public:
explicit HyprsunsetManager(QObject* parent = nullptr);
[[nodiscard]] int startTime() const;
[[nodiscard]] int endTime() const;
[[nodiscard]] bool enabled() const;
[[nodiscard]] int temp() const;
[[nodiscard]] bool activeAuto() const;
[[nodiscard]] bool manualToggle() const;
Q_INVOKABLE void toggle();
Q_INVOKABLE void apply();
void setStartTime(const int& time);
void setEndTime(const int& time);
void setTemp(const int& temp);
void setActiveAuto(bool activeAuto);
void setManualToggle(bool toggle);
signals:
void enabledChanged();
void startTimeChanged();
void activeAutoChanged();
void endTimeChanged();
void tempChanged();
void manualToggleChanged();
private:
int m_startTime;
int m_endTime;
bool m_enabled = false;
bool m_manualToggle = false;
bool m_activeAuto;
bool m_startAllowed = false;
bool m_initialized = false;
QTimer m_startCooldown;
int m_temp;
QProcess m_process;
QTimer m_timer;
QTimer m_manualTimer;
void start();
void end();
};
};
+63 -63
View File
@@ -9,98 +9,98 @@
namespace ZShell {
class AppEntry : public QObject {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("AppEntry instances can only be retrieved from an AppDb")
// The actual DesktopEntry, but we don't have access to the type so it's a QObject
Q_PROPERTY(QObject* entry READ entry CONSTANT)
// The actual DesktopEntry, but we don't have access to the type so it's a QObject
Q_PROPERTY(QObject* entry READ entry CONSTANT)
Q_PROPERTY(quint32 frequency READ frequency NOTIFY frequencyChanged)
Q_PROPERTY(QString id READ id CONSTANT)
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
Q_PROPERTY(QString comment READ comment NOTIFY commentChanged)
Q_PROPERTY(QString execString READ execString NOTIFY execStringChanged)
Q_PROPERTY(QString startupClass READ startupClass NOTIFY startupClassChanged)
Q_PROPERTY(QString genericName READ genericName NOTIFY genericNameChanged)
Q_PROPERTY(QString categories READ categories NOTIFY categoriesChanged)
Q_PROPERTY(QString keywords READ keywords NOTIFY keywordsChanged)
Q_PROPERTY(quint32 frequency READ frequency NOTIFY frequencyChanged)
Q_PROPERTY(QString id READ id CONSTANT)
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
Q_PROPERTY(QString comment READ comment NOTIFY commentChanged)
Q_PROPERTY(QString execString READ execString NOTIFY execStringChanged)
Q_PROPERTY(QString startupClass READ startupClass NOTIFY startupClassChanged)
Q_PROPERTY(QString genericName READ genericName NOTIFY genericNameChanged)
Q_PROPERTY(QString categories READ categories NOTIFY categoriesChanged)
Q_PROPERTY(QString keywords READ keywords NOTIFY keywordsChanged)
public:
explicit AppEntry(QObject* entry, quint32 frequency, QObject* parent = nullptr);
explicit AppEntry(QObject* entry, quint32 frequency, QObject* parent = nullptr);
[[nodiscard]] QObject* entry() const;
[[nodiscard]] QObject* entry() const;
[[nodiscard]] quint32 frequency() const;
void setFrequency(quint32 frequency);
void incrementFrequency();
[[nodiscard]] quint32 frequency() const;
void setFrequency(quint32 frequency);
void incrementFrequency();
[[nodiscard]] QString id() const;
[[nodiscard]] QString name() const;
[[nodiscard]] QString comment() const;
[[nodiscard]] QString execString() const;
[[nodiscard]] QString startupClass() const;
[[nodiscard]] QString genericName() const;
[[nodiscard]] QString categories() const;
[[nodiscard]] QString keywords() const;
[[nodiscard]] QString id() const;
[[nodiscard]] QString name() const;
[[nodiscard]] QString comment() const;
[[nodiscard]] QString execString() const;
[[nodiscard]] QString startupClass() const;
[[nodiscard]] QString genericName() const;
[[nodiscard]] QString categories() const;
[[nodiscard]] QString keywords() const;
signals:
void frequencyChanged();
void nameChanged();
void commentChanged();
void execStringChanged();
void startupClassChanged();
void genericNameChanged();
void categoriesChanged();
void keywordsChanged();
void frequencyChanged();
void nameChanged();
void commentChanged();
void execStringChanged();
void startupClassChanged();
void genericNameChanged();
void categoriesChanged();
void keywordsChanged();
private:
QObject* m_entry;
quint32 m_frequency;
QObject* m_entry;
quint32 m_frequency;
};
class AppDb : public QObject {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QString uuid READ uuid CONSTANT)
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged REQUIRED)
Q_PROPERTY(QObjectList entries READ entries WRITE setEntries NOTIFY entriesChanged REQUIRED)
Q_PROPERTY(QQmlListProperty<ZShell::AppEntry> apps READ apps NOTIFY appsChanged)
Q_PROPERTY(QString uuid READ uuid CONSTANT)
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged REQUIRED)
Q_PROPERTY(QObjectList entries READ entries WRITE setEntries NOTIFY entriesChanged REQUIRED)
Q_PROPERTY(QQmlListProperty<ZShell::AppEntry> apps READ apps NOTIFY appsChanged)
public:
explicit AppDb(QObject* parent = nullptr);
explicit AppDb(QObject* parent = nullptr);
[[nodiscard]] QString uuid() const;
[[nodiscard]] QString uuid() const;
[[nodiscard]] QString path() const;
void setPath(const QString& path);
[[nodiscard]] QString path() const;
void setPath(const QString& path);
[[nodiscard]] QObjectList entries() const;
void setEntries(const QObjectList& entries);
[[nodiscard]] QObjectList entries() const;
void setEntries(const QObjectList& entries);
[[nodiscard]] QQmlListProperty<AppEntry> apps();
[[nodiscard]] QQmlListProperty<AppEntry> apps();
Q_INVOKABLE void incrementFrequency(const QString& id);
Q_INVOKABLE void incrementFrequency(const QString& id);
signals:
void pathChanged();
void entriesChanged();
void appsChanged();
void pathChanged();
void entriesChanged();
void appsChanged();
private:
QTimer* m_timer;
QTimer* m_timer;
const QString m_uuid;
QString m_path;
QObjectList m_entries;
QHash<QString, AppEntry*> m_apps;
mutable QList<AppEntry*> m_sortedApps;
const QString m_uuid;
QString m_path;
QObjectList m_entries;
QHash<QString, AppEntry*> m_apps;
mutable QList<AppEntry*> m_sortedApps;
QList<AppEntry*>& getSortedApps() const;
quint32 getFrequency(const QString& id) const;
void updateAppFrequencies();
void updateApps();
QList<AppEntry*>& getSortedApps() const;
quint32 getFrequency(const QString& id) const;
void updateAppFrequencies();
void updateApps();
};
} // namespace ZShell
+31 -31
View File
@@ -9,53 +9,53 @@
namespace ZShell {
class ImageAnalyser : public QObject {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged)
Q_PROPERTY(QQuickItem* sourceItem READ sourceItem WRITE setSourceItem NOTIFY sourceItemChanged)
Q_PROPERTY(int rescaleSize READ rescaleSize WRITE setRescaleSize NOTIFY rescaleSizeChanged)
Q_PROPERTY(QColor dominantColour READ dominantColour NOTIFY dominantColourChanged)
Q_PROPERTY(qreal luminance READ luminance NOTIFY luminanceChanged)
Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged)
Q_PROPERTY(QQuickItem* sourceItem READ sourceItem WRITE setSourceItem NOTIFY sourceItemChanged)
Q_PROPERTY(int rescaleSize READ rescaleSize WRITE setRescaleSize NOTIFY rescaleSizeChanged)
Q_PROPERTY(QColor dominantColour READ dominantColour NOTIFY dominantColourChanged)
Q_PROPERTY(qreal luminance READ luminance NOTIFY luminanceChanged)
public:
explicit ImageAnalyser(QObject* parent = nullptr);
explicit ImageAnalyser(QObject* parent = nullptr);
[[nodiscard]] QString source() const;
void setSource(const QString& source);
[[nodiscard]] QString source() const;
void setSource(const QString& source);
[[nodiscard]] QQuickItem* sourceItem() const;
void setSourceItem(QQuickItem* sourceItem);
[[nodiscard]] QQuickItem* sourceItem() const;
void setSourceItem(QQuickItem* sourceItem);
[[nodiscard]] int rescaleSize() const;
void setRescaleSize(int rescaleSize);
[[nodiscard]] int rescaleSize() const;
void setRescaleSize(int rescaleSize);
[[nodiscard]] QColor dominantColour() const;
[[nodiscard]] qreal luminance() const;
[[nodiscard]] QColor dominantColour() const;
[[nodiscard]] qreal luminance() const;
Q_INVOKABLE void requestUpdate();
Q_INVOKABLE void requestUpdate();
signals:
void sourceChanged();
void sourceItemChanged();
void rescaleSizeChanged();
void dominantColourChanged();
void luminanceChanged();
void sourceChanged();
void sourceItemChanged();
void rescaleSizeChanged();
void dominantColourChanged();
void luminanceChanged();
private:
using AnalyseResult = QPair<QColor, qreal>;
using AnalyseResult = QPair<QColor, qreal>;
QFutureWatcher<AnalyseResult>* const m_futureWatcher;
QFutureWatcher<AnalyseResult>* const m_futureWatcher;
QString m_source;
QQuickItem* m_sourceItem;
int m_rescaleSize;
QString m_source;
QQuickItem* m_sourceItem;
int m_rescaleSize;
QColor m_dominantColour;
qreal m_luminance;
QColor m_dominantColour;
qreal m_luminance;
void update();
static void analyse(QPromise<AnalyseResult>& promise, const QImage& image, int rescaleSize);
void update();
static void analyse(QPromise<AnalyseResult>& promise, const QImage& image, int rescaleSize);
};
} // namespace ZShell
+1 -1
View File
@@ -13,7 +13,7 @@ QML_SINGLETON
public:
explicit Qalculator(QObject* parent = nullptr);
Q_INVOKABLE QString eval(const QString& expr, bool printExpr = true) const;
Q_INVOKABLE [[nodiscard]] QString eval(const QString& expr, bool printExpr = true) const;
};
} // namespace ZShell
+3 -3
View File
@@ -29,9 +29,9 @@ Q_INVOKABLE void cacheImage(const QUrl& source, const QString& cacheDir, QJSValu
Q_INVOKABLE void cacheImage(const QUrl& source, const QString& cacheDir, QJSValue onSaved, QJSValue onFailed);
// clang-format on
Q_INVOKABLE bool copyFile(const QUrl& source, const QUrl& target, bool overwrite = true) const;
Q_INVOKABLE bool deleteFile(const QUrl& path) const;
Q_INVOKABLE QString toLocalFile(const QUrl& url) const;
Q_INVOKABLE [[nodiscard]] bool copyFile(const QUrl& source, const QUrl& target, bool overwrite = true) const;
Q_INVOKABLE [[nodiscard]] bool deleteFile(const QUrl& path) const;
Q_INVOKABLE [[nodiscard]] QString toLocalFile(const QUrl& url) const;
private:
bool loadSourceImage(const QUrl& source, QImage& image) const;
+145
View File
@@ -0,0 +1,145 @@
#include "zutils.hpp"
#include <QtConcurrent/qtconcurrentrun.h>
#include <QtQuick/qquickitemgrabresult.h>
#include <QtQuick/qquickwindow.h>
#include <qdir.h>
#include <qfileinfo.h>
#include <qfuturewatcher.h>
#include <qloggingcategory.h>
#include <qqmlengine.h>
Q_LOGGING_CATEGORY(lcZUtils, "ZShell.cutils", QtInfoMsg)
namespace ZShell {
void ZUtils::saveItem(QQuickItem* target, const QUrl& path) {
this->saveItem(target, path, QRect(), QJSValue(), QJSValue());
}
void ZUtils::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect) {
this->saveItem(target, path, rect, QJSValue(), QJSValue());
}
void ZUtils::saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved) {
this->saveItem(target, path, QRect(), onSaved, QJSValue());
}
void ZUtils::saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved, QJSValue onFailed) {
this->saveItem(target, path, QRect(), onSaved, onFailed);
}
void ZUtils::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved) {
this->saveItem(target, path, rect, onSaved, QJSValue());
}
void ZUtils::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved, QJSValue onFailed) {
if (!target) {
qCWarning(lcZUtils) << "saveItem: a target is required";
return;
}
if (!path.isLocalFile()) {
qCWarning(lcZUtils) << "saveItem:" << path << "is not a local file";
return;
}
if (!target->window()) {
qCWarning(lcZUtils) << "saveItem: unable to save target" << target << "without a window";
return;
}
auto scaledRect = rect;
const qreal scale = target->window()->devicePixelRatio();
if (rect.isValid() && !qFuzzyCompare(scale + 1.0, 2.0)) {
scaledRect =
QRectF(rect.left() * scale, rect.top() * scale, rect.width() * scale, rect.height() * scale).toRect();
}
const QSharedPointer<const QQuickItemGrabResult> grabResult = target->grabToImage();
QObject::connect(grabResult.data(), &QQuickItemGrabResult::ready, this,
[grabResult, scaledRect, path, onSaved, onFailed, this]() {
const auto future = QtConcurrent::run([=]() {
QImage image = grabResult->image();
if (scaledRect.isValid()) {
image = image.copy(scaledRect);
}
const QString file = path.toLocalFile();
const QString parent = QFileInfo(file).absolutePath();
return QDir().mkpath(parent) && image.save(file);
});
auto* watcher = new QFutureWatcher<bool>(this);
auto* engine = qmlEngine(this);
QObject::connect(watcher, &QFutureWatcher<bool>::finished, this, [=]() {
if (watcher->result()) {
if (onSaved.isCallable()) {
QJSValueList args = { QJSValue(path.toLocalFile()) };
if (engine) {
args << engine->toScriptValue(QVariant::fromValue(path));
}
onSaved.call(args);
}
} else {
qCWarning(lcZUtils) << "saveItem: failed to save" << path;
if (onFailed.isCallable()) {
if (engine) {
onFailed.call({ engine->toScriptValue(QVariant::fromValue(path)) });
} else {
onFailed.call();
}
}
}
watcher->deleteLater();
});
watcher->setFuture(future);
});
}
bool ZUtils::copyFile(const QUrl& source, const QUrl& target, bool overwrite) {
if (!source.isLocalFile()) {
qCWarning(lcZUtils) << "copyFile: source" << source << "is not a local file";
return false;
}
if (!target.isLocalFile()) {
qCWarning(lcZUtils) << "copyFile: target" << target << "is not a local file";
return false;
}
if (overwrite && QFile::exists(target.toLocalFile())) {
if (!QFile::remove(target.toLocalFile())) {
qCWarning(lcZUtils) << "copyFile: overwrite was specified but failed to remove" << target.toLocalFile();
return false;
}
}
return QFile::copy(source.toLocalFile(), target.toLocalFile());
}
bool ZUtils::deleteFile(const QUrl& path) {
if (!path.isLocalFile()) {
qCWarning(lcZUtils) << "deleteFile: path" << path << "is not a local file";
return false;
}
return QFile::remove(path.toLocalFile());
}
QString ZUtils::toLocalFile(const QUrl& url) {
if (!url.isLocalFile()) {
qCWarning(lcZUtils) << "toLocalFile: given url is not a local file" << url;
return QString();
}
return url.toLocalFile();
}
qreal ZUtils::clamp(qreal value, qreal min, qreal max) {
return qBound(min, value, max);
}
} // namespace ZShell
+31
View File
@@ -0,0 +1,31 @@
#pragma once
#include <QtQuick/qquickitem.h>
#include <qobject.h>
#include <qqmlintegration.h>
namespace ZShell {
class ZUtils : public QObject {
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
public:
// clang-format off
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved, QJSValue onFailed);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved, QJSValue onFailed);
// clang-format on
Q_INVOKABLE static bool copyFile(const QUrl& source, const QUrl& target, bool overwrite = true);
Q_INVOKABLE static bool deleteFile(const QUrl& path);
Q_INVOKABLE static QString toLocalFile(const QUrl& url);
Q_INVOKABLE static qreal clamp(qreal value, qreal min, qreal max);
};
} // namespace ZShell
+9 -4
View File
@@ -1,9 +1,11 @@
from __future__ import annotations
import os
import sys
from pathlib import Path
import typer
from typer._completion_shared import install, _get_shell_name
from typer._completion_classes import completion_init
from zshell.subcommands import shell, scheme, screenshot, wallpaper, record
app = typer.Typer(name="zshell-cli", add_completion=False)
@@ -30,23 +32,26 @@ def _completion_installed() -> bool:
def _install_completion() -> None:
if _completion_installed():
print("zshell-cli: Shell completion already installed.")
raise typer.Exit()
sys.exit(0)
shell = _get_shell_name()
if shell is None:
print("zshell-cli: Unable to detect shell type.", file=sys.stderr)
raise typer.Exit(code=1)
sys.exit(1)
try:
_, path = install(prog_name="zshell-cli")
print(f"zshell-cli: Shell completion installed ({shell}: {path})")
print("zshell-cli: Restart your shell or source the file to enable tab-completion.")
except Exception:
pass
except Exception as e:
print(f"zshell-cli: Failed to install shell completion: {e}", file=sys.stderr)
raise typer.Exit(code=1)
def main() -> None:
if "--install-autocomplete" in sys.argv:
_install_completion()
return
if "_ZSHELL_CLI_COMPLETE" in os.environ:
completion_init()
if sys.stdout.isatty() and not _completion_installed():
print("zshell-cli: Tip: run with --install-autocomplete for tab completion.", file=sys.stderr)
app()
+3 -2
View File
@@ -4,6 +4,7 @@
//@ pragma Env QSG_NO_VSYNC=1
//@ pragma Env QS_NO_RELOAD_POPUP=1
//@ pragma Env QT_SCALE_FACTOR_ROUNDING_POLICY=Round
//@ pragma DefaultEnv QT_QUICK_FLICKABLE_WHEEL_DECELERATION=10000
//@ pragma DropExpensiveFonts
import Quickshell
import Quickshell.Services.UPower
@@ -22,7 +23,7 @@ ShellRoot {
settings.watchFiles: true
Windows {
Drawers {
}
Wallpaper {
@@ -48,7 +49,7 @@ ShellRoot {
LazyLoader {
activeAsync: root.laptop
component: Battery {
component: BatteryService {
}
}
}
+2
View File
@@ -0,0 +1,2 @@
edition = "2024"
style_edition = "2024"
+4 -4
View File
@@ -10,10 +10,10 @@ pub struct Config {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EffectsConfig {
pub corner_radius: f32,
pub drop_shadow: bool,
pub rounded_corners: bool,
pub shadow_blur_radius: f32,
pub radius: f32,
pub shadow: bool,
pub rounding: bool,
pub shadow_blur: f32,
pub shadow_color: [u8; 4],
pub shadow_offset_x: f32,
pub shadow_offset_y: f32,
+9 -16
View File
@@ -5,15 +5,15 @@ use tiny_skia::{
};
pub fn apply_effects(img: RgbaImage, cfg: &EffectsConfig) -> RgbaImage {
let img = if cfg.rounded_corners {
apply_rounded_corners(img, cfg.corner_radius)
let img = if cfg.rounding {
apply_rounding(img, cfg.radius)
} else {
img
};
if cfg.drop_shadow {
apply_drop_shadow(
if cfg.shadow {
apply_shadow(
img,
cfg.shadow_blur_radius,
cfg.shadow_blur,
cfg.shadow_offset_x,
cfg.shadow_offset_y,
cfg.shadow_color,
@@ -23,7 +23,7 @@ pub fn apply_effects(img: RgbaImage, cfg: &EffectsConfig) -> RgbaImage {
}
}
pub fn apply_rounded_corners(img: RgbaImage, radius: f32) -> RgbaImage {
pub fn apply_rounding(img: RgbaImage, radius: f32) -> RgbaImage {
let (w, h) = img.dimensions();
let mut mask = Pixmap::new(w, h).expect("mask pixmap");
let path = rounded_rect_path(0.0, 0.0, w as f32, h as f32, radius);
@@ -47,20 +47,16 @@ pub fn apply_rounded_corners(img: RgbaImage, radius: f32) -> RgbaImage {
pixmap_to_rgba_image(pixmap)
}
pub fn apply_drop_shadow(
pub fn apply_shadow(
img: RgbaImage,
blur_radius: f32,
blur: f32,
offset_x: f32,
offset_y: f32,
// blur_passes: u32,
shadow_color: [u8; 4],
) -> RgbaImage {
let (iw, ih) = img.dimensions();
let br = blur_radius.ceil() as u32;
let br = blur.ceil() as u32;
let bp = 1;
// Original idea
// let spread = br * bp;
// Claude is hallucinating but let's try it **Worked btw**
let spread = (br as f32 * (bp as f32).sqrt() * 2.0).ceil() as u32;
let extra_left = spread + (-offset_x).max(0.0).ceil() as u32;
@@ -91,11 +87,8 @@ pub fn apply_drop_shadow(
tint_pixmap_as_shadow(&mut shadow_pixmap, shadow_color);
// Shadow
let shadow_img = pixmap_to_rgba_image(shadow_pixmap);
// Shadow blur
let blurred = box_blur_rgba(&shadow_img, br, bp);
// Shadow pos
let blurred_pixmap = rgba_image_to_pixmap(&blurred);
let mut canvas = Pixmap::new(canvas_w, canvas_h).expect("canvas pixmap");
+38 -43
View File
@@ -1,19 +1,18 @@
mod config;
mod effects;
use anyhow::{bail, Context, Result};
use anyhow::{Context, Result, bail};
use std::io::Write as _;
use std::process::{Command, Stdio};
#[derive(Default)]
struct CliOverrides {
rounded_corners: Option<bool>,
corner_radius: Option<f32>,
drop_shadow: Option<bool>,
shadow_blur_radius: Option<f32>,
rounding: Option<bool>,
radius: Option<f32>,
shadow: Option<bool>,
shadow_blur: Option<f32>,
shadow_offset_x: Option<f32>,
shadow_offset_y: Option<f32>,
// Accepted as four comma-separated u8 values, e.g. `255,0,0,200`
shadow_color: Option<[u8; 4]>,
}
@@ -57,13 +56,14 @@ fn extract_image_path() -> Option<String> {
}
fn main() {
let image_path = extract_image_path();
if let Some(path) = image_path {
if let Err(e) = run() {
// Fundamental issue when supplying args it won't give output unless --image is used.
// Will have to be fixed in a later patch upcoming week
if let Some(path) = extract_image_path()
&& let Err(e) = run()
{
eprintln!("Error: {}", e);
push_image(&path).ok();
}
}
}
fn run() -> Result<()> {
@@ -79,26 +79,23 @@ fn run() -> Result<()> {
"--image" => {
image_path = Some(next_arg(&args, &mut i, "--image")?);
}
"--corner" => {
let val = next_arg(&args, &mut i, "--corners")?;
overrides.rounded_corners = Some(parse_bool(&val)?);
"--rounding" => {
let val = next_arg(&args, &mut i, "--rounding")?;
overrides.rounding = Some(parse_bool(&val)?);
}
"--corner-radius" => {
let val = next_arg(&args, &mut i, "--corner-radius")?;
overrides.corner_radius = Some(
val.parse::<f32>()
.context("--corner-radius must be a number")?,
);
"--radius" => {
let val = next_arg(&args, &mut i, "--radius")?;
overrides.radius = Some(val.parse::<f32>().context("--radius must be a number")?);
}
"--shadow" => {
let val = next_arg(&args, &mut i, "--shadow")?;
overrides.drop_shadow = Some(parse_bool(&val)?);
overrides.shadow = Some(parse_bool(&val)?);
}
"--shadow-blur" => {
let val = next_arg(&args, &mut i, "--shadow-blur")?;
overrides.shadow_blur_radius = Some(
overrides.shadow_blur = Some(
val.parse::<f32>()
.context("--shadow-blur-radius must be a number")?,
.context("--shadow-blur must be a number")?,
);
}
"--shadow-offset-x" => {
@@ -123,7 +120,11 @@ fn run() -> Result<()> {
let val = next_arg(&args, &mut i, "--scale")?;
scale = Some(val.parse::<f32>().context("--scale must be a number")?);
}
unknown => bail!("Unknown argument: {}", unknown),
unknown => {
let unknown_args = unknown.to_string();
println!("Warning: Unknown argument '{}'", unknown);
next_arg(&args, &mut i, &unknown_args)?;
}
}
i += 1;
@@ -131,22 +132,18 @@ fn run() -> Result<()> {
let image_path = image_path.context("Missing --image <path>")?;
// Check if any arguments were provided
let cli_args_provided = overrides.rounded_corners.is_some()
|| overrides.corner_radius.is_some()
|| overrides.drop_shadow.is_some()
|| overrides.shadow_blur_radius.is_some()
let cli_args_provided = overrides.rounding.is_some()
|| overrides.radius.is_some()
|| overrides.shadow.is_some()
|| overrides.shadow_blur.is_some()
|| overrides.shadow_offset_x.is_some()
|| overrides.shadow_offset_y.is_some()
|| overrides.shadow_color.is_some();
let mut effects = if cli_args_provided {
// If all args provided
let rounded_corners = overrides.rounded_corners.context("Missing --corner")?;
let corner_radius = overrides.corner_radius.context("Missing --corner-radius")?;
let drop_shadow = overrides.drop_shadow.context("Missing --shadow")?;
let shadow_blur_radius = overrides
.shadow_blur_radius
.context("Missing --shadow-blur")?;
let rounding = overrides.rounding.context("Missing --rounding")?;
let radius = overrides.radius.context("Missing --radius")?;
let shadow = overrides.shadow.context("Missing --shadow")?;
let shadow_blur = overrides.shadow_blur.context("Missing --shadow-blur")?;
let shadow_offset_x = overrides
.shadow_offset_x
.context("Missing --shadow-offset-x")?;
@@ -155,24 +152,22 @@ fn run() -> Result<()> {
.context("Missing --shadow-offset-y")?;
let shadow_color = overrides.shadow_color.context("Missing --shadow-color")?;
config::EffectsConfig {
rounded_corners,
corner_radius,
drop_shadow,
shadow_blur_radius,
rounding,
radius,
shadow,
shadow_blur,
shadow_offset_x,
shadow_offset_y,
shadow_color,
}
} else {
// If not all args were provided use config file
let config = config::Config::load()?;
config.screenshot
};
// if scale is set do
if let Some(scale) = scale.filter(|&s| s != 1.0) {
effects.corner_radius *= scale;
effects.shadow_blur_radius *= scale;
effects.radius *= scale;
effects.shadow_blur *= scale;
effects.shadow_offset_x *= scale;
effects.shadow_offset_y *= scale;
}