81 Commits

Author SHA1 Message Date
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 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 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 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 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
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
zach 9a026b2484 respect new arguments
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 21s
Python / lint-format (pull_request) Successful in 36s
Python / test (pull_request) Successful in 1m11s
Lint & Format (Rust) / lint-format (pull_request) Failing after 1m16s
2026-05-28 20:21:22 +02:00
Inorishio 0305f5a2be --corners > --corner 2026-05-28 19:11:47 +02:00
Inorishio 6e43bac52b 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 12s
Python / lint-format (pull_request) Successful in 20s
Python / test (pull_request) Successful in 50s
Lint & Format (Rust) / lint-format (pull_request) Failing after 1m2s
2026-05-28 18:55:43 +02:00
Inorishio d098375da6 SHOULD have a handler for config/CLI parses 2026-05-28 18:55:03 +02:00
zach 239de807d4 Merge branch 'main' into zshell-img-tools
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 1m3s
2026-05-28 17:06:19 +02:00
zach 54e8a265d0 Merge branch 'main' into zshell-img-tools
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 13s
Python / lint-format (pull_request) Successful in 21s
Python / test (pull_request) Successful in 43s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-05-26 23:03:28 +02:00
AramJonghu 93ffe6ca2d Merge branch 'main' into zshell-img-tools
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 13s
Python / lint-format (pull_request) Successful in 24s
Python / test (pull_request) Successful in 49s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m10s
2026-05-26 18:54:18 +02:00
zach 74ce5bb868 Merge branch 'main' into zshell-img-tools
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 25s
Python / lint-format (pull_request) Successful in 31s
Python / test (pull_request) Failing after 54s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m13s
2026-05-26 18:42:18 +02:00
zach a3f38e6414 fetch necessary hyprland options for screenshot tool
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 20s
Python / test (pull_request) Successful in 45s
Python / lint-format (pull_request) Successful in 51s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m50s
2026-05-26 16:46:45 +02:00
Inorishio 6aedf6f8b7 scale fix 2026-05-26 11:41:31 +02:00
zach 4ea74ed516 pass monitor scale to screenshot tool 2026-05-26 01:25:09 +02:00
zach f00af9d70f actually fixed config fetching in hyprland lua 2026-05-26 01:12:36 +02:00
Inorishio de11767d3b zshell-img-tools crate reduction to 53, process release fix, blur-passes, scale impl, settings passes setting, scale only avail in config 2026-05-25 23:15:00 +02:00
71 changed files with 1684 additions and 1741 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
...
+1
View File
@@ -25,6 +25,7 @@ add_compile_options(
if("plugin" IN_LIST ENABLE_MODULES) if("plugin" IN_LIST ENABLE_MODULES)
add_subdirectory(Plugins) add_subdirectory(Plugins)
endif() endif()
if("shell" IN_LIST ENABLE_MODULES) if("shell" IN_LIST ENABLE_MODULES)
+11 -7
View File
@@ -10,31 +10,35 @@ Slider {
background: Item { background: Item {
CustomRect { CustomRect {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.bottomMargin: root.implicitHeight / 6
anchors.left: parent.left anchors.left: parent.left
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: root.implicitHeight / 6
bottomRightRadius: root.implicitHeight / 6 bottomRightRadius: root.implicitHeight / 6
color: root.color color: root.color
implicitWidth: root.handle.x - root.implicitHeight / 2 implicitWidth: root.handle.x - root.implicitHeight / 6
radius: Appearance.rounding.full radius: root.implicitHeight / 6
topRightRadius: root.implicitHeight / 6 topRightRadius: root.implicitHeight / 6
} }
CustomRect { CustomRect {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.bottomMargin: root.implicitHeight / 6
anchors.right: parent.right anchors.right: parent.right
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: root.implicitHeight / 6
bottomLeftRadius: root.implicitHeight / 6 bottomLeftRadius: root.implicitHeight / 6
color: DynamicColors.tPalette.m3surfaceContainer color: DynamicColors.tPalette.m3surfaceContainerHighest
implicitWidth: parent.width - root.handle.x - root.handle.implicitWidth - root.implicitHeight / 2 implicitWidth: parent.width - root.handle.x - root.handle.implicitWidth - root.implicitHeight / 6
radius: Appearance.rounding.full radius: root.implicitHeight / 6
topLeftRadius: root.implicitHeight / 6 topLeftRadius: root.implicitHeight / 6
} }
} }
handle: CustomRect { handle: CustomRect {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: root.color color: root.color
implicitHeight: 15 implicitHeight: root.implicitHeight
implicitWidth: 5 implicitWidth: root.implicitHeight / 4.5
radius: Appearance.rounding.full radius: Appearance.rounding.full
x: root.visualPosition * root.availableWidth - implicitWidth / 2 x: root.visualPosition * root.availableWidth - implicitWidth / 2
+6 -5
View File
@@ -28,6 +28,7 @@ RowLayout {
CustomTextField { CustomTextField {
id: textField id: textField
color: root.enabled ? DynamicColors.palette.m3onSurface : Qt.alpha(DynamicColors.palette.m3onSurface, 0.5)
implicitHeight: upButton.implicitHeight implicitHeight: upButton.implicitHeight
inputMethodHints: Qt.ImhFormattedNumbersOnly inputMethodHints: Qt.ImhFormattedNumbersOnly
leftPadding: Appearance.padding.normal leftPadding: Appearance.padding.normal
@@ -36,7 +37,7 @@ RowLayout {
text: root.isEditing ? text : root.displayText text: root.isEditing ? text : root.displayText
background: CustomRect { background: CustomRect {
color: DynamicColors.tPalette.m3surfaceContainerHigh color: root.enabled ? DynamicColors.tPalette.m3surfaceContainerHigh : DynamicColors.tPalette.m3surfaceContainerLow
implicitWidth: 100 implicitWidth: 100
radius: Appearance.rounding.full radius: Appearance.rounding.full
} }
@@ -85,7 +86,7 @@ RowLayout {
CustomRect { CustomRect {
id: upButton 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 implicitHeight: upIcon.implicitHeight + Appearance.padding.small * 2
implicitWidth: implicitHeight implicitWidth: implicitHeight
radius: Appearance.rounding.full radius: Appearance.rounding.full
@@ -113,13 +114,13 @@ RowLayout {
id: upIcon id: upIcon
anchors.centerIn: parent anchors.centerIn: parent
color: DynamicColors.palette.m3onPrimary color: root.enabled ? DynamicColors.palette.m3onPrimary : Qt.alpha(DynamicColors.palette.m3onSurface, 0.5)
text: "keyboard_arrow_up" text: "keyboard_arrow_up"
} }
} }
CustomRect { CustomRect {
color: DynamicColors.palette.m3primary color: root.enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 1)
implicitHeight: downIcon.implicitHeight + Appearance.padding.small * 2 implicitHeight: downIcon.implicitHeight + Appearance.padding.small * 2
implicitWidth: implicitHeight implicitWidth: implicitHeight
radius: Appearance.rounding.full radius: Appearance.rounding.full
@@ -147,7 +148,7 @@ RowLayout {
id: downIcon id: downIcon
anchors.centerIn: parent anchors.centerIn: parent
color: DynamicColors.palette.m3onPrimary color: root.enabled ? DynamicColors.palette.m3onPrimary : Qt.alpha(DynamicColors.palette.m3onSurface, 0.5)
text: "keyboard_arrow_down" text: "keyboard_arrow_down"
} }
} }
+7 -11
View File
@@ -35,14 +35,10 @@ Row {
} }
function openDropdown(): void { function openDropdown(): void {
if (root.disabled)
return;
SettingsDropdowns.open(menu, root); SettingsDropdowns.open(menu, root);
} }
function toggleDropdown(): void { function toggleDropdown(): void {
if (root.disabled)
return;
SettingsDropdowns.toggle(menu, root); SettingsDropdowns.toggle(menu, root);
} }
@@ -55,7 +51,7 @@ Row {
CustomRect { CustomRect {
bottomRightRadius: Appearance.rounding.small / 2 bottomRightRadius: Appearance.rounding.small / 2
color: root.disabled ? root.disabledColor : root.color color: !root.enabled ? root.disabledColor : root.color
implicitHeight: expandBtn.implicitHeight implicitHeight: expandBtn.implicitHeight
implicitWidth: textRow.implicitWidth + root.horizontalPadding * 2 implicitWidth: textRow.implicitWidth + root.horizontalPadding * 2
radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale)
@@ -69,7 +65,7 @@ Row {
} }
color: root.textColor color: root.textColor
disabled: root.disabled disabled: !root.enabled
rect.bottomRightRadius: parent.bottomRightRadius rect.bottomRightRadius: parent.bottomRightRadius
rect.topRightRadius: parent.topRightRadius rect.topRightRadius: parent.topRightRadius
} }
@@ -86,7 +82,7 @@ Row {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
animate: true animate: true
color: root.disabled ? root.disabledTextColor : root.textColor color: !root.enabled ? root.disabledTextColor : root.textColor
fill: 1 fill: 1
text: root.active?.activeIcon ?? root.fallbackIcon text: root.active?.activeIcon ?? root.fallbackIcon
} }
@@ -98,7 +94,7 @@ Row {
Layout.preferredWidth: implicitWidth Layout.preferredWidth: implicitWidth
animate: true animate: true
clip: true clip: true
color: root.disabled ? root.disabledTextColor : root.textColor color: !root.enabled ? root.disabledTextColor : root.textColor
text: root.active?.activeText ?? root.fallbackText text: root.active?.activeText ?? root.fallbackText
Behavior on Layout.preferredWidth { Behavior on Layout.preferredWidth {
@@ -116,7 +112,7 @@ Row {
property real rad: root.expanded ? implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) : Appearance.rounding.small / 2 property real rad: root.expanded ? implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) : Appearance.rounding.small / 2
bottomLeftRadius: rad bottomLeftRadius: rad
color: root.disabled ? root.disabledColor : root.color color: !root.enabled ? root.disabledColor : root.color
implicitHeight: expandIcon.implicitHeight + root.verticalPadding * 2 implicitHeight: expandIcon.implicitHeight + root.verticalPadding * 2
implicitWidth: implicitHeight implicitWidth: implicitHeight
radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale)
@@ -135,7 +131,7 @@ Row {
} }
color: root.textColor color: root.textColor
disabled: root.disabled disabled: !root.enabled
rect.bottomLeftRadius: parent.bottomLeftRadius rect.bottomLeftRadius: parent.bottomLeftRadius
rect.topLeftRadius: parent.topLeftRadius rect.topLeftRadius: parent.topLeftRadius
} }
@@ -145,7 +141,7 @@ Row {
anchors.centerIn: parent anchors.centerIn: parent
anchors.horizontalCenterOffset: root.expanded ? 0 : -Math.floor(root.verticalPadding / 4) anchors.horizontalCenterOffset: root.expanded ? 0 : -Math.floor(root.verticalPadding / 4)
color: root.disabled ? root.disabledTextColor : root.textColor color: !root.enabled ? root.disabledTextColor : root.textColor
rotation: root.expanded ? 180 : 0 rotation: root.expanded ? 180 : 0
text: "expand_more" text: "expand_more"
+4 -4
View File
@@ -12,7 +12,7 @@ Switch {
implicitWidth: implicitIndicatorWidth implicitWidth: implicitIndicatorWidth
indicator: CustomRect { indicator: CustomRect {
color: root.checked ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer) color: root.checked && root.enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer)
implicitHeight: 13 + 7 * 2 implicitHeight: 13 + 7 * 2
implicitWidth: implicitHeight * 1.7 implicitWidth: implicitHeight * 1.7
radius: Appearance.rounding.full radius: Appearance.rounding.full
@@ -21,7 +21,7 @@ Switch {
readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.3 : implicitHeight readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.3 : implicitHeight
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: root.checked ? DynamicColors.palette.m3onPrimary : DynamicColors.layer(DynamicColors.palette.m3outline, root.cLayer + 1) color: root.checked && root.enabled ? DynamicColors.palette.m3onPrimary : DynamicColors.layer(DynamicColors.palette.m3outline, root.cLayer + 1)
implicitHeight: parent.implicitHeight - 10 implicitHeight: parent.implicitHeight - 10
implicitWidth: nonAnimWidth implicitWidth: nonAnimWidth
radius: Appearance.rounding.full radius: Appearance.rounding.full
@@ -38,7 +38,7 @@ Switch {
CustomRect { CustomRect {
anchors.fill: parent 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 opacity: root.pressed ? 0.1 : root.hovered ? 0.08 : 0
radius: parent.radius radius: parent.radius
@@ -114,7 +114,7 @@ Switch {
fillColor: "transparent" fillColor: "transparent"
startX: icon.start1.x startX: icon.start1.x
startY: icon.start1.y 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 strokeWidth: Appearance.font.size.larger * 0.15
Behavior on strokeColor { Behavior on strokeColor {
+4 -4
View File
@@ -300,10 +300,10 @@ Singleton {
return { return {
enable_pp: screenshot.enable_pp, enable_pp: screenshot.enable_pp,
mode: screenshot.mode, mode: screenshot.mode,
corner_radius: screenshot.corner_radius, radius: screenshot.radius,
drop_shadow: screenshot.drop_shadow, shadow: screenshot.shadow,
rounded_corners: screenshot.rounded_corners, rounding: screenshot.rounding,
shadow_blur_radius: screenshot.shadow_blur_radius, shadow_blur: screenshot.shadow_blur,
shadow_color: screenshot.shadow_color, shadow_color: screenshot.shadow_color,
shadow_offset_x: screenshot.shadow_offset_x, shadow_offset_x: screenshot.shadow_offset_x,
shadow_offset_y: screenshot.shadow_offset_y shadow_offset_y: screenshot.shadow_offset_y
+4
View File
@@ -115,6 +115,10 @@ Singleton {
Config.save(); Config.save();
} }
function swapRG(c: color): color {
return Qt.rgba(c.g, c.r, c.b, c.a);
}
Component.onCompleted: debounceTimer.triggered() Component.onCompleted: debounceTimer.triggered()
Connections { Connections {
+4 -4
View File
@@ -1,12 +1,12 @@
import Quickshell.Io import Quickshell.Io
JsonObject { JsonObject {
property real corner_radius: 12.0
property bool drop_shadow: true
property bool enable_pp: true property bool enable_pp: true
property string mode: "manual" property string mode: "manual"
property bool rounded_corners: false property real radius: 12.0
property real shadow_blur_radius: 22.0 property bool rounding: false
property bool shadow: true
property real shadow_blur: 22.0
property list<int> shadow_color: [0, 0, 0, 160] property list<int> shadow_color: [0, 0, 0, 160]
property real shadow_offset_x: 5.0 property real shadow_offset_x: 5.0
property real shadow_offset_y: 5.0 property real shadow_offset_y: 5.0
@@ -4,17 +4,16 @@ import QtQuick
import ZShell import ZShell
import qs.Config import qs.Config
import qs.Components.Toast import qs.Components.Toast
import qs.Helpers
Scope { Scope {
id: root 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) readonly property list<var> popupThresholds: [...Config.general.battery.popupThresholds].sort((a, b) => b.perc - a.perc)
function nearestThresholdAbove(p: real): var { function nearestThresholdAbove(p: real): var {
const thresholds = [...root.popupThresholds]; const thresholds = [...root.popupThresholds];
for (const perc of thresholds) { for (const perc of thresholds) {
console.log(perc.message);
if (p < perc.perc) if (p < perc.perc)
return perc; return perc;
} }
@@ -24,13 +23,13 @@ Scope {
Connections { Connections {
function onOnBatteryChanged(): void { function onOnBatteryChanged(): void {
if (!UPower.displayDevice.ready) if (!Battery.ready)
return; return;
if (UPower.onBattery) { if (Battery.onBattery) {
if (Config.utilities.toasts.chargingChanged) if (Config.utilities.toasts.chargingChanged)
Toaster.toast(qsTr("Charger unplugged"), qsTr("Battery is discharging"), "power_off"); Toaster.toast(qsTr("Charger unplugged"), qsTr("Battery is discharging"), "power_off");
const p = root.currentPerc * 100; const p = Battery.currentPerc * 100;
const perc = root.nearestThresholdAbove(p); const perc = root.nearestThresholdAbove(p);
if (perc) 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); Toaster.toast(perc.title ?? qsTr("Battery warning"), perc.message ?? qsTr("Battery is low"), perc.icon ?? "battery_android_alert", perc.critical ? Toast.Error : Toast.Warning);
@@ -40,15 +39,15 @@ Scope {
} }
} }
target: UPower target: Battery
} }
Connections { Connections {
function onPercentageChanged(): void { function onCurrentPercChanged(): void {
if (!UPower.onBattery) if (!Battery.onBattery)
return; return;
const p = root.currentPerc * 100; const p = Battery.currentPerc * 100;
for (const perc of root.popupThresholds) { for (const perc of root.popupThresholds) {
if (p == perc.perc) { 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); 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);
@@ -57,15 +56,15 @@ Scope {
} }
function onReadyChanged(): void { function onReadyChanged(): void {
if (!UPower.displayDevice.ready) if (!Battery.ready)
return; return;
const p = root.currentPerc * 100; const p = Battery.currentPerc * 100;
const perc = root.nearestThresholdAbove(p); const perc = root.nearestThresholdAbove(p);
if (perc) 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); 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: UPower.displayDevice target: Battery
} }
} }
+88 -43
View File
@@ -2,20 +2,21 @@ import Quickshell
import QtQuick import QtQuick
import qs.Components import qs.Components
import qs.Config import qs.Config
import qs.Helpers
import qs.Modules as BarPopouts import qs.Modules as BarPopouts
CustomMouseArea { Item {
id: root id: root
required property Item bar required property Item bar
property bool dashboardShortcutActive property bool dashboardShortcutActive
property point dragStart
required property Drawing drawing required property Drawing drawing
required property DrawingInput input required property DrawingInput input
property bool osdShortcutActive property bool osdShortcutActive
required property Panels panels required property Panels panels
required property BarPopouts.Wrapper popouts required property BarPopouts.Wrapper popouts
required property ShellScreen screen required property ShellScreen screen
property bool singleGestureTriggered: false
property bool utilitiesShortcutActive property bool utilitiesShortcutActive
required property PersistentProperties visibilities required property PersistentProperties visibilities
@@ -52,78 +53,123 @@ CustomMouseArea {
} }
anchors.fill: parent anchors.fill: parent
cursorShape: (pressed && dragStart.y < bar.implicitHeight) ? Qt.ClosedHandCursor : undefined
hoverEnabled: true
propagateComposedEvents: false
onContainsMouseChanged: { DragHandler {
if (!containsMouse) { id: singleHandler
if (!osdShortcutActive) {
visibilities.osd = false; cursorShape: (active && centroid.pressPosition.y < root.bar.implicitHeight) ? Qt.ClosedHandCursor : undefined
dragThreshold: 0
grabPermissions: PointerHandler.CanTakeOverFromHandlersOfDifferentType | 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; root.panels.osd.hovered = false;
} }
if (!popouts.currentName.startsWith("traymenu")) { if (!root.popouts.currentName.startsWith("traymenu")) {
popouts.hasCurrent = false; root.popouts.hasCurrent = false;
} }
if (Config.barConfig.autoHide) if (Config.barConfig.autoHide)
bar.isHovered = false; root.bar.isHovered = false;
} }
} }
onPositionChanged: event => { onPointChanged: {
const x = event.x; const x = point.position.x;
const y = event.y; const y = point.position.y;
const dragX = x - dragStart.x;
const dragY = y - dragStart.y;
if (root.visibilities.isDrawing && !root.inLeftPanel(root.panels.drawing, x, y)) { if (root.visibilities.isDrawing && !root.inLeftPanel(root.panels.drawing, x, y)) {
root.input.z = 2; root.input.z = 2;
root.panels.drawing.expanded = false; root.panels.drawing.expanded = false;
} }
if (!visibilities.bar && Config.barConfig.autoHide && y < bar.implicitHeight) if (!root.visibilities.bar && Config.barConfig.autoHide && y < root.bar.implicitHeight)
bar.isHovered = true; root.bar.isHovered = true;
if (pressed && dragStart.y < bar.implicitHeight) { if (root.panels.sidebar.width === 0) {
if (dragY > 20) const showOsd = root.inRightPanel(root.panels.osdWrapper, x, y);
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 (showOsd) { if (showOsd) {
osdShortcutActive = false; root.osdShortcutActive = false;
root.panels.osd.hovered = true; root.panels.osd.hovered = true;
} }
} else { } else {
const outOfSidebar = x < width - panels.sidebar.width; const outOfSidebar = x < root.width - root.panels.sidebar.width;
const showOsd = outOfSidebar && inRightPanel(panels.osdWrapper, x, y); const showOsd = outOfSidebar && root.inRightPanel(root.panels.osdWrapper, x, y);
if (!osdShortcutActive) { if (!root.osdShortcutActive) {
visibilities.osd = showOsd; root.visibilities.osd = showOsd;
root.panels.osd.hovered = showOsd; root.panels.osd.hovered = showOsd;
} else if (showOsd) { } else if (showOsd) {
osdShortcutActive = false; root.osdShortcutActive = false;
root.panels.osd.hovered = true; root.panels.osd.hovered = true;
} }
} }
if (Config.dock.enable && !Config.dock.hoverToReveal && !visibilities.dock && !visibilities.launcher && inBottomPanel(panels.dock, x, y)) if (Config.dock.enable && Config.dock.hoverToReveal && !root.visibilities.dock && !root.visibilities.launcher && root.inBottomPanel(root.panels.dock, x, y))
visibilities.dock = true; root.visibilities.dock = true;
if (y < root.bar.implicitHeight) { if (y < root.bar.implicitHeight)
root.bar.checkPopout(x); root.bar.checkPopout(x);
} }
} }
onPressed: event => dragStart = Qt.point(event.x, event.y)
Connections { Connections {
function onDashboardChanged() { function onDashboardChanged() {
@@ -164,7 +210,6 @@ CustomMouseArea {
if (root.visibilities.launcher) { if (root.visibilities.launcher) {
if (root.panels.dashboardWrapper.x < root.panels.launcher.x + root.panels.launcher.width) { if (root.panels.dashboardWrapper.x < root.panels.launcher.x + root.panels.launcher.width) {
console.log("true");
root.visibilities.dashboard = false; root.visibilities.dashboard = false;
} }
+1
View File
@@ -341,6 +341,7 @@ Variants {
anchors.fill: parent anchors.fill: parent
bar: bar bar: bar
drawing: drawingLoader.item drawing: drawingLoader.item
enabled: true
input: inputLoader.item input: inputLoader.item
panels: panels panels: panels
popouts: panels.popouts popouts: panels.popouts
+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
}
+1 -1
View File
@@ -36,7 +36,7 @@ Singleton {
PersistentProperties { PersistentProperties {
id: props id: props
property bool enabled: Hypr.options["animations:enabled"] === 0 property bool enabled: Hypr.options.animations.enabled === 0
reloadableId: "gamemode" reloadableId: "gamemode"
} }
-1
View File
@@ -158,6 +158,5 @@ Singleton {
HyprExtras { HyprExtras {
id: extras id: extras
} }
} }
+11 -59
View File
@@ -2,12 +2,13 @@ pragma Singleton
import Quickshell import Quickshell
import QtQuick import QtQuick
import ZShell.Services
import qs.Config import qs.Config
Singleton { Singleton {
id: root id: root
property bool enabled readonly property bool enabled: service.enabled
readonly property int end: Config.general.color.scheduleHyprsunsetEnd readonly property int end: Config.general.color.scheduleHyprsunsetEnd
property bool manualToggle: false property bool manualToggle: false
readonly property int start: Config.general.color.scheduleHyprsunsetStart readonly property int start: Config.general.color.scheduleHyprsunsetStart
@@ -17,69 +18,20 @@ Singleton {
if (!Config.general.color.scheduleHyprsunset) if (!Config.general.color.scheduleHyprsunset)
return; return;
var now = new Date(); service.apply();
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;
} }
function toggleNightLight(): void { function toggleNightLight(): void {
if (enabled) service.manualToggle = true;
stopNightLight(); service.toggle();
else
startNightLight(temp);
} }
onManualToggleChanged: { HyprsunsetManager {
if (root.manualToggle) id: service
manualTimer.start();
}
Timer { activeAuto: Config.general.color.scheduleHyprsunset
id: manualTimer endTime: root.end
startTime: root.start
interval: 60000 * 60 temp: root.temp
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();
}
}
} }
} }
+17 -3
View File
@@ -26,16 +26,23 @@ MouseArea {
return (bc.pinned - ac.pinned) || ((bc.fullscreen !== 0) - (ac.fullscreen !== 0)) || (bc.floating - ac.floating); 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 ex: screen.width
property real ey: screen.height property real ey: screen.height
required property LazyLoader loader required property LazyLoader loader
property bool onClient property bool onClient
property real realBorderWidth: onClient ? (Hypr.options["general:border_size"] ?? 1) : 2 property real realBorderWidth: onClient ? (Hypr.options.general.border_size ?? 1) : 2
property real realRounding: onClient ? (Hypr.options["decoration:rounding"] ?? 0) : 0 property real realRounding: onClient ? (Hypr.options.decoration.rounding ?? 0) : 0
property real rsx: Math.min(sx, ex) property real rsx: Math.min(sx, ex)
property real rsy: Math.min(sy, ey) property real rsy: Math.min(sy, ey)
readonly property real scaleRatio: Hypr.monitorFor(screen).scale
required property ShellScreen screen required property ShellScreen screen
property real sh: Math.abs(sy - ey) 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
property real ssx property real ssx
property real ssy property real ssy
property real sw: Math.abs(sx - ex) property real sw: Math.abs(sx - ex)
@@ -66,7 +73,14 @@ MouseArea {
function save(): void { function save(): void {
const tmpfile = Qt.resolvedUrl(`/tmp/zshell-picker-${Quickshell.processId}-${Date.now()}.png`); const tmpfile = Qt.resolvedUrl(`/tmp/zshell-picker-${Quickshell.processId}-${Date.now()}.png`);
const cmd = Config.screenshot.enable_pp ? ["zshell-img-tools", "--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])); 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(); 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 anchors.centerIn: parent
color: root.visibilities.dashboard ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface 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 text: Time.dateStr
Behavior on color { Behavior on color {
+5
View File
@@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound
import Quickshell import Quickshell
import Quickshell.Wayland import Quickshell.Wayland
import ZShell.Internal
import qs.Config import qs.Config
import qs.Helpers import qs.Helpers
@@ -29,6 +30,10 @@ Scope {
Quickshell.execDetached(action); Quickshell.execDetached(action);
} }
LidWatcher {
onAboutToSleep: root.lock.lock.locked = true
}
Variants { Variants {
model: Config.general.idle.timeouts model: Config.general.idle.timeouts
+2 -2
View File
@@ -69,7 +69,7 @@ SettingsPage {
CustomSplitButtonRow { CustomSplitButtonRow {
active: Config.general.color.mode === "light" ? menuItems[0] : menuItems[1] 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") label: qsTr("Scheme mode")
menuItems: [ menuItems: [
@@ -103,7 +103,7 @@ SettingsPage {
id: schemeType id: schemeType
active: root.schemeTypeItem(menuItems, Config.colors.schemeType) active: root.schemeTypeItem(menuItems, Config.colors.schemeType)
buttonAlias.disabled: !Config.general.color.schemeGeneration enabled: Config.general.color.schemeGeneration
label: qsTr("Scheme type") label: qsTr("Scheme type")
z: 2 z: 2
@@ -1,5 +1,6 @@
import qs.Modules.Settings.Categories.Lockscreen import qs.Modules.Settings.Categories.Lockscreen
import qs.Modules.Settings.Controls import qs.Modules.Settings.Controls
import qs.Helpers
import qs.Config import qs.Config
SettingsPage { SettingsPage {
+56 -36
View File
@@ -20,7 +20,8 @@ SettingsPage {
} }
CustomSplitButtonRow { CustomSplitButtonRow {
// active: true active: Config.screenshot.mode === "manual" ? menuItems[0] : menuItems[1]
enabled: Config.screenshot.enable_pp
label: qsTr("Effects mode") label: qsTr("Effects mode")
menuItems: [ menuItems: [
@@ -43,88 +44,107 @@ SettingsPage {
} }
Separator { Separator {
visible: Config.screenshot.mode === "manual" shouldBeActive: Config.screenshot.mode === "manual"
}
SettingSwitch {
enabled: Config.screenshot.enable_pp
name: "Enable rounded corners"
object: Config.screenshot
setting: "rounding"
shouldBeActive: Config.screenshot.mode === "manual"
}
Separator {
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.rounding
} }
SettingSpinBox { SettingSpinBox {
enabled: Config.screenshot.enable_pp
min: 0 min: 0
name: "Corner radius" name: "Corner radius"
object: Config.screenshot object: Config.screenshot
setting: "corner_radius" setting: "radius"
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.rounding
step: 1 step: 1
visible: Config.screenshot.mode === "manual"
} }
Separator { Separator {
visible: Config.screenshot.mode === "manual" shouldBeActive: Config.screenshot.mode === "manual"
} }
SettingSwitch { SettingSwitch {
name: "Enable drop shadow" enabled: Config.screenshot.enable_pp
name: "Enable shadow"
object: Config.screenshot object: Config.screenshot
setting: "drop_shadow" setting: "shadow"
visible: Config.screenshot.mode === "manual" shouldBeActive: Config.screenshot.mode === "manual"
} }
Separator { Separator {
visible: Config.screenshot.mode === "manual" shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow
}
SettingSwitch {
name: "Enable rounded corners"
object: Config.screenshot
setting: "rounded_corners"
visible: Config.screenshot.mode === "manual"
}
Separator {
visible: Config.screenshot.mode === "manual"
} }
SettingSpinBox { SettingSpinBox {
enabled: Config.screenshot.enable_pp
min: 0 min: 0
name: "Shadow blur radius" name: "Shadow blur amount"
object: Config.screenshot object: Config.screenshot
setting: "shadow_blur_radius" setting: "shadow_blur"
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow
step: 1 step: 1
visible: Config.screenshot.mode === "manual"
} }
Separator { Separator {
visible: Config.screenshot.mode === "manual" shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow
} }
SettingSwitch { // SettingSwitch {
name: "Shadow color broken atm" // name: "Shadow color broken atm"
object: Config.Screenshot // object: Config.Screenshot
setting: "shadow_color" // setting: "shadow_color"
visible: Config.screenshot.mode === "manual" // shouldBeActive: Config.screenshot.mode === "manual"
} // }
//
// Separator {
// shouldBeActive: Config.screenshot.mode === "manual"
// }
Separator { // SettingSpinBox {
visible: Config.screenshot.mode === "manual" // min: 1
} // name: "Shadow passes"
// object: Config.screenshot
// setting: "shadow_blur_passes"
// shouldBeActive: Config.screenshot.mode === "manual"
// step: 1
// }
//
// Separator {
// shouldBeActive: Config.screenshot.mode === "manual"
// }
SettingSpinBox { SettingSpinBox {
enabled: Config.screenshot.enable_pp
min: 0 min: 0
name: "Shadow offset X" name: "Shadow offset X"
object: Config.screenshot object: Config.screenshot
setting: "shadow_offset_x" setting: "shadow_offset_x"
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow
step: 1 step: 1
visible: Config.screenshot.mode === "manual"
} }
Separator { Separator {
visible: Config.screenshot.mode === "manual" shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow
} }
SettingSpinBox { SettingSpinBox {
enabled: Config.screenshot.enable_pp
min: 0 min: 0
name: "Shadow offset Y" name: "Shadow offset Y"
object: Config.screenshot object: Config.screenshot
setting: "shadow_offset_y" setting: "shadow_offset_y"
shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow
step: 1 step: 1
visible: Config.screenshot.mode === "manual"
} }
} }
} }
@@ -80,7 +80,8 @@ Item {
required property ShellScreen modelData required property ShellScreen modelData
function applyCrop(): void { function applyCrop(): void {
if (!cropRectLoader.item) return; if (!cropRectLoader.item)
return;
const cropRect = cropRectLoader.item; const cropRect = cropRectLoader.item;
// We need to calculate the exact percentage coordinates that map perfectly // We need to calculate the exact percentage coordinates that map perfectly
@@ -97,7 +98,8 @@ Item {
} }
function zoomClipRect(zoom: real): void { function zoomClipRect(zoom: real): void {
if (!cropRectLoader.item) return; if (!cropRectLoader.item)
return;
const cropRect = cropRectLoader.item; const cropRect = cropRectLoader.item;
let oldCenterX = cropRect.x + cropRect.width * 0.5; let oldCenterX = cropRect.x + cropRect.width * 0.5;
@@ -139,8 +141,9 @@ Item {
id: zoomSlider id: zoomSlider
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 10 Layout.preferredHeight: 30
from: 1.0 from: 1.0
implicitHeight: 30
to: 5.0 to: 5.0
value: cropRectLoader.item ? cropRectLoader.item.zoom : 1.0 value: cropRectLoader.item ? cropRectLoader.item.zoom : 1.0
@@ -198,6 +201,7 @@ Item {
Loader { Loader {
id: cropRectLoader id: cropRectLoader
active: scaledImg.paintedWidth > 0 && scaledImg.status == Image.Ready active: scaledImg.paintedWidth > 0 && scaledImg.status == Image.Ready
sourceComponent: Component { sourceComponent: Component {
@@ -272,7 +276,8 @@ Item {
id: mouse id: mouse
function updateCrop(mouseX, mouseY) { function updateCrop(mouseX, mouseY) {
if (!cropRectLoader.item) return; if (!cropRectLoader.item)
return;
const cropRect = cropRectLoader.item; const cropRect = cropRectLoader.item;
let nx = mouseX - cropRect.width * 0.5; let nx = mouseX - cropRect.width * 0.5;
+67 -47
View File
@@ -52,25 +52,25 @@ Item {
CustomRect { CustomRect {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 42 + Appearance.spacing.smaller * 2 Layout.preferredHeight: 50 + Appearance.spacing.smaller * 2
Layout.topMargin: root.topMargin Layout.topMargin: root.topMargin
color: DynamicColors.tPalette.m3surfaceContainer color: DynamicColors.tPalette.m3surfaceContainer
radius: root.rounding radius: root.rounding
RowLayout { Item {
id: outputVolume id: sinkIcon
anchors.bottom: parent.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.margins: Appearance.spacing.smaller anchors.leftMargin: Appearance.padding.normal
anchors.right: parent.right anchors.top: parent.top
anchors.verticalCenter: parent.verticalCenter implicitWidth: childrenRect.width
spacing: 15
CustomRect { CustomRect {
Layout.alignment: Qt.AlignVCenter anchors.centerIn: parent
Layout.preferredHeight: 40
Layout.preferredWidth: 40
color: Audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary color: Audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary
implicitHeight: 40
implicitWidth: 40
radius: Appearance.rounding.full radius: Appearance.rounding.full
MaterialIcon { MaterialIcon {
@@ -92,11 +92,20 @@ Item {
} }
} }
} }
}
ColumnLayout { ColumnLayout {
Layout.fillWidth: true anchors.bottom: parent.bottom
anchors.bottomMargin: Appearance.padding.smallest
anchors.left: sinkIcon.right
anchors.leftMargin: Appearance.spacing.normal
anchors.right: parent.right
anchors.rightMargin: Appearance.padding.large
anchors.top: parent.top
anchors.topMargin: Appearance.padding.smaller
RowLayout { RowLayout {
Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
CustomText { CustomText {
@@ -114,14 +123,14 @@ Item {
CustomMouseArea { CustomMouseArea {
Layout.bottomMargin: 5 Layout.bottomMargin: 5
Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 10
CustomSlider { CustomSlider {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
color: Audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary color: Audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary
implicitHeight: 10 implicitHeight: parent.height
value: Audio.volume value: Audio.volume
Behavior on value { Behavior on value {
@@ -134,11 +143,10 @@ Item {
} }
} }
} }
}
CustomClippingRect { CustomClippingRect {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 42 + Appearance.spacing.smaller * 2 Layout.preferredHeight: 50 + Appearance.spacing.smaller * 2
Layout.topMargin: root.topMargin Layout.topMargin: root.topMargin
color: DynamicColors.tPalette.m3surfaceContainer color: DynamicColors.tPalette.m3surfaceContainer
radius: root.rounding radius: root.rounding
@@ -165,20 +173,20 @@ Item {
} }
} }
RowLayout { Item {
id: inputVolume id: sourceIcon
anchors.bottom: parent.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.margins: Appearance.spacing.smaller anchors.leftMargin: Appearance.padding.normal
anchors.right: parent.right anchors.top: parent.top
anchors.verticalCenter: parent.verticalCenter implicitWidth: childrenRect.width
spacing: 15
CustomRect { CustomRect {
Layout.alignment: Qt.AlignVCenter anchors.centerIn: parent
Layout.preferredHeight: 40
Layout.preferredWidth: 40
color: Audio.sourceMuted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary color: Audio.sourceMuted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary
implicitHeight: 40
implicitWidth: 40
radius: Appearance.rounding.full radius: Appearance.rounding.full
MaterialIcon { MaterialIcon {
@@ -200,9 +208,17 @@ Item {
} }
} }
} }
}
ColumnLayout { ColumnLayout {
Layout.fillWidth: true anchors.bottom: parent.bottom
anchors.bottomMargin: Appearance.padding.smallest
anchors.left: sourceIcon.right
anchors.leftMargin: Appearance.spacing.normal
anchors.right: parent.right
anchors.rightMargin: Appearance.padding.large
anchors.top: parent.top
anchors.topMargin: Appearance.padding.smaller
RowLayout { RowLayout {
Layout.fillHeight: true Layout.fillHeight: true
@@ -223,14 +239,14 @@ Item {
CustomMouseArea { CustomMouseArea {
Layout.bottomMargin: 5 Layout.bottomMargin: 5
Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
implicitHeight: 10
CustomSlider { CustomSlider {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
color: Audio.sourceMuted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary color: Audio.sourceMuted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary
implicitHeight: 10 implicitHeight: parent.height
value: Audio.sourceVolume value: Audio.sourceVolume
Behavior on value { Behavior on value {
@@ -243,7 +259,6 @@ Item {
} }
} }
} }
}
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
@@ -265,7 +280,7 @@ Item {
required property var modelData required property var modelData
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 42 + Appearance.spacing.smaller * 2 Layout.preferredHeight: 50 + Appearance.spacing.smaller * 2
Layout.topMargin: root.topMargin Layout.topMargin: root.topMargin
color: DynamicColors.tPalette.m3surfaceContainer color: DynamicColors.tPalette.m3surfaceContainer
radius: root.rounding radius: root.rounding
@@ -292,18 +307,20 @@ Item {
} }
} }
RowLayout { Item {
id: layoutVolume id: appBoxIcon
anchors.fill: parent anchors.bottom: parent.bottom
anchors.margins: Appearance.spacing.smaller anchors.left: parent.left
spacing: 15 anchors.leftMargin: Appearance.padding.normal
anchors.top: parent.top
implicitWidth: childrenRect.width
CustomRect { CustomRect {
Layout.alignment: Qt.AlignVCenter anchors.centerIn: parent
Layout.preferredHeight: 40
Layout.preferredWidth: 40
color: appBox.modelData.audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary color: appBox.modelData.audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary
implicitHeight: 40
implicitWidth: 40
radius: Appearance.rounding.full radius: Appearance.rounding.full
MaterialIcon { MaterialIcon {
@@ -325,10 +342,7 @@ Item {
} }
} }
} }
}
ColumnLayout {
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Layout.fillHeight: true
TextMetrics { TextMetrics {
id: metrics id: metrics
@@ -338,13 +352,22 @@ Item {
text: Audio.getStreamName(appBox.modelData) text: Audio.getStreamName(appBox.modelData)
} }
ColumnLayout {
anchors.bottom: parent.bottom
anchors.bottomMargin: Appearance.padding.smallest
anchors.left: appBoxIcon.right
anchors.leftMargin: Appearance.spacing.normal
anchors.right: parent.right
anchors.rightMargin: Appearance.padding.large
anchors.top: parent.top
anchors.topMargin: Appearance.padding.smaller
RowLayout { RowLayout {
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
CustomText { CustomText {
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
elide: Text.ElideRight elide: Text.ElideRight
text: metrics.elidedText text: metrics.elidedText
@@ -352,23 +375,21 @@ Item {
CustomText { CustomText {
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
Layout.fillHeight: true
font.bold: true font.bold: true
text: qsTr("%1").arg(appBox.modelData.audio.muted ? qsTr("Muted") : `${Math.round(appBox.modelData.audio.volume * 100)}%`) text: qsTr("%1").arg(appBox.modelData.audio.muted ? qsTr("Muted") : `${Math.round(appBox.modelData.audio.volume * 100)}%`)
} }
} }
CustomMouseArea { CustomMouseArea {
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft Layout.bottomMargin: 5
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
implicitHeight: 10
CustomSlider { CustomSlider {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
color: appBox.modelData.audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary color: appBox.modelData.audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary
implicitHeight: 10 implicitHeight: parent.height
value: appBox.modelData.audio.volume value: appBox.modelData.audio.volume
onMoved: { onMoved: {
@@ -381,4 +402,3 @@ Item {
} }
} }
} }
}
+100 -13
View File
@@ -3,33 +3,120 @@ import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import qs.Components import qs.Components
import qs.Config import qs.Config
import qs.Helpers as Helpers import qs.Helpers
RowLayout { Item {
id: root 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 { MaterialIcon {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
animate: true animate: true
color: !Helpers.UPower.onBattery || UPower.displayDevice.percentage > 0.2 ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3error
fill: 1 fill: 1
text: { text: {
if (!Helpers.UPower.displayDevice.isLaptopBattery) {
if (PowerProfiles.profile === PowerProfile.PowerSaver) if (PowerProfiles.profile === PowerProfile.PowerSaver)
return "nest_eco_leaf"; return "nest_eco_leaf";
if (PowerProfiles.profile === PowerProfile.Performance) if (PowerProfiles.profile === PowerProfile.Performance)
return "bolt"; return "bolt";
return "power_settings_new"; 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); explicit BlobGroup(QObject* parent = nullptr);
~BlobGroup() override; ~BlobGroup() override;
qreal smoothing() const { [[nodiscard]] qreal smoothing() const {
return m_smoothing; return m_smoothing;
} }
void setSmoothing(qreal s); void setSmoothing(qreal s);
QColor color() const { [[nodiscard]] QColor color() const {
return m_color; return m_color;
} }
@@ -36,11 +36,11 @@ void removeShape(BlobShape* shape);
void setInvertedRect(BlobInvertedRect* rect); void setInvertedRect(BlobInvertedRect* rect);
void clearInvertedRect(BlobInvertedRect* rect); void clearInvertedRect(BlobInvertedRect* rect);
const QList<BlobShape*>& shapes() const { [[nodiscard]] const QList<BlobShape*>& shapes() const {
return m_shapes; return m_shapes;
} }
BlobInvertedRect* invertedRect() const { [[nodiscard]] BlobInvertedRect* invertedRect() const {
return m_invertedRect; return m_invertedRect;
} }
+5 -5
View File
@@ -16,25 +16,25 @@ public:
explicit BlobInvertedRect(QQuickItem* parent = nullptr); explicit BlobInvertedRect(QQuickItem* parent = nullptr);
~BlobInvertedRect() override; ~BlobInvertedRect() override;
qreal borderLeft() const { [[nodiscard]] qreal borderLeft() const {
return m_borderLeft; return m_borderLeft;
} }
void setBorderLeft(qreal v); void setBorderLeft(qreal v);
qreal borderRight() const { [[nodiscard]] qreal borderRight() const {
return m_borderRight; return m_borderRight;
} }
void setBorderRight(qreal v); void setBorderRight(qreal v);
qreal borderTop() const { [[nodiscard]] qreal borderTop() const {
return m_borderTop; return m_borderTop;
} }
void setBorderTop(qreal v); void setBorderTop(qreal v);
qreal borderBottom() const { [[nodiscard]] qreal borderBottom() const {
return m_borderBottom; return m_borderBottom;
} }
@@ -47,7 +47,7 @@ void borderTopChanged();
void borderBottomChanged(); void borderBottomChanged();
protected: protected:
bool isInvertedRect() const override { [[nodiscard]] bool isInvertedRect() const override {
return true; return true;
} }
+2 -2
View File
@@ -21,8 +21,8 @@ struct BlobRectData {
class BlobMaterial : public QSGMaterial { class BlobMaterial : public QSGMaterial {
public: public:
QSGMaterialType* type() const override; [[nodiscard]] QSGMaterialType* type() const override;
QSGMaterialShader* createShader(QSGRendererInterface::RenderMode) const override; [[nodiscard]] QSGMaterialShader* createShader(QSGRendererInterface::RenderMode) const override;
int compare(const QSGMaterial* other) const override; int compare(const QSGMaterial* other) const override;
float m_paddedX = 0; float m_paddedX = 0;
+23 -9
View File
@@ -14,15 +14,11 @@ BlobRect::~BlobRect() {
} }
void BlobRect::updatePolish() { void BlobRect::updatePolish() {
BlobShape::updatePolish();
if (m_physicsActive) { 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 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); float totalVel = std::abs(m_dmVel00) + std::abs(m_dmVel01) + std::abs(m_dmVel11);
if (totalDelta < 0.004f && totalVel < 0.05f) { if (totalDelta < 0.004f && totalVel < 0.05f) {
// Snap to rest, no visible deformation
m_dm00 = 1.0f; m_dm00 = 1.0f;
m_dm01 = 0.0f; m_dm01 = 0.0f;
m_dm11 = 1.0f; m_dm11 = 1.0f;
@@ -31,6 +27,16 @@ void BlobRect::updatePolish() {
emit rawDeformMatrixChanged(); emit rawDeformMatrixChanged();
updateCenteredDeformMatrix(); updateCenteredDeformMatrix();
m_physicsActive = false; m_physicsActive = false;
if (m_group) {
QMetaObject::invokeMethod(
this,
[this]() {
if (m_group)
m_group->markDirty();
},
Qt::QueuedConnection);
}
} else { } else {
QMetaObject::invokeMethod( QMetaObject::invokeMethod(
this, this,
@@ -41,6 +47,8 @@ void BlobRect::updatePolish() {
Qt::QueuedConnection); Qt::QueuedConnection);
} }
} }
BlobShape::updatePolish();
} }
void BlobRect::updatePhysics() { void BlobRect::updatePhysics() {
@@ -56,7 +64,6 @@ void BlobRect::updatePhysics() {
const float dt = static_cast<float>(m_elapsed.restart()) / 1000.0f; const float dt = static_cast<float>(m_elapsed.restart()) / 1000.0f;
if (dt > 0.1f || dt < 0.001f) { if (dt > 0.1f || dt < 0.001f) {
m_prevScenePos = scenePos; m_prevScenePos = scenePos;
// Still check atRest on skipped frames to avoid getting stuck
if (m_physicsActive) if (m_physicsActive)
checkAtRest(0.0f); checkAtRest(0.0f);
return; return;
@@ -74,8 +81,6 @@ void BlobRect::updatePhysics() {
m_physicsActive = true; m_physicsActive = true;
} }
// Compute target deformation matrix from velocity
// R(θ) * diag(stretch, compress) * R(θ)^T
const float kStretchFactor = static_cast<float>(m_deformScale); const float kStretchFactor = static_cast<float>(m_deformScale);
constexpr float kMaxStretch = 0.35f; constexpr float kMaxStretch = 0.35f;
@@ -98,7 +103,6 @@ void BlobRect::updatePhysics() {
target11 = targetStretch * sin2 + targetCompress * cos2; target11 = targetStretch * sin2 + targetCompress * cos2;
} }
// Underdamped spring on each matrix component
const float kStiffness = static_cast<float>(m_stiffness); const float kStiffness = static_cast<float>(m_stiffness);
const float kDamping = static_cast<float>(m_damping); const float kDamping = static_cast<float>(m_damping);
@@ -238,9 +242,19 @@ void BlobRect::checkAtRest(float speed) {
m_dmVel00 = 0.0f; m_dmVel00 = 0.0f;
m_dmVel01 = 0.0f; m_dmVel01 = 0.0f;
m_dmVel11 = 0.0f; m_dmVel11 = 0.0f;
m_deformMatrix = QMatrix4x4(); // identity m_deformMatrix = QMatrix4x4();
emit rawDeformMatrixChanged(); emit rawDeformMatrixChanged();
updateCenteredDeformMatrix(); updateCenteredDeformMatrix();
m_physicsActive = false; 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); explicit BlobRect(QQuickItem* parent = nullptr);
~BlobRect() override; ~BlobRect() override;
qreal stiffness() const { [[nodiscard]] qreal stiffness() const {
return m_stiffness; return m_stiffness;
} }
@@ -35,7 +35,7 @@ void setStiffness(qreal s) {
} }
} }
qreal damping() const { [[nodiscard]] qreal damping() const {
return m_damping; return m_damping;
} }
@@ -46,7 +46,7 @@ void setDamping(qreal d) {
} }
} }
qreal deformScale() const { [[nodiscard]] qreal deformScale() const {
return m_deformScale; return m_deformScale;
} }
@@ -62,25 +62,25 @@ QQmlListProperty<BlobRect> exclude();
bool isExcluded(const BlobShape* other) const override; bool isExcluded(const BlobShape* other) const override;
void cornerRadii(float out[4]) const override; void cornerRadii(float out[4]) const override;
qreal topLeftRadius() const { [[nodiscard]] qreal topLeftRadius() const {
return m_topLeftRadius; return m_topLeftRadius;
} }
void setTopLeftRadius(qreal r); void setTopLeftRadius(qreal r);
qreal topRightRadius() const { [[nodiscard]] qreal topRightRadius() const {
return m_topRightRadius; return m_topRightRadius;
} }
void setTopRightRadius(qreal r); void setTopRightRadius(qreal r);
qreal bottomLeftRadius() const { [[nodiscard]] qreal bottomLeftRadius() const {
return m_bottomLeftRadius; return m_bottomLeftRadius;
} }
void setBottomLeftRadius(qreal r); void setBottomLeftRadius(qreal r);
qreal bottomRightRadius() const { [[nodiscard]] qreal bottomRightRadius() const {
return m_bottomRightRadius; return m_bottomRightRadius;
} }
+7 -23
View File
@@ -9,7 +9,6 @@
#include <cmath> #include <cmath>
static float deformPadding(const QMatrix4x4& dm, float hw, float hh) { 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 dm00 = dm(0, 0), dm01 = dm(0, 1);
const float dm10 = dm(1, 0), dm11 = dm(1, 1); const float dm10 = dm(1, 0), dm11 = dm(1, 1);
const float boundX = std::abs(dm00) * hw + std::abs(dm01) * hh; 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); QQuickItem::geometryChange(newGeometry, oldGeometry);
updateCenteredDeformMatrix(); updateCenteredDeformMatrix();
if (m_group) { 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_pendingDx += static_cast<float>(newGeometry.x() - oldGeometry.x());
m_pendingDy += static_cast<float>(newGeometry.y() - oldGeometry.y()); 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_pendingDw += static_cast<float>(newGeometry.width() - oldGeometry.width());
m_pendingDh += static_cast<float>(newGeometry.height() - oldGeometry.height()); 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_pendingDx = 0;
m_pendingDy = 0; m_pendingDy = 0;
m_pendingDw = 0; m_pendingDw = 0;
@@ -124,7 +124,6 @@ void BlobShape::updatePolish() {
if (!m_group) if (!m_group)
return; return;
// Ensure all shapes have up-to-date physics (only once per frame)
m_group->ensurePhysicsUpdated(); m_group->ensurePhysicsUpdated();
const QPointF scenePos = mapToScene(QPointF(0, 0)); 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)); width() + 2.0 * static_cast<double>(totalPad), height() + 2.0 * static_cast<double>(totalPad));
} }
// Filter nearby normal rects
m_cachedRects.clear(); m_cachedRects.clear();
m_cachedMyIndex = -2; m_cachedMyIndex = -2;
const QRectF myPadded(static_cast<double>(m_cachedPaddedX), static_cast<double>(m_cachedPaddedY), const QRectF myPadded(static_cast<double>(m_cachedPaddedX), static_cast<double>(m_cachedPaddedY),
static_cast<double>(m_cachedPaddedW), static_cast<double>(m_cachedPaddedH)); static_cast<double>(m_cachedPaddedW), static_cast<double>(m_cachedPaddedH));
// Track shape pointers parallel to m_cachedRects for pairwise exclusion lookups
QVector<BlobShape*> rectShapes; QVector<BlobShape*> rectShapes;
rectShapes.reserve(m_group->shapes().size()); rectShapes.reserve(m_group->shapes().size());
@@ -163,7 +160,6 @@ void BlobShape::updatePolish() {
if (other->isInvertedRect()) if (other->isInvertedRect())
continue; continue;
// Skip zero-size rects
if (other->width() <= 0 || other->height() <= 0) if (other->width() <= 0 || other->height() <= 0)
continue; continue;
@@ -202,7 +198,6 @@ void BlobShape::updatePolish() {
r.offsetX = dm(0, 3); r.offsetX = dm(0, 3);
r.offsetY = dm(1, 3); r.offsetY = dm(1, 3);
// Pre-compute inverse deformation matrix
const float det = a * d - c * b; const float det = a * d - c * b;
const float invDet = std::abs(det) > 1e-6f ? 1.0f / det : 1.0f; const float invDet = std::abs(det) > 1e-6f ? 1.0f / det : 1.0f;
r.invDeform[0] = d * invDet; r.invDeform[0] = d * invDet;
@@ -210,12 +205,10 @@ void BlobShape::updatePolish() {
r.invDeform[2] = -c * invDet; r.invDeform[2] = -c * invDet;
r.invDeform[3] = a * invDet; r.invDeform[3] = a * invDet;
// Pre-compute minimum eigenvalue (avoids per-pixel sqrt)
const float halfTr = 0.5f * (a + d); const float halfTr = 0.5f * (a + d);
const float halfDiff = 0.5f * (a - d); const float halfDiff = 0.5f * (a - d);
r.minEig = halfTr - std::sqrt(halfDiff * halfDiff + c * c); 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.screenHalfX = std::abs(a) * r.hw + std::abs(c) * r.hh;
r.screenHalfY = std::abs(b) * r.hw + std::abs(d) * r.hh; r.screenHalfY = std::abs(b) * r.hw + std::abs(d) * r.hh;
@@ -227,8 +220,6 @@ void BlobShape::updatePolish() {
if (isInvertedRect()) if (isInvertedRect())
m_cachedMyIndex = -1; 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(); const auto cachedCount = m_cachedRects.size();
for (qsizetype i = 0; i < cachedCount; ++i) { for (qsizetype i = 0; i < cachedCount; ++i) {
int mask = 0; int mask = 0;
@@ -243,7 +234,6 @@ void BlobShape::updatePolish() {
m_cachedRects[i].excludeMask = mask; m_cachedRects[i].excludeMask = mask;
} }
// Cache inverted rect data
m_cachedHasInverted = false; m_cachedHasInverted = false;
m_cachedInvertedRadius = 0; m_cachedInvertedRadius = 0;
memset(m_cachedInvertedOuter, 0, sizeof(m_cachedInvertedOuter)); 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 innerHW = outerHW - static_cast<float>((inv->borderLeft() + inv->borderRight()) / 2.0);
const float innerHH = outerHH - static_cast<float>((inv->borderTop() + inv->borderBottom()) / 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(); bool nearBorder = isInvertedRect();
if (!nearBorder) { if (!nearBorder) {
const float margin = pad * 2.0f; const float margin = pad * 2.0f;
@@ -270,7 +259,6 @@ void BlobShape::updatePolish() {
const float myCY = m_cachedPaddedY + m_cachedPaddedH * 0.5f; const float myCY = m_cachedPaddedY + m_cachedPaddedH * 0.5f;
const float myHW = m_cachedPaddedW * 0.5f; const float myHW = m_cachedPaddedW * 0.5f;
const float myHH = m_cachedPaddedH * 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) || nearBorder = (myCX - myHW < innerCX - innerHW + margin) || (myCX + myHW > innerCX + innerHW - margin) ||
(myCY - myHH < innerCY - innerHH + margin) || (myCY + myHH > innerCY + innerHH - 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; const float smoothFactor = pad;
constexpr float minR = 2.0f; constexpr float minR = 2.0f;
const auto rectCount = m_cachedRects.size(); 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))); 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[0] = std::max(ri.radius[0] * fTr, minR);
ri.radius[1] = std::max(ri.radius[1] * fBr, minR); ri.radius[1] = std::max(ri.radius[1] * fBr, minR);
ri.radius[2] = std::max(ri.radius[2] * fBl, 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); node->setFlag(QSGNode::OwnsMaterial);
} }
// Update geometry
auto* geometry = node->geometry(); auto* geometry = node->geometry();
auto* v = geometry->vertexDataAsTexturedPoint2D(); auto* v = geometry->vertexDataAsTexturedPoint2D();
@@ -373,7 +358,6 @@ QSGNode* BlobShape::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) {
node->markDirty(QSGNode::DirtyGeometry); node->markDirty(QSGNode::DirtyGeometry);
// Update material
auto* material = static_cast<BlobMaterial*>(node->material()); auto* material = static_cast<BlobMaterial*>(node->material());
material->m_paddedX = m_cachedPaddedX; material->m_paddedX = m_cachedPaddedX;
material->m_paddedY = m_cachedPaddedY; material->m_paddedY = m_cachedPaddedY;
+6 -7
View File
@@ -21,23 +21,23 @@ public:
explicit BlobShape(QQuickItem* parent = nullptr); explicit BlobShape(QQuickItem* parent = nullptr);
~BlobShape() override = default; ~BlobShape() override = default;
BlobGroup* group() const { [[nodiscard]] BlobGroup* group() const {
return m_group; return m_group;
} }
void setGroup(BlobGroup* g); void setGroup(BlobGroup* g);
qreal radius() const { [[nodiscard]] qreal radius() const {
return m_radius; return m_radius;
} }
void setRadius(qreal r); void setRadius(qreal r);
QMatrix4x4 deformMatrix() const { [[nodiscard]] QMatrix4x4 deformMatrix() const {
return m_centeredDeformMatrix; return m_centeredDeformMatrix;
} }
QMatrix4x4 rawDeformMatrix() const { [[nodiscard]] QMatrix4x4 rawDeformMatrix() const {
return m_deformMatrix; return m_deformMatrix;
} }
@@ -53,7 +53,7 @@ void geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) overri
void updatePolish() override; void updatePolish() override;
QSGNode* updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) override; QSGNode* updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) override;
virtual bool isInvertedRect() const { [[nodiscard]] virtual bool isInvertedRect() const {
return false; return false;
} }
@@ -72,10 +72,9 @@ void updateCenteredDeformMatrix();
BlobGroup* m_group = nullptr; BlobGroup* m_group = nullptr;
qreal m_radius = 0; qreal m_radius = 0;
QMatrix4x4 m_deformMatrix; // identity by default QMatrix4x4 m_deformMatrix;
QMatrix4x4 m_centeredDeformMatrix; QMatrix4x4 m_centeredDeformMatrix;
// Cached data from updatePolish
float m_cachedPaddedX = 0; float m_cachedPaddedX = 0;
float m_cachedPaddedY = 0; float m_cachedPaddedY = 0;
float m_cachedPaddedW = 0; float m_cachedPaddedW = 0;
+1
View File
@@ -50,6 +50,7 @@ qml_module(ZShell
Qt::Quick Qt::Quick
Qt::Concurrent Qt::Concurrent
Qt::Sql Qt::Sql
Qt::DBus
PkgConfig::Qalculate PkgConfig::Qalculate
) )
+1
View File
@@ -9,6 +9,7 @@ qml_module(ZShell-internal
sparklineitem.hpp sparklineitem.cpp sparklineitem.hpp sparklineitem.cpp
arcgauge.hpp arcgauge.cpp arcgauge.hpp arcgauge.cpp
wallpaperimage.hpp wallpaperimage.cpp wallpaperimage.hpp wallpaperimage.cpp
lidwatcher.hpp lidwatcher.cpp
LIBRARIES LIBRARIES
Qt::Gui Qt::Gui
Qt::Quick Qt::Quick
+1 -1
View File
@@ -58,4 +58,4 @@ qreal m_sweepAngle = 1.5 * M_PI;
qreal m_lineWidth = 10.0; qreal m_lineWidth = 10.0;
}; };
} // namespace ZShell::internal } // namespace ZShell::Internal
@@ -19,7 +19,8 @@ class CachingImageManager : public QObject {
public: public:
explicit CachingImageManager(QObject* parent = nullptr) explicit CachingImageManager(QObject* parent = nullptr)
: QObject(parent) : QObject(parent)
, m_item(nullptr) {} , m_item(nullptr) {
}
[[nodiscard]] QQuickItem* item() const; [[nodiscard]] QQuickItem* item() const;
void setItem(QQuickItem* item); void setItem(QQuickItem* item);
+2 -2
View File
@@ -2,7 +2,7 @@
#include <algorithm> #include <algorithm>
namespace caelestia::internal { namespace ZShell::internal {
CircularBuffer::CircularBuffer(QObject* parent) CircularBuffer::CircularBuffer(QObject* parent)
: QObject(parent) { : QObject(parent) {
@@ -92,4 +92,4 @@ qreal CircularBuffer::at(int index) const {
return m_data[actualIndex]; return m_data[actualIndex];
} }
} // namespace caelestia::internal } // namespace ZShell::internal
+2 -2
View File
@@ -4,7 +4,7 @@
#include <qqmlintegration.h> #include <qqmlintegration.h>
#include <qvector.h> #include <qvector.h>
namespace caelestia::internal { namespace ZShell::internal {
class CircularBuffer : public QObject { class CircularBuffer : public QObject {
Q_OBJECT Q_OBJECT
@@ -41,4 +41,4 @@ int m_count = 0;
int m_capacity = 0; int m_capacity = 0;
}; };
} // namespace caelestia::internal } // namespace ZShell::internal
@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <cstdint>
#include <qeasingcurve.h> #include <qeasingcurve.h>
#include <qobject.h> #include <qobject.h>
#include <qqmlintegration.h> #include <qqmlintegration.h>
+140 -19
View File
@@ -1,11 +1,18 @@
#include "hyprextras.hpp" #include "hyprextras.hpp"
#include "hyprdevices.hpp" #include "hyprdevices.hpp"
#include <functional>
#include <memory>
#include <qdir.h> #include <qdir.h>
#include <qcolor.h>
#include <qjsonarray.h> #include <qjsonarray.h>
#include <qjsondocument.h>
#include <qjsonobject.h>
#include <qlocalsocket.h> #include <qlocalsocket.h>
#include <qloggingcategory.h> #include <qloggingcategory.h>
#include <qmetatype.h> #include <qmetatype.h>
#include <qobject.h>
#include <qregularexpression.h> #include <qregularexpression.h>
#include <qvariant.h> #include <qvariant.h>
@@ -163,6 +170,86 @@ static QString buildHlConfigCall(const QString& key, const QVariant& value) {
return out; return out;
} }
static QColor colorFromInt(quint32 value) {
const int a = (value >> 24) & 0xFF;
const int r = (value >> 16) & 0xFF;
const int g = (value >> 8) & 0xFF;
const int b = value & 0xFF;
return QColor(r, g, b, a);
}
static QVariant parseGetOptionValue(const QJsonObject& obj) {
if (obj.contains(QStringLiteral("bool"))) {
return obj.value(QStringLiteral("bool")).toBool();
}
if (obj.contains(QStringLiteral("int"))) {
const auto value = obj.value(QStringLiteral("int")).toInt();
const auto option = obj.value(QStringLiteral("option")).toString();
if (option.contains(QStringLiteral("color")) || option.contains(QStringLiteral("col."))) {
return colorFromInt(static_cast<quint32>(value));
}
return value;
}
if (obj.contains(QStringLiteral("float"))) {
return obj.value(QStringLiteral("float")).toDouble();
}
if (obj.contains(QStringLiteral("str"))) {
return obj.value(QStringLiteral("str")).toString();
}
if (obj.contains(QStringLiteral("current"))) {
return obj.value(QStringLiteral("current")).toVariant();
}
if (obj.contains(QStringLiteral("value"))) {
return obj.value(QStringLiteral("value")).toVariant();
}
if (obj.contains(QStringLiteral("vec2"))) {
return obj.value(QStringLiteral("vec2")).toVariant();
}
if (obj.contains(QStringLiteral("data"))) {
const auto data = obj.value(QStringLiteral("data"));
if (data.isObject()) {
const auto d = data.toObject();
if (d.contains(QStringLiteral("current"))) {
return d.value(QStringLiteral("current")).toVariant();
}
if (d.contains(QStringLiteral("value"))) {
return d.value(QStringLiteral("value")).toVariant();
}
} else {
return data.toVariant();
}
}
return {};
}
static void insertNestedValue(QVariantMap& root, const QStringList& path, const QVariant& value) {
if (path.isEmpty()) {
return;
}
if (path.size() == 1) {
root.insert(path.first(), value);
return;
}
const QString head = path.first();
QVariantMap child = root.value(head).toMap();
insertNestedValue(child, path.mid(1), value);
root.insert(head, child);
}
} // namespace } // namespace
HyprExtras::HyprExtras(QObject* parent) HyprExtras::HyprExtras(QObject* parent)
@@ -203,7 +290,7 @@ HyprExtras::HyprExtras(QObject* parent)
m_socket->connectToServer(m_eventSocket, QLocalSocket::ReadOnly); m_socket->connectToServer(m_eventSocket, QLocalSocket::ReadOnly);
} }
QVariantHash HyprExtras::options() const { QVariantMap HyprExtras::options() const {
return m_options; return m_options;
} }
@@ -269,30 +356,64 @@ void HyprExtras::refreshOptions() {
m_optionsRefresh->close(); m_optionsRefresh->close();
} }
m_optionsRefresh = makeRequestJson(QStringLiteral("descriptions"), [this](bool success, const QJsonDocument& response) { ++m_optionsRefreshGeneration;
m_optionsRefresh.reset(); const quint64 generation = m_optionsRefreshGeneration;
if (!success) {
static const QStringList optionKeys = {
QStringLiteral("general:border_size"),
QStringLiteral("decoration:rounding"),
QStringLiteral("animations:enabled"),
QStringLiteral("decoration:shadow:enabled"),
QStringLiteral("decoration:shadow:offset"),
QStringLiteral("decoration:shadow:color"),
QStringLiteral("decoration:shadow:range"),
QStringLiteral("decoration:shadow:render_power"),
};
auto nextOptions = std::make_shared<QVariantMap>();
auto step = std::make_shared<std::function<void(int)> >();
*step = [this, generation, nextOptions, step](int index) {
if (generation != m_optionsRefreshGeneration) {
return; return;
} }
const auto options = response.array(); if (index >= optionKeys.size()) {
bool dirty = false; if (m_options != *nextOptions) {
m_options = *nextOptions;
for (const auto& o : std::as_const(options)) {
const auto obj = o.toObject();
const auto key = obj.value(QStringLiteral("value")).toString();
const auto value = obj.value(QStringLiteral("data")).toObject().value(QStringLiteral("current")).toVariant();
if (m_options.value(key) != value) {
dirty = true;
m_options.insert(key, value);
}
}
if (dirty) {
emit optionsChanged(); emit optionsChanged();
} }
return;
}
const QString key = optionKeys.at(index);
m_optionsRefresh = makeRequestJson(
QStringLiteral("getoption ") + key,
[this, generation, nextOptions, step, index, key](bool success, const QJsonDocument& response)
{
m_optionsRefresh.reset();
if (generation != m_optionsRefreshGeneration) {
return;
}
if (success && response.isObject()) {
const QVariant value = parseGetOptionValue(response.object());
if (value.isValid()) {
insertNestedValue(*nextOptions, key.split(QLatin1Char(':'), Qt::SkipEmptyParts), value);
} else {
qCWarning(lcHypr) << "refreshOptions: getoption returned no usable value for" << key;
}
} else if (!success) {
qCWarning(lcHypr) << "refreshOptions: getoption request error for" << key;
}
(*step)(index + 1);
}); });
};
(*step)(0);
} }
void HyprExtras::refreshDevices() { void HyprExtras::refreshDevices() {
+8 -3
View File
@@ -1,9 +1,13 @@
#pragma once #pragma once
#include <functional>
#include <qjsondocument.h>
#include <qlocalsocket.h> #include <qlocalsocket.h>
#include <qobject.h> #include <qobject.h>
#include <qqmlintegration.h> #include <qqmlintegration.h>
#include <qsharedpointer.h> #include <qsharedpointer.h>
#include <qstringlist.h>
#include <qvariant.h> #include <qvariant.h>
namespace ZShell::internal::hypr { namespace ZShell::internal::hypr {
@@ -15,13 +19,13 @@ Q_OBJECT
QML_ELEMENT QML_ELEMENT
Q_MOC_INCLUDE("hyprdevices.hpp") Q_MOC_INCLUDE("hyprdevices.hpp")
Q_PROPERTY(QVariantHash options READ options NOTIFY optionsChanged) Q_PROPERTY(QVariantMap options READ options NOTIFY optionsChanged)
Q_PROPERTY(ZShell::internal::hypr::HyprDevices* devices READ devices CONSTANT) Q_PROPERTY(ZShell::internal::hypr::HyprDevices* devices READ devices CONSTANT)
public: public:
explicit HyprExtras(QObject* parent = nullptr); explicit HyprExtras(QObject* parent = nullptr);
[[nodiscard]] QVariantHash options() const; [[nodiscard]] QVariantMap options() const;
[[nodiscard]] HyprDevices* devices() const; [[nodiscard]] HyprDevices* devices() const;
Q_INVOKABLE void message(const QString& message); Q_INVOKABLE void message(const QString& message);
@@ -42,11 +46,12 @@ QString m_eventSocket;
QLocalSocket* m_socket; QLocalSocket* m_socket;
bool m_socketValid; bool m_socketValid;
QVariantHash m_options; QVariantMap m_options;
HyprDevices* const m_devices; HyprDevices* const m_devices;
SocketPtr m_optionsRefresh; SocketPtr m_optionsRefresh;
SocketPtr m_devicesRefresh; SocketPtr m_devicesRefresh;
quint64 m_optionsRefreshGeneration = 0;
void socketError(QLocalSocket::LocalSocketError error) const; void socketError(QLocalSocket::LocalSocketError error) const;
void socketStateChanged(QLocalSocket::LocalSocketState state); void socketStateChanged(QLocalSocket::LocalSocketState state);
+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 <qpainterpath.h>
#include <qpen.h> #include <qpen.h>
namespace caelestia::internal { namespace ZShell::internal {
SparklineItem::SparklineItem(QQuickItem* parent) SparklineItem::SparklineItem(QQuickItem* parent)
: QQuickPaintedItem(parent) { : QQuickPaintedItem(parent) {
@@ -212,4 +212,4 @@ void SparklineItem::setLineWidth(qreal width) {
update(); update();
} }
} // namespace caelestia::internal } // namespace ZShell::internal
+2 -2
View File
@@ -7,7 +7,7 @@
#include "circularbuffer.hpp" #include "circularbuffer.hpp"
namespace caelestia::internal { namespace ZShell::internal {
class SparklineItem : public QQuickPaintedItem { class SparklineItem : public QQuickPaintedItem {
Q_OBJECT Q_OBJECT
@@ -87,4 +87,4 @@ int m_historyLength = 30;
qreal m_lineWidth = 2.0; qreal m_lineWidth = 2.0;
}; };
} // namespace caelestia::internal } // namespace ZShell::internal
+4 -3
View File
@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <cstdint>
#include <qabstractitemmodel.h> #include <qabstractitemmodel.h>
#include <qdir.h> #include <qdir.h>
#include <qfilesystemwatcher.h> #include <qfilesystemwatcher.h>
@@ -85,9 +86,9 @@ public:
explicit FileSystemModel(QObject* parent = nullptr); explicit FileSystemModel(QObject* parent = nullptr);
int rowCount(const QModelIndex& parent = QModelIndex()) const override; [[nodiscard]] int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; [[nodiscard]] QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override; [[nodiscard]] QHash<int, QByteArray> roleNames() const override;
[[nodiscard]] QString path() const; [[nodiscard]] QString path() const;
void setPath(const QString& path); void setPath(const QString& path);
+1
View File
@@ -9,6 +9,7 @@ qml_module(ZShell-services
cavaprovider.hpp cavaprovider.cpp cavaprovider.hpp cavaprovider.cpp
desktopmodel.hpp desktopmodel.cpp desktopmodel.hpp desktopmodel.cpp
desktopstatemanager.hpp desktopstatemanager.cpp desktopstatemanager.hpp desktopstatemanager.cpp
hyprsunsetmanager.hpp hyprsunsetmanager.cpp
LIBRARIES LIBRARIES
Qt6::Core Qt6::Core
Qt6::Qml Qt6::Qml
+1 -1
View File
@@ -59,7 +59,7 @@ quint32 readChunk(double* out, quint32 count = 0);
private: private:
explicit AudioCollector(QObject* parent = nullptr); explicit AudioCollector(QObject* parent = nullptr);
~AudioCollector(); ~AudioCollector() override;
std::jthread m_thread; std::jthread m_thread;
std::vector<float> m_buffer1; std::vector<float> m_buffer1;
+2 -3
View File
@@ -11,11 +11,10 @@ Q_OBJECT
public: public:
explicit AudioProcessor(QObject* parent = nullptr); explicit AudioProcessor(QObject* parent = nullptr);
~AudioProcessor(); ~AudioProcessor() override;
void init(); void init();
public slots:
void start(); void start();
void stop(); void stop();
@@ -31,7 +30,7 @@ Q_OBJECT
public: public:
explicit AudioProvider(QObject* parent = nullptr); explicit AudioProvider(QObject* parent = nullptr);
~AudioProvider(); ~AudioProvider() override;
protected: protected:
AudioProcessor* m_processor; AudioProcessor* m_processor;
+1 -1
View File
@@ -11,7 +11,7 @@ Q_OBJECT
public: public:
explicit BeatProcessor(QObject* parent = nullptr); explicit BeatProcessor(QObject* parent = nullptr);
~BeatProcessor(); ~BeatProcessor() override;
signals: signals:
void beat(smpl_t bpm); void beat(smpl_t bpm);
+1 -1
View File
@@ -11,7 +11,7 @@ Q_OBJECT
public: public:
explicit CavaProcessor(QObject* parent = nullptr); explicit CavaProcessor(QObject* parent = nullptr);
~CavaProcessor(); ~CavaProcessor() override;
void setBars(int bars); void setBars(int bars);
+5 -4
View File
@@ -4,6 +4,7 @@
#include <QList> #include <QList>
#include <QString> #include <QString>
#include <QQmlEngine> #include <QQmlEngine>
#include <cstdint>
namespace ZShell::services { namespace ZShell::services {
@@ -30,9 +31,9 @@ enum DesktopRoles {
explicit DesktopModel(QObject *parent = nullptr); explicit DesktopModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override; [[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; [[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override; [[nodiscard]] QHash<int, QByteArray> roleNames() const override;
Q_INVOKABLE void loadDirectory(const QString &path); Q_INVOKABLE void loadDirectory(const QString &path);
Q_INVOKABLE void moveIcon(int index, int newX, int newY); Q_INVOKABLE void moveIcon(int index, int newX, int newY);
@@ -43,4 +44,4 @@ QList<DesktopItem> m_items;
void saveCurrentLayout(); void saveCurrentLayout();
}; };
} // namespace ZShell::services } // namespace ZShell::Services
@@ -18,7 +18,7 @@ Q_INVOKABLE void saveLayout(const QVariantMap& layout);
Q_INVOKABLE QVariantMap getLayout(); Q_INVOKABLE QVariantMap getLayout();
private: private:
QString getConfigFilePath() const; [[nodiscard]] QString getConfigFilePath() const;
}; };
} // namespace ZShell::services } // 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();
};
};
+1 -1
View File
@@ -13,7 +13,7 @@ QML_SINGLETON
public: public:
explicit Qalculator(QObject* parent = nullptr); 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 } // 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); Q_INVOKABLE void cacheImage(const QUrl& source, const QString& cacheDir, QJSValue onSaved, QJSValue onFailed);
// clang-format on // clang-format on
Q_INVOKABLE bool copyFile(const QUrl& source, const QUrl& target, bool overwrite = true) const; Q_INVOKABLE [[nodiscard]] bool copyFile(const QUrl& source, const QUrl& target, bool overwrite = true) const;
Q_INVOKABLE bool deleteFile(const QUrl& path) const; Q_INVOKABLE [[nodiscard]] bool deleteFile(const QUrl& path) const;
Q_INVOKABLE QString toLocalFile(const QUrl& url) const; Q_INVOKABLE [[nodiscard]] QString toLocalFile(const QUrl& url) const;
private: private:
bool loadSourceImage(const QUrl& source, QImage& image) const; bool loadSourceImage(const QUrl& source, QImage& image) const;
+9 -9
View File
@@ -1011,7 +1011,7 @@ export const settingsIndex = [
keywords: ["corner", "radius"], keywords: ["corner", "radius"],
}, },
{ {
name: "Enable drop shadow", name: "Enable shadow",
category: "screenshot", category: "screenshot",
categoryName: "Screenshot", categoryName: "Screenshot",
section: "Screenshot", section: "Screenshot",
@@ -1025,19 +1025,19 @@ export const settingsIndex = [
keywords: ["rounded", "corners"], keywords: ["rounded", "corners"],
}, },
{ {
name: "Shadow blur radius", name: "Shadow blur amount",
category: "screenshot", category: "screenshot",
categoryName: "Screenshot", categoryName: "Screenshot",
section: "Screenshot", section: "Screenshot",
keywords: ["blur", "shadow", "radius"], keywords: ["blur", "shadow", "radius"],
}, },
{ // {
name: "Shadow color", // name: "Shadow color",
category: "screenshot", // category: "screenshot",
categoryName: "Screenshot", // categoryName: "Screenshot",
section: "Screenshot", // section: "Screenshot",
keywords: ["color", "shadow"], // keywords: ["color", "shadow"],
}, // },
{ {
name: "Shadow offset X", name: "Shadow offset X",
category: "screenshot", category: "screenshot",
+1 -1
View File
@@ -48,7 +48,7 @@ ShellRoot {
LazyLoader { LazyLoader {
activeAsync: root.laptop activeAsync: root.laptop
component: Battery { component: BatteryService {
} }
} }
} }
+2
View File
@@ -0,0 +1,2 @@
edition = "2024"
style_edition = "2024"
+3 -807
View File
@@ -8,47 +8,12 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "aligned"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685"
dependencies = [
"as-slice",
]
[[package]]
name = "aligned-vec"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b"
dependencies = [
"equator",
]
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.102" version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "arbitrary"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
[[package]]
name = "arg_enum_proc_macro"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "arrayref" name = "arrayref"
version = "0.3.9" version = "0.3.9"
@@ -61,103 +26,18 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "as-slice"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516"
dependencies = [
"stable_deref_trait",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.5.0" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "av-scenechange"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394"
dependencies = [
"aligned",
"anyhow",
"arg_enum_proc_macro",
"arrayvec",
"log",
"num-rational",
"num-traits",
"pastey",
"rayon",
"thiserror",
"v_frame",
"y4m",
]
[[package]]
name = "av1-grain"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8"
dependencies = [
"anyhow",
"arrayvec",
"log",
"nom",
"num-rational",
"v_frame",
]
[[package]]
name = "avif-serialize"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "375082f007bd67184fb9c0374614b29f9aaa604ec301635f72338bb65386a53d"
dependencies = [
"arrayvec",
]
[[package]]
name = "bit_field"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.11.1" version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
[[package]]
name = "bitstream-io"
version = "4.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7eff00be299a18769011411c9def0d827e8f2d7bf0c3dbf53633147a8867fd1f"
dependencies = [
"no_std_io2",
]
[[package]]
name = "built"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64"
[[package]]
name = "bumpalo"
version = "3.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
[[package]] [[package]]
name = "bytemuck" name = "bytemuck"
version = "1.25.0" version = "1.25.0"
@@ -170,30 +50,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]]
name = "cc"
version = "1.2.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d"
dependencies = [
"find-msvc-tools",
"jobserver",
"libc",
"shlex",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.4" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.5.0" version = "1.5.0"
@@ -203,84 +65,6 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "crossbeam-deque"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crunchy"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "equator"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc"
dependencies = [
"equator-macro",
]
[[package]]
name = "equator-macro"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "exr"
version = "1.74.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be"
dependencies = [
"bit_field",
"half",
"lebe",
"miniz_oxide",
"rayon-core",
"smallvec",
"zune-inflate",
]
[[package]]
name = "fax"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a"
[[package]] [[package]]
name = "fdeflate" name = "fdeflate"
version = "0.3.7" version = "0.3.7"
@@ -290,12 +74,6 @@ dependencies = [
"simd-adler32", "simd-adler32",
] ]
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.1.9" version = "1.1.9"
@@ -306,39 +84,6 @@ dependencies = [
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "getrandom"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasip2",
]
[[package]]
name = "gif"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159"
dependencies = [
"color_quant",
"weezl",
]
[[package]]
name = "half"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
dependencies = [
"cfg-if",
"crunchy",
"zerocopy",
]
[[package]] [[package]]
name = "image" name = "image"
version = "0.25.10" version = "0.25.10"
@@ -347,56 +92,9 @@ checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"byteorder-lite", "byteorder-lite",
"color_quant",
"exr",
"gif",
"image-webp",
"moxcms", "moxcms",
"num-traits", "num-traits",
"png 0.18.1", "png",
"qoi",
"ravif",
"rayon",
"rgb",
"tiff",
"zune-core",
"zune-jpeg",
]
[[package]]
name = "image-webp"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3"
dependencies = [
"byteorder-lite",
"quick-error",
]
[[package]]
name = "imgref"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40fac9d56ed6437b198fddba683305e8e2d651aa42647f00f5ae542e7f5c94a2"
[[package]]
name = "interpolate_name"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "itertools"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
dependencies = [
"either",
] ]
[[package]] [[package]]
@@ -405,63 +103,12 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "jobserver"
version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
dependencies = [
"getrandom",
"libc",
]
[[package]]
name = "lebe"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8"
[[package]]
name = "libc"
version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "libfuzzer-sys"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d"
dependencies = [
"arbitrary",
"cc",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.29" version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "loop9"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062"
dependencies = [
"imgref",
]
[[package]]
name = "maybe-rayon"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519"
dependencies = [
"cfg-if",
"rayon",
]
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.8.0" version = "2.8.0"
@@ -488,77 +135,6 @@ dependencies = [
"pxfm", "pxfm",
] ]
[[package]]
name = "new_debug_unreachable"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
[[package]]
name = "no_std_io2"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b51ed7824b6e07d354605f4abb3d9d300350701299da96642ee084f5ce631550"
dependencies = [
"memchr",
]
[[package]]
name = "nom"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
dependencies = [
"memchr",
]
[[package]]
name = "noop_proc_macro"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
[[package]]
name = "num-bigint"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-derive"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"num-bigint",
"num-integer",
"num-traits",
]
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.19" version = "0.2.19"
@@ -568,59 +144,19 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pastey"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec"
[[package]]
name = "png"
version = "0.17.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
dependencies = [
"bitflags 1.3.2",
"crc32fast",
"fdeflate",
"flate2",
"miniz_oxide",
]
[[package]] [[package]]
name = "png" name = "png"
version = "0.18.1" version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags",
"crc32fast", "crc32fast",
"fdeflate", "fdeflate",
"flate2", "flate2",
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.106" version = "1.0.106"
@@ -630,46 +166,12 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "profiling"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773"
dependencies = [
"profiling-procmacros",
]
[[package]]
name = "profiling-procmacros"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b"
dependencies = [
"quote",
"syn",
]
[[package]] [[package]]
name = "pxfm" name = "pxfm"
version = "0.1.29" version = "0.1.29"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f" checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f"
[[package]]
name = "qoi"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
dependencies = [
"bytemuck",
]
[[package]]
name = "quick-error"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.45" version = "1.0.45"
@@ -679,123 +181,6 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "rand"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
dependencies = [
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
dependencies = [
"getrandom",
]
[[package]]
name = "rav1e"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b"
dependencies = [
"aligned-vec",
"arbitrary",
"arg_enum_proc_macro",
"arrayvec",
"av-scenechange",
"av1-grain",
"bitstream-io",
"built",
"cfg-if",
"interpolate_name",
"itertools",
"libc",
"libfuzzer-sys",
"log",
"maybe-rayon",
"new_debug_unreachable",
"noop_proc_macro",
"num-derive",
"num-traits",
"paste",
"profiling",
"rand",
"rand_chacha",
"simd_helpers",
"thiserror",
"v_frame",
"wasm-bindgen",
]
[[package]]
name = "ravif"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e52310197d971b0f5be7fe6b57530dcd27beb35c1b013f29d66c1ad73fbbcc45"
dependencies = [
"avif-serialize",
"imgref",
"loop9",
"quick-error",
"rav1e",
"rayon",
"rgb",
]
[[package]]
name = "rayon"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "rgb"
version = "0.8.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4"
[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.228" version = "1.0.228"
@@ -839,39 +224,12 @@ dependencies = [
"zmij", "zmij",
] ]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]] [[package]]
name = "simd-adler32" name = "simd-adler32"
version = "0.3.9" version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
[[package]]
name = "simd_helpers"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6"
dependencies = [
"quote",
]
[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "stable_deref_trait"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]] [[package]]
name = "strict-num" name = "strict-num"
version = "0.1.1" version = "0.1.1"
@@ -889,40 +247,6 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "thiserror"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tiff"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52"
dependencies = [
"fax",
"flate2",
"half",
"quick-error",
"weezl",
"zune-jpeg",
]
[[package]] [[package]]
name = "tiny-skia" name = "tiny-skia"
version = "0.11.4" version = "0.11.4"
@@ -934,7 +258,6 @@ dependencies = [
"bytemuck", "bytemuck",
"cfg-if", "cfg-if",
"log", "log",
"png 0.17.16",
"tiny-skia-path", "tiny-skia-path",
] ]
@@ -955,109 +278,6 @@ version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "v_frame"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2"
dependencies = [
"aligned-vec",
"num-traits",
"wasm-bindgen",
]
[[package]]
name = "wasip2"
version = "1.0.3+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea"
dependencies = [
"unicode-ident",
]
[[package]]
name = "weezl"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
[[package]]
name = "wit-bindgen"
version = "0.57.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
[[package]]
name = "y4m"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448"
[[package]]
name = "zerocopy"
version = "0.8.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "zmij" name = "zmij"
version = "1.0.21" version = "1.0.21"
@@ -1066,7 +286,7 @@ checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
[[package]] [[package]]
name = "zshell-img-tools" name = "zshell-img-tools"
version = "0.1.0" version = "0.2.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"image", "image",
@@ -1074,27 +294,3 @@ dependencies = [
"serde_json", "serde_json",
"tiny-skia", "tiny-skia",
] ]
[[package]]
name = "zune-core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9"
[[package]]
name = "zune-inflate"
version = "0.2.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
dependencies = [
"simd-adler32",
]
[[package]]
name = "zune-jpeg"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296"
dependencies = [
"zune-core",
]
+4 -4
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "zshell-img-tools" name = "zshell-img-tools"
version = "0.1.0" version = "0.2.0"
edition = "2024" edition = "2024"
[[bin]] [[bin]]
@@ -8,10 +8,10 @@ name = "zshell-img-tools"
path = "src/main.rs" path = "src/main.rs"
[dependencies] [dependencies]
image = { version = "0.25", features = ["png"] } image = { version = "0.25", default-features = false, features = ["png"] }
tiny-skia = "0.11" tiny-skia = { version = "0.11", default-features = false, features = ["std", "simd"] }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
anyhow = "1" anyhow = "1.0"
serde_json = "1.0.149" serde_json = "1.0.149"
[profile.release] [profile.release]
-6
View File
@@ -1,6 +0,0 @@
# What_That_Claude_DO?
What That Claude Do? (WTCD)
A repository of random things I ask Claude to do for me.
In this case it is creating a screenshot tool
+5 -6
View File
@@ -10,14 +10,13 @@ pub struct Config {
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EffectsConfig { pub struct EffectsConfig {
pub mode: String, pub radius: f32,
pub rounded_corners: bool, pub shadow: bool,
pub corner_radius: f32, pub rounding: bool,
pub drop_shadow: bool, pub shadow_blur: f32,
pub shadow_blur_radius: f32, pub shadow_color: [u8; 4],
pub shadow_offset_x: f32, pub shadow_offset_x: f32,
pub shadow_offset_y: f32, pub shadow_offset_y: f32,
pub shadow_color: [u8; 4],
} }
impl Config { impl Config {
+36 -31
View File
@@ -5,15 +5,15 @@ use tiny_skia::{
}; };
pub fn apply_effects(img: RgbaImage, cfg: &EffectsConfig) -> RgbaImage { pub fn apply_effects(img: RgbaImage, cfg: &EffectsConfig) -> RgbaImage {
let img = if cfg.rounded_corners { let img = if cfg.rounding {
apply_rounded_corners(img, cfg.corner_radius) apply_rounding(img, cfg.radius)
} else { } else {
img img
}; };
if cfg.drop_shadow { if cfg.shadow {
apply_drop_shadow( apply_shadow(
img, img,
cfg.shadow_blur_radius, cfg.shadow_blur,
cfg.shadow_offset_x, cfg.shadow_offset_x,
cfg.shadow_offset_y, cfg.shadow_offset_y,
cfg.shadow_color, 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 (w, h) = img.dimensions();
let mut mask = Pixmap::new(w, h).expect("mask pixmap"); 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); let path = rounded_rect_path(0.0, 0.0, w as f32, h as f32, radius);
@@ -47,16 +47,17 @@ pub fn apply_rounded_corners(img: RgbaImage, radius: f32) -> RgbaImage {
pixmap_to_rgba_image(pixmap) pixmap_to_rgba_image(pixmap)
} }
pub fn apply_drop_shadow( pub fn apply_shadow(
img: RgbaImage, img: RgbaImage,
blur_radius: f32, blur: f32,
offset_x: f32, offset_x: f32,
offset_y: f32, offset_y: f32,
shadow_color: [u8; 4], shadow_color: [u8; 4],
) -> RgbaImage { ) -> RgbaImage {
let (iw, ih) = img.dimensions(); let (iw, ih) = img.dimensions();
let br = blur_radius.ceil() as u32; let br = blur.ceil() as u32;
let spread = br * 2; let bp = 1;
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; let extra_left = spread + (-offset_x).max(0.0).ceil() as u32;
let extra_top = spread + (-offset_y).max(0.0).ceil() as u32; let extra_top = spread + (-offset_y).max(0.0).ceil() as u32;
@@ -87,7 +88,7 @@ pub fn apply_drop_shadow(
tint_pixmap_as_shadow(&mut shadow_pixmap, shadow_color); tint_pixmap_as_shadow(&mut shadow_pixmap, shadow_color);
let shadow_img = pixmap_to_rgba_image(shadow_pixmap); let shadow_img = pixmap_to_rgba_image(shadow_pixmap);
let blurred = box_blur_rgba(&shadow_img, br); let blurred = box_blur_rgba(&shadow_img, br, bp);
let blurred_pixmap = rgba_image_to_pixmap(&blurred); let blurred_pixmap = rgba_image_to_pixmap(&blurred);
let mut canvas = Pixmap::new(canvas_w, canvas_h).expect("canvas pixmap"); let mut canvas = Pixmap::new(canvas_w, canvas_h).expect("canvas pixmap");
@@ -136,6 +137,7 @@ fn rounded_rect_path(x: f32, y: f32, w: f32, h: f32, r: f32) -> Path {
pb.finish().expect("rounded rect path") pb.finish().expect("rounded rect path")
} }
// Shadow pos
fn rgba_image_to_pixmap(img: &RgbaImage) -> Pixmap { fn rgba_image_to_pixmap(img: &RgbaImage) -> Pixmap {
let (w, h) = img.dimensions(); let (w, h) = img.dimensions();
let mut pixmap = Pixmap::new(w, h).expect("pixmap alloc"); let mut pixmap = Pixmap::new(w, h).expect("pixmap alloc");
@@ -154,6 +156,7 @@ fn rgba_image_to_pixmap(img: &RgbaImage) -> Pixmap {
pixmap pixmap
} }
// Shadow
fn pixmap_to_rgba_image(pixmap: Pixmap) -> RgbaImage { fn pixmap_to_rgba_image(pixmap: Pixmap) -> RgbaImage {
let (w, h) = (pixmap.width(), pixmap.height()); let (w, h) = (pixmap.width(), pixmap.height());
let mut out = RgbaImage::new(w, h); let mut out = RgbaImage::new(w, h);
@@ -176,31 +179,16 @@ fn pixmap_to_rgba_image(pixmap: Pixmap) -> RgbaImage {
out out
} }
fn tint_pixmap_as_shadow(pixmap: &mut Pixmap, color: [u8; 4]) { // Shadow blur
let [sr, sg, sb, _] = color; fn box_blur_rgba(img: &RgbaImage, radius: u32, bp: u32) -> RgbaImage {
for px in pixmap.pixels_mut() {
let a = px.alpha();
if a > 0 {
let af = a as f32 / 255.0;
*px = tiny_skia::PremultipliedColorU8::from_rgba(
(sr as f32 * af) as u8,
(sg as f32 * af) as u8,
(sb as f32 * af) as u8,
a,
)
.unwrap_or(tiny_skia::PremultipliedColorU8::TRANSPARENT);
}
}
}
fn box_blur_rgba(img: &RgbaImage, radius: u32) -> RgbaImage {
if radius == 0 { if radius == 0 {
return img.clone(); return img.clone();
} }
let mut buf = sliding_horizontal(img, radius); let mut buf = img.clone();
buf = sliding_vertical(&buf, radius); for _ in 0..bp {
buf = sliding_horizontal(&buf, radius); buf = sliding_horizontal(&buf, radius);
buf = sliding_vertical(&buf, radius); buf = sliding_vertical(&buf, radius);
}
buf buf
} }
@@ -250,6 +238,23 @@ fn sliding_horizontal(img: &RgbaImage, radius: u32) -> RgbaImage {
out out
} }
fn tint_pixmap_as_shadow(pixmap: &mut Pixmap, color: [u8; 4]) {
let [sr, sg, sb, _] = color;
for px in pixmap.pixels_mut() {
let a = px.alpha();
if a > 0 {
let af = a as f32 / 255.0;
*px = tiny_skia::PremultipliedColorU8::from_rgba(
(sr as f32 * af) as u8,
(sg as f32 * af) as u8,
(sb as f32 * af) as u8,
a,
)
.unwrap_or(tiny_skia::PremultipliedColorU8::TRANSPARENT);
}
}
}
fn sliding_vertical(img: &RgbaImage, radius: u32) -> RgbaImage { fn sliding_vertical(img: &RgbaImage, radius: u32) -> RgbaImage {
let (w, h) = img.dimensions(); let (w, h) = img.dimensions();
let r = radius as i32; let r = radius as i32;
+147 -104
View File
@@ -5,17 +5,14 @@ use anyhow::{Context, Result, bail};
use std::io::Write as _; use std::io::Write as _;
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
/// CLI overrides that map 1:1 to `EffectsConfig` fields.
/// All fields are `Option<T>` so we can tell "not supplied" from any concrete value.
#[derive(Default)] #[derive(Default)]
struct CliOverrides { struct CliOverrides {
rounded_corners: Option<bool>, rounding: Option<bool>,
corner_radius: Option<f32>, radius: Option<f32>,
drop_shadow: Option<bool>, shadow: Option<bool>,
shadow_blur_radius: Option<f32>, shadow_blur: Option<f32>,
shadow_offset_x: Option<f32>, shadow_offset_x: Option<f32>,
shadow_offset_y: 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]>, shadow_color: Option<[u8; 4]>,
} }
@@ -30,137 +27,149 @@ fn parse_bool(s: &str) -> Result<bool> {
fn parse_shadow_color(s: &str) -> Result<[u8; 4]> { fn parse_shadow_color(s: &str) -> Result<[u8; 4]> {
let parts: Vec<&str> = s.split(',').collect(); let parts: Vec<&str> = s.split(',').collect();
if parts.len() != 4 { if parts.len() != 4 {
bail!("--shadow_color expects four comma-separated u8 values, e.g. 255,0,0,200"); bail!("--shadow-color expects four comma-separated u8 values, e.g. 255,0,0,200");
} }
let r = parts[0] let r = parts[0]
.trim() .trim()
.parse::<u8>() .parse::<u8>()
.context("shadow_color red channel")?; .context("shadow-color red channel")?;
let g = parts[1] let g = parts[1]
.trim() .trim()
.parse::<u8>() .parse::<u8>()
.context("shadow_color green channel")?; .context("shadow-color green channel")?;
let b = parts[2] let b = parts[2]
.trim() .trim()
.parse::<u8>() .parse::<u8>()
.context("shadow_color blue channel")?; .context("shadow-color blue channel")?;
let a = parts[3] let a = parts[3]
.trim() .trim()
.parse::<u8>() .parse::<u8>()
.context("shadow_color alpha channel")?; .context("shadow-color alpha channel")?;
Ok([r, g, b, a]) Ok([r, g, b, a])
} }
fn main() -> Result<()> { fn extract_image_path() -> Option<String> {
let args: Vec<String> = std::env::args().skip(1).collect();
args.windows(2)
.find(|w| w[0] == "--image")
.map(|w| w[1].clone())
}
fn main() {
// 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<()> {
let args: Vec<String> = std::env::args().skip(1).collect(); let args: Vec<String> = std::env::args().skip(1).collect();
let mut image_path: Option<String> = None; let mut image_path: Option<String> = None;
let mut overrides = CliOverrides::default(); let mut overrides = CliOverrides::default();
let mut scale: Option<f32> = None;
let mut i = 0; let mut i = 0;
while i < args.len() { while i < args.len() {
match args[i].as_str() { match args[i].as_str() {
"--image" => { "--image" => {
i += 1; image_path = Some(next_arg(&args, &mut i, "--image")?);
image_path = Some(
args.get(i)
.cloned()
.context("Expected a path after --image")?,
);
} }
"--rounded_corners" => { "--rounding" => {
i += 1; let val = next_arg(&args, &mut i, "--rounding")?;
let val = args overrides.rounding = Some(parse_bool(&val)?);
.get(i)
.context("Expected true/false after --rounded_corners")?;
overrides.rounded_corners = Some(parse_bool(val)?);
} }
"--corner_radius" => { "--radius" => {
i += 1; let val = next_arg(&args, &mut i, "--radius")?;
let val = args overrides.radius = Some(val.parse::<f32>().context("--radius must be a number")?);
.get(i) }
.context("Expected a number after --corner_radius")?; "--shadow" => {
overrides.corner_radius = Some( let val = next_arg(&args, &mut i, "--shadow")?;
overrides.shadow = Some(parse_bool(&val)?);
}
"--shadow-blur" => {
let val = next_arg(&args, &mut i, "--shadow-blur")?;
overrides.shadow_blur = Some(
val.parse::<f32>() val.parse::<f32>()
.context("--corner_radius must be a number")?, .context("--shadow-blur must be a number")?,
); );
} }
"--drop_shadow" => { "--shadow-offset-x" => {
i += 1; let val = next_arg(&args, &mut i, "--shadow-offset-x")?;
let val = args
.get(i)
.context("Expected true/false after --drop_shadow")?;
overrides.drop_shadow = Some(parse_bool(val)?);
}
"--shadow_blur_radius" => {
i += 1;
let val = args
.get(i)
.context("Expected a number after --shadow_blur_radius")?;
overrides.shadow_blur_radius = Some(
val.parse::<f32>()
.context("--shadow_blur_radius must be a number")?,
);
}
"--shadow_offset_x" => {
i += 1;
let val = args
.get(i)
.context("Expected a number after --shadow_offset_x")?;
overrides.shadow_offset_x = Some( overrides.shadow_offset_x = Some(
val.parse::<f32>() val.parse::<f32>()
.context("--shadow_offset_x must be a number")?, .context("--shadow-offset-x must be a number")?,
); );
} }
"--shadow_offset_y" => { "--shadow-offset-y" => {
i += 1; let val = next_arg(&args, &mut i, "--shadow-offset-y")?;
let val = args
.get(i)
.context("Expected a number after --shadow_offset_y")?;
overrides.shadow_offset_y = Some( overrides.shadow_offset_y = Some(
val.parse::<f32>() val.parse::<f32>()
.context("--shadow_offset_y must be a number")?, .context("--shadow-offset-y must be a number")?,
); );
} }
"--shadow_color" => { "--shadow-color" => {
i += 1; let val = next_arg(&args, &mut i, "--shadow-color")?;
let val = args overrides.shadow_color = Some(parse_shadow_color(&val)?);
.get(i)
.context("Expected r,g,b,a after --shadow_color")?;
overrides.shadow_color = Some(parse_shadow_color(val)?);
} }
unknown => bail!("Unknown argument: {unknown}"), "--scale" => {
let val = next_arg(&args, &mut i, "--scale")?;
scale = Some(val.parse::<f32>().context("--scale must be a number")?);
} }
unknown => {
let unknown_args = unknown.to_string();
println!("Warning: Unknown argument '{}'", unknown);
next_arg(&args, &mut i, &unknown_args)?;
}
}
i += 1; i += 1;
} }
let image_path = image_path.context("Missing --image <path>")?; let image_path = image_path.context("Missing --image <path>")?;
let config = config::Config::load().context("Failed to load config")?; 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 {
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")?;
let shadow_offset_y = overrides
.shadow_offset_y
.context("Missing --shadow-offset-y")?;
let shadow_color = overrides.shadow_color.context("Missing --shadow-color")?;
config::EffectsConfig {
rounding,
radius,
shadow,
shadow_blur,
shadow_offset_x,
shadow_offset_y,
shadow_color,
}
} else {
let config = config::Config::load()?;
config.screenshot
};
let mut effects = config.screenshot; if let Some(scale) = scale.filter(|&s| s != 1.0) {
if effects.mode == "auto" { effects.radius *= scale;
if let Some(v) = overrides.rounded_corners { effects.shadow_blur *= scale;
effects.rounded_corners = v; effects.shadow_offset_x *= scale;
} effects.shadow_offset_y *= scale;
if let Some(v) = overrides.corner_radius {
effects.corner_radius = v;
}
if let Some(v) = overrides.drop_shadow {
effects.drop_shadow = v;
}
if let Some(v) = overrides.shadow_blur_radius {
effects.shadow_blur_radius = v;
}
if let Some(v) = overrides.shadow_offset_x {
effects.shadow_offset_x = v;
}
if let Some(v) = overrides.shadow_offset_y {
effects.shadow_offset_y = v;
}
if let Some(v) = overrides.shadow_color {
effects.shadow_color = v;
}
} }
if let Err(e) = process_image(&image_path, &effects) { if let Err(e) = process_image(&image_path, &effects) {
@@ -170,6 +179,49 @@ fn main() -> Result<()> {
Ok(()) Ok(())
} }
fn next_arg(args: &[String], i: &mut usize, flag: &str) -> Result<String> {
*i += 1;
let val = args
.get(*i)
.context(format!("Expected value after {}", flag))?;
if val.starts_with('-') {
bail!("Expected value after {}, found flag {}", flag, val);
}
Ok(val.clone())
}
fn push_image(path: &str) -> Result<()> {
let img = image::open(path)
.with_context(|| format!("Failed to open image '{path}'"))?
.into_rgba8();
let mut png_bytes: Vec<u8> = Vec::new();
image::DynamicImage::ImageRgba8(img)
.write_to(
&mut std::io::Cursor::new(&mut png_bytes),
image::ImageFormat::Png,
)
.context("Failed to encode processed image as PNG")?;
let mut child = Command::new("swappy")
.args(["-f", "-"])
.stdin(Stdio::piped())
.spawn()
.context("Failed to spawn swappy. Is it installed and in PATH?")?;
// Writes the PNG bytes to swappy's stdin and then closes
if let Some(mut stdin) = child.stdin.take() {
stdin
.write_all(&png_bytes)
.context("Failed to write image data to swappy")?;
}
Ok(())
}
fn process_image(path: &str, effects: &config::EffectsConfig) -> Result<()> { fn process_image(path: &str, effects: &config::EffectsConfig) -> Result<()> {
let img = image::open(path) let img = image::open(path)
.with_context(|| format!("Failed to open image '{path}'"))? .with_context(|| format!("Failed to open image '{path}'"))?
@@ -191,20 +243,11 @@ fn process_image(path: &str, effects: &config::EffectsConfig) -> Result<()> {
.spawn() .spawn()
.context("Failed to spawn swappy. Is it installed and in PATH?")?; .context("Failed to spawn swappy. Is it installed and in PATH?")?;
child // Writes the PNG bytes to swappy's stdin and then closes
.stdin if let Some(mut stdin) = child.stdin.take() {
.take() stdin
.context("Failed to get swappy stdin")?
.write_all(&png_bytes) .write_all(&png_bytes)
.context("Failed to write image data to swappy")?; .context("Failed to write image data to swappy")?;
let status = child.wait().context("Failed to wait for swappy")?;
if !status.success() {
eprintln!(
"swappy exited with non-zero status for '{}': {}",
path, status
);
} }
Ok(()) Ok(())