244 Commits
Author SHA1 Message Date
zach e6f7742bd8 Merge pull request 'updated components' (#122) from feat/updated-components into main
Reviewed-on: #122
Reviewed-by: AramJonghu <2+aramjonghu@noreply.git.zach-dev.cc>
2026-06-23 11:33:20 +02:00
zach a37f6075d1 GPU plugin
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 29s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m8s
2026-06-22 23:13:18 +02:00
zach 80d5f13663 cursor crosshair + outline of pen width
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 14s
Python / test (pull_request) Successful in 32s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m7s
2026-06-22 11:52:11 +02:00
zach 2f3f41cedd fill icons when relevant
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 16s
Python / test (pull_request) Successful in 30s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m5s
2026-06-22 09:27:13 +02:00
zach faad2dbd09 sidebar toggle buttons
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 15s
Python / test (pull_request) Successful in 31s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m7s
2026-06-21 22:31:10 +02:00
zach 164b48ab6a better spacing of action buttons in notification popups
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 20s
Python / test (pull_request) Successful in 28s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m14s
2026-06-21 17:44:58 +02:00
zach e55d4aa36b clear and exit buttons on drawing popout + pin button
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 29s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m11s
2026-06-21 16:14:36 +02:00
zach 91b4466773 early return in hover handler if drawing
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 17s
Python / test (pull_request) Successful in 28s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m5s
2026-06-20 20:57:41 +02:00
zach 7be81e584e GPU caching for better performance after many strokes
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 31s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m6s
2026-06-20 20:55:15 +02:00
zach 8a87f86758 apply curve smoothing to drawn lines
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 17s
Python / test (pull_request) Successful in 29s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m10s
2026-06-20 20:09:03 +02:00
zach e619632077 show hover dot instead of cursor while drawing
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 28s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m11s
2026-06-20 19:00:58 +02:00
zach 81eb56e1d0 fixes for drawing
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 9s
Python / lint-format (pull_request) Successful in 14s
Python / test (pull_request) Successful in 29s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m8s
2026-06-20 14:23:15 +02:00
zach a1c148bf70 current color rectangle centered in arc
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 14s
Python / test (pull_request) Successful in 27s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m8s
2026-06-20 13:37:00 +02:00
zach fc9be754fd preset color buttons rework + curve smoothing
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 9s
Python / lint-format (pull_request) Successful in 17s
Python / test (pull_request) Successful in 30s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m6s
2026-06-20 13:24:47 +02:00
zach ca1243f9c7 disable interactible elements when drawing
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 32s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m28s
2026-06-19 23:21:01 +02:00
zach 697058146e notif actions rework
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 9s
Python / lint-format (pull_request) Successful in 17s
Python / test (pull_request) Successful in 29s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m25s
2026-06-19 22:50:52 +02:00
zach d516dd8be4 fix text input
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 17s
Python / test (pull_request) Successful in 30s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m15s
2026-06-19 14:06:31 +02:00
zach b17bc61d80 remove logging
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 15s
Python / test (pull_request) Successful in 30s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m9s
2026-06-18 22:26:52 +02:00
zach e37332fe6b add right click clear
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 18s
Python / test (pull_request) Successful in 28s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m14s
2026-06-18 20:36:28 +02:00
zach c102d0e38f drawing reworked into c++ plugin, moved input handler to Interactions.qml as a PointHandler
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 16s
Python / test (pull_request) Successful in 31s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m17s
2026-06-18 13:48:58 +02:00
zach fbadcbb717 sidebar doesn't reduce height when new notifs come in
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 28s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m14s
2026-06-17 21:49:52 +02:00
zach 2cb4d9b739 revert sensors process removal
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 32s
Python / lint-format (pull_request) Successful in 38s
Python / test (pull_request) Successful in 49s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m24s
2026-06-17 18:53:23 +02:00
zach a98a7944b9 show notification in fullscreen
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 18s
Python / test (pull_request) Successful in 27s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m11s
2026-06-17 18:44:50 +02:00
zach 5fb137699f visualizer wave graph instead of lines
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 24s
Python / test (pull_request) Successful in 28s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m13s
2026-06-17 13:19:37 +02:00
zach 866373be48 remove accidental log files
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 21s
Python / test (pull_request) Successful in 29s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m7s
2026-06-17 12:02:22 +02:00
zach b034400df9 hacky english month and day names while using system locale
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 17s
Python / test (pull_request) Successful in 29s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m8s
2026-06-17 11:59:29 +02:00
zach c0b62dfb6c rounded sparkline graph, fixed network usage accumulation spikes
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 9s
Python / lint-format (pull_request) Successful in 19s
Python / test (pull_request) Successful in 29s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m6s
2026-06-16 13:52:43 +02:00
zach ea82b8cf22 calendar month change animation
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 9s
Python / lint-format (pull_request) Successful in 18s
Python / test (pull_request) Successful in 31s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m5s
2026-06-16 10:55:30 +02:00
zach be8b7ae436 config option for min brightness + clipboard config options now in settings
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 9s
Python / lint-format (pull_request) Successful in 16s
Python / test (pull_request) Successful in 31s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m5s
2026-06-16 10:15:57 +02:00
zach e3d3daf807 fix function call in wallpaper cropper
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 9s
Python / lint-format (pull_request) Successful in 18s
Python / test (pull_request) Successful in 27s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m8s
2026-06-16 09:46:36 +02:00
zach 0057830fbc resource popout spacing and larger percent text size
Python / lint-format (pull_request) Successful in 30s
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 52s
Python / test (pull_request) Successful in 1m26s
Lint & Format (Rust) / lint-format (pull_request) Successful in 2m20s
2026-06-15 19:27:16 +02:00
zach eafffdcc8f fix slider inset icons initial position
Python / lint-format (pull_request) Failing after 2m25s
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 3m14s
Python / test (pull_request) Successful in 3m55s
Lint & Format (Rust) / lint-format (pull_request) Successful in 4m49s
2026-06-15 19:20:02 +02:00
zach 578a10c950 clipboard preview leading tab trimming while preserving indentation + proper width calculations
Python / lint-format (pull_request) Successful in 27s
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 30s
Python / test (pull_request) Successful in 32s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m6s
2026-06-15 18:23:56 +02:00
zach 2034f3a8db fixed applying crop being overwritten by multiple write calls, added wallpaper fade anim
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 14s
Python / test (pull_request) Successful in 31s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m6s
2026-06-15 15:52:55 +02:00
zach 7e38f3f428 slight changes to clipboard history preview and item delegate
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 16s
Python / test (pull_request) Successful in 32s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m10s
2026-06-14 23:33:12 +02:00
zach 2a50732bc2 resources popout refresh + new components & reskinned old components
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 23s
Python / test (pull_request) Successful in 31s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m12s
2026-06-14 23:09:10 +02:00
AramJonghu 4cb2d843a5 Merge branch 'main' into feat/updated-components
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 15s
Python / test (pull_request) Successful in 31s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m7s
2026-06-14 22:01:36 +02:00
zach 7cf6e5f8a5 Merge pull request '95: Fix light/dark mode mismatch causes preset to not load' (#124) from 95-fix-light-mode-scheme-mismatch into main
Reviewed-on: #124
2026-06-14 21:27:27 +02:00
zach eb99237a31 remove accidental material symbols font from assets
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 29s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m8s
2026-06-14 12:49:04 +02:00
zach 3f5cc2de07 MaterialIcon opsz clamp, UPower popout and widget changes, improved SysTray hover detection logic
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 13s
Python / lint-format (pull_request) Successful in 18s
Python / test (pull_request) Successful in 32s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m13s
2026-06-14 12:47:20 +02:00
AramJonghu f31a1e224b Not using direct write, but tmp write to avoid JSON parse error
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 31s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m9s
2026-06-14 01:08:36 +02:00
AramJonghu fecf8a886f added auto-resolve preset mode (light/dark mode conflict) 2026-06-14 00:56:42 +02:00
zach 6034c4816d clipboard preview with syntax support
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 16s
Python / test (pull_request) Successful in 28s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m6s
2026-06-13 20:11:58 +02:00
zach 7756e46355 fix scrollbar mouse pointer change not being accurate to handle position
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 31s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m12s
2026-06-13 13:14:58 +02:00
zach 39895bf75b fix scrollbar visible even when contentHeight < height
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 9s
Python / lint-format (pull_request) Successful in 22s
Python / test (pull_request) Successful in 29s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m8s
2026-06-13 13:12:15 +02:00
zach fea468dd0c remove logging
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 32s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m10s
2026-06-13 13:08:36 +02:00
zach f1dbff1208 scrollbars QOL updates
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 17s
Python / test (pull_request) Successful in 29s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m10s
2026-06-13 12:56:53 +02:00
zach 4b7ba30272 clipboard preview with image support
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 17s
Python / test (pull_request) Successful in 30s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m6s
2026-06-13 01:44:12 +02:00
Inorishio d1085b749d category flickable addition content.height
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 15s
Python / test (pull_request) Successful in 30s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m7s
2026-06-12 14:50:16 +02:00
zach e29763134e clipboard config options + fade text and keyboard input handling
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 36s
Python / lint-format (pull_request) Successful in 35s
Python / test (pull_request) Successful in 58s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m46s
2026-06-11 20:02:38 +02:00
zach 130e613eb5 clipboard history using cliphist
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 15s
Python / test (pull_request) Successful in 29s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m6s
2026-06-11 14:35:42 +02:00
zach e90f1facb7 default apps config entries, revert focus property on root shell
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 28s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m10s
2026-06-10 19:29:20 +02:00
zach 6b77ebd9be more efficient desktop icons
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 9s
Python / lint-format (pull_request) Successful in 16s
Python / test (pull_request) Successful in 30s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m6s
2026-06-10 14:40:39 +02:00
zach 01556e66f3 settings window pages replace anims
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 35s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m22s
2026-06-10 00:48:17 +02:00
zach dbccc14f9a settings window category navrail changes and padding decreases
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 28s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m8s
2026-06-09 13:09:31 +02:00
zach 6bab341190 fix splitbutton menu popout region for clicking outside panel bounds
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 9s
Python / lint-format (pull_request) Successful in 17s
Python / test (pull_request) Successful in 28s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m8s
2026-06-08 23:35:47 +02:00
zach 8ec7faaafd fix statelayer colors for various components, add logic for color background feedback on tray items when icon doesn't have color layer
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 16s
Python / test (pull_request) Successful in 27s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m5s
2026-06-08 18:35:34 +02:00
zach ea911996a8 fix text wrapping on time input components
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 34s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m21s
2026-06-08 18:17:13 +02:00
zach c0202ef015 fix text set on escape, add focus shift with tab and shift + tab
Python / lint-format (pull_request) Successful in 15s
Python / test (pull_request) Successful in 28s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m6s
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 9s
2026-06-08 15:28:28 +02:00
zach 345d558317 fix start/end text for hyprsunset
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 9s
Python / lint-format (pull_request) Successful in 13s
Python / test (pull_request) Successful in 30s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-06-08 14:57:21 +02:00
zach 7df5af332c fix bool switch on timeinput
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 27s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m8s
2026-06-08 14:53:58 +02:00
zach 52980e4d84 new time input picker, added support for minutes in timeframe
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 16s
Python / test (pull_request) Successful in 27s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m10s
2026-06-08 14:51:46 +02:00
zach fe91a65324 wallpaper cropper slider inset icon, fixed disabled splitbutton state
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 17s
Python / test (pull_request) Successful in 28s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m8s
2026-06-08 10:03:26 +02:00
zach 628c4b32a5 audio popout changes, slider inset icons added
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 9s
Python / lint-format (pull_request) Successful in 15s
Python / test (pull_request) Successful in 27s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m1s
2026-06-08 01:22:37 +02:00
zach 1f9630ed76 port new components to greeter
Python / lint-format (pull_request) Successful in 21s
Python / test (pull_request) Successful in 50s
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Lint & Format (Rust) / lint-format (pull_request) Successful in 42s
2026-06-07 12:55:32 +02:00
zach 04fb534a05 scroll deceleration env
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 20s
Python / test (pull_request) Successful in 48s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m8s
2026-06-06 21:21:32 +02:00
zach 50e99501de switches and popouts
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 25s
Python / test (pull_request) Successful in 51s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m11s
2026-06-06 21:13:44 +02:00
zach 65c56bc598 updated components
Python / lint-format (pull_request) Successful in 41s
Python / test (pull_request) Successful in 1m9s
Lint & Format (Rust) / lint-format (pull_request) Successful in 2m42s
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 20s
2026-06-06 00:49:19 +02:00
zach 82518006c3 revert toaster enum 2026-06-05 19:45:43 +02:00
zach 9cefdf509c cmake build hotfix 2026-06-05 12:31:37 +02:00
zach 27924aca37 disable incompatible-type for qmllint in clock 2026-06-04 23:22:28 +02:00
zach b4716d25c0 nodiscard 2026-06-04 22:57:45 +02:00
zach d8f047dbc9 nodiscard 2026-06-04 22:38:17 +02:00
zach 91b50b312d open launcher with gesture from bottom border 2026-06-04 19:01:02 +02:00
zach 3e933a8b78 remove logging 2026-06-04 18:56:42 +02:00
zach e127928126 Merge pull request 'Rewrite the manager responsible for handling automatic hyprsunset temperature activation as a qml plugin' (#117) from hyprsunset-manager-rewrite into main
Reviewed-on: #117
2026-06-04 15:05:45 +02:00
zach 94f2cf076c fix applying end() on init
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 23s
Python / test (pull_request) Successful in 49s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m3s
2026-06-04 15:05:18 +02:00
zach 9168b6e893 Merge branch 'main' into hyprsunset-manager-rewrite
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 24s
Python / test (pull_request) Successful in 42s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m3s
2026-06-04 14:56:53 +02:00
zach a477fb2e22 fix namespaces and logging names 2026-06-04 14:56:20 +02:00
zach 6586cc2788 do not use apply() in initializer
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 17s
Python / test (pull_request) Successful in 48s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-06-03 23:21:07 +02:00
zach 0be98a64ac Merge branch 'main' into hyprsunset-manager-rewrite
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 15s
Python / lint-format (pull_request) Successful in 23s
Python / test (pull_request) Successful in 54s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-06-03 18:02:27 +02:00
zach b6e7ee7a54 Merge pull request 'lid behavior watcher to lock session' (#115) from lid-switch-behavior into main
Reviewed-on: #115
2026-06-03 18:02:21 +02:00
AramJonghu 59789ab8d3 unused imports in shell.qml
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 21s
Python / test (pull_request) Successful in 51s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-06-03 18:01:10 +02:00
AramJonghu a128c0fa40 removal reduntant config option and settings, unused lines in Lock.qml
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 21s
Python / test (pull_request) Successful in 52s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-06-03 17:59:03 +02:00
AramJonghu 6f856e2162 fix typo
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 18s
Python / test (pull_request) Successful in 44s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m5s
2026-06-03 17:47:12 +02:00
AramJonghu a19701222b forgotton import and removal LidService
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 26s
Python / test (pull_request) Successful in 41s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-06-03 17:43:58 +02:00
AramJonghu 0d8f558f66 Took caelestia lid logic (prepare to sleep) instead
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 19s
Python / test (pull_request) Successful in 45s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m3s
2026-06-03 17:41:29 +02:00
AramJonghu ed28d8b56a Took caelestia lid logic (prepare to sleep) instead 2026-06-03 17:41:17 +02:00
AramJonghu deef85d10c troubleshooting dbus
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 15s
Python / lint-format (pull_request) Successful in 19s
Python / test (pull_request) Successful in 49s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m5s
2026-06-03 17:02:46 +02:00
AramJonghu f9ab1e2a10 inability to connect to DBus.Properties resolved
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 13s
Python / lint-format (pull_request) Successful in 20s
Python / test (pull_request) Successful in 51s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m3s
2026-06-03 16:51:15 +02:00
AramJonghu 7dbce0bf8c qmlls lied to me - import ZShell returned
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 23s
Python / test (pull_request) Successful in 45s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-06-03 16:29:11 +02:00
AramJonghu 2920c57163 removes direct extern access, receives signal properly now, removed declaration unused method
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 14s
Python / lint-format (pull_request) Successful in 21s
Python / test (pull_request) Successful in 44s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m6s
2026-06-03 16:27:31 +02:00
AramJonghu 0584cd618e missing changes
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 31s
Python / test (pull_request) Successful in 50s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m3s
2026-06-03 02:30:13 +02:00
AramJonghu 896e5e520d Added clang-format/tidy for additional rules. Adjusted inbranch cpp files.
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 15s
Python / lint-format (pull_request) Successful in 37s
Python / test (pull_request) Successful in 59s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m20s
2026-06-03 02:05:18 +02:00
AramJonghu 45f36ce71c added lock. in shell.qml, wrong scope
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 20s
Python / test (pull_request) Successful in 47s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m5s
2026-06-03 01:14:37 +02:00
AramJonghu 3bcdbbabbb build fix
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 16s
Python / lint-format (pull_request) Successful in 23s
Python / test (pull_request) Successful in 48s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m9s
2026-06-03 01:02:50 +02:00
AramJonghu 016dcc008f Cpp changes, minor refactor plus separate signal logic, typo.
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 26s
Python / test (pull_request) Successful in 1m3s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m9s
- typo in searchindex
- Cpp plugin changed to use enum lidstate
- minor refactor
- Using signals instead of direct property access.
- Using states instead
- checking lidclosed instead of preparetosleep
2026-06-03 00:54:44 +02:00
zach d246ba1800 rewrite the manager responsible for handling automatic hyprsunset temperature activation as a qml plugin
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 13s
Python / lint-format (pull_request) Successful in 19s
Python / test (pull_request) Successful in 50s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m2s
2026-06-02 16:09:48 +02:00
AramJonghu c91b53fbaa QTQuick
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 24s
Python / test (pull_request) Successful in 50s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m7s
2026-06-02 00:03:30 +02:00
AramJonghu ad2ee99d9c format using qmlformat
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 20s
Python / test (pull_request) Successful in 51s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m5s
2026-06-01 23:59:07 +02:00
AramJonghu 7da2c7827a refactor: using more oop
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 14s
Python / lint-format (pull_request) Successful in 20s
Python / test (pull_request) Successful in 52s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m6s
2026-06-01 23:53:26 +02:00
AramJonghu db9c98b322 set default enabled for laptops, disabled for non battery devices
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 15s
Python / lint-format (pull_request) Successful in 22s
Python / test (pull_request) Successful in 46s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m5s
2026-06-01 21:03:59 +02:00
AramJonghu 1c1c6275df move lid watch toggle into lock settings 2026-06-01 20:45:04 +02:00
AramJonghu 83cf008a19 setting option to disable lidwatcher, also in settingswindow 2026-06-01 20:28:55 +02:00
AramJonghu c514e98687 nonexistant locked should be resolved 2026-06-01 20:12:17 +02:00
AramJonghu 0d097524c3 shell.qml could not load Connections, now fixed 2026-06-01 20:07:09 +02:00
AramJonghu 7de8cc3104 lidwatcher plugin initial setup 2026-06-01 20:02:37 +02:00
AramJonghu 82aa7c415f initial commit 2026-06-01 19:50:40 +02:00
zach 807b2525b7 Merge pull request 'initial refactor of Interactions.qml to add better support for touch screen gestures' (#114) from feat/improved-gestures into main
Reviewed-on: #114
Reviewed-by: AramJonghu <2+aramjonghu@noreply.git.zach-dev.cc>
Reviewed-by: Inorishio <inorishio@gmail.com>
2026-06-01 19:18:53 +02:00
zach 9e3cad6dbd Merge branch 'main' into feat/improved-gestures
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 13s
Python / lint-format (pull_request) Successful in 25s
Python / test (pull_request) Successful in 51s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m3s
2026-06-01 19:01:12 +02:00
zach 9436acd626 change charging color 2026-06-01 17:12:28 +02:00
AramJonghu 17e600e78e Merge branch 'main' into feat/improved-gestures
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 18s
Python / test (pull_request) Successful in 47s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m6s
2026-06-01 15:05:04 +02:00
zach 7eba84c8be Better battery indicator and dedicated Battery singleton in helpers 2026-06-01 15:04:48 +02:00
zach 0820c8e023 Merge branch 'main' into feat/improved-gestures
Python / lint-format (pull_request) Successful in 22s
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 27s
Python / test (pull_request) Successful in 50s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m2s
2026-06-01 12:13:33 +02:00
zach ebedf4b6fe increase slider visibility and remove debug logging 2026-06-01 12:12:00 +02:00
zach 6926880074 remove multi-touch handlers, single point for sidebar
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 21s
Python / test (pull_request) Successful in 50s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m1s
2026-05-31 23:05:31 +02:00
zach 1e1c90a0c5 remove redundant logging
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 22s
Python / test (pull_request) Successful in 43s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m2s
2026-05-31 22:45:32 +02:00
zach db6051457f fix drag gestures firing more than once from one drag
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 20s
Python / test (pull_request) Successful in 47s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m2s
2026-05-31 22:30:20 +02:00
zach 8f381fa8f0 Merge branch 'main' into feat/improved-gestures
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 14s
Python / lint-format (pull_request) Successful in 20s
Python / test (pull_request) Successful in 51s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-05-31 22:08:09 +02:00
zach 2a7cd66f40 Actually fix background desync, blobs didn't follow final settle after deform shader 2026-05-31 19:06:17 +02:00
zach cf22b41f07 allow pointer takeover by anything, restrict takeover from items
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 23s
Python / test (pull_request) Successful in 41s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m2s
2026-05-31 14:32:39 +02:00
zach c267dfb0c2 remove leftover test timer and import
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 19s
Python / test (pull_request) Successful in 58s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-05-31 14:16:36 +02:00
zach e06fd71f11 initial refactor of Interactions.qml to add better support for touch screen gestures
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 19s
Python / test (pull_request) Successful in 51s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m5s
2026-05-31 13:58:21 +02:00
zach d041ce2471 Merge pull request 'zshell-img-tools' (#104) from zshell-img-tools into main
Reviewed-on: #104
2026-05-31 00:30:12 +02:00
Inorishio 7f3397e27c changes comments
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 19s
Python / test (pull_request) Successful in 42s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-05-31 00:23:13 +02:00
Inorishio 0d7c5d199a Update zshell-img-tools/src/main.rs
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 19s
Python / test (pull_request) Successful in 47s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m2s
Lint had issues with --help so for now I'm just removing it.
2026-05-30 20:59:30 +02:00
Inorishio 8ea295d1ce Some prework for the binary, linter issue is resolved, unnecessary comments removed
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 19s
Python / test (pull_request) Successful in 50s
Lint & Format (Rust) / lint-format (pull_request) Failing after 49s
2026-05-30 20:40:14 +02:00
zach 164c0a18c2 disable screenshot options in gui when disabling effects
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 17s
Python / lint-format (pull_request) Successful in 27s
Python / test (pull_request) Successful in 52s
Lint & Format (Rust) / lint-format (pull_request) Failing after 1m7s
2026-05-29 23:44:36 +02:00
zach 1d0fc63177 Merge branch 'main' into zshell-img-tools
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 33s
Python / test (pull_request) Successful in 44s
Lint & Format (Rust) / lint-format (pull_request) Failing after 1m9s
2026-05-29 23:25:07 +02:00
zach 79998a36f0 Merge pull request 'either fail ? then check fail else pass' (#112) from format-check-rust-fix into main
Reviewed-on: #112
Reviewed-by: zach <zach@brohn.se>
2026-05-29 23:24:49 +02:00
AramJonghu a747f21af5 Merge branch 'main' into format-check-rust-fix
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 23s
Python / lint-format (pull_request) Successful in 23s
Python / test (pull_request) Successful in 43s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m43s
2026-05-29 20:14:06 +02:00
AramJonghu 21c6940fb1 either fail ? then check fail else pass
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Lint & Format (Rust) / lint-format (pull_request) Has been cancelled
Python / lint-format (pull_request) Successful in 29s
Python / test (pull_request) Successful in 45s
2026-05-29 20:12:36 +02:00
zach c4cca73fd9 Merge branch 'main' into zshell-img-tools
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 24s
Python / test (pull_request) Successful in 45s
Lint & Format (Rust) / lint-format (pull_request) Successful in 50s
2026-05-29 12:28:13 +02:00
zach d6102d9ebe fix battery popup thresholds firing more than once per charging period 2026-05-29 12:25:28 +02:00
zach 84db7f41af Merge branch 'main' into zshell-img-tools
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 32s
Python / test (pull_request) Successful in 47s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m3s
2026-05-29 11:50:27 +02:00
zach 0819e8e2d1 Merge pull request 'format check shows green check, not anymore when format shows fail' (#110) from format-check-rust-fix into main
Reviewed-on: #110
Reviewed-by: zach <zach@brohn.se>
2026-05-29 11:50:10 +02:00
AramJonghu 0c47ba805f format check shows green check, not anymore when format shows fail
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 21s
Python / test (pull_request) Successful in 51s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m43s
2026-05-29 00:19:25 +02:00
zach 84ecd1481b Merge branch 'main' into zshell-img-tools
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 25s
Python / test (pull_request) Successful in 50s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m8s
2026-05-28 23:24:26 +02:00
zach 428809fee8 Merge pull request 'hotfix zshell-cli autocompletion failing' (#108) from hotfix-zshell-autocompletion into main
Reviewed-on: #108
Reviewed-by: zach <zach@brohn.se>
2026-05-28 23:24:17 +02:00
zach 1d667bedfd incorrect color channel assignment fixed
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 17s
Python / lint-format (pull_request) Successful in 24s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m11s
Python / test (pull_request) Successful in 1m16s
2026-05-28 23:19:57 +02:00
zach 760b8bfb93 pass hyprland options when auto is enabled
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 21s
Python / test (pull_request) Successful in 53s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m4s
2026-05-28 23:16:04 +02:00
AramJonghu 52be099914 removed exception in favor of sys.exit() to exit silently with print
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 20s
Python / test (pull_request) Successful in 44s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m46s
2026-05-28 22:43:25 +02:00
Inorishio 1d0ee72498 Merge branch 'zshell-img-tools' of git.zach-dev.cc:zach/z-bar-qt into zshell-img-tools
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 15s
Python / lint-format (pull_request) Successful in 19s
Python / test (pull_request) Successful in 43s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m3s
2026-05-28 20:23:38 +02:00
Inorishio d0a3a0a269 updated 2026-05-28 20:23:28 +02:00
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 494653e029 Merge branch 'main' into hotfix-zshell-autocompletion
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 18s
Python / test (pull_request) Successful in 1m20s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m57s
2026-05-28 16:55:42 +02:00
zach d4a53b06e0 remove obsolete background files 2026-05-28 16:53:27 +02:00
zach 6d0813089a fix launcher height calculations 2026-05-28 16:47:19 +02:00
zach 4005e197eb better wallpaper cropping on load, cache images to disk and fix image aspect ratio from creating black bars 2026-05-28 14:40:47 +02:00
AramJonghu e9aa8268be ctx is unused
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 13s
Python / lint-format (pull_request) Successful in 19s
Python / test (pull_request) Successful in 47s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m46s
2026-05-28 14:25:43 +02:00
AramJonghu 0ad28ac017 now a check if cli completion is installed 2026-05-28 14:08:02 +02:00
AramJonghu fda3712855 attempt hotfix 2026-05-28 13:49:34 +02:00
zach ef1bcf6c73 new config option to set tray icon base size 2026-05-28 11:48:23 +02:00
zach ba67e56fda properly load/unload settings 2026-05-28 02:12:11 +02:00
zach f22c08991c revert notification icon oopsie 2026-05-28 02:03:39 +02:00
zach 8323bc31a0 properly handle disabling popouts 2026-05-28 01:10:00 +02:00
zach fa87789fcd remove logging 2026-05-28 00:54:39 +02:00
zach 6209264744 use loader for updates popout 2026-05-28 00:53:49 +02:00
zach 8e14993633 debug logging of battery percent props 2026-05-27 23:22:09 +02:00
zach 36fb925495 fix gpu name in resource popout 2026-05-27 23:14:02 +02:00
zach 1a72757e41 more tray icon replacements 2026-05-27 22:51:03 +02:00
zach 90e0987f22 load icon-theme versions of tray icons for some apps 2026-05-27 14:19:13 +02:00
zach afa3b0e3c4 cache icons based on pixel content instead of image string 2026-05-27 13:46:15 +02:00
zach 41c9d9e9b4 avoid unnecessary JS bindings by using narrower conditions, remove logging 2026-05-27 13:05:45 +02:00
zach d92e5b4cd7 fix drawing input anchoring incorrectly when using loader 2026-05-27 12:03:06 +02:00
zach 3963a48a9d load higher resolution tray icons for high-dpi screens 2026-05-27 11:43:42 +02:00
zach bd576e17dc Revert to OpenGL 2026-05-27 09:28:35 +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
zach ae2a349247 optimize notification icon caching by copying image rather than item 2026-05-26 22:52:54 +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 e33901b23c Merge pull request 'hotfix zshell-cli shell show had no output' (#103) from 100-cli-autocompletion into main
Reviewed-on: #103
2026-05-26 18:50:27 +02:00
AramJonghu 7d4f563b43 replaced click
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 25s
Python / lint-format (pull_request) Successful in 33s
Python / test (pull_request) Successful in 48s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m49s
2026-05-26 18:47:56 +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
AramJonghu 439aa9ed1e fix ci/cl for python testing. Added click dep
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 22s
Python / test (pull_request) Failing after 52s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m46s
2026-05-26 18:36:07 +02:00
AramJonghu 697de725fb change test to expect stdout
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 19s
Python / test (pull_request) Failing after 48s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m47s
2026-05-26 18:31:07 +02:00
AramJonghu f8a10698ea Merge branch 'main' into 100-cli-autocompletion
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 17s
Python / lint-format (pull_request) Successful in 29s
Python / test (pull_request) Failing after 1m5s
Lint & Format (Rust) / lint-format (pull_request) Successful in 3m1s
2026-05-26 18:26:57 +02:00
AramJonghu e5936aa730 hotfix zshell-cli shell show had no output
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 23s
Python / test (pull_request) Failing after 54s
Lint & Format (Rust) / lint-format (pull_request) Successful in 3m19s
2026-05-26 18:25:15 +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
zach a2505ee875 add low battery toast, unload if not laptop battery. 2026-05-26 15:57:40 +02:00
zach f475e43c54 Merge pull request '100 shell autocomplete, type fixes, Pillow deprecation cleanup' (#101) from 100-cli-autocompletion into main
Reviewed-on: #101
Reviewed-by: zach <zach@brohn.se>
2026-05-26 13:10:56 +02:00
zach c33d6ae2dd Merge branch 'main' into 100-cli-autocompletion
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 49s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m46s
2026-05-26 13:05:10 +02:00
zach ca19a60e5c temporary fix for focus being stolen even after release. Change to async loaders 2026-05-26 13:03:46 +02:00
Inorishio 6aedf6f8b7 scale fix 2026-05-26 11:41:31 +02:00
AramJonghu 233ea3efb9 accidental duplicate logic removed
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 48s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m45s
2026-05-26 09:30:58 +02:00
AramJonghu d19eead1f5 autocomplete now optional. Includes hint on first command input
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 57s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m46s
2026-05-26 09:25:06 +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
AramJonghu d0b2a5fc1d adding hint if is ran without -- flag
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 45s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m47s
2026-05-25 19:19:55 +02:00
AramJonghu 32acfa6b9f pyright/ruff error fixes. Autoinstall check of autocomplete 2026-05-25 19:03:00 +02:00
AramJonghu 17fcf1a02c pyright error fixes. added autocomplete to some commands 2026-05-25 18:42:34 +02:00
AramJonghu 1c11549811 initial commit 2026-05-25 17:45:39 +02:00
zach 06ebc4ffbf remove irrelevant settings options plus bugfixes 2026-05-25 11:51:16 +02:00
zach f2f9fa1302 fix opening links on non-uwsm and disable file watcher when shell is installed 2026-05-25 11:01:01 +02:00
zach b4020438f9 Merge pull request 'Tune transparency scale impact on luminance scaling' (#98) from fix/transparency-luminance-scaling into main
Reviewed-on: #98
Reviewed-by: AramJonghu <2+aramjonghu@noreply.git.zach-dev.cc>
2026-05-24 22:37:03 +02:00
zach 184ab20d11 Merge branch 'main' into fix/transparency-luminance-scaling
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 42s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m45s
2026-05-24 22:36:52 +02:00
zach 5097e30a77 fix anchors used in Layouts 2026-05-24 19:33:06 +02:00
zach ef71ae8afd button to install colorscheme and wallpaper to greeter in settings 2026-05-24 19:28:02 +02:00
AramJonghu 6533533936 Merge branch 'main' into fix/transparency-luminance-scaling
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 51s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m47s
2026-05-24 18:53:39 +02:00
zach 9688072e93 record.py 2026-05-24 18:48:03 +02:00
AramJonghu 9c36f0de5b pycache in cache removal 2026-05-24 18:28:32 +02:00
zach 9ca46967d9 Merge pull request 'hotfix(cli): replace raw subprocess tracebacks with styled error messages and fix restart race condition' (#96) from hotfix-restart-race-condition into main
Reviewed-on: #96
Reviewed-by: zach <zach@brohn.se>
2026-05-24 18:23:43 +02:00
zach 16e84ca998 fixed region selection for recording, plus cache file cleanup
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Lint & Format (Python) / lint-format (pull_request) Successful in 18s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m34s
2026-05-24 18:21:37 +02:00
AramJonghu c30128cf95 check every 50ms -> 250ms for restart
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 30s
Python / test (pull_request) Successful in 46s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m44s
2026-05-24 18:03:32 +02:00
zach ba9926af18 Increased floor and decreased ceiling of offset for brightening darker colors in dark mode/darkening brighter colors in light mode
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 13s
Python / lint-format (pull_request) Successful in 28s
Python / test (pull_request) Successful in 43s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m45s
2026-05-24 11:40:44 +02:00
AramJonghu 78fcf33b3a refactor(cli): clean shell start/restart, drop redundant ipc check
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 48s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m44s
2026-05-24 03:09:19 +02:00
AramJonghu 5e9b373405 tests did not match changed code logic
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 1m46s
2026-05-23 20:54:35 +02:00
AramJonghu b49165e7ea minor typer adjustments to use typer in error/exception throws
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 14s
Python / lint-format (pull_request) Successful in 34s
Python / test (pull_request) Failing after 53s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m48s
2026-05-23 20:48:51 +02:00
AramJonghu d0cda51639 wait for instance to fully terminate before restart 2026-05-23 20:31:48 +02:00
zach ad57764636 Merge pull request '#73 dynamic color scheme presets from .txt palettes + shell restart command' (#92) from 73-colorscheme-options into main
Reviewed-on: #92
2026-05-23 20:15:30 +02:00
zach 96afbdb30b Settings UI for color scheme presets
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 25s
Python / test (pull_request) Successful in 53s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m52s
2026-05-23 20:14:12 +02:00
AramJonghu 5df46160f6 documentation in README.md added
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 50s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m50s
2026-05-23 17:53:16 +02:00
AramJonghu d118c02e75 README: document scheme CLI and shell subcommands 2026-05-23 17:52:06 +02:00
AramJonghu 21ed178bbc scheme: add --json flag to list-presets, --accent flag, drop :accent from preset spec
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 44s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m48s
- list-presets --json outputs structured JSON with variants,
  modes, accents, and default_accent for accent-aware schemes
- --accent flag replaces :accent shorthand in preset string
- Validate --accent against variant's available accents
- resolve_preset returns tuple[str, str] (scheme + variant only)
- Update tests for new signature
2026-05-23 17:42:58 +02:00
AramJonghu 2934d863ca --accent removed. Accidental inclusion
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Python / lint-format (pull_request) Successful in 22s
Python / test (pull_request) Successful in 46s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m46s
2026-05-23 00:51:14 +02:00
AramJonghu 7c29921a6b removal of plans package/directory (we have project board now)
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 49s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m45s
2026-05-23 00:42:25 +02:00
AramJonghu 0309fde3aa format check and lint resolved
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 46s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m50s
2026-05-22 23:26:33 +02:00
AramJonghu f147969f37 added workflow running python tests
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Failing after 24s
Python / test (pull_request) Successful in 50s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m48s
2026-05-22 22:56:03 +02:00
AramJonghu 90a1954658 cache 2026-05-22 22:53:36 +02:00
AramJonghu 2b550763e7 ruff unused import 2026-05-22 22:46:02 +02:00
AramJonghu 3037cfab53 cache 2026-05-22 22:38:56 +02:00
AramJonghu 5f92b6f6de shell: add restart command, fix call None arg, add tests 2026-05-22 22:38:35 +02:00
AramJonghu 67ae693d0c Color preset schemes now exposed for use.
- Catppuccin txt colors extracted from dankmaterialyou and created txt
  files for each.
- Preset is now an option and are exposed.
- Tests test presets, might add workflow to run tests.
2026-05-22 22:32:31 +02:00
zach fd620e7487 prep for replay 2026-05-22 20:42:51 +02:00
zach 0ec426e0f0 Record module added to sidebar, file list and buttons. Region recording is broken 2026-05-22 12:51:06 +02:00
zach ec5e6d3995 add typer command 2026-05-22 11:06:17 +02:00
zach 41a129bb90 init commit 2026-05-22 11:04:54 +02:00
Inorishio 8c48ddbbe7 Merge branch 'main' of git.zach-dev.cc:zach/z-bar-qt 2026-05-21 23:58:49 +02:00
Inorishio 625d766719 added escape to lock + greeter 2026-05-21 23:58:24 +02:00
zach 88526b9e98 show notif icon true by default 2026-05-21 23:43:27 +02:00
zach a0d56b965c toggle to show notif icon on lockscreen 2026-05-21 23:29:49 +02:00
zach 2342edcf66 apply button, wheel doesn't zoom 2026-05-21 23:12:01 +02:00
zach 9e75b593f4 scale slider for crop tool 2026-05-21 19:14:29 +02:00
zach 4663c7d683 better wallpaper preview positioning 2026-05-21 17:55:31 +02:00
zach 80683800eb crop region now restores correctly 2026-05-21 16:52:12 +02:00
zach 57836f974c remove residue test files 2026-05-21 15:52:43 +02:00
zach 8dbb88e136 crop region now correct 2026-05-21 15:51:42 +02:00
zach 06c402c050 better crop region handling, but coordinate math is wrong 2026-05-20 23:16:55 +02:00
zach e425a1701b small fix for wallpaper grid 2026-05-20 14:34:11 +02:00
zach 41666d0150 Merge pull request 'hyprland lua support' (#91) from hypr-plugin into main
Reviewed-on: #91
2026-05-20 14:08:30 +02:00
390 changed files with 23992 additions and 8956 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
...
+32 -1
View File
@@ -1,4 +1,4 @@
name: Lint & Format (Python)
name: Python
on:
pull_request:
@@ -32,3 +32,34 @@ jobs:
run: |
. .venv/bin/activate
ruff check .
test:
runs-on: alpine
container: node:26-alpine
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install tools
run: |
apk add --no-cache \
git \
python3 \
py3-pip \
py3-pillow \
build-base
python3 -m venv .venv
. .venv/bin/activate
pip install --no-cache-dir \
typer \
pillow \
materialyoucolor \
jinja2 \
pytest
- name: Test
run: |
. .venv/bin/activate
cd cli
python -m pytest tests/ -v
+16 -3
View File
@@ -38,15 +38,18 @@ jobs:
rustfmt \
rust-clippy
- name: Format check
- id: format-check
name: Format check
continue-on-error: true
run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
status=0
for manifest in $(find . -name "Cargo.toml"); do
cargo fmt --manifest-path "$manifest" --check && \
echo "$manifest: formatting OK" || \
echo "$manifest: needs formatting"
{ echo "$manifest: needs formatting"; status=1; }
done
exit $status
elif [ -n "$(find . -name "*.rs" -print -quit)" ]; then
echo "Rust files found but no Cargo.toml"
exit 1
@@ -54,7 +57,8 @@ jobs:
echo "No Rust project found"
fi
- name: Clippy
- id: clippy
name: Clippy
run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
status=0
@@ -70,3 +74,12 @@ jobs:
else
echo "No Rust project found"
fi
- name: Check results
if: always()
run: |
if [ "${{ steps.format-check.outcome }}" = "failure" ] || [ "${{ steps.clippy.outcome }}" = "failure" ]; then
echo "One or more checks failed"
exit 1
fi
echo "All checks passed"
+2
View File
@@ -13,3 +13,5 @@ uv.lock
.qtcreator/
dist/
**/target/
**/test-plugins/
**/Charts/
+9 -1
View File
@@ -25,12 +25,20 @@ add_compile_options(
if("plugin" IN_LIST ENABLE_MODULES)
add_subdirectory(Plugins)
endif()
if("shell" IN_LIST ENABLE_MODULES)
foreach(dir assets scripts Components Config Modules Daemons Drawers Effects Helpers Paths)
install(DIRECTORY ${dir} DESTINATION "${INSTALL_QSCONFDIR}")
endforeach()
install(FILES shell.qml DESTINATION "${INSTALL_QSCONFDIR}")
# Disable watching for changes
file(READ shell.qml SHELL_QML)
string(REPLACE "settings.watchFiles: true" "settings.watchFiles: false" SHELL_QML "${SHELL_QML}")
file(WRITE "${CMAKE_BINARY_DIR}/qml/shell.qml" "${SHELL_QML}")
install(FILES "${CMAKE_BINARY_DIR}/qml/shell.qml" DESTINATION "${INSTALL_QSCONFDIR}")
# Greeter
install(DIRECTORY Greeter/ DESTINATION "${INSTALL_GREETERCONFDIR}")
endif()
+51
View File
@@ -0,0 +1,51 @@
import QtQuick
import qs.Config
AnchorAnimation {
enum Type {
StandardSmall = 0,
Standard,
StandardLarge,
StandardExtraLarge,
EmphasizedSmall,
Emphasized,
EmphasizedLarge,
EmphasizedExtraLarge,
FastSpatial,
DefaultSpatial,
SlowSpatial
}
property int type: AnchorAnim.DefaultSpatial
duration: {
if (type < AnchorAnim.StandardSmall || type > AnchorAnim.SlowSpatial)
return Appearance.anim.durations.expressiveDefaultSpatial;
if (type == AnchorAnim.FastSpatial)
return Appearance.anim.durations.expressiveFastSpatial;
if (type == AnchorAnim.DefaultSpatial)
return Appearance.anim.durations.expressiveDefaultSpatial;
if (type == AnchorAnim.SlowSpatial)
return Appearance.anim.durations.large;
const types = ["small", "normal", "large", "extraLarge"];
const idx = type % 4;
return Appearance.anim.durations[types[idx]];
}
easing.bezierCurve: {
if (type == AnchorAnim.FastSpatial)
return Appearance.anim.curves.expressiveFastSpatial;
if (type == AnchorAnim.DefaultSpatial)
return Appearance.anim.curves.expressiveDefaultSpatial;
if (type == AnchorAnim.SlowSpatial)
return Appearance.anim.curves.expressiveSlowSpatial;
if (type >= AnchorAnim.StandardSmall && type <= AnchorAnim.StandardExtraLarge)
return Appearance.anim.curves.standard;
if (type >= AnchorAnim.EmphasizedSmall && type <= AnchorAnim.EmphasizedExtraLarge)
return Appearance.anim.curves.emphasized;
return Appearance.anim.curves.expressiveDefaultSpatial;
}
}
+58 -3
View File
@@ -2,7 +2,62 @@ import QtQuick
import qs.Config
NumberAnimation {
duration: Appearance.anim.durations.normal
easing.bezierCurve: Appearance.anim.curves.standard
easing.type: Easing.BezierSpline
enum Type {
StandardSmall = 0,
Standard,
StandardLarge,
StandardExtraLarge,
EmphasizedSmall,
Emphasized,
EmphasizedLarge,
EmphasizedExtraLarge,
FastSpatial,
DefaultSpatial,
SlowSpatial,
FastEffects,
DefaultEffects,
SlowEffects
}
property int type: Anim.DefaultSpatial
duration: {
if (type < Anim.StandardSmall || type > Anim.SlowEffects)
return Appearance.anim.durations.normal;
if (type === Anim.FastSpatial)
return Appearance.anim.durations.expressiveFastSpatial;
if (type === Anim.DefaultSpatial)
return Appearance.anim.durations.expressiveDefaultSpatial;
if (type === Anim.SlowSpatial)
return Appearance.anim.durations.large;
if (type === Anim.FastEffects)
return Appearance.anim.durations.expressiveFastEffects;
if (type === Anim.DefaultEffects)
return Appearance.anim.durations.expressiveEffects;
if (type === Anim.SlowEffects)
return Appearance.anim.durations.expressiveSlowEffects;
const types = ["small", "normal", "large", "extraLarge"];
const idx = type % 4;
return Appearance.anim.durations[types[idx]];
}
easing.bezierCurve: {
if (type === Anim.FastSpatial)
return Appearance.anim.curves.expressiveFastSpatial;
if (type === Anim.DefaultSpatial)
return Appearance.anim.curves.expressiveDefaultSpatial;
if (type === Anim.SlowSpatial)
return Appearance.anim.curves.expressiveSlowSpatial;
if (type === Anim.FastEffects)
return Appearance.anim.curves.expressiveFastEffects;
if (type === Anim.DefaultEffects)
return Appearance.anim.curves.expressiveDefaultEffects;
if (type === Anim.SlowEffects)
return Appearance.anim.curves.expressiveSlowEffects;
if (type >= Anim.EmphasizedSmall && type <= Anim.EmphasizedExtraLarge)
return Appearance.anim.curves.emphasized;
return Appearance.anim.curves.standard;
}
}
-3
View File
@@ -12,11 +12,8 @@ Slider {
readonly property bool isVertical: orientation === Qt.Vertical
property real multiplier: 100
property real oldValue
// Wrapper components can inject their own track visuals here.
property Component trackContent
// Keep current behavior for existing usages.
orientation: Qt.Vertical
background: CustomRect {
+87
View File
@@ -0,0 +1,87 @@
import QtQuick
import qs.Config
CustomRect {
id: root
enum ButtonType {
Filled,
Tonal,
Text
}
property color activeColor
property color activeOnColor
property bool checked
property real checkedRadius: Appearance.rounding.medium
property real defaultRadius: Appearance.rounding.large
property color disabledColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
property color disabledOnColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
property bool fillWidth
property font font: ({
family: Appearance.font.family.sans,
pointSize: Appearance.font.size.larger,
bold: false
})
property real horizontalPadding: padding
readonly property alias hovered: stateLayer.containsMouse
required implicitHeight
required implicitWidth
property color inactiveColor
property color inactiveOnColor
property bool internalChecked
property bool isRound
property bool isToggle
readonly property color onColor: !enabled ? disabledOnColor : internalChecked ? activeOnColor : inactiveOnColor
property real padding
readonly property alias pressed: stateLayer.pressed
property real pressedRadius: Appearance.rounding.small
readonly property alias radiusAnim: radiusAnim
property bool radiusMorph: true
property alias shapeMorph: stateLayer.shapeMorph
property real shapeMorphExpansion: shapeMorph && pressed ? 24 : 0
readonly property alias stateLayer: stateLayer
property int type: ButtonBase.Filled
property real verticalPadding: padding
signal clicked
color: type === ButtonBase.Text ? "transparent" : !enabled ? disabledColor : internalChecked ? activeColor : inactiveColor
radius: {
if (radiusMorph && pressed)
return pressedRadius;
if (internalChecked)
return checkedRadius;
if (isRound)
return (height || implicitHeight) / 2 * Math.min(1, Appearance.rounding.scale);
return defaultRadius;
}
Behavior on radius {
Anim {
id: radiusAnim
type: Anim.DefaultEffects
}
}
Behavior on shapeMorphExpansion {
Anim {
type: Anim.FastSpatial
}
}
onCheckedChanged: internalChecked = checked
StateLayer {
id: stateLayer
color: root.internalChecked ? root.activeOnColor : root.inactiveOnColor
enabled: root.enabled
onClicked: {
if (root.isToggle)
root.internalChecked = !root.internalChecked;
root.clicked();
}
}
}
+4 -5
View File
@@ -17,8 +17,8 @@ BusyIndicator {
}
property int animState
property color bgColour: DynamicColors.palette.m3secondaryContainer
property color fgColour: DynamicColors.palette.m3primary
property color bgColor: DynamicColors.palette.m3secondaryContainer
property color fgColor: DynamicColors.palette.m3primary
property real implicitSize: Appearance.font.size.normal * 3
property real internalStrokeWidth: strokeWidth
readonly property alias progress: manager.progress
@@ -31,8 +31,8 @@ BusyIndicator {
contentItem: CircularProgress {
anchors.fill: parent
bgColour: root.bgColour
fgColour: root.fgColour
bgColor: root.bgColor
fgColor: root.fgColor
padding: root.padding
rotation: manager.rotation
startAngle: manager.startFraction * 360
@@ -73,7 +73,6 @@ BusyIndicator {
CircularIndicatorManager {
id: manager
}
NumberAnimation {
+89 -39
View File
@@ -1,66 +1,116 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Shapes
import ZShell.Components
import qs.Config
Shape {
Item {
id: root
readonly property real arcRadius: (size - padding - strokeWidth) / 2
property color bgColour: DynamicColors.palette.m3secondaryContainer
property color fgColour: DynamicColors.palette.m3primary
readonly property real arcRadius: (size - padding - strokeWidth * (1 + waveAmplitude * 2)) / 2
property color bgColor: DynamicColors.palette.m3secondaryContainer
property real clampedVal: Math.max(1 / 360, Math.min(1, isNaN(value) ? 0 : value))
readonly property real dotAngleRad: (startAngle + sweepAngle - gapAngle * (sweepAngle < 360 ? 0 : 1)) * Math.PI / 180
property color fgColor: DynamicColors.palette.m3primary
readonly property real gapAngle: ((spacing + strokeWidth) / (arcRadius || 1)) * (180 / Math.PI)
property alias hasEndIndicator: dot.active
property real implicitSize
property int padding: 0
readonly property real size: Math.min(width, height)
property int spacing: Appearance.spacing.small
property int startAngle: -90
property int strokeWidth: Appearance.padding.smaller
readonly property real vValue: value || 1 / 360
property int strokeWidth: Appearance.padding.small
property int sweepAngle: 360
readonly property real thickness: strokeWidth * (1 + waveAmplitude) * 2
property real value
property alias waveAmplitude: wave.amplitudeMultiplier
property alias waveDuration: waveProgAnim.duration
property alias waveFrequency: wave.frequency
property bool wavePaused
property bool wavy: false
asynchronous: true
preferredRendererType: Shape.CurveRenderer
implicitHeight: implicitSize
implicitWidth: implicitSize
ShapePath {
capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
fillColor: "transparent"
strokeColor: root.bgColour
strokeWidth: root.strokeWidth
Shape {
asynchronous: true
opacity: Math.min(1, remainingArc.sweepAngle)
preferredRendererType: Shape.CurveRenderer
Behavior on strokeColor {
CAnim {
duration: Appearance.anim.durations.large
ShapePath {
capStyle: ShapePath.RoundCap
fillColor: "transparent"
strokeColor: root.bgColor
strokeWidth: Math.min(1, remainingArc.sweepAngle) * root.strokeWidth
Behavior on strokeColor {
CAnim {
}
}
}
PathAngleArc {
centerX: root.size / 2
centerY: root.size / 2
radiusX: root.arcRadius
radiusY: root.arcRadius
startAngle: root.startAngle + 360 * root.vValue + root.gapAngle
sweepAngle: Math.max(-root.gapAngle, 360 * (1 - root.vValue) - root.gapAngle * 2)
PathAngleArc {
id: remainingArc
centerX: root.size / 2
centerY: root.size / 2
radiusX: root.arcRadius
radiusY: root.arcRadius
startAngle: root.startAngle + root.clampedVal * root.sweepAngle + root.gapAngle
sweepAngle: Math.max(1 / 360, root.sweepAngle * (1 - root.clampedVal) - root.gapAngle * (root.sweepAngle < 360 ? 1 : 2))
}
}
}
ShapePath {
capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
fillColor: "transparent"
strokeColor: root.fgColour
strokeWidth: root.strokeWidth
WavyLine {
id: wave
Behavior on strokeColor {
CAnim {
duration: Appearance.anim.durations.large
amplitudeMultiplier: root.wavy ? 0.5 : 0
anchors.fill: parent
anchors.margins: -lineWidth * amplitudeMultiplier
color: root.fgColor
frequency: 8
fullAngle: root.sweepAngle
lineWidth: root.strokeWidth
pathType: WavyLine.Arc
radius: root.arcRadius
startAngle: root.startAngle
value: root.clampedVal
Behavior on amplitudeMultiplier {
Anim {
type: Anim.DefaultEffects
}
}
Behavior on color {
CAnim {
}
}
Anim on waveProgress {
id: waveProgAnim
PathAngleArc {
centerX: root.size / 2
centerY: root.size / 2
radiusX: root.arcRadius
radiusY: root.arcRadius
startAngle: root.startAngle
sweepAngle: 360 * root.vValue
duration: 2000
easing.type: Easing.Linear
from: 0
loops: Animation.Infinite
paused: root.wavePaused || wave.amplitudeMultiplier === 0
running: true
to: 1
}
}
Loader {
id: dot
x: root.size / 2 + root.arcRadius * Math.cos(root.dotAngleRad) - width / 2
y: root.size / 2 + root.arcRadius * Math.sin(root.dotAngleRad) - height / 2
sourceComponent: CustomRect {
color: root.fgColor
implicitHeight: Math.min(1, remainingArc.sweepAngle) * Math.min(4, root.strokeWidth)
implicitWidth: Math.min(1, remainingArc.sweepAngle) * Math.min(4, root.strokeWidth)
opacity: Math.min(1, remainingArc.sweepAngle)
radius: Appearance.rounding.full
}
}
}
+5 -5
View File
@@ -59,15 +59,15 @@ ColumnLayout {
}
StateLayer {
function onClicked(): void {
root.toggleRequested();
root.expanded = !root.expanded;
}
anchors.fill: parent
color: DynamicColors.palette.m3onSurface
radius: Appearance.rounding.normal
showHoverBackground: false
onClicked: {
root.toggleRequested();
root.expanded = !root.expanded;
}
}
}
+28 -5
View File
@@ -45,10 +45,10 @@ Item {
}
function syncFromPenColor() {
if (!drawing)
if (!drawing.drawingState)
return;
const c = drawing.penColor;
const c = drawing.drawingState.penColor;
if (c.hsvSaturation > 0) {
currentHue = c.hsvHue;
@@ -85,12 +85,20 @@ Item {
currentHue = relative / arcSweep;
lastChromaticHue = currentHue;
drawing.penColor = Qt.hsva(currentHue, drawing.penColor.hsvSaturation, drawing.penColor.hsvValue, drawing.penColor.a);
drawing.drawingState.penColor = Qt.hsva(currentHue, drawing.drawingState.penColor.hsvSaturation, drawing.drawingState.penColor.hsvValue, drawing.drawingState.penColor.a);
}
implicitHeight: 180
implicitWidth: 220
Behavior on currentHue {
enabled: !root.dragActive
Anim {
type: Anim.StandardLarge
}
}
Component.onCompleted: syncFromPenColor()
onCurrentHueChanged: canvas.requestPaint()
onDrawingChanged: syncFromPenColor()
@@ -103,7 +111,7 @@ Item {
root.syncFromPenColor();
}
target: root.drawing
target: root.drawing.drawingState
}
Canvas {
@@ -141,6 +149,21 @@ Item {
}
}
CustomRect {
anchors.centerIn: parent
color: root.drawing?.drawingState.penColor
implicitHeight: implicitWidth
implicitWidth: canvas.height - root.handleSize - Appearance.padding.extraLarge * 2
radius: Appearance.rounding.full
Behavior on color {
enabled: false
CAnim {
}
}
}
Item {
id: handle
@@ -174,7 +197,7 @@ Item {
Rectangle {
anchors.centerIn: parent
color: root.drawing ? root.drawing.penColor : Qt.hsla(root.currentHue, 1.0, 0.5, 1.0)
color: Qt.hsla(root.currentHue, 1.0, 0.5, 1.0)
height: width
radius: width / 2
width: parent.width - 12
+3 -3
View File
@@ -23,10 +23,10 @@ Button {
}
StateLayer {
function onClicked(): void {
radius: control.radius
onClicked: {
control.clicked();
}
radius: control.radius
}
}
+13
View File
@@ -0,0 +1,13 @@
import Quickshell.Widgets
import QtQuick
ClippingWrapperRectangle {
id: root
color: "transparent"
Behavior on color {
CAnim {
}
}
}
+2
View File
@@ -1,8 +1,10 @@
import QtQuick
import qs.Helpers
Flickable {
id: root
interactive: !Visibilities.getForActive().isDrawing
maximumFlickVelocity: 3000
rebound: Transition {
+20
View File
@@ -1,13 +1,33 @@
import QtQuick
import qs.Helpers
ListView {
id: root
property bool doneFakeFlick
interactive: !Visibilities.getForActive().isDrawing
maximumFlickVelocity: 3000
rebound: Transition {
onRunningChanged: {
if (!running && !root.doneFakeFlick) {
root.doneFakeFlick = true;
root.flick(1, 1);
root.flick(-1, -1);
Qt.callLater(() => root.cancelFlick());
}
}
Anim {
properties: "x,y"
}
}
Timer {
interval: 10
running: root.doneFakeFlick
onTriggered: root.doneFakeFlick = false
}
}
+237
View File
@@ -0,0 +1,237 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Templates
import ZShell
import ZShell.Components
import ZShell.Internal
import qs.Config
ProgressBar {
id: root
enum IndeterminateAnimState {
Running,
Completing,
Stopped
}
property color bgColor: DynamicColors.palette.m3secondaryContainer
property color fgColor: DynamicColors.palette.m3primary
property int indeterminateAnimState: CustomProgressBar.Stopped
property real waveAmplitude: 0.5
property int waveDuration: 1000
property int waveFrequency: 6
property bool wavePaused
property bool wavy
function toBounds(startFrac: real, endFrac: real, gapSize: real): point {
startFrac = ZUtils.clamp(startFrac, 0, 1);
endFrac = ZUtils.clamp(endFrac, 0, 1);
// Ramp down gap size
const GAP_RAMP_DOWN_THRESHOLD = 0.01;
gapSize += height / 2;
const startGapSize = (gapSize * ZUtils.clamp(startFrac, 0, GAP_RAMP_DOWN_THRESHOLD) / GAP_RAMP_DOWN_THRESHOLD);
const endGapSize = (gapSize * (1 - ZUtils.clamp(endFrac, 1 - GAP_RAMP_DOWN_THRESHOLD, 1)) / GAP_RAMP_DOWN_THRESHOLD);
const start = width * startFrac + startGapSize;
const end = width * endFrac - endGapSize;
return start >= end ? Qt.point(0, 0) : Qt.point(start, end);
}
function updateIAnimState(): void {
if (indeterminate) {
manager.completeEndProgress = 0;
indeterminateAnimState = CustomProgressBar.Running;
} else if (indeterminateAnimState === CustomProgressBar.Running) {
indeterminateAnimState = CustomProgressBar.Completing;
}
}
implicitHeight: 4
implicitWidth: 200
contentItem: Loader {
anchors.fill: parent
asynchronous: true
sourceComponent: root.indeterminate || root.indeterminateAnimState !== CustomProgressBar.Stopped ? indeterminateComp : determinateComp
}
Component.onCompleted: updateIAnimState()
onIndeterminateChanged: updateIAnimState()
LinearIndicatorManager {
id: manager
gap: Appearance.spacing.extraSmall
Anim on completeEndProgress {
duration: manager.completeEndDuration
running: root.indeterminateAnimState === CustomProgressBar.Completing
to: 1
onFinished: {
if (root.indeterminateAnimState === CustomProgressBar.Completing)
root.indeterminateAnimState = CustomProgressBar.Stopped;
}
}
Anim on progress {
duration: manager.duration
easing.type: Easing.Linear
from: 0
loops: Animation.Infinite
running: root.indeterminateAnimState !== CustomProgressBar.Stopped
to: 1
}
}
Component {
id: determinateComp
Item {
Line {
id: remaining
anchors.right: parent.right
implicitWidth: parent.width - wave.implicitWidth - Appearance.spacing.extraSmall
}
Line {
property real implicitSize
anchors.right: parent.right
anchors.rightMargin: (parent.height - implicitHeight) / 2
anchors.verticalCenter: parent.verticalCenter
color: root.fgColor
implicitHeight: implicitSize
implicitWidth: implicitSize
radius: Appearance.rounding.full
Behavior on implicitSize {
Anim {
type: Anim.FastSpatial
}
}
Component.onCompleted: implicitSize = Qt.binding(() => parent.width - wave.implicitWidth < parent.height ? parent.height : 4)
}
Wave {
id: wave
anchors.left: parent.left
Behavior on implicitWidth {
Anim {
}
}
Component.onCompleted: implicitWidth = Qt.binding(() => parent.width * root.visualPosition)
}
}
}
Component {
id: indeterminateComp
Item {
id: content
Line {
bounds: {
const i = manager.activeIndicators[0]; // qmllint disable unresolved-type
return i ? root.toBounds(0, i.startFraction, i.gapSize / 2) : Qt.point(0, 0);
}
}
Line {
bounds: {
const i = manager.activeIndicators[manager.activeIndicators.length - 1]; // qmllint disable unresolved-type
return i ? root.toBounds(i.endFraction, 1, i.gapSize / 2) : Qt.point(0, 0);
}
}
Instantiator {
model: Math.max(manager.activeIndicators.length, 1) - 1 // qmllint disable unresolved-type
delegate: Line {
readonly property LinearIndicatorSegment cur: manager.activeIndicators[index] // qmllint disable unresolved-type
required property int index
readonly property LinearIndicatorSegment next: manager.activeIndicators[index + 1 % manager.activeIndicators.length] // qmllint disable unresolved-type
bounds: root.toBounds(cur.endFraction, next.startFraction, cur.gapSize / 2)
}
onObjectAdded: (_, obj) => content.data.push(obj)
onObjectRemoved: (_, obj) => {
const idx = content.data.indexOf(obj);
if (idx !== -1)
content.data.splice(idx, 1);
}
}
Instantiator {
model: manager.activeIndicators // qmllint disable unresolved-type
delegate: Wave {
readonly property point bounds: root.toBounds(modelData.startFraction, modelData.endFraction, modelData.gapSize / 2)
required property LinearIndicatorSegment modelData
color: root.fgColor
implicitWidth: bounds.y - bounds.x
x: bounds.x
}
onObjectAdded: (_, obj) => content.data.push(obj)
onObjectRemoved: (_, obj) => {
const idx = content.data.indexOf(obj);
if (idx !== -1)
content.data.splice(idx, 1);
}
}
}
}
component Line: CustomRect {
property point bounds
anchors.verticalCenter: parent.verticalCenter
color: root.bgColor
implicitHeight: parent.height
implicitWidth: bounds.y - bounds.x
radius: Appearance.rounding.full
x: bounds.x
}
component Wave: WavyLine {
id: wave
amplitudeMultiplier: root.wavy ? root.waveAmplitude : 0
anchors.verticalCenter: parent.verticalCenter
color: root.fgColor
frequency: root.waveFrequency
fullLength: parent.width
implicitHeight: lineWidth * amplitudeMultiplier * 2 + lineWidth
lineWidth: parent.height
startX: x
Behavior on amplitudeMultiplier {
Anim {
type: Anim.DefaultEffects
}
}
Behavior on color {
CAnim {
}
}
Anim on waveProgress {
duration: root.waveDuration
easing.type: Easing.Linear
from: 0
loops: Animation.Infinite
paused: wave.amplitudeMultiplier === 0 || root.wavePaused
running: true
to: 1
}
}
}
+4 -4
View File
@@ -33,13 +33,13 @@ RadioButton {
}
StateLayer {
function onClicked(): void {
root.click();
}
anchors.margins: -7
color: root.checked ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3primary
z: -1
onClicked: {
root.click();
}
}
CustomRect {
+106 -133
View File
@@ -1,49 +1,25 @@
import qs.Config
import QtQuick
import QtQuick.Templates
import qs.Helpers
import qs.Config
ScrollBar {
id: root
property bool _updatingFromFlickable: false
property bool _updatingFromUser: false
property bool animating
readonly property real effectiveSize: Math.max(nonAnimHeight, root.minimumSize)
readonly property real effectiveTravel: Math.max(0, 1 - root.effectiveSize)
required property Flickable flickable
property real nonAnimPosition
readonly property real nonAnimHeight: flickable.height / flickable.contentHeight
readonly property real nonAnimY: flickable.contentY / flickable.contentHeight
readonly property real rawTravel: Math.max(0, 1 - root.nonAnimHeight)
readonly property bool reversed: flickable instanceof ListView && flickable.verticalLayoutDirection === ListView.BottomToTop
property bool shouldBeActive
readonly property real travelScale: root.rawTravel > 0 ? root.effectiveTravel / root.rawTravel : 0
implicitWidth: 8
enabled: !Visibilities.getForActive().isDrawing
implicitWidth: Appearance.padding.extraSmall * 2
contentItem: CustomRect {
anchors.left: parent.left
anchors.right: parent.right
color: DynamicColors.palette.m3secondary
opacity: {
if (root.size === 1)
return 0;
if (fullMouse.pressed)
return 1;
if (mouse.containsMouse)
return 0.8;
if (root.policy === ScrollBar.AlwaysOn || root.shouldBeActive)
return 0.6;
return 0;
}
radius: Appearance.rounding.full
Behavior on opacity {
Anim {
}
}
MouseArea {
id: mouse
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
}
contentItem: Item {
}
Behavior on position {
enabled: !fullMouse.pressed
@@ -52,58 +28,15 @@ ScrollBar {
}
}
Component.onCompleted: {
if (flickable) {
const contentHeight = flickable.contentHeight;
const height = flickable.height;
if (contentHeight > height) {
nonAnimPosition = Math.max(0, Math.min(1, flickable.contentY / (contentHeight - height)));
}
}
}
onHoveredChanged: {
if (hovered)
shouldBeActive = true;
shouldBeActive = hovered;
else
shouldBeActive = flickable.moving;
}
// Sync nonAnimPosition with Qt's automatic position binding
onPositionChanged: {
if (_updatingFromUser) {
_updatingFromUser = false;
return;
}
if (position === nonAnimPosition) {
animating = false;
return;
}
if (!animating && !_updatingFromFlickable && !fullMouse.pressed) {
nonAnimPosition = position;
}
}
// Sync nonAnimPosition with flickable when not animating
Connections {
function onContentYChanged() {
if (!animating && !fullMouse.pressed) {
_updatingFromFlickable = true;
const contentHeight = flickable.contentHeight;
const height = flickable.height;
if (contentHeight > height) {
nonAnimPosition = Math.max(0, Math.min(1, flickable.contentY / (contentHeight - height)));
} else {
nonAnimPosition = 0;
}
_updatingFromFlickable = false;
}
}
target: flickable
hideDelay.restart();
}
Connections {
function onMovingChanged(): void {
function onMovingChanged() {
if (root.flickable.moving)
root.shouldBeActive = true;
else
@@ -113,6 +46,57 @@ ScrollBar {
target: root.flickable
}
CustomClippingRect {
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.top: parent.top
implicitWidth: handle.implicitWidth
radius: Appearance.rounding.full
CustomRect {
id: handle
anchors.right: parent.right
color: DynamicColors.palette.m3secondary
implicitHeight: root.height * root.effectiveSize
implicitWidth: fullMouse.pressed || fullMouse.containsMouse ? Appearance.padding.extraSmall * 2 : Appearance.padding.extraSmall
opacity: {
if (!root.enabled)
return 0;
if (root.size === 1)
return 0;
if (fullMouse.pressed)
return 1;
if (fullMouse.containsMouse)
return 0.8;
if (root.policy === ScrollBar.AlwaysOn || root.shouldBeActive)
return 0.6;
return 0;
}
radius: Appearance.rounding.full
y: root.reversed ? root.height * (1 + root.nonAnimY) * root.travelScale : root.height * root.nonAnimY * root.travelScale
Behavior on implicitWidth {
Anim {
}
}
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
MouseArea {
id: mouse
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
}
}
}
Timer {
id: hideDelay
@@ -124,66 +108,55 @@ ScrollBar {
CustomMouseArea {
id: fullMouse
function onWheel(event: WheelEvent): void {
root.animating = true;
root._updatingFromUser = true;
let newPos = root.nonAnimPosition;
if (event.angleDelta.y > 0)
newPos = Math.max(0, root.nonAnimPosition - 0.1);
else if (event.angleDelta.y < 0)
newPos = Math.min(1 - root.size, root.nonAnimPosition + 0.1);
root.nonAnimPosition = newPos;
// Update flickable position
// Map scrollbar position [0, 1-size] to contentY [0, maxContentY]
if (root.flickable) {
const contentHeight = root.flickable.contentHeight;
const height = root.flickable.height;
if (contentHeight > height) {
const maxContentY = contentHeight - height;
const maxPos = 1 - root.size;
const contentY = maxPos > 0 ? (newPos / maxPos) * maxContentY : 0;
root.flickable.contentY = Math.max(0, Math.min(maxContentY, contentY));
}
}
property real pressOffset: 0
function contentYFromThumbTop(thumbTop) {
var visualPos = root.effectiveTravel > 0 ? thumbTop / root.travelScale : 0;
return root.reversed ? (visualPos - 1) * root.flickable.contentHeight : visualPos * root.flickable.contentHeight;
}
function updateFromEvent(event) {
var posInTrack = event.y / root.height;
var thumbTop = posInTrack - pressOffset;
thumbTop = Math.max(0, Math.min(root.effectiveTravel, thumbTop));
root.flickable.contentY = contentYFromThumbTop(thumbTop);
}
function visualThumbTop() {
const visualPos = root.reversed ? (1 + root.nonAnimY) : root.nonAnimY;
return visualPos * root.travelScale;
}
anchors.fill: parent
cursorShape: undefined
hoverEnabled: true
preventStealing: true
onPositionChanged: event => {
root._updatingFromUser = true;
const newPos = Math.max(0, Math.min(1 - root.size, event.y / root.height - root.size / 2));
root.nonAnimPosition = newPos;
// Update flickable position
// Map scrollbar position [0, 1-size] to contentY [0, maxContentY]
if (root.flickable) {
const contentHeight = root.flickable.contentHeight;
const height = root.flickable.height;
if (contentHeight > height) {
const maxContentY = contentHeight - height;
const maxPos = 1 - root.size;
const contentY = maxPos > 0 ? (newPos / maxPos) * maxContentY : 0;
root.flickable.contentY = Math.max(0, Math.min(maxContentY, contentY));
}
}
if (pressed)
updateFromEvent(event);
}
onPressed: event => {
root.animating = true;
root._updatingFromUser = true;
const newPos = Math.max(0, Math.min(1 - root.size, event.y / root.height - root.size / 2));
root.nonAnimPosition = newPos;
// Update flickable position
// Map scrollbar position [0, 1-size] to contentY [0, maxContentY]
if (root.flickable) {
const contentHeight = root.flickable.contentHeight;
const height = root.flickable.height;
if (contentHeight > height) {
const maxContentY = contentHeight - height;
const maxPos = 1 - root.size;
const contentY = maxPos > 0 ? (newPos / maxPos) * maxContentY : 0;
root.flickable.contentY = Math.max(0, Math.min(maxContentY, contentY));
}
var currentTop = visualThumbTop();
var currentBottom = currentTop + root.effectiveSize;
var clickPos = event.y / root.height;
var clickedInsideThumb = clickPos >= currentTop && clickPos <= currentBottom;
if (clickedInsideThumb) {
pressOffset = clickPos - currentTop;
} else {
pressOffset = root.effectiveSize / 2;
}
updateFromEvent(event);
}
onWheel: event => {
var delta = event.angleDelta.y > 0 ? -0.1 : 0.1;
var newPos = Math.max(0, Math.min(1 - root.size, root.position + delta));
root.position = newPos;
}
}
}
+181 -27
View File
@@ -1,47 +1,201 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Templates
import ZShell.Components
import ZShell
import qs.Components
import qs.Config
Slider {
id: root
property color color: DynamicColors.palette.m3primary
property bool animateWave
property color bgColor: enabled ? DynamicColors.palette.m3secondaryContainer : Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
property color fgColor: enabled ? DynamicColors.palette.m3primary : Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
property real filledWidth
property alias inset: inset
property color insetColor: enabled ? (inset.attached ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onPrimary) : DynamicColors.palette.m3onSurface
property string insetIcon: ""
property real pos: visualPosition
property int radius: Appearance.rounding.small
property int waveDuration: 1000
property real waveFrequency: 6
property bool wavy
signal interaction(v: real)
implicitHeight: 12
implicitWidth: 200
contentItem: Item {
anchors.fill: parent
background: Item {
CustomRect {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: parent.top
bottomRightRadius: root.implicitHeight / 6
color: root.color
implicitWidth: root.handle.x - root.implicitHeight / 2
radius: Appearance.rounding.full
topRightRadius: root.implicitHeight / 6
id: remaining
anchors.left: handle.right
anchors.leftMargin: Appearance.spacing.extraSmall
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
bottomLeftRadius: Appearance.rounding.extraSmall / 2
color: root.bgColor
implicitHeight: parent.height * (parent.height <= 12 ? opacity : Math.min(opacity * 2, 1))
opacity: Math.min(width, 12) / 12
radius: root.radius
topLeftRadius: Appearance.rounding.extraSmall / 2
}
CustomRect {
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.top: parent.top
bottomLeftRadius: root.implicitHeight / 6
color: DynamicColors.tPalette.m3surfaceContainer
implicitWidth: parent.width - root.handle.x - root.handle.implicitWidth - root.implicitHeight / 2
anchors.rightMargin: 4 * remaining.opacity
anchors.verticalCenter: parent.verticalCenter
color: root.fgColor
implicitHeight: 4 * remaining.opacity
implicitWidth: implicitHeight
opacity: remaining.opacity
radius: Appearance.rounding.full
topLeftRadius: root.implicitHeight / 6
}
CustomRect {
id: handle
anchors.left: filled.right
anchors.leftMargin: Appearance.spacing.extraSmall
anchors.verticalCenter: parent.verticalCenter
color: root.fgColor
implicitHeight: {
const t = ZUtils.clamp((parent.height - 12) / 16, 0, 1);
const lerp = (a, b) => a + (b - a) * t;
return parent.height * (mouse.pressed ? lerp(3.5, 1.5) : lerp(3, 1.2));
}
implicitWidth: 4
radius: Appearance.rounding.full
Behavior on implicitHeight {
Anim {
type: Anim.FastSpatial
}
}
}
Loader {
id: filled
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
asynchronous: true
sourceComponent: root.wavy ? waveComp : lineComp
}
MaterialIcon {
id: inset
readonly property bool attached: root.width ? (root.width - handle.implicitWidth - handle.anchors.leftMargin) * root.pos < inset.paintedWidth + Appearance.spacing.extraSmall * 2 : false
property real dockT: attached ? 1 : 0
anchors.bottom: parent.bottom
anchors.top: parent.top
color: root.insetColor
font.pixelSize: Appearance.font.size.smaller * 2
text: root.insetIcon
verticalAlignment: Text.AlignVCenter
visible: !root.wavy && root.insetIcon !== ""
x: Appearance.spacing.extraSmall + dockT * (handle.x + Appearance.spacing.small)
Behavior on dockT {
Anim {
}
}
}
Component {
id: lineComp
CustomRect {
bottomRightRadius: Appearance.rounding.extraSmall / 2
color: root.fgColor
implicitHeight: root.height
implicitWidth: root.filledWidth
radius: root.radius
topRightRadius: Appearance.rounding.extraSmall / 2
}
}
Component {
id: waveComp
WavyLine {
color: root.fgColor
frequency: root.waveFrequency
fullLength: root.width - handle.implicitWidth - handle.anchors.leftMargin
implicitHeight: lineWidth * amplitudeMultiplier * 2 + lineWidth
implicitWidth: root.filledWidth
lineWidth: root.height * 0.7
startX: x
Behavior on color {
CAnim {
}
}
Anim on waveProgress {
duration: root.waveDuration
easing.type: Easing.Linear
from: 0
loops: Animation.Infinite
paused: !root.animateWave
running: true
to: 1
}
}
}
}
handle: CustomRect {
anchors.verticalCenter: parent.verticalCenter
color: root.color
implicitHeight: 15
implicitWidth: 5
radius: Appearance.rounding.full
x: root.visualPosition * root.availableWidth - implicitWidth / 2
Behavior on filledWidth {
id: widthBehavior
MouseArea {
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
Anim {
}
}
Component.onCompleted: {
filledWidth = Qt.binding(() => (width - handle.implicitWidth - handle.anchors.leftMargin) * pos);
}
Binding {
id: posBinding
property: "pos"
target: root
value: ZUtils.clamp(mouse.pressStartPos + mouse.dragMovement, 0, 1)
when: mouse.pressed
}
MouseArea {
id: mouse
property real dragMovement
property real pressStartPos
property real pressStartX
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
implicitHeight: handle.implicitHeight
preventStealing: true
onPositionChanged: e => {
dragMovement = (e.x - pressStartX) / width;
root.interaction(posBinding.value);
}
onPressed: e => {
widthBehavior.enabled = false;
pressStartX = e.x;
pressStartPos = root.visualPosition;
}
onReleased: e => {
root.interaction(posBinding.value);
widthBehavior.enabled = true;
dragMovement = 0;
}
}
}
+12 -13
View File
@@ -28,6 +28,7 @@ RowLayout {
CustomTextField {
id: textField
color: root.enabled ? DynamicColors.palette.m3onSurface : Qt.alpha(DynamicColors.palette.m3onSurface, 0.5)
implicitHeight: upButton.implicitHeight
inputMethodHints: Qt.ImhFormattedNumbersOnly
leftPadding: Appearance.padding.normal
@@ -36,7 +37,7 @@ RowLayout {
text: root.isEditing ? text : root.displayText
background: CustomRect {
color: DynamicColors.tPalette.m3surfaceContainerHigh
color: root.enabled ? DynamicColors.tPalette.m3surfaceContainerHigh : DynamicColors.tPalette.m3surfaceContainerLow
implicitWidth: 100
radius: Appearance.rounding.full
}
@@ -85,7 +86,7 @@ RowLayout {
CustomRect {
id: upButton
color: DynamicColors.palette.m3primary
color: root.enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 1)
implicitHeight: upIcon.implicitHeight + Appearance.padding.small * 2
implicitWidth: implicitHeight
radius: Appearance.rounding.full
@@ -93,7 +94,9 @@ RowLayout {
StateLayer {
id: upState
function onClicked(): void {
color: DynamicColors.palette.m3onPrimary
onClicked: {
let newValue = Math.min(root.max, root.value + root.step);
// Round to avoid floating point precision errors
const decimals = root.step < 1 ? Math.max(1, Math.ceil(-Math.log10(root.step))) : 0;
@@ -102,9 +105,6 @@ RowLayout {
root.displayText = newValue.toString();
root.valueModified(newValue);
}
color: DynamicColors.palette.m3onPrimary
onPressAndHold: timer.start()
onReleased: timer.stop()
}
@@ -113,13 +113,13 @@ RowLayout {
id: upIcon
anchors.centerIn: parent
color: DynamicColors.palette.m3onPrimary
color: root.enabled ? DynamicColors.palette.m3onPrimary : Qt.alpha(DynamicColors.palette.m3onSurface, 0.5)
text: "keyboard_arrow_up"
}
}
CustomRect {
color: DynamicColors.palette.m3primary
color: root.enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 1)
implicitHeight: downIcon.implicitHeight + Appearance.padding.small * 2
implicitWidth: implicitHeight
radius: Appearance.rounding.full
@@ -127,7 +127,9 @@ RowLayout {
StateLayer {
id: downState
function onClicked(): void {
color: DynamicColors.palette.m3onPrimary
onClicked: {
let newValue = Math.max(root.min, root.value - root.step);
// Round to avoid floating point precision errors
const decimals = root.step < 1 ? Math.max(1, Math.ceil(-Math.log10(root.step))) : 0;
@@ -136,9 +138,6 @@ RowLayout {
root.displayText = newValue.toString();
root.valueModified(newValue);
}
color: DynamicColors.palette.m3onPrimary
onPressAndHold: timer.start()
onReleased: timer.stop()
}
@@ -147,7 +146,7 @@ RowLayout {
id: downIcon
anchors.centerIn: parent
color: DynamicColors.palette.m3onPrimary
color: root.enabled ? DynamicColors.palette.m3onPrimary : Qt.alpha(DynamicColors.palette.m3onSurface, 0.5)
text: "keyboard_arrow_down"
}
}
+32 -66
View File
@@ -1,7 +1,6 @@
import QtQuick
import QtQuick.Layouts
import qs.Config
import qs.Helpers
Row {
id: root
@@ -13,65 +12,44 @@ Row {
property alias active: menu.active
property color color: type == CustomSplitButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondaryContainer
property bool disabled
property color disabledColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
property color disabledTextColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
readonly property alias expandBtn: expandBtn
property alias expanded: menu.expanded
property string fallbackIcon
property string fallbackText
property real horizontalPadding: Appearance.padding.normal
property alias iconLabel: iconLabel
property alias label: label
property alias menu: menu
property real horizontalPadding: Appearance.padding.larger
readonly property alias iconLabel: iconLabel
readonly property alias label: label
readonly property alias menu: menu
property alias menuItems: menu.items
property bool menuOnTop
property alias stateLayer: stateLayer
property real minLeftWidth
readonly property alias stateLayer: stateLayer
property color textColor: type == CustomSplitButton.Filled ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSecondaryContainer
readonly property alias textRow: textRow
property int type: CustomSplitButton.Filled
property real verticalPadding: Appearance.padding.smaller
property real verticalPadding: Appearance.padding.small
function closeDropdown(): void {
SettingsDropdowns.close(menu);
}
function openDropdown(): void {
if (root.disabled)
return;
SettingsDropdowns.open(menu, root);
}
function toggleDropdown(): void {
if (root.disabled)
return;
SettingsDropdowns.toggle(menu, root);
}
spacing: Math.floor(Appearance.spacing.small / 2)
onExpandedChanged: {
if (!expanded)
SettingsDropdowns.forget(menu);
}
spacing: Math.floor(Appearance.spacing.extraSmall)
CustomRect {
bottomRightRadius: Appearance.rounding.small / 2
color: root.disabled ? root.disabledColor : root.color
color: !root.enabled ? root.disabledColor : root.color
implicitHeight: expandBtn.implicitHeight
implicitWidth: textRow.implicitWidth + root.horizontalPadding * 2
implicitWidth: Math.max(root.minLeftWidth, textRow.implicitWidth + root.horizontalPadding * 2)
radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale)
topRightRadius: Appearance.rounding.small / 2
StateLayer {
id: stateLayer
function onClicked(): void {
root.active?.clicked();
}
bottomRightRadius: parent.bottomRightRadius
color: root.textColor
disabled: root.disabled
rect.bottomRightRadius: parent.bottomRightRadius
rect.topRightRadius: parent.topRightRadius
enabled: root.enabled
topRightRadius: parent.topRightRadius
onClicked: root.active?.clicked()
}
RowLayout {
@@ -86,7 +64,7 @@ Row {
Layout.alignment: Qt.AlignVCenter
animate: true
color: root.disabled ? root.disabledTextColor : root.textColor
color: !root.enabled ? root.disabledTextColor : root.textColor
fill: 1
text: root.active?.activeIcon ?? root.fallbackIcon
}
@@ -98,12 +76,12 @@ Row {
Layout.preferredWidth: implicitWidth
animate: true
clip: true
color: root.disabled ? root.disabledTextColor : root.textColor
color: !root.enabled ? root.disabledTextColor : root.textColor
text: root.active?.activeText ?? root.fallbackText
Behavior on Layout.preferredWidth {
Anim {
easing.bezierCurve: Appearance.anim.curves.emphasized
type: Anim.Emphasized
}
}
}
@@ -116,7 +94,7 @@ Row {
property real rad: root.expanded ? implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) : Appearance.rounding.small / 2
bottomLeftRadius: rad
color: root.disabled ? root.disabledColor : root.color
color: !root.enabled ? root.disabledColor : root.color
implicitHeight: expandIcon.implicitHeight + root.verticalPadding * 2
implicitWidth: implicitHeight
radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale)
@@ -130,14 +108,12 @@ Row {
StateLayer {
id: expandStateLayer
function onClicked(): void {
root.toggleDropdown();
}
color: root.textColor
disabled: root.disabled
enabled: root.enabled
rect.bottomLeftRadius: parent.bottomLeftRadius
rect.topLeftRadius: parent.topLeftRadius
onClicked: root.expanded = !root.expanded
}
MaterialIcon {
@@ -145,7 +121,7 @@ Row {
anchors.centerIn: parent
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
text: "expand_more"
@@ -158,24 +134,14 @@ Row {
}
}
}
}
Menu {
id: menu
Menu {
id: menu
anchors.bottomMargin: Appearance.spacing.small
anchors.right: parent.right
anchors.top: parent.bottom
anchors.topMargin: Appearance.spacing.small
states: State {
when: root.menuOnTop
AnchorChanges {
anchors.bottom: expandBtn.top
anchors.top: undefined
target: menu
}
}
}
attachSideY: root.menuOnTop ? Menu.Top : Menu.Bottom
attachTo: expandBtn
marginY: Appearance.spacing.small * (root.menuOnTop ? -1 : 1)
thisSideY: root.menuOnTop ? Menu.Bottom : Menu.Top
}
}
+19 -8
View File
@@ -8,19 +8,32 @@ Item {
id: root
property alias active: splitButton.active
property bool enabled: true
property alias buttonAlias: splitButton
property alias expanded: splitButton.expanded
property int expandedZ: 100
required property string label
property alias menuItems: splitButton.menuItems
property bool shouldBeActive: true
property alias type: splitButton.type
signal selected(item: MenuItem)
Layout.fillWidth: true
Layout.preferredHeight: row.implicitHeight + Appearance.padding.smaller * 2
anchors.left: parent.left
anchors.right: parent.right
clip: false
z: root.expanded ? expandedZ : -1
implicitHeight: row.implicitHeight + Appearance.padding.smaller * 2
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
z: splitButton.menu.implicitHeight > 0 ? expandedZ : 1
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
RowLayout {
id: row
@@ -36,7 +49,6 @@ Item {
color: root.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.larger
text: root.label
z: root.expanded ? root.expandedZ : -1
}
CustomSplitButton {
@@ -44,14 +56,13 @@ Item {
enabled: root.enabled
type: CustomSplitButton.Filled
z: root.expanded ? root.expandedZ : -1
z: 2
menu.onItemSelected: item => {
root.selected(item);
splitButton.closeDropdown();
}
stateLayer.onClicked: {
splitButton.toggleDropdown();
splitButton.expanded = !splitButton.expanded;
}
}
}
+18 -16
View File
@@ -1,6 +1,6 @@
import QtQuick
import QtQuick.Templates
import QtQuick.Shapes
import QtQuick.Templates
import qs.Config
Switch {
@@ -12,38 +12,41 @@ Switch {
implicitWidth: implicitIndicatorWidth
indicator: CustomRect {
color: root.checked ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer)
implicitHeight: 13 + 7 * 2
color: root.checked && root.enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer)
implicitHeight: Appearance.font.size.medium + Appearance.padding.normal * 2
implicitWidth: implicitHeight * 1.7
radius: Appearance.rounding.full
CustomRect {
readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.3 : implicitHeight
readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.2 : implicitHeight
anchors.verticalCenter: parent.verticalCenter
color: root.checked ? DynamicColors.palette.m3onPrimary : DynamicColors.layer(DynamicColors.palette.m3outline, root.cLayer + 1)
implicitHeight: parent.implicitHeight - 10
color: root.checked && root.enabled ? DynamicColors.palette.m3onPrimary : DynamicColors.layer(DynamicColors.palette.m3outline, root.cLayer + 1)
implicitHeight: parent.implicitHeight - Appearance.padding.extraSmall
implicitWidth: nonAnimWidth
radius: Appearance.rounding.full
x: root.checked ? parent.implicitWidth - nonAnimWidth - 10 / 2 : 10 / 2
x: root.checked ? parent.implicitWidth - nonAnimWidth - Appearance.padding.extraSmall / 2 : Appearance.padding.extraSmall / 2
Behavior on implicitWidth {
Anim {
type: Anim.FastSpatial
}
}
Behavior on x {
Anim {
type: Anim.FastSpatial
}
}
CustomRect {
anchors.fill: parent
color: root.checked ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface
color: root.checked && root.enabled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface
opacity: root.pressed ? 0.1 : root.hovered ? 0.08 : 0
radius: parent.radius
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
}
@@ -63,14 +66,14 @@ Switch {
}
property point end2: {
if (root.pressed)
return Qt.point(width, height / 2);
return Qt.point(width * 0.8, height / 2);
if (root.checked)
return Qt.point(width * 0.85, height * 0.2);
return Qt.point(width * 0.85, height * 0.15);
}
property point start1: {
if (root.pressed)
return Qt.point(width * 0.1, height / 2);
return Qt.point(width * 0.2, height / 2);
if (root.checked)
return Qt.point(width * 0.15, height / 2);
return Qt.point(width * 0.15, height * 0.15);
@@ -88,7 +91,7 @@ Switch {
anchors.centerIn: parent
asynchronous: true
height: parent.implicitHeight - Appearance.padding.small * 2
height: parent.implicitHeight - Appearance.padding.larger
preferredRendererType: Shape.CurveRenderer
width: height
@@ -110,11 +113,11 @@ Switch {
}
ShapePath {
capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
capStyle: ShapePath.RoundCap
fillColor: "transparent"
startX: icon.start1.x
startY: icon.start1.y
strokeColor: root.checked ? DynamicColors.palette.m3primary : DynamicColors.palette.m3surfaceContainerHighest
strokeColor: root.checked && root.enabled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3surfaceContainerHighest
strokeWidth: Appearance.font.size.larger * 0.15
Behavior on strokeColor {
@@ -148,8 +151,7 @@ Switch {
}
component PropAnim: PropertyAnimation {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
easing.type: Easing.BezierSpline
duration: Appearance.anim.durations.expressiveFastSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
}
}
+8 -6
View File
@@ -15,6 +15,7 @@ Text {
color: DynamicColors.palette.m3onSurface
font.family: Appearance.font.family.sans
font.pointSize: Appearance.font.size.normal
linkColor: DynamicColors.palette.m3onPrimaryFixedVariant
renderType: Text.NativeRendering
textFormat: Text.PlainText
@@ -26,24 +27,25 @@ Text {
enabled: root.animate
SequentialAnimation {
Anim {
easing.bezierCurve: MaterialEasing.standardAccel
TAnim {
target: root
to: root.animateFrom
type: Anim.FastEffects
}
PropertyAction {
}
Anim {
easing.bezierCurve: MaterialEasing.standardDecel
TAnim {
target: root
to: root.animateTo
type: Anim.DefaultEffects
}
}
}
component Anim: NumberAnimation {
component TAnim: Anim {
duration: root.animateDuration / 2
easing.type: Easing.BezierSpline
properties: root.animateProp.split(",").length > 1 ? root.animateProp : ""
property: root.animateProp.split(",").length === 1 ? root.animateProp : ""
target: root
+2
View File
@@ -7,6 +7,8 @@ import qs.Config
TextField {
id: root
property int cursorHeight: root.height
background: null
color: DynamicColors.palette.m3onSurface
cursorVisible: !readOnly
+2 -1
View File
@@ -1,6 +1,6 @@
import qs.Config
import QtQuick
import QtQuick.Effects
import qs.Config
RectangularShadow {
property real dp: [0, 1, 3, 6, 8, 12][level]
@@ -13,6 +13,7 @@ RectangularShadow {
Behavior on dp {
Anim {
type: Anim.SlowEffects
}
}
}
+23 -56
View File
@@ -1,78 +1,45 @@
import qs.Config
import QtQuick
import qs.Config
CustomRect {
ButtonBase {
id: root
enum Type {
Filled,
Tonal,
Text
}
property color activeColour: type === IconButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondary
property color activeOnColour: type === IconButton.Filled ? DynamicColors.palette.m3onPrimary : type === IconButton.Tonal ? DynamicColors.palette.m3onSecondary : DynamicColors.palette.m3primary
property bool checked
property bool disabled
property color disabledColour: Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
property color disabledOnColour: Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
property alias font: label.font
property alias icon: label.text
property color inactiveColour: {
if (!toggle && type === IconButton.Filled)
readonly property alias label: label
activeColor: type === IconButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondary
activeOnColor: type === IconButton.Filled ? DynamicColors.palette.m3onPrimary : type === IconButton.Tonal ? DynamicColors.palette.m3onSecondary : DynamicColors.palette.m3primary
implicitHeight: {
const h = label.implicitHeight + padding * 2;
if (h % 2 !== 0)
return h + 1;
return h;
}
implicitWidth: implicitHeight
inactiveColor: {
if (!isToggle && type === IconButton.Filled)
return DynamicColors.palette.m3primary;
return type === IconButton.Filled ? DynamicColors.tPalette.m3surfaceContainer : DynamicColors.palette.m3secondaryContainer;
}
property color inactiveOnColour: {
if (!toggle && type === IconButton.Filled)
inactiveOnColor: {
if (!isToggle && type === IconButton.Filled)
return DynamicColors.palette.m3onPrimary;
return type === IconButton.Tonal ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurfaceVariant;
}
property bool internalChecked
property alias label: label
property real padding: type === IconButton.Text ? 10 / 2 : 7
property alias radiusAnim: radiusAnim
property alias stateLayer: stateLayer
property bool toggle
property int type: IconButton.Filled
signal clicked
color: type === IconButton.Text ? "transparent" : disabled ? disabledColour : internalChecked ? activeColour : inactiveColour
implicitHeight: label.implicitHeight + padding * 2
implicitWidth: implicitHeight
radius: internalChecked ? 6 : (implicitHeight / 2 * Math.min(1, 1)) * Appearance.rounding.scale
Behavior on radius {
Anim {
id: radiusAnim
}
}
onCheckedChanged: internalChecked = checked
StateLayer {
id: stateLayer
function onClicked(): void {
if (root.toggle)
root.internalChecked = !root.internalChecked;
root.clicked();
}
color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour
disabled: root.disabled
}
padding: type === IconButton.Text ? Appearance.padding.extraSmall / 2 : Appearance.padding.small
MaterialIcon {
id: label
anchors.centerIn: parent
color: root.disabled ? root.disabledOnColour : root.internalChecked ? root.activeOnColour : root.inactiveOnColour
fill: !root.toggle || root.internalChecked ? 1 : 0
anchors.verticalCenterOffset: 1
color: root.onColor
fill: !root.isToggle || root.internalChecked ? 1 : 0
font.pointSize: root.font.pointSize
Behavior on fill {
Anim {
type: Anim.DefaultEffects
}
}
}
+70
View File
@@ -0,0 +1,70 @@
import QtQuick
import QtQuick.Layouts
import qs.Config
ButtonBase {
id: root
property alias icon: iconLabel.text
readonly property alias iconLabel: iconLabel
readonly property alias label: label
property alias text: label.text
activeColor: type === TextButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondary
activeOnColor: {
if (type === TextButton.Text)
return DynamicColors.palette.m3primary;
return type === TextButton.Filled ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSecondary;
}
horizontalPadding: Appearance.padding.larger
implicitHeight: row.implicitHeight + verticalPadding * 2
implicitWidth: row.implicitWidth + horizontalPadding * 2
inactiveColor: {
if (!isToggle && type === TextButton.Filled)
return DynamicColors.palette.m3primary;
return type === TextButton.Filled ? DynamicColors.tPalette.m3surfaceContainer : DynamicColors.palette.m3secondaryContainer;
}
inactiveOnColor: {
if (!isToggle && type === TextButton.Filled)
return DynamicColors.palette.m3onPrimary;
if (type === TextButton.Text)
return DynamicColors.palette.m3primary;
return type === TextButton.Filled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer;
}
verticalPadding: Appearance.padding.small
RowLayout {
id: row
anchors.centerIn: parent
spacing: Appearance.spacing.small
MaterialIcon {
id: iconLabel
Layout.alignment: Qt.AlignVCenter
color: root.onColor
fill: root.internalChecked ? 1 : 0
font: {
const f = Qt.font(root.font);
f.pointSize = Math.round(root.font.pointSize * 1.2);
f.family = "Material Symbols Rounded";
return f;
}
Behavior on fill {
Anim {
type: Anim.DefaultEffects
}
}
}
CustomText {
id: label
Layout.alignment: Qt.AlignVCenter
color: root.onColor
font: root.font
}
}
}
+14 -3
View File
@@ -1,15 +1,26 @@
import qs.Config
CustomText {
property real fill
id: root
readonly property list<int> allowed: [20, 24, 40, 48]
property real fill: 0
property int grade: DynamicColors.light ? 0 : -25
readonly property int opsz: clampOpsz(optical)
property int optical: fontInfo.pixelSize
function clampOpsz(value): int {
return allowed.reduce((closest, current) => {
return Math.abs(current - value) < Math.abs(closest - value) ? current : closest;
});
}
font.family: "Material Symbols Rounded"
font.pointSize: Appearance.font.size.larger
font.variableAxes: ({
FILL: fill.toFixed(1),
FILL: fill,
GRAD: grade,
opsz: fontInfo.pixelSize,
opsz: opsz,
wght: fontInfo.weight
})
}
+145 -69
View File
@@ -2,109 +2,185 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.Config
import qs.Drawers
Elevation {
MouseArea {
id: root
enum Side {
Top,
Bottom,
Left,
Right
}
property MenuItem active: items[0] ?? null
property int attachSideX: Menu.Right
property int attachSideY: Menu.Bottom
required property Item attachTo
property bool expanded
property list<MenuItem> items
property real marginX
property real marginY
property int thisSideX: Menu.Right
property int thisSideY: Menu.Top
signal itemSelected(item: MenuItem)
implicitHeight: root.expanded ? column.implicitHeight + Appearance.padding.small * 2 : 0
implicitWidth: Math.max(200, column.implicitWidth)
level: 2
opacity: root.expanded ? 1 : 0
radius: Appearance.rounding.normal
Behavior on implicitHeight {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
anchors.fill: parent
cursorShape: undefined
enabled: expanded
layer.enabled: opacity < 1
opacity: expanded ? 1 : 0
parent: {
const win = QsWindow.window;
const contentWin = win as Windows;
return contentWin ? contentWin.interactionWrapper : (win as QsWindow).contentItem;
}
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
type: Anim.DefaultEffects
}
}
CustomClippingRect {
anchors.fill: parent
color: DynamicColors.palette.m3surfaceContainer
radius: parent.radius
onClicked: expanded = false
onExpandedChanged: {
const win = QsWindow.window;
const contentWin = win as Windows;
if (expanded) {
contentWin.menuRegion.x = menu.x;
contentWin.menuRegion.y = menu.y;
contentWin.menuRegion.width = menu.width;
contentWin.menuRegion.height = menu.height;
} else {
contentWin.menuRegion.x = 0;
contentWin.menuRegion.y = 0;
contentWin.menuRegion.width = 0;
contentWin.menuRegion.height = 0;
}
}
ColumnLayout {
id: column
TransformWatcher {
id: watcher
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 5
a: root.parent
b: root.attachTo
}
Repeater {
model: root.items
Elevation {
id: menu
CustomRect {
id: item
implicitHeight: column.implicitHeight + column.anchors.margins * 2
implicitWidth: Math.max(200, column.implicitWidth + column.anchors.margins * 2)
level: 2
radius: Appearance.rounding.medium
x: {
watcher.transform;
const item = root.attachTo;
let off = root.attachSideX === Menu.Left ? 0 : item.width;
if (root.thisSideX === Menu.Right)
off -= width;
return item.mapToItem(root.parent, off, 0).x + root.marginX;
}
y: {
watcher.transform;
const item = root.attachTo;
let off = root.attachSideY === Menu.Top ? 0 : item.height;
if (root.thisSideY === Menu.Bottom)
off -= height;
return item.mapToItem(root.parent, 0, off).y + root.marginY;
}
readonly property bool active: modelData === root.active
required property int index
required property MenuItem modelData
transform: Scale {
origin.y: root.thisSideY === Menu.Bottom ? menu.height : 0
yScale: root.expanded ? 1 : 0.1
Layout.fillWidth: true
implicitHeight: menuOptionRow.implicitHeight + Appearance.padding.normal * 2
implicitWidth: menuOptionRow.implicitWidth + Appearance.padding.normal * 2
Behavior on yScale {
Anim {
}
}
}
CustomRect {
anchors.fill: parent
color: DynamicColors.palette.m3surfaceContainerLow
radius: parent.radius
ColumnLayout {
id: column
anchors.fill: parent
anchors.margins: Appearance.padding.extraSmall
spacing: Appearance.spacing.extraSmall
Repeater {
id: repeater
model: root.items
CustomRect {
anchors.fill: parent
anchors.leftMargin: Appearance.padding.small
anchors.rightMargin: Appearance.padding.small
color: Qt.alpha(DynamicColors.palette.m3secondaryContainer, active ? 1 : 0)
radius: Appearance.rounding.normal - Appearance.padding.small
id: item
readonly property bool active: modelData === root.active
required property int index
required property MenuItem modelData
Layout.fillWidth: true
color: Qt.alpha(DynamicColors.palette.m3tertiaryContainer, active ? 1 : 0)
implicitHeight: menuOptionRow.implicitHeight + Appearance.padding.larger * 2
implicitWidth: menuOptionRow.implicitWidth + Appearance.padding.larger * 2
radius: Appearance.rounding.small
Behavior on radius {
Anim {
}
}
StateLayer {
function onClicked(): void {
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurface
enabled: root.expanded
onClicked: {
root.itemSelected(item.modelData);
root.active = item.modelData;
item.modelData.clicked();
root.expanded = false;
}
color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
disabled: !root.expanded
}
}
RowLayout {
id: menuOptionRow
anchors.fill: parent
anchors.margins: Appearance.padding.normal
spacing: Appearance.spacing.small
MaterialIcon {
Layout.alignment: Qt.AlignVCenter
color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurfaceVariant
text: item.modelData.icon
}
CustomText {
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
text: item.modelData.text
}
RowLayout {
id: menuOptionRow
Loader {
Layout.alignment: Qt.AlignVCenter
active: item.modelData.trailingIcon.length > 0
visible: active
anchors.fill: parent
anchors.margins: Appearance.padding.larger
spacing: Appearance.spacing.small
sourceComponent: MaterialIcon {
color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
text: item.modelData.trailingIcon
MaterialIcon {
Layout.alignment: Qt.AlignVCenter
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurfaceVariant
text: item.modelData.icon
}
CustomText {
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurface
text: item.modelData.text
}
Loader {
Layout.alignment: Qt.AlignVCenter
active: item.modelData.trailingIcon.length > 0
asynchronous: true
visible: active
sourceComponent: MaterialIcon {
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurfaceVariant
text: item.modelData.trailingIcon
}
}
}
}
+173 -70
View File
@@ -1,95 +1,198 @@
import qs.Config
import QtQuick
import QtQuick.Shapes
import ZShell
import ZShell.Components
import qs.Helpers
import qs.Config
MouseArea {
id: root
property color color: DynamicColors.palette.m3onSurface
property bool disabled
property real radius: parent?.radius ?? 0
property alias rect: hoverLayer
property alias bottomLeftRadius: base.bottomLeftRadius
property alias bottomRightRadius: base.bottomRightRadius
property real circleRadius
property alias color: base.color
readonly property real endRadius: {
const d1 = distSq(0, 0);
const d2 = distSq(width, 0);
const d3 = distSq(0, height);
const d4 = distSq(width, height);
return (Math.sqrt(Math.max(d1, d2, d3, d4)) + (shapeMorph ? 24 : 0)) * 1.3;
}
property real endRadiusAtPress
property bool manualPressOverride
property real pressX: width / 2
property real pressY: height / 2
property alias radius: base.radius
readonly property alias rect: base
property bool shapeMorph
property bool showHoverBackground: true
property real stateOpacity: containsMouse ? 0.08 : 0
property alias topLeftRadius: base.topLeftRadius
property alias topRightRadius: base.topRightRadius
function onClicked(): void {
function clamp(r: real): real {
return Math.max(0, Math.min(r, width / 2, height / 2));
}
function distSq(x: real, y: real): real {
return (pressX - x) ** 2 + (pressY - y) ** 2;
}
function press(x: real, y: real): void {
pressX = x;
pressY = y;
fadeAnim.complete();
circleRadius = 0;
circle.opacity = 0.1;
rippleAnim.restart();
endRadiusAtPress = endRadius;
}
anchors.fill: parent
cursorShape: disabled ? undefined : Qt.PointingHandCursor
enabled: !disabled
cursorShape: !enabled ? undefined : Qt.PointingHandCursor
enabled: parent.enabled && !Visibilities.getForActive().isDrawing
hoverEnabled: true
onClicked: event => !disabled && onClicked(event)
onPressed: event => {
if (disabled)
return;
rippleAnim.x = event.x;
rippleAnim.y = event.y;
const dist = (ox, oy) => ox * ox + oy * oy;
rippleAnim.radius = Math.sqrt(Math.max(dist(event.x, event.y), dist(event.x, height - event.y), dist(width - event.x, event.y), dist(width - event.x, height - event.y)));
rippleAnim.restart();
Behavior on stateOpacity {
Anim {
type: Anim.DefaultEffects
}
}
SequentialAnimation {
onCircleRadiusChanged: {
if (!(pressed || manualPressOverride) && circleRadius > endRadiusAtPress * 0.99 && !fadeAnim.running)
fadeAnim.start();
}
onManualPressOverrideChanged: {
if (!(pressed || manualPressOverride) && circleRadius > endRadiusAtPress * 0.99 && !fadeAnim.running)
fadeAnim.start();
}
onPressed: e => press(e.x, e.y)
onPressedChanged: {
if (!(pressed || manualPressOverride) && !rippleAnim.running && circle.opacity > 0)
fadeAnim.start();
}
Anim {
id: rippleAnim
property real radius
property real x
property real y
PropertyAction {
property: "x"
target: ripple
value: rippleAnim.x
}
PropertyAction {
property: "y"
target: ripple
value: rippleAnim.y
}
PropertyAction {
property: "opacity"
target: ripple
value: 0.08
}
Anim {
easing.bezierCurve: MaterialEasing.standardDecel
from: 0
properties: "implicitWidth,implicitHeight"
target: ripple
to: rippleAnim.radius * 2
}
Anim {
property: "opacity"
target: ripple
to: 0
}
alwaysRunToEnd: true
duration: Appearance.anim.durations.expressiveSlowEffects * 2
easing.bezierCurve: Appearance.anim.curves.standard
property: "circleRadius"
target: root
to: root.endRadius
}
CustomClippingRect {
id: hoverLayer
Anim {
id: fadeAnim
property: "opacity"
target: circle
to: 0
type: Anim.SlowEffects
}
CustomRect {
id: base
anchors.fill: parent
border.pixelAligned: false
color: Qt.alpha(root.color, root.disabled ? 0 : root.pressed ? 0.1 : root.containsMouse ? 0.08 : 0)
radius: root.radius
bottomLeftRadius: root.parent?.bottomLeftRadius ?? radius ?? 0
bottomRightRadius: root.parent?.bottomRightRadius ?? radius ?? 0
color: DynamicColors.palette.m3onSurface
opacity: root.stateOpacity
// Pick up radius from parent if it has one (parent can be anything with radius props)
// qmllint disable missing-property
radius: root.parent?.radius ?? 0
topLeftRadius: root.parent?.topLeftRadius ?? radius ?? 0
topRightRadius: root.parent?.topRightRadius ?? radius ?? 0
// qmllint enable missing-property
}
CustomRect {
id: ripple
Shape {
id: circle
border.pixelAligned: false
color: root.color
opacity: 0
radius: Appearance.rounding.full
anchors.fill: parent
opacity: 0
preferredRendererType: Shape.CurveRenderer
transform: Translate {
x: -ripple.width / 2
y: -ripple.height / 2
ShapePath {
fillColor: base.color
startX: root.clamp(base.topLeftRadius)
startY: 0
strokeColor: "transparent"
strokeWidth: 0
fillGradient: RadialGradient {
centerRadius: root.circleRadius
centerX: root.pressX
centerY: root.pressY
focalX: centerX
focalY: centerY
GradientStop {
color: Qt.alpha(base.color, 1)
position: 0
}
GradientStop {
color: Qt.alpha(base.color, 1)
position: ZUtils.clamp(1 - 0.2 * root.endRadius / root.circleRadius, 0.01, 0.99)
}
GradientStop {
color: Qt.alpha(base.color, ZUtils.clamp((root.circleRadius / root.endRadius - 0.9) / 0.1, 0, 1))
position: 1
}
}
PathLine {
x: root.width - root.clamp(base.topRightRadius)
y: 0
}
PathArc {
radiusX: root.clamp(base.topRightRadius)
radiusY: root.clamp(base.topRightRadius)
relativeX: root.clamp(base.topRightRadius)
relativeY: root.clamp(base.topRightRadius)
}
PathLine {
x: root.width
y: root.height - root.clamp(base.bottomRightRadius)
}
PathArc {
radiusX: root.clamp(base.bottomRightRadius)
radiusY: root.clamp(base.bottomRightRadius)
relativeX: -root.clamp(base.bottomRightRadius)
relativeY: root.clamp(base.bottomRightRadius)
}
PathLine {
x: root.clamp(base.bottomLeftRadius)
y: root.height
}
PathArc {
radiusX: root.clamp(base.bottomLeftRadius)
radiusY: root.clamp(base.bottomLeftRadius)
relativeX: -root.clamp(base.bottomLeftRadius)
relativeY: -root.clamp(base.bottomLeftRadius)
}
PathLine {
x: 0
y: root.clamp(base.topLeftRadius)
}
PathArc {
radiusX: root.clamp(base.topLeftRadius)
radiusY: root.clamp(base.topLeftRadius)
x: root.clamp(base.topLeftRadius)
y: 0
}
}
}
+40
View File
@@ -0,0 +1,40 @@
import QtQuick
import qs.Config
ButtonBase {
id: root
readonly property alias label: label
property alias text: label.text
activeColor: type === TextButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondary
activeOnColor: {
if (type === TextButton.Text)
return DynamicColors.palette.m3primary;
return type === TextButton.Filled ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSecondary;
}
horizontalPadding: Appearance.padding.normal
implicitHeight: label.implicitHeight + verticalPadding * 2
implicitWidth: label.implicitWidth + horizontalPadding * 2
inactiveColor: {
if (!isToggle && type === TextButton.Filled)
return DynamicColors.palette.m3primary;
return type === TextButton.Filled ? DynamicColors.tPalette.m3surfaceContainer : DynamicColors.palette.m3secondaryContainer;
}
inactiveOnColor: {
if (!isToggle && type === TextButton.Filled)
return DynamicColors.palette.m3onPrimary;
if (type === TextButton.Text)
return DynamicColors.palette.m3primary;
return type === TextButton.Filled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer;
}
verticalPadding: Appearance.padding.small
CustomText {
id: label
anchors.centerIn: parent
color: root.onColor
font: root.font
}
}
-2
View File
@@ -7,8 +7,6 @@ Singleton {
readonly property AppearanceConf.Deform deform: Config.appearance.deform
readonly property AppearanceConf.FontStuff font: Config.appearance.font
readonly property AppearanceConf.Padding padding: Config.appearance.padding
// Literally just here to shorten accessing stuff :woe:
// Also kinda so I can keep accessing it with `Appearance.xxx` instead of `Conf.appearance.xxx`
readonly property AppearanceConf.Rounding rounding: Config.appearance.rounding
readonly property AppearanceConf.Spacing spacing: Config.appearance.spacing
readonly property AppearanceConf.Transparency transparency: Config.appearance.transparency
+18 -4
View File
@@ -28,9 +28,13 @@ JsonObject {
property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1]
property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1]
property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1]
property list<real> expressiveDefaultEffects: [0.34, 0.8, 0.34, 1, 1, 1]
property list<real> expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1, 1, 1]
property list<real> expressiveEffects: [0.34, 0.8, 0.34, 1, 1, 1]
property list<real> expressiveFastEffects: [0.31, 0.94, 0.34, 1, 1, 1]
property list<real> expressiveFastSpatial: [0.42, 1.67, 0.21, 0.9, 1, 1]
property list<real> expressiveSlowEffects: [0.34, 0.88, 0.34, 1, 1, 1]
property list<real> expressiveSlowSpatial: [0.39, 1.29, 0.35, 0.98, 1, 1]
property list<real> standard: [0.2, 0, 0, 1, 1, 1]
property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1]
property list<real> standardDecel: [0, 0, 0, 1, 1, 1]
@@ -38,7 +42,9 @@ JsonObject {
component AnimDurations: JsonObject {
property int expressiveDefaultSpatial: 500 * scale
property int expressiveEffects: 200 * scale
property int expressiveFastEffects: 150 * scale
property int expressiveFastSpatial: 350 * scale
property int expressiveSlowEffects: 300 * scale
property int extraLarge: 1000 * scale
property int large: 600 * scale
property int normal: 400 * scale
@@ -57,7 +63,8 @@ JsonObject {
component FontSize: JsonObject {
property int extraLarge: 28 * scale
property int large: 18 * scale
property int larger: 15 * scale
property int larger: 16 * scale
property int medium: 14 * scale
property int normal: 13 * scale
property real scale: 1
property int small: 11 * scale
@@ -70,23 +77,30 @@ JsonObject {
}
}
component Padding: JsonObject {
property int large: 15 * scale
property int larger: 13 * scale
property int normal: 9 * scale
property int extraLarge: 28 * scale
property int extraLargeIncreased: 32 * scale
property int extraSmall: 4 * scale
property int large: 16 * scale
property int largeIncreased: 20 * scale
property int larger: 12 * scale
property int normal: 8 * scale
property real scale: 1
property int small: 5 * scale
property int smaller: 7 * scale
property int smallest: 2 * scale
}
component Rounding: JsonObject {
property int extraSmall: 4 * scale
property int full: 1000 * scale
property int large: 24 * scale
property int medium: 16 * scale
property int normal: 18 * scale
property real scale: 1
property int small: 12 * scale
property int smallest: 8 * scale
}
component Spacing: JsonObject {
property int extraSmall: 4 * scale
property int large: 20 * scale
property int larger: 16 * scale
property int normal: 12 * scale
+5
View File
@@ -59,6 +59,8 @@ JsonObject {
}
property int rounding: 8
property int smoothing: 32
property Tray tray: Tray {
}
component Popouts: JsonObject {
property bool activeWindow: true
@@ -69,4 +71,7 @@ JsonObject {
property bool tray: true
property bool upower: true
}
component Tray: JsonObject {
property int trayIconSize: 24
}
}
+15
View File
@@ -0,0 +1,15 @@
import Quickshell.Io
JsonObject {
property bool enabled: true
property int maxEntriesShown: 10
property Sizes sizes: Sizes {
}
component Sizes: JsonObject {
property int itemHeight: 60
property int minPreviewWidth: 200
property int previewWidth: 500
property int width: 500
}
}
+8
View File
@@ -1,5 +1,13 @@
import Quickshell.Io
JsonObject {
property Presets presets: Presets {
}
property string schemeType: "vibrant"
component Presets: JsonObject {
property string accent: ""
property string name: ""
property string variant: ""
}
}
+39 -7
View File
@@ -13,6 +13,7 @@ Singleton {
property alias appearance: adapter.appearance
property alias background: adapter.background
property alias barConfig: adapter.barConfig
property alias clipboard: adapter.clipboard
property alias colors: adapter.colors
property alias dashboard: adapter.dashboard
property alias dock: adapter.dock
@@ -100,6 +101,9 @@ Singleton {
border: barConfig.border,
smoothing: barConfig.smoothing,
height: barConfig.height,
tray: {
trayIconSize: barConfig.tray.trayIconSize
},
popouts: {
tray: barConfig.popouts.tray,
audio: barConfig.popouts.audio,
@@ -113,9 +117,27 @@ Singleton {
};
}
function serializeClipboard(): var {
return {
enabled: clipboard.enabled,
maxEntriesShown: clipboard.maxEntriesShown,
sizes: {
width: clipboard.sizes.width,
previewWidth: clipboard.sizes.previewWidth,
minPreviewWidth: clipboard.sizes.minPreviewWidth,
itemHeight: clipboard.sizes.itemHeight
}
};
}
function serializeColors(): var {
return {
schemeType: colors.schemeType
schemeType: colors.schemeType,
presets: {
name: colors.presets.name,
variant: colors.presets.variant,
accent: colors.presets.accent
}
};
}
@@ -135,7 +157,8 @@ Singleton {
launcher: serializeLauncher(),
colors: serializeColors(),
dock: serializeDock(),
screenshot: serializeScreenshot()
screenshot: serializeScreenshot(),
clipboard: serializeClipboard()
};
}
@@ -165,7 +188,7 @@ Singleton {
resourceProgessThickness: dashboard.sizes.resourceProgessThickness,
weatherWidth: dashboard.sizes.weatherWidth,
mediaCoverArtSize: dashboard.sizes.mediaCoverArtSize,
mediaVisualiserSize: dashboard.sizes.mediaVisualiserSize,
mediaVisualizerSize: dashboard.sizes.mediaVisualizerSize,
resourceSize: dashboard.sizes.resourceSize
}
};
@@ -186,6 +209,7 @@ Singleton {
return {
logo: general.logo,
wallpaperPath: general.wallpaperPath,
showOverFullscreen: general.showOverFullscreen,
desktopIcons: general.desktopIcons,
dateFormat: general.dateFormat,
color: {
@@ -209,6 +233,10 @@ Singleton {
},
idle: {
timeouts: general.idle.timeouts
},
battery: {
popupThresholds: general.battery.popupThresholds,
critPerc: general.battery.critPerc
}
};
}
@@ -242,6 +270,7 @@ Singleton {
recolorLogo: lock.recolorLogo,
enableFprint: lock.enableFprint,
showNotifContent: lock.showNotifContent,
showNotifIcon: lock.showNotifIcon,
maxFprintTries: lock.maxFprintTries,
blurAmount: lock.blurAmount,
sizes: {
@@ -287,10 +316,10 @@ Singleton {
return {
enable_pp: screenshot.enable_pp,
mode: screenshot.mode,
corner_radius: screenshot.corner_radius,
drop_shadow: screenshot.drop_shadow,
rounded_corners: screenshot.rounded_corners,
shadow_blur_radius: screenshot.shadow_blur_radius,
radius: screenshot.radius,
shadow: screenshot.shadow,
rounding: screenshot.rounding,
shadow_blur: screenshot.shadow_blur,
shadow_color: screenshot.shadow_color,
shadow_offset_x: screenshot.shadow_offset_x,
shadow_offset_y: screenshot.shadow_offset_y
@@ -302,6 +331,7 @@ Singleton {
weatherLocation: services.weatherLocation,
updates: services.updates,
useFahrenheit: services.useFahrenheit,
minBrightness: services.minBrightness,
ddcutilService: services.ddcutilService,
useTwelveHourClock: services.useTwelveHourClock,
gpuType: services.gpuType,
@@ -434,6 +464,8 @@ Singleton {
}
property BarConfig barConfig: BarConfig {
}
property ClipboardConfig clipboard: ClipboardConfig {
}
property Colors colors: Colors {
}
property DashboardConfig dashboard: DashboardConfig {
+1 -1
View File
@@ -25,7 +25,7 @@ JsonObject {
readonly property int mediaCoverArtSize: 150
readonly property int mediaProgressSweep: 180
readonly property int mediaProgressThickness: 8
readonly property int mediaVisualiserSize: 80
readonly property int mediaVisualizerSize: 200
readonly property int mediaWidth: 200
readonly property int resourceProgessThickness: 10
readonly property int resourceSize: 200
+7 -2
View File
@@ -30,9 +30,10 @@ Singleton {
readonly property alias wallLuminance: analyser.luminance
function alterColor(c: color, a: real, layer: int): color {
const luminance = getLuminance(c);
const initLuminance = getLuminance(c);
const luminance = Math.max(initLuminance, 0.001);
const offset = (!light || layer == 1 ? 1 : -layer / 2) * (light ? 0.2 : 0.3) * (1 - transparency.base) * (1 + wallLuminance * (light ? (layer == 1 ? 3 : 1) : 2.5));
const offset = (!light || layer == 1 ? 1 : -layer / 2) * (light ? 0.2 : 0.3) * (0.2 + 0.3 * (1 - transparency.base)) * (1 + wallLuminance * (light ? (layer == 1 ? 3 : 1) : 2.5));
const scale = (luminance + offset) / luminance;
const r = Math.max(0, Math.min(1, c.r * scale));
const g = Math.max(0, Math.min(1, c.g * scale));
@@ -114,6 +115,10 @@ Singleton {
Config.save();
}
function swapRG(c: color): color {
return Qt.rgba(c.g, c.r, c.b, c.a);
}
Component.onCompleted: debounceTimer.triggered()
Connections {
+18
View File
@@ -4,6 +4,8 @@ import Quickshell
JsonObject {
property Apps apps: Apps {
}
property Battery battery: Battery {
}
property Color color: Color {
}
property string dateFormat: "ddd d MMM - hh:mm:ss"
@@ -11,14 +13,30 @@ JsonObject {
property Idle idle: Idle {
}
property string logo: ""
property bool showOverFullscreen: true
property string wallpaperPath: Quickshell.env("HOME") + "/Pictures/Wallpapers"
component Apps: JsonObject {
property list<string> archiver: ["ark"]
property list<string> audio: ["pavucontrol"]
property list<string> document: ["libreoffice"]
property list<string> editor: ["kate"]
property list<string> explorer: ["dolphin"]
property list<string> image: ["imv"]
property list<string> playback: ["mpv"]
property list<string> terminal: ["kitty"]
}
component Battery: JsonObject {
property int critPerc: 5
property list<var> popupThresholds: [
{
perc: 20,
name: qsTr("Low battery"),
message: qsTr("Battery is low"),
icon: "battery_android_frame_2"
},
]
}
component Color: JsonObject {
property int hyprsunsetTemp: 5000
property string mode: "dark"
+1
View File
@@ -6,6 +6,7 @@ JsonObject {
property int maxFprintTries: 3
property bool recolorLogo: false
property bool showNotifContent: false
property bool showNotifIcon: true
property Sizes sizes: Sizes {
}
+4 -4
View File
@@ -1,12 +1,12 @@
import Quickshell.Io
JsonObject {
property real corner_radius: 12.0
property bool drop_shadow: true
property bool enable_pp: true
property string mode: "manual"
property bool rounded_corners: false
property real shadow_blur_radius: 22.0
property real radius: 12.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 real shadow_offset_x: 5.0
property real shadow_offset_y: 5.0
+1
View File
@@ -8,6 +8,7 @@ JsonObject {
property string defaultPlayer: "Spotify"
property string gpuType: ""
property real maxVolume: 1.0
property real minBrightness: 0.01
property list<var> playerAliases: [
{
"from": "com.github.th_ch.youtube_music",
+70
View File
@@ -0,0 +1,70 @@
import Quickshell
import Quickshell.Services.UPower
import QtQuick
import ZShell
import qs.Config
import qs.Components.Toast
import qs.Helpers
Scope {
id: root
readonly property list<var> popupThresholds: [...Config.general.battery.popupThresholds].sort((a, b) => b.perc - a.perc)
function nearestThresholdAbove(p: real): var {
const thresholds = [...root.popupThresholds];
for (const perc of thresholds) {
if (p < perc.perc)
return perc;
}
return null;
}
Connections {
function onOnBatteryChanged(): void {
if (!Battery.ready)
return;
if (Battery.onBattery) {
if (Config.utilities.toasts.chargingChanged)
Toaster.toast(qsTr("Charger unplugged"), qsTr("Battery is discharging"), "power_off");
const p = Battery.currentPerc * 100;
const perc = root.nearestThresholdAbove(p);
if (perc)
Toaster.toast(perc.title ?? qsTr("Battery warning"), perc.message ?? qsTr("Battery is low"), perc.icon ?? "battery_android_alert", perc.critical ? Toast.Error : Toast.Warning);
} else {
if (Config.utilities.toasts.chargingChanged)
Toaster.toast(qsTr("Charger plugged in"), qsTr("Battery is charging"), "power");
}
}
target: Battery
}
Connections {
function onCurrentPercChanged(): void {
if (!Battery.onBattery)
return;
const p = Battery.currentPerc * 100;
for (const perc of root.popupThresholds) {
if (p == perc.perc) {
Toaster.toast(perc.title ?? qsTr("Battery warning"), perc.message ?? qsTr("Battery perc is low"), perc.icon ?? "battery_android_alert", perc.critical ? Toast.Error : Toast.Warning);
}
}
}
function onReadyChanged(): void {
if (!Battery.ready)
return;
const p = Battery.currentPerc * 100;
const perc = root.nearestThresholdAbove(p);
if (perc)
Toaster.toast(perc.title ?? qsTr("Battery warning"), perc.message ?? qsTr("Battery is low"), perc.icon ?? "battery_android_alert", perc.critical ? Toast.Error : Toast.Warning);
}
target: Battery
}
}
+38 -57
View File
@@ -179,6 +179,8 @@ Singleton {
property string appIcon
property string appName
property string body
property string cachedImageSource: ""
property bool cachingImage: false
property bool closed
readonly property Connections conn: Connections {
function onActionsChanged(): void {
@@ -213,10 +215,14 @@ Singleton {
notif.hasActionIcons = notif.notification.hasActionIcons;
}
function onHintsChanged(): void {
notif.hints = notif.notification.hints;
}
function onImageChanged(): void {
notif.image = notif.notification.image;
if (notif.notification?.image)
notif.dummyImageLoader.active = true;
notif.imageSource = notif.notification.image || "";
notif.image = notif.imageSource;
notif.cacheImageIfNeeded();
}
function onResidentChanged(): void {
@@ -233,60 +239,13 @@ Singleton {
target: notif.notification
}
readonly property LazyLoader dummyImageLoader: LazyLoader {
active: false
PanelWindow {
color: "transparent"
implicitHeight: Config.notifs.sizes.image
implicitWidth: Config.notifs.sizes.image
mask: Region {
}
Image {
function tryCache(): void {
if (status !== Image.Ready || width != Config.notifs.sizes.image || height != Config.notifs.sizes.image)
return;
const cacheKey = notif.appName + notif.summary + notif.id;
let h1 = 0xdeadbeef, h2 = 0x41c6ce57, ch;
for (let i = 0; i < cacheKey.length; i++) {
ch = cacheKey.charCodeAt(i);
h1 = Math.imul(h1 ^ ch, 2654435761);
h2 = Math.imul(h2 ^ ch, 1597334677);
}
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
const hash = (h2 >>> 0).toString(16).padStart(8, 0) + (h1 >>> 0).toString(16).padStart(8, 0);
const cache = `${Paths.notifimagecache}/${hash}.png`;
ZShellIo.saveItem(this, Qt.resolvedUrl(cache), () => {
notif.image = cache;
notif.dummyImageLoader.active = false;
});
}
anchors.fill: parent
asynchronous: true
cache: false
fillMode: Image.PreserveAspectCrop
opacity: 0
source: Qt.resolvedUrl(notif.image)
onHeightChanged: tryCache()
onStatusChanged: tryCache()
onWidthChanged: tryCache()
}
}
}
property real expireTimeout: 5
property bool hasActionIcons
property string id
property var hints
property string image
property string imageSource
property var locks: new Set()
property string notifId
property Notification notification
property bool popup
property bool resident
@@ -329,6 +288,26 @@ Singleton {
}
property int urgency: NotificationUrgency.Normal
function cacheImageIfNeeded(): void {
const source = imageSource;
if (!source || cachingImage)
return;
if (cachedImageSource === source)
return;
cachingImage = true;
ZShellIo.cacheImage(Qt.resolvedUrl(source), Paths.notifimagecache, (path, url) => {
cachedImageSource = source;
image = path;
cachingImage = false;
}, () => {
cachingImage = false;
});
}
function close(): void {
closed = true;
if (locks.size === 0 && root.list.includes(this)) {
@@ -352,14 +331,14 @@ Singleton {
if (!notification)
return;
id = notification.id;
notifId = notification.id;
summary = notification.summary;
body = notification.body;
appIcon = notification.appIcon;
appName = notification.appName;
image = notification.image;
if (notification?.image)
dummyImageLoader.active = true;
imageSource = notification.image || "";
image = imageSource;
hints = notification.hints;
expireTimeout = notification.expireTimeout;
urgency = notification.urgency;
resident = notification.resident;
@@ -369,6 +348,8 @@ Singleton {
text: a.text,
invoke: () => a.invoke()
}));
cacheImageIfNeeded();
}
}
}
-107
View File
@@ -1,107 +0,0 @@
import Quickshell
import QtQuick
import QtQuick.Shapes
import qs.Components
import qs.Config
import qs.Modules as Modules
import qs.Modules.Notifications as Notifications
import qs.Modules.Notifications.Sidebar as Sidebar
import qs.Modules.Notifications.Sidebar.Utils as Utils
import qs.Modules.Dashboard as Dashboard
import qs.Modules.Osd as Osd
import qs.Modules.Launcher as Launcher
import qs.Modules.Resources as Resources
import qs.Modules.Drawing as Drawing
import qs.Modules.Settings as Settings
import qs.Modules.Dock as Dock
Shape {
id: root
required property Item bar
required property Panels panels
required property PersistentProperties visibilities
anchors.fill: parent
anchors.margins: Config.barConfig.border
anchors.topMargin: bar.implicitHeight
asynchronous: true
preferredRendererType: Shape.CurveRenderer
Drawing.Background {
startX: 0
startY: wrapper.y - rounding
wrapper: root.panels.drawing
}
Resources.Background {
startX: 0 - rounding
startY: 0
wrapper: root.panels.resources
}
Osd.Background {
startX: root.width - root.panels.sidebar.width
startY: (root.height - wrapper.height) / 2 - rounding
wrapper: root.panels.osd
}
Modules.Background {
invertBottomRounding: wrapper.x <= 0
rounding: root.panels.popouts.currentName.startsWith("updates") || root.panels.popouts.currentName.startsWith("audio") ? Appearance.rounding.normal : Appearance.rounding.smallest
startX: wrapper.x - rounding
startY: wrapper.y
wrapper: root.panels.popouts
}
Notifications.Background {
sidebar: sidebar
startX: root.width
startY: 0
wrapper: root.panels.notifications
}
Launcher.Background {
startX: (root.width - wrapper.width) / 2 - rounding
startY: root.height
wrapper: root.panels.launcher
}
Dashboard.Background {
startX: root.width - root.panels.dashboard.width - rounding
startY: 0
wrapper: root.panels.dashboard
}
Utils.Background {
sidebar: sidebar
startX: root.width
startY: root.height
wrapper: root.panels.utilities
}
Sidebar.Background {
id: sidebar
panels: root.panels
startX: root.width
startY: root.panels.notifications.height
wrapper: root.panels.sidebar
}
Settings.Background {
id: settings
startX: (root.width - wrapper.width) / 2 - rounding
startY: 0
wrapper: root.panels.settings
}
Dock.Background {
id: dock
startX: (root.width - wrapper.width) / 2 - rounding
startY: root.height
wrapper: root.panels.dock
}
}
+25
View File
@@ -0,0 +1,25 @@
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
Variants {
model: Quickshell.screens
Scope {
id: scope
required property ShellScreen modelData
Exclusions {
bar: content.bar
screen: scope.modelData
}
Windows {
id: content
screen: scope.modelData
}
}
}
+21 -26
View File
@@ -1,35 +1,30 @@
import QtQuick
pragma ComponentBehavior: Bound
Canvas {
import Quickshell
import QtQuick
import ZShell.Internal
Item {
id: root
property color penColor: "white"
property real penWidth: 4
property var points: []
readonly property alias content: contentLoader.item
readonly property PersistentProperties drawingState: PersistentProperties {
property color penColor: "white"
property int penWidth: 4
function clear(): void {
var ctx = getContext('2d');
root.points = [];
ctx.reset();
root.requestPaint();
reloadableId: "drawingState"
}
required property PersistentProperties visibilities
renderStrategy: Canvas.Cooperative
Loader {
id: contentLoader
onPaint: {
if (points.length < 2)
return;
var ctx = root.getContext('2d');
ctx.save();
ctx.lineWidth = root.penWidth;
ctx.strokeStyle = root.penColor;
ctx.lineCap = "round";
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
for (var i = 1; i < points.length; i++)
ctx.lineTo(points[i].x, points[i].y);
ctx.stroke();
points = points.slice(points.length - 2);
ctx.restore();
active: root.visibilities.isDrawing
anchors.fill: parent
sourceComponent: StrokeCanvas {
penColor: root.drawingState.penColor
penWidth: root.drawingState.penWidth
}
}
}
-60
View File
@@ -1,60 +0,0 @@
import Quickshell
import QtQuick
import qs.Components
import qs.Config
CustomMouseArea {
id: root
required property var bar
required property Drawing drawing
required property Panels panels
required property var popout
required property PersistentProperties visibilities
function inLeftPanel(panel: Item, x: real, y: real): bool {
return x < panel.x + panel.width + Config.barConfig.border && withinPanelHeight(panel, x, y);
}
function withinPanelHeight(panel: Item, x: real, y: real): bool {
const panelY = panel.y + bar.implicitHeight;
return y >= panelY && y <= panelY + panel.height;
}
acceptedButtons: Qt.LeftButton | Qt.RightButton
anchors.fill: root.visibilities.isDrawing ? parent : undefined
hoverEnabled: true
visible: root.visibilities.isDrawing
onPositionChanged: event => {
const x = event.x;
const y = event.y;
if (root.visibilities.isDrawing && (event.buttons & Qt.LeftButton)) {
root.drawing.points.push(Qt.point(x, y));
root.drawing.requestPaint();
}
if (root.inLeftPanel(root.popout, x, y)) {
root.z = -2;
root.panels.drawing.expanded = true;
}
}
onPressed: event => {
const x = event.x;
const y = event.y;
if (root.visibilities.isDrawing && (event.buttons & Qt.LeftButton)) {
root.panels.drawing.expanded = false;
root.drawing.points.push(Qt.point(x, y));
root.drawing.requestPaint();
return;
}
if (event.buttons & Qt.RightButton)
root.drawing.clear();
}
onReleased: {
root.drawing.points = [];
}
}
+164 -59
View File
@@ -2,20 +2,21 @@ import Quickshell
import QtQuick
import qs.Components
import qs.Config
import qs.Helpers
import qs.Modules as BarPopouts
CustomMouseArea {
Item {
id: root
required property Item bar
required property real borderThickness
property bool dashboardShortcutActive
property point dragStart
required property Drawing drawing
required property DrawingInput input
property bool osdShortcutActive
required property Panels panels
required property BarPopouts.Wrapper popouts
required property ShellScreen screen
property bool singleGestureTriggered: false
property bool utilitiesShortcutActive
required property PersistentProperties visibilities
@@ -47,83 +48,181 @@ CustomMouseArea {
}
function withinPanelWidth(panel: Item, x: real, y: real): bool {
const panelX = panel.x;
const panelX = panel.x + root.borderThickness;
return x >= panelX && x <= panelX + panel.width;
}
anchors.fill: parent
cursorShape: (pressed && dragStart.y < bar.implicitHeight) ? Qt.ClosedHandCursor : undefined
hoverEnabled: true
propagateComposedEvents: false
onContainsMouseChanged: {
if (!containsMouse) {
if (!osdShortcutActive) {
visibilities.osd = false;
root.panels.osd.hovered = false;
DragHandler {
id: singleHandler
cursorShape: (active && centroid.pressPosition.y < root.bar.implicitHeight) ? Qt.ClosedHandCursor : undefined
dragThreshold: 0
enabled: !root.visibilities.isDrawing
grabPermissions: PointerHandler.CanTakeOverFromHandlersOfSameType | PointerHandler.ApprovesTakeOverByAnything
maximumPointCount: 1
minimumPointCount: 1
target: null
onActiveChanged: {
if (!active)
root.singleGestureTriggered = false;
}
onCentroidChanged: {
const x = centroid.position.x;
const y = centroid.position.y;
const dragX = x - centroid.pressPosition.x;
const dragY = y - centroid.pressPosition.y;
if (centroid.pressPosition.y >= root.screen.height - Config.barConfig.border && centroid.pressPosition.x > root.screen.width / 5 && 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 (!popouts.currentName.startsWith("traymenu")) {
popouts.hasCurrent = false;
if (centroid.pressPosition.y > root.screen.height - Config.barConfig.border && centroid.pressPosition.x < root.screen.width / 5 && dragY < -50)
root.visibilities.clipboard = true;
if (!Config.dock.hoverToReveal && centroid.pressPosition.y > root.screen.height - root.bar.implicitHeight && centroid.pressPosition.x > root.screen.width / 5 && !root.visibilities.launcher)
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 (Config.barConfig.autoHide)
bar.isHovered = false;
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;
}
}
}
onPositionChanged: event => {
const x = event.x;
const y = event.y;
const dragX = x - dragStart.x;
const dragY = y - dragStart.y;
if (root.visibilities.isDrawing && !root.inLeftPanel(root.panels.drawing, x, y)) {
// root.input.z = 2;
root.panels.drawing.expanded = false;
}
PointHandler {
id: drawingHandler
if (!visibilities.bar && Config.barConfig.autoHide && y < bar.implicitHeight)
bar.isHovered = true;
property bool setInitialPoint: false
if (pressed && dragStart.y < bar.implicitHeight) {
if (dragY > 20)
visibilities.settings = true;
else if (dragY < -20)
visibilities.settings = false;
}
acceptedButtons: Qt.LeftButton | Qt.RightButton
enabled: root.visibilities.isDrawing && (!root.inLeftPanel(root.panels.drawing, hoverHandler.point.position.x, hoverHandler.point.position.y) || !root.panels.drawing.expanded)
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) {
osdShortcutActive = false;
root.panels.osd.hovered = true;
}
} else {
const outOfSidebar = x < width - panels.sidebar.width;
const showOsd = outOfSidebar && inRightPanel(panels.osdWrapper, x, y);
if (!osdShortcutActive) {
visibilities.osd = showOsd;
root.panels.osd.hovered = showOsd;
} else if (showOsd) {
osdShortcutActive = false;
root.panels.osd.hovered = true;
onActiveChanged: {
if (!active) {
setInitialPoint = false;
root.drawing.content.endStroke();
} else {
root.panels.drawing.collapse();
}
}
onPointChanged: {
if (!active)
return;
const x = point.position.x;
const y = point.position.y;
const origX = point.pressPosition.x;
const origY = point.pressPosition.y;
if (Config.dock.enable && !Config.dock.hoverToReveal && !visibilities.dock && !visibilities.launcher && inBottomPanel(panels.dock, x, y))
visibilities.dock = true;
if (point.pressedButtons & Qt.RightButton) {
root.drawing.content.clear();
return;
}
if (x === 0 && y === 0 && origX === 0 && origY === 0)
return;
if (y < root.bar.implicitHeight) {
root.bar.checkPopout(x);
if (!setInitialPoint) {
setInitialPoint = true;
root.drawing.content.beginStroke(origX, origY);
return;
}
root.drawing.content.appendPoint(x, y);
}
}
HoverHandler {
id: hoverHandler
cursorShape: root.visibilities.isDrawing && !root.inLeftPanel(root.panels.drawing, point.position.x, point.position.y) ? Qt.BlankCursor : undefined
onHoveredChanged: {
if (!hovered) {
if (!root.osdShortcutActive) {
root.visibilities.osd = false;
root.panels.osd.hovered = false;
}
if (!root.popouts.currentName.startsWith("traymenu")) {
root.popouts.hasCurrent = false;
}
if (Config.barConfig.autoHide)
root.bar.isHovered = false;
}
}
onPointChanged: {
const x = point.position.x;
const y = point.position.y;
if (root.visibilities.isDrawing) {
if (root.inLeftPanel(root.panels.drawing, x, y) && !(drawingHandler.point.pressedButtons & Qt.LeftButton)) {
root.panels.drawing.expand();
root.drawing.content.hideHover();
return;
}
root.drawing.content.showHover(x, y);
return;
}
if (!root.visibilities.bar && Config.barConfig.autoHide && y < root.bar.implicitHeight)
root.bar.isHovered = true;
if (root.panels.sidebar.width === 0) {
const showOsd = root.inRightPanel(root.panels.osdWrapper, x, y);
if (showOsd) {
root.osdShortcutActive = false;
root.panels.osd.hovered = true;
}
} else {
const outOfSidebar = x < root.width - root.panels.sidebar.width;
const showOsd = outOfSidebar && root.inRightPanel(root.panels.osdWrapper, x, y);
if (!root.osdShortcutActive) {
root.visibilities.osd = showOsd;
root.panels.osd.hovered = showOsd;
} else if (showOsd) {
root.osdShortcutActive = false;
root.panels.osd.hovered = true;
}
}
if (Config.dock.enable && Config.dock.hoverToReveal && !root.visibilities.dock && !root.visibilities.launcher && root.inBottomPanel(root.panels.dock, x, y))
root.visibilities.dock = true;
if (y < root.bar.implicitHeight)
root.bar.checkPopout(x);
}
}
onPressed: event => dragStart = Qt.point(event.x, event.y)
Connections {
function onDashboardChanged() {
@@ -132,6 +231,8 @@ CustomMouseArea {
if (!inDashboardArea) {
root.dashboardShortcutActive = true;
}
if (root.panels.launcher.x + root.panels.launcher.width > root.panels.dashboardWrapper.x)
root.visibilities.launcher = false;
root.visibilities.settings = false;
root.visibilities.sidebar = false;
@@ -161,6 +262,10 @@ CustomMouseArea {
}
if (root.visibilities.launcher) {
if (root.panels.dashboardWrapper.x < root.panels.launcher.x + root.panels.launcher.width) {
root.visibilities.dashboard = false;
}
root.visibilities.dock = false;
root.visibilities.settings = false;
}
+15 -77
View File
@@ -13,18 +13,20 @@ import qs.Modules.Resources as Resources
import qs.Modules.Settings as Settings
import qs.Modules.Drawing as Drawing
import qs.Modules.Dock as Dock
import qs.Modules.SysTray.Popouts as SysPopouts
import qs.Modules.Clipboard as Clipboard
import qs.Config
Item {
id: root
required property Item bar
required property real borderThickness
readonly property alias clipboard: clipboard
readonly property alias dashboard: dashboard
readonly property alias dashboardWrapper: dashboardWrapper
readonly property alias dock: dock
readonly property alias drawing: drawing
required property Canvas drawingItem
required property var drawingItem
readonly property alias launcher: launcher
readonly property alias notifications: notifications
readonly property alias osd: osd
@@ -38,12 +40,11 @@ Item {
readonly property alias settingsWrapper: settingsWrapper
readonly property alias sidebar: sidebar
readonly property alias toasts: toasts
readonly property alias traySubmenus: traySubmenus
readonly property alias utilities: utilities
required property PersistentProperties visibilities
anchors.fill: parent
anchors.margins: Config.barConfig.border
anchors.margins: borderThickness
anchors.topMargin: bar.implicitHeight
Item {
@@ -95,83 +96,11 @@ Item {
visibilities: root.visibilities
}
Item {
id: traySubmenus
Repeater {
model: popouts.content.state.submenus
CustomClippingRect {
id: subMenuWrapper
required property int index
required property var modelData
property real targetX: 0
property real targetY: 0
function updatePosition() {
let sourceItem = modelData.sourceItem;
if (!sourceItem || !sourceItem.parent)
return;
let mapped = sourceItem.mapToItem(root, 0, -Appearance.padding.small);
let rightX = mapped.x + modelData.sourceWidth + Config.barConfig.border;
let leftX = mapped.x - implicitWidth - Config.barConfig.border;
if (rightX + implicitWidth > root.width) {
targetX = leftX;
} else {
targetX = rightX;
}
targetY = mapped.y;
if (targetY + implicitHeight > root.height) {
targetY = root.height - implicitHeight;
}
if (targetY < 0)
targetY = 0;
}
implicitHeight: subMenuContent.implicitHeight + Appearance.padding.small * 2
implicitWidth: subMenuContent.implicitWidth + Appearance.padding.small * 2
radius: Appearance.rounding.normal
x: targetX
y: targetY
Behavior on implicitHeight {
Anim {
}
}
Behavior on implicitWidth {
Anim {
}
}
Component.onCompleted: {
updatePosition();
}
onImplicitHeightChanged: updatePosition()
onImplicitWidthChanged: updatePosition()
SysPopouts.SubMenu {
id: subMenuContent
anchors.centerIn: parent
handle: subMenuWrapper.modelData.handle
level: subMenuWrapper.index + 1
popouts: root.popouts.state
screen: root.screen
}
}
}
}
Modules.ClipWrapper {
id: popouts
anchors.top: parent.top
borderThickness: root.borderThickness
screen: root.screen
}
@@ -282,4 +211,13 @@ Item {
screen: root.screen
visibilities: root.visibilities
}
Clipboard.Wrapper {
id: clipboard
anchors.bottom: parent.bottom
anchors.left: parent.left
screen: root.screen
visibilities: root.visibilities
}
}
+420 -371
View File
@@ -9,411 +9,460 @@ import Quickshell.Hyprland
import ZShell.Blobs
import qs.Daemons
import qs.Components
import qs.Modules
import qs.Modules.Bar
import qs.Config
import qs.Helpers
import qs.Drawers
Variants {
model: Quickshell.screens
CustomWindow {
id: root
Scope {
id: scope
readonly property alias bar: bar
readonly property real borderLayoutThickness: hasFullscreen ? 0 : Config.barConfig.border
readonly property real borderRounding: Config.barConfig.rounding * (1 - fsTransitionProg)
readonly property real borderThickness: Config.barConfig.border * (1 - fsTransitionProg)
readonly property int dragMaskPadding: {
if (focusGrab.active)
return 0;
required property var modelData
if (monitor?.lastIpcObject.specialWorkspace?.name || monitor?.activeWorkspace.lastIpcObject.windows > 0)
return 0;
Exclusions {
bar: bar
screen: scope.modelData
return 100;
}
property real fsTransitionProg: hasFullscreen ? 1 : 0
readonly property bool hasFullscreen: {
if (hasSpecialWorkspace) {
const specialName = monitor?.lastIpcObject.specialWorkspace?.name;
if (!specialName)
return false;
const specialWs = Hypr.workspaces.values.find(ws => ws.name === specialName);
return specialWs?.toplevels.values.some(t => t.lastIpcObject.fullscreen > 1) ?? false;
}
return hasFullscreenOnNormalWs;
}
readonly property bool hasFullscreenOnNormalWs: monitor?.activeWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen > 1) ?? false
readonly property bool hasSpecialWorkspace: (monitor?.lastIpcObject.specialWorkspace?.name.length ?? 0) > 0
readonly property alias interactionWrapper: interactions
readonly property alias menuRegion: menuPopoutRegion
readonly property HyprlandMonitor monitor: Hypr.monitorFor(screen)
property var root: Quickshell.shellDir
readonly property real sdfBorderOffset: 2 * fsTransitionProg
readonly property real shadowOpacity: 0.7 * (1 - fsTransitionProg)
property color surfaceColor: DynamicColors.tPalette.m3surface
WlrLayershell.exclusionMode: ExclusionMode.Ignore
WlrLayershell.keyboardFocus: visibilities.launcher || visibilities.settings ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
WlrLayershell.layer: (fsTransitionProg > 0 && Config.general.showOverFullscreen) || (hasSpecialWorkspace && hasFullscreenOnNormalWs) ? WlrLayer.Overlay : WlrLayer.Top
color: "transparent"
contentItem.focus: true
mask: visibilities.isDrawing ? null : (hasFullscreen ? emptyRegion : region)
name: "Bar"
Behavior on fsTransitionProg {
Anim {
}
}
Behavior on surfaceColor {
CAnim {
}
}
contentItem.Keys.onEscapePressed: {
if (Config.barConfig.autoHide)
visibilities.bar = false;
visibilities.launcher = false;
visibilities.sidebar = false;
visibilities.dashboard = false;
visibilities.osd = false;
visibilities.settings = false;
visibilities.resources = false;
visibilities.dock = false;
visibilities.clipboard = false;
panels.popouts.hasCurrent = false;
}
onHasFullscreenChanged: {
visibilities.launcher = false;
visibilities.sidebar = false;
visibilities.dashboard = false;
visibilities.osd = false;
visibilities.settings = false;
visibilities.resources = false;
visibilities.clipboard = false;
visibilities.dock = false;
panels.popouts.hasCurrent = false;
}
Region {
id: menuPopoutRegion
intersection: Intersection.Subtract
}
Region {
id: emptyRegion
height: panels.notifications.height
width: panels.notifications.width
x: panels.notifications.x + root.borderThickness
y: panels.notifications.y + bar.implicitHeight
}
Region {
id: region
height: root.height - bar.implicitHeight - root.borderThickness - root.dragMaskPadding * 2
intersection: Intersection.Xor
regions: [...popoutRegions.instances, menuPopoutRegion]
width: root.width - root.borderThickness * 2 - root.dragMaskPadding * 2
x: root.borderThickness + root.dragMaskPadding
y: bar.implicitHeight + root.dragMaskPadding
}
anchors {
bottom: true
left: true
right: true
top: true
}
Variants {
id: popoutRegions
model: panels.children
Region {
required property Item modelData
height: modelData.height
intersection: Intersection.Subtract
width: modelData.width
x: modelData.x + root.borderThickness
y: modelData.y + bar.implicitHeight
}
}
HyprlandFocusGrab {
id: focusGrab
active: visibilities.dock || visibilities.resources || visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || visibilities.clipboard || (panels.popouts.hasCurrent && panels.popouts.currentName.startsWith("traymenu"))
windows: [root]
onCleared: {
visibilities.launcher = false;
visibilities.sidebar = false;
visibilities.dashboard = false;
visibilities.osd = false;
visibilities.settings = false;
visibilities.resources = false;
visibilities.clipboard = false;
visibilities.dock = false;
panels.popouts.hasCurrent = false;
}
}
PersistentProperties {
id: visibilities
property bool bar
property bool clipboard
property bool dashboard
property bool dock
property bool isDrawing
property bool launcher
property bool notif: NotifServer.popups.length > 0
property bool osd
property bool resources
property bool settings
property bool sidebar
Component.onCompleted: Visibilities.load(root.screen, this)
}
IpcHandler {
function toggleLauncher(fix: string): void {
visibilities.launcher = !visibilities.launcher;
}
CustomWindow {
id: win
target: "visibilities"
}
readonly property bool hasFullscreen: Hypr.monitorFor(screen)?.activeWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen === 2)
property var root: Quickshell.shellDir
Binding {
property: "bar"
target: visibilities
value: visibilities.sidebar || visibilities.dashboard || visibilities.osd || (!Config.barConfig.hideWhenNotif && visibilities.notif) || visibilities.resources || visibilities.settings || bar.isHovered
when: Config.barConfig.autoHide
}
WlrLayershell.exclusionMode: ExclusionMode.Ignore
WlrLayershell.keyboardFocus: visibilities.dock || visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || visibilities.resources ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
color: "transparent"
contentItem.focus: true
mask: visibilities.isDrawing ? null : region
name: "Bar"
screen: scope.modelData
Item {
id: surface
contentItem.Keys.onEscapePressed: {
if (Config.barConfig.autoHide)
visibilities.bar = false;
visibilities.sidebar = false;
visibilities.dashboard = false;
visibilities.osd = false;
visibilities.settings = false;
visibilities.resources = false;
}
onHasFullscreenChanged: {
visibilities.launcher = false;
visibilities.dashboard = false;
visibilities.osd = false;
visibilities.settings = false;
visibilities.resources = false;
}
anchors.fill: parent
layer.enabled: true
opacity: root.surfaceColor.a
Region {
id: region
layer.effect: MultiEffect {
blurMax: 32
shadowColor: Qt.alpha(DynamicColors.palette.m3shadow, Math.max(0, root.shadowOpacity))
shadowEnabled: true
}
height: win.height - bar.implicitHeight - Config.barConfig.border
intersection: Intersection.Xor
regions: [...popoutRegions.instances, ...subMenuRegions.instances]
width: win.width - Config.barConfig.border * 2
x: Config.barConfig.border
y: bar.implicitHeight
}
BlobGroup {
id: blobGroup
anchors {
bottom: true
left: true
right: true
top: true
}
color: root.surfaceColor
smoothing: Config.barConfig.smoothing
}
Variants {
id: popoutRegions
BlobInvertedRect {
anchors.fill: parent
anchors.margins: -50
borderBottom: root.borderThickness - anchors.margins - root.sdfBorderOffset
borderLeft: root.borderThickness - anchors.margins - root.sdfBorderOffset
borderRight: root.borderThickness - anchors.margins - root.sdfBorderOffset
borderTop: bar.implicitHeight - anchors.margins - root.sdfBorderOffset
group: blobGroup
radius: root.borderRounding
}
model: panels.children
PanelBg {
id: dashBg
Region {
required property Item modelData
property real extraHeight: 0.2
height: modelData.height
intersection: Intersection.Subtract
width: modelData.width
x: modelData.x + Config.barConfig.border
y: modelData.y + bar.implicitHeight
deformAmount: 0.06
implicitHeight: panels.dashboard.height * (1 + extraHeight)
implicitWidth: panels.dashboard.width
panel: panels.dashboardWrapper
radius: Appearance.rounding.normal
x: panels.dashboardWrapper.x + panels.dashboard.x + root.borderThickness
y: panels.dashboardWrapper.y + panels.dashboard.y + bar.implicitHeight - panels.dashboard.height * extraHeight
}
PanelBg {
id: launcherBg
property real extraHeight: 0.2
deformAmount: 0.06
implicitHeight: panels.launcher.height * (1 + extraHeight)
panel: panels.launcher
radius: Appearance.rounding.smallest + 5
y: panels.launcher.y + bar.implicitHeight
}
PanelBg {
id: sidebarBg
bottomLeftRadius: 0
deformAmount: 0.04
exclude: panels.sidebar.offsetScale > 0.08 ? [] : [utilsBg]
implicitHeight: panel.height * (1 / rawDeformMatrix.m22) + 2
panel: panels.sidebar
}
PanelBg {
id: osdBg
deformAmount: 0.1
implicitHeight: panels.osd.height
implicitWidth: panels.osd.width
panel: panels.osdWrapper
radius: 20
x: panels.osdWrapper.x + panels.osd.x + root.borderThickness
y: panels.osdWrapper.y + panels.osd.y + bar.implicitHeight
}
PanelBg {
id: notifsBg
panel: panels.notifications
radius: Appearance.rounding.normal
}
PanelBg {
id: utilsBg
deformAmount: 0.1
exclude: panels.sidebar.offsetScale > 0.08 ? [] : [sidebarBg]
panel: panels.utilities
topLeftRadius: 0
}
PanelBg {
id: popoutBg
property real extraHeight: 0.2
deformAmount: panels.popouts.currentName.startsWith("traymenu") ? 0.15 : 0.08
implicitHeight: panels.popouts.height * (1 + extraHeight)
implicitWidth: panels.popouts.width
panel: panels.popoutsWrapper
radius: panels.popouts.current?.panelRadius ?? Appearance.rounding.normal
x: panels.popoutsWrapper.x + panels.popouts.x + root.borderThickness
y: panels.popoutsWrapper.y + panels.popouts.y + bar.implicitHeight - panels.popouts.height * extraHeight
Behavior on extraHeight {
Anim {
}
}
}
Variants {
id: subMenuRegions
PanelBg {
id: resourcesBg
model: panels.traySubmenus.children
deformAmount: 0.05
implicitHeight: panels.resources.height
implicitWidth: panels.resources.width
panel: panels.resourcesWrapper
radius: Appearance.rounding.large
x: panels.resourcesWrapper.x + panels.resources.x + root.borderThickness
y: panels.resourcesWrapper.y + panels.resources.y + bar.implicitHeight
}
Region {
required property Item modelData
PanelBg {
id: settingsBg
height: modelData.height
intersection: Intersection.Subtract
width: modelData.width
x: modelData.x + panels.traySubmenus.x + Config.barConfig.border
y: modelData.y + panels.traySubmenus.y + bar.implicitHeight
}
property real extraHeight: 0.2
deformAmount: 0.03
implicitHeight: panels.settings.height * (1 + extraHeight)
implicitWidth: panels.settings.width
panel: panels.settings
radius: Appearance.rounding.large
topLeftRadius: Appearance.rounding.large + Appearance.padding.smaller
topRightRadius: Appearance.rounding.large + Appearance.padding.smaller
x: panels.settingsWrapper.x + panels.settings.x + root.borderThickness
y: panels.settingsWrapper.y + panels.settings.y + bar.implicitHeight - panels.settings.height * extraHeight
}
PanelBg {
id: dockBg
deformAmount: 0.08
panel: panels.dock
radius: Appearance.rounding.normal
}
PanelBg {
id: drawingBg
deformAmount: 0.08
panel: panels.drawing
radius: Appearance.rounding.normal
}
PanelBg {
id: clipboardBg
deformAmount: 0.03
panel: panels.clipboard
radius: 29
}
}
Drawing {
id: drawing
anchors.fill: parent
layer.enabled: true
visibilities: visibilities
z: 2
layer.effect: MultiEffect {
maskEnabled: true
maskInverted: true
maskSource: maskSource
}
}
Item {
id: maskSource
anchors.fill: parent
layer.enabled: true
visible: false
CustomRect {
readonly property int extraWidth: radius
color: "white"
implicitHeight: panels.drawing.height
implicitWidth: panels.drawing.width + extraWidth
radius: drawingBg.radius
x: -extraWidth + root.borderThickness + panels.drawing.x
y: panels.drawing.y + bar.implicitHeight
}
}
Interactions {
id: interactions
anchors.fill: parent
bar: bar
borderThickness: root.borderLayoutThickness
drawing: drawing
enabled: true
panels: panels
popouts: panels.popouts
screen: root.screen
visibilities: visibilities
Panels {
id: panels
bar: bar
borderThickness: root.borderThickness
drawingItem: drawing
screen: root.screen
visibilities: visibilities
clipboard.transform: Matrix4x4 {
matrix: clipboardBg.deformMatrix
}
HyprlandFocusGrab {
id: focusGrab
active: visibilities.dock || visibilities.resources || visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || (panels.popouts.hasCurrent && panels.popouts.currentName.startsWith("traymenu"))
windows: [win]
onCleared: {
visibilities.launcher = false;
visibilities.sidebar = false;
visibilities.dashboard = false;
visibilities.osd = false;
visibilities.settings = false;
visibilities.resources = false;
visibilities.dock = false;
panels.popouts.hasCurrent = false;
}
dashboard.transform: Matrix4x4 {
matrix: dashBg.deformMatrix
}
PersistentProperties {
id: visibilities
property bool bar
property bool dashboard
property bool dock
property bool isDrawing
property bool launcher
property bool notif: NotifServer.popups.length > 0
property bool osd
property bool resources
property bool settings
property bool sidebar
Component.onCompleted: Visibilities.load(scope.modelData, this)
dock.transform: Matrix4x4 {
matrix: dockBg.deformMatrix
}
IpcHandler {
function toggleLauncher(fix: string): void {
visibilities.launcher = !visibilities.launcher;
}
target: "visibilities"
launcher.transform: Matrix4x4 {
matrix: launcherBg.deformMatrix
}
Binding {
property: "bar"
target: visibilities
value: visibilities.sidebar || visibilities.dashboard || visibilities.osd || (!Config.barConfig.hideWhenNotif && visibilities.notif) || visibilities.resources || visibilities.settings || bar.isHovered
when: Config.barConfig.autoHide
notifications.transform: Matrix4x4 {
matrix: notifsBg.deformMatrix
}
Item {
anchors.fill: parent
layer.enabled: true
opacity: Appearance.transparency.enabled ? DynamicColors.transparency.base : 1
layer.effect: MultiEffect {
blurMax: 32
shadowColor: Qt.alpha(DynamicColors.palette.m3shadow, 1)
shadowEnabled: true
}
BlobGroup {
id: blobGroup
color: DynamicColors.palette.m3surface
smoothing: Config.barConfig.smoothing
Behavior on color {
CAnim {
}
}
}
BlobInvertedRect {
anchors.fill: parent
anchors.margins: -50
borderBottom: Config.barConfig.border - anchors.margins
borderLeft: Config.barConfig.border - anchors.margins
borderRight: Config.barConfig.border - anchors.margins
borderTop: bar.implicitHeight - anchors.margins
group: blobGroup
radius: Config.barConfig.rounding
}
PanelBg {
id: dashBg
property real extraHeight: 0.2
deformAmount: 0.06
implicitHeight: panels.dashboard.height * (1 + extraHeight)
implicitWidth: panels.dashboard.width
panel: panels.dashboardWrapper
radius: Appearance.rounding.normal
x: panels.dashboardWrapper.x + panels.dashboard.x + Config.barConfig.border
y: panels.dashboardWrapper.y + panels.dashboard.y + bar.implicitHeight - panels.dashboard.height * extraHeight
}
PanelBg {
id: launcherBg
property real extraHeight: 0.2
deformAmount: 0.06
implicitHeight: panels.launcher.height * (1 + extraHeight)
panel: panels.launcher
radius: Appearance.rounding.smallest + 5
y: panels.launcher.y + bar.implicitHeight
}
PanelBg {
id: sidebarBg
bottomLeftRadius: 0
deformAmount: 0.04
exclude: panels.sidebar.offsetScale > 0.08 ? [] : [utilsBg]
implicitHeight: panel.height * (1 / rawDeformMatrix.m22) + 2
panel: panels.sidebar
}
PanelBg {
id: osdBg
deformAmount: 0.1
implicitHeight: panels.osd.height
implicitWidth: panels.osd.width
panel: panels.osdWrapper
radius: 20
x: panels.osdWrapper.x + panels.osd.x + Config.barConfig.border
y: panels.osdWrapper.y + panels.osd.y + bar.implicitHeight
}
PanelBg {
id: notifsBg
panel: panels.notifications
}
PanelBg {
id: utilsBg
deformAmount: panels.sidebar.visible ? (0.1) : (0.1)
exclude: panels.sidebar.offsetScale > 0.08 ? [] : [sidebarBg]
panel: panels.utilities
topLeftRadius: 0
}
PanelBg {
id: popoutBg
property real extraHeight: panels.popouts.isDetached ? 0 : 0.2
deformAmount: panels.popouts.isDetached ? 0.05 : panels.popouts.hasCurrent ? 0.15 : 0.1
implicitHeight: panels.popouts.height * (1 + extraHeight)
implicitWidth: panels.popouts.width
panel: panels.popoutsWrapper
radius: (panels.popouts.currentName.startsWith("audio") || panels.popouts.currentName.startsWith("updates")) ? Appearance.rounding.normal : 20 * Appearance.rounding.scale
x: panels.popoutsWrapper.x + panels.popouts.x + Config.barConfig.border
y: panels.popoutsWrapper.y + panels.popouts.y + bar.implicitHeight - panels.popouts.height * extraHeight
Behavior on extraHeight {
Anim {
}
}
}
PanelBg {
id: resourcesBg
deformAmount: 0.05
implicitHeight: panels.resources.height
implicitWidth: panels.resources.width
panel: panels.resourcesWrapper
radius: Appearance.rounding.normal
x: panels.resourcesWrapper.x + panels.resources.x + Config.barConfig.border
y: panels.resourcesWrapper.y + panels.resources.y + bar.implicitHeight
}
PanelBg {
id: settingsBg
property real extraHeight: 0.2
deformAmount: 0.03
implicitHeight: panels.settings.height * (1 + extraHeight)
implicitWidth: panels.settings.width
panel: panels.settings
radius: Appearance.rounding.large
topLeftRadius: Appearance.rounding.large + Appearance.padding.smaller
topRightRadius: Appearance.rounding.large + Appearance.padding.smaller
x: panels.settingsWrapper.x + panels.settings.x + Config.barConfig.border
y: panels.settingsWrapper.y + panels.settings.y + bar.implicitHeight - panels.settings.height * extraHeight
}
PanelBg {
id: dockBg
deformAmount: 0.08
panel: panels.dock
radius: Appearance.rounding.normal
}
PanelBg {
id: drawingBg
deformAmount: 0.08
panel: panels.drawing
radius: Appearance.rounding.normal
}
Repeater {
model: panels.traySubmenus.children
PanelBg {
required property Item modelData
deformAmount: 0.1
panel: modelData
radius: 20 * Appearance.rounding.scale
}
}
osd.transform: Matrix4x4 {
matrix: osdBg.deformMatrix
}
Drawing {
id: drawing
anchors.fill: parent
z: 2
popouts.transform: Matrix4x4 {
matrix: popoutBg.deformMatrix
}
DrawingInput {
id: input
bar: bar
drawing: drawing
panels: panels
popout: panels.drawing
visibilities: visibilities
z: 2
resources.transform: Matrix4x4 {
matrix: resourcesBg.deformMatrix
}
Interactions {
id: mouseArea
anchors.fill: parent
bar: bar
drawing: drawing
input: input
panels: panels
popouts: panels.popouts
screen: scope.modelData
visibilities: visibilities
z: 1
Panels {
id: panels
bar: bar
drawingItem: drawing
screen: scope.modelData
visibilities: visibilities
dashboard.transform: Matrix4x4 {
matrix: dashBg.deformMatrix
}
dock.transform: Matrix4x4 {
matrix: dockBg.deformMatrix
}
launcher.transform: Matrix4x4 {
matrix: launcherBg.deformMatrix
}
notifications.transform: Matrix4x4 {
matrix: notifsBg.deformMatrix
}
osd.transform: Matrix4x4 {
matrix: osdBg.deformMatrix
}
popouts.transform: Matrix4x4 {
matrix: popoutBg.deformMatrix
}
resources.transform: Matrix4x4 {
matrix: resourcesBg.deformMatrix
}
settings.transform: Matrix4x4 {
matrix: settingsBg.deformMatrix
}
sidebar.transform: Matrix4x4 {
matrix: sidebarBg.deformMatrix
}
utilities.transform: Matrix4x4 {
matrix: utilsBg.deformMatrix
}
}
BarLoader {
id: bar
anchors.left: parent.left
anchors.right: parent.right
popouts: panels.popouts
popoutsWrapper: panels.popoutsWrapper
screen: scope.modelData
visibilities: visibilities
}
settings.transform: Matrix4x4 {
matrix: settingsBg.deformMatrix
}
sidebar.transform: Matrix4x4 {
matrix: sidebarBg.deformMatrix
}
utilities.transform: Matrix4x4 {
matrix: utilsBg.deformMatrix
}
}
BarLoader {
id: bar
anchors.left: parent.left
anchors.right: parent.right
enabled: !visibilities.isDrawing
fullscreen: root.hasFullscreen
popouts: panels.popouts
popoutsWrapper: panels.popoutsWrapper
screen: root.screen
visibilities: visibilities
}
}
@@ -426,7 +475,7 @@ Variants {
implicitHeight: panel.height
implicitWidth: panel.width
radius: Appearance.rounding.smallest
x: panel.x + Config.barConfig.border
x: panel.x + root.borderThickness
y: panel.y + bar.implicitHeight
}
}
+7 -7
View File
@@ -118,12 +118,12 @@ ColumnLayout {
}
StateLayer {
function onClicked(): void {
parent.forceActiveFocus();
}
cursorShape: Qt.IBeamCursor
hoverEnabled: false
onClicked: {
parent.forceActiveFocus();
}
}
RowLayout {
@@ -179,11 +179,11 @@ ColumnLayout {
radius: Appearance.rounding.full
StateLayer {
function onClicked(): void {
color: root.greeter.buffer && !root.greeter.launching ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
onClicked: {
root.greeter.submit();
}
color: root.greeter.buffer && !root.greeter.launching ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
}
MaterialIcon {
+58 -3
View File
@@ -2,7 +2,62 @@ import QtQuick
import qs.Config
NumberAnimation {
duration: MaterialEasing.standardTime
easing.bezierCurve: MaterialEasing.standard
easing.type: Easing.BezierSpline
enum Type {
StandardSmall = 0,
Standard,
StandardLarge,
StandardExtraLarge,
EmphasizedSmall,
Emphasized,
EmphasizedLarge,
EmphasizedExtraLarge,
FastSpatial,
DefaultSpatial,
SlowSpatial,
FastEffects,
DefaultEffects,
SlowEffects
}
property int type: Anim.DefaultSpatial
duration: {
if (type < Anim.StandardSmall || type > Anim.SlowEffects)
return Appearance.anim.durations.normal;
if (type === Anim.FastSpatial)
return Appearance.anim.durations.expressiveFastSpatial;
if (type === Anim.DefaultSpatial)
return Appearance.anim.durations.expressiveDefaultSpatial;
if (type === Anim.SlowSpatial)
return Appearance.anim.durations.large;
if (type === Anim.FastEffects)
return Appearance.anim.durations.expressiveFastEffects;
if (type === Anim.DefaultEffects)
return Appearance.anim.durations.expressiveEffects;
if (type === Anim.SlowEffects)
return Appearance.anim.durations.expressiveSlowEffects;
const types = ["small", "normal", "large", "extraLarge"];
const idx = type % 4; // 0-7 are the 4 standard types
return Appearance.anim.durations[types[idx]];
}
easing.bezierCurve: {
if (type === Anim.FastSpatial)
return Appearance.anim.curves.expressiveFastSpatial;
if (type === Anim.DefaultSpatial)
return Appearance.anim.curves.expressiveDefaultSpatial;
if (type === Anim.SlowSpatial)
return Appearance.anim.curves.expressiveSlowSpatial;
if (type === Anim.FastEffects)
return Appearance.anim.curves.expressiveFastEffects;
if (type === Anim.DefaultEffects)
return Appearance.anim.curves.expressiveDefaultEffects;
if (type === Anim.SlowEffects)
return Appearance.anim.curves.expressiveSlowEffects;
if (type >= Anim.EmphasizedSmall && type <= Anim.EmphasizedExtraLarge)
return Appearance.anim.curves.emphasized;
return Appearance.anim.curves.standard;
}
}
+5 -5
View File
@@ -59,15 +59,15 @@ ColumnLayout {
}
StateLayer {
function onClicked(): void {
root.toggleRequested();
root.expanded = !root.expanded;
}
anchors.fill: parent
color: DynamicColors.palette.m3onSurface
radius: Appearance.rounding.normal
showHoverBackground: false
onClicked: {
root.toggleRequested();
root.expanded = !root.expanded;
}
}
}
+3 -3
View File
@@ -23,10 +23,10 @@ Button {
}
StateLayer {
function onClicked(): void {
radius: control.radius
onClicked: {
control.clicked();
}
radius: control.radius
}
}
+4 -4
View File
@@ -33,13 +33,13 @@ RadioButton {
}
StateLayer {
function onClicked(): void {
root.click();
}
anchors.margins: -7
color: root.checked ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3primary
z: -1
onClicked: {
root.click();
}
}
CustomRect {
+155 -26
View File
@@ -1,45 +1,174 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Templates
import ZShell.Components
import ZShell
import qs.Components
import qs.Config
Slider {
id: root
background: Item {
property bool animateWave
property color bgColor: enabled ? DynamicColors.palette.m3secondaryContainer : Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
property color fgColor: enabled ? DynamicColors.palette.m3primary : Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
property real filledWidth
property real pos: visualPosition
property int waveDuration: 1000
property real waveFrequency: 6
property bool wavy
signal interaction(v: real)
implicitHeight: 12
implicitWidth: 200
contentItem: Item {
anchors.fill: parent
CustomRect {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: parent.top
bottomRightRadius: root.implicitHeight / 6
color: DynamicColors.palette.m3primary
implicitWidth: root.handle.x - root.implicitHeight / 2
radius: Appearance.rounding.full
topRightRadius: root.implicitHeight / 6
id: remaining
anchors.left: handle.right
anchors.leftMargin: Appearance.spacing.extraSmall
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
bottomLeftRadius: Appearance.rounding.extraSmall / 2
color: root.bgColor
implicitHeight: parent.height * (parent.height <= 12 ? opacity : Math.min(opacity * 2, 1))
opacity: Math.min(width, 12) / 12
radius: Appearance.rounding.small
topLeftRadius: Appearance.rounding.extraSmall / 2
}
CustomRect {
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.top: parent.top
bottomLeftRadius: root.implicitHeight / 6
color: DynamicColors.tPalette.m3surfaceContainer
implicitWidth: parent.width - root.handle.x - root.handle.implicitWidth - root.implicitHeight / 2
anchors.rightMargin: 4 * remaining.opacity
anchors.verticalCenter: parent.verticalCenter
color: root.fgColor
implicitHeight: 4 * remaining.opacity
implicitWidth: implicitHeight
opacity: remaining.opacity
radius: Appearance.rounding.full
topLeftRadius: root.implicitHeight / 6
}
CustomRect {
id: handle
anchors.left: filled.right
anchors.leftMargin: Appearance.spacing.extraSmall
anchors.verticalCenter: parent.verticalCenter
color: root.fgColor
implicitHeight: {
const mult = parent.height <= 12 ? 3 : 1.2;
const pressMult = parent.height <= 12 ? 4 : 1.5;
return parent.height * (mouse.pressed ? pressMult : mult);
}
implicitWidth: 4
radius: Appearance.rounding.full
Behavior on implicitHeight {
Anim {
type: Anim.FastSpatial
}
}
}
Loader {
id: filled
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
asynchronous: true
sourceComponent: root.wavy ? waveComp : lineComp
}
Component {
id: lineComp
CustomRect {
bottomRightRadius: Appearance.rounding.extraSmall / 2
color: root.fgColor
implicitHeight: root.height
implicitWidth: root.filledWidth
radius: Appearance.rounding.small
topRightRadius: Appearance.rounding.extraSmall / 2
}
}
Component {
id: waveComp
WavyLine {
color: root.fgColor
frequency: root.waveFrequency
fullLength: root.width - handle.implicitWidth - handle.anchors.leftMargin
implicitHeight: lineWidth * amplitudeMultiplier * 2 + lineWidth
implicitWidth: root.filledWidth
lineWidth: root.height * 0.7
startX: x
Behavior on color {
CAnim {
}
}
Anim on waveProgress {
duration: root.waveDuration
easing.type: Easing.Linear
from: 0
loops: Animation.Infinite
paused: !root.animateWave
running: true
to: 1
}
}
}
}
handle: CustomRect {
anchors.verticalCenter: parent.verticalCenter
color: DynamicColors.palette.m3primary
implicitHeight: 15
implicitWidth: 5
radius: Appearance.rounding.full
x: root.visualPosition * root.availableWidth - implicitWidth / 2
Behavior on filledWidth {
id: widthBehavior
MouseArea {
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
Anim {
}
}
Component.onCompleted: filledWidth = Qt.binding(() => (width - handle.implicitWidth - handle.anchors.leftMargin) * pos)
Binding {
id: posBinding
property: "pos"
target: root
value: ZUtils.clamp(mouse.pressStartPos + mouse.dragMovement, 0, 1)
when: mouse.pressed
}
MouseArea {
id: mouse
property real dragMovement
property real pressStartPos
property real pressStartX
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
implicitHeight: handle.implicitHeight
preventStealing: true
onPositionChanged: e => {
dragMovement = (e.x - pressStartX) / width;
root.interaction(posBinding.value);
}
onPressed: e => {
widthBehavior.enabled = false;
pressStartX = e.x;
pressStartPos = root.visualPosition;
}
onReleased: e => {
root.interaction(posBinding.value);
widthBehavior.enabled = true;
dragMovement = 0;
}
}
}
+12 -13
View File
@@ -28,6 +28,7 @@ RowLayout {
CustomTextField {
id: textField
color: root.enabled ? DynamicColors.palette.m3onSurface : Qt.alpha(DynamicColors.palette.m3onSurface, 0.5)
implicitHeight: upButton.implicitHeight
inputMethodHints: Qt.ImhFormattedNumbersOnly
leftPadding: Appearance.padding.normal
@@ -36,7 +37,7 @@ RowLayout {
text: root.isEditing ? text : root.displayText
background: CustomRect {
color: DynamicColors.tPalette.m3surfaceContainerHigh
color: root.enabled ? DynamicColors.tPalette.m3surfaceContainerHigh : DynamicColors.tPalette.m3surfaceContainerLow
implicitWidth: 100
radius: Appearance.rounding.full
}
@@ -85,7 +86,7 @@ RowLayout {
CustomRect {
id: upButton
color: DynamicColors.palette.m3primary
color: root.enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 1)
implicitHeight: upIcon.implicitHeight + Appearance.padding.small * 2
implicitWidth: implicitHeight
radius: Appearance.rounding.full
@@ -93,7 +94,9 @@ RowLayout {
StateLayer {
id: upState
function onClicked(): void {
color: DynamicColors.palette.m3onPrimary
onClicked: {
let newValue = Math.min(root.max, root.value + root.step);
// Round to avoid floating point precision errors
const decimals = root.step < 1 ? Math.max(1, Math.ceil(-Math.log10(root.step))) : 0;
@@ -102,9 +105,6 @@ RowLayout {
root.displayText = newValue.toString();
root.valueModified(newValue);
}
color: DynamicColors.palette.m3onPrimary
onPressAndHold: timer.start()
onReleased: timer.stop()
}
@@ -113,13 +113,13 @@ RowLayout {
id: upIcon
anchors.centerIn: parent
color: DynamicColors.palette.m3onPrimary
color: root.enabled ? DynamicColors.palette.m3onPrimary : Qt.alpha(DynamicColors.palette.m3onSurface, 0.5)
text: "keyboard_arrow_up"
}
}
CustomRect {
color: DynamicColors.palette.m3primary
color: root.enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 1)
implicitHeight: downIcon.implicitHeight + Appearance.padding.small * 2
implicitWidth: implicitHeight
radius: Appearance.rounding.full
@@ -127,7 +127,9 @@ RowLayout {
StateLayer {
id: downState
function onClicked(): void {
color: DynamicColors.palette.m3onPrimary
onClicked: {
let newValue = Math.max(root.min, root.value - root.step);
// Round to avoid floating point precision errors
const decimals = root.step < 1 ? Math.max(1, Math.ceil(-Math.log10(root.step))) : 0;
@@ -136,9 +138,6 @@ RowLayout {
root.displayText = newValue.toString();
root.valueModified(newValue);
}
color: DynamicColors.palette.m3onPrimary
onPressAndHold: timer.start()
onReleased: timer.stop()
}
@@ -147,7 +146,7 @@ RowLayout {
id: downIcon
anchors.centerIn: parent
color: DynamicColors.palette.m3onPrimary
color: root.enabled ? DynamicColors.palette.m3onPrimary : Qt.alpha(DynamicColors.palette.m3onSurface, 0.5)
text: "keyboard_arrow_down"
}
}
+36 -69
View File
@@ -1,7 +1,6 @@
import QtQuick
import QtQuick.Layouts
import qs.Config
import qs.Helpers
Row {
id: root
@@ -12,66 +11,46 @@ Row {
}
property alias active: menu.active
property color color: type == CustomSplitButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondaryContainer
property color colour: type == CustomSplitButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondaryContainer
property bool disabled
property color disabledColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
property color disabledTextColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
property color disabledColour: Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
property color disabledTextColour: Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
readonly property alias expandBtn: expandBtn
property alias expanded: menu.expanded
property string fallbackIcon
property string fallbackText
property real horizontalPadding: Appearance.padding.normal
property alias iconLabel: iconLabel
property alias label: label
property alias menu: menu
property real horizontalPadding: Appearance.padding.larger
readonly property alias iconLabel: iconLabel
readonly property alias label: label
readonly property alias menu: menu
property alias menuItems: menu.items
property bool menuOnTop
property alias stateLayer: stateLayer
property color textColor: type == CustomSplitButton.Filled ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSecondaryContainer
property real minLeftWidth
readonly property alias stateLayer: stateLayer
property color textColour: type == CustomSplitButton.Filled ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSecondaryContainer
readonly property alias textRow: textRow
property int type: CustomSplitButton.Filled
property real verticalPadding: Appearance.padding.smaller
property real verticalPadding: Appearance.padding.small
function closeDropdown(): void {
SettingsDropdowns.close(menu);
}
function openDropdown(): void {
if (root.disabled)
return;
SettingsDropdowns.open(menu, root);
}
function toggleDropdown(): void {
if (root.disabled)
return;
SettingsDropdowns.toggle(menu, root);
}
spacing: Math.floor(Appearance.spacing.small / 2)
onExpandedChanged: {
if (!expanded)
SettingsDropdowns.forget(menu);
}
spacing: Math.floor(Appearance.spacing.extraSmall)
CustomRect {
bottomRightRadius: Appearance.rounding.small / 2
color: root.disabled ? root.disabledColor : root.color
color: root.disabled ? root.disabledColour : root.colour
implicitHeight: expandBtn.implicitHeight
implicitWidth: textRow.implicitWidth + root.horizontalPadding * 2
implicitWidth: Math.max(root.minLeftWidth, textRow.implicitWidth + root.horizontalPadding * 2)
radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale)
topRightRadius: Appearance.rounding.small / 2
StateLayer {
id: stateLayer
function onClicked(): void {
root.active?.clicked();
}
color: root.textColor
bottomRightRadius: parent.bottomRightRadius
color: root.textColour
disabled: root.disabled
rect.bottomRightRadius: parent.bottomRightRadius
rect.topRightRadius: parent.topRightRadius
topRightRadius: parent.topRightRadius
onClicked: root.active?.clicked()
}
RowLayout {
@@ -86,7 +65,7 @@ Row {
Layout.alignment: Qt.AlignVCenter
animate: true
color: root.disabled ? root.disabledTextColor : root.textColor
color: root.disabled ? root.disabledTextColour : root.textColour
fill: 1
text: root.active?.activeIcon ?? root.fallbackIcon
}
@@ -98,12 +77,12 @@ Row {
Layout.preferredWidth: implicitWidth
animate: true
clip: true
color: root.disabled ? root.disabledTextColor : root.textColor
color: root.disabled ? root.disabledTextColour : root.textColour
text: root.active?.activeText ?? root.fallbackText
Behavior on Layout.preferredWidth {
Anim {
easing.bezierCurve: Appearance.anim.curves.emphasized
type: Anim.Emphasized
}
}
}
@@ -116,7 +95,7 @@ Row {
property real rad: root.expanded ? implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) : Appearance.rounding.small / 2
bottomLeftRadius: rad
color: root.disabled ? root.disabledColor : root.color
color: root.disabled ? root.disabledColour : root.colour
implicitHeight: expandIcon.implicitHeight + root.verticalPadding * 2
implicitWidth: implicitHeight
radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale)
@@ -130,14 +109,12 @@ Row {
StateLayer {
id: expandStateLayer
function onClicked(): void {
root.toggleDropdown();
}
color: root.textColor
color: root.textColour
disabled: root.disabled
rect.bottomLeftRadius: parent.bottomLeftRadius
rect.topLeftRadius: parent.topLeftRadius
onClicked: root.expanded = !root.expanded
}
MaterialIcon {
@@ -145,7 +122,7 @@ Row {
anchors.centerIn: parent
anchors.horizontalCenterOffset: root.expanded ? 0 : -Math.floor(root.verticalPadding / 4)
color: root.disabled ? root.disabledTextColor : root.textColor
color: root.disabled ? root.disabledTextColour : root.textColour
rotation: root.expanded ? 180 : 0
text: "expand_more"
@@ -158,24 +135,14 @@ Row {
}
}
}
}
Menu {
id: menu
Menu {
id: menu
anchors.bottomMargin: Appearance.spacing.small
anchors.right: parent.right
anchors.top: parent.bottom
anchors.topMargin: Appearance.spacing.small
states: State {
when: root.menuOnTop
AnchorChanges {
anchors.bottom: expandBtn.top
anchors.top: undefined
target: menu
}
}
}
attachSideY: root.menuOnTop ? Menu.Top : Menu.Bottom
attachTo: expandBtn
marginY: Appearance.spacing.small * (root.menuOnTop ? -1 : 1)
thisSideY: root.menuOnTop ? Menu.Bottom : Menu.Top
}
}
+19 -8
View File
@@ -8,19 +8,32 @@ Item {
id: root
property alias active: splitButton.active
property bool enabled: true
property alias buttonAlias: splitButton
property alias expanded: splitButton.expanded
property int expandedZ: 100
required property string label
property alias menuItems: splitButton.menuItems
property bool shouldBeActive: true
property alias type: splitButton.type
signal selected(item: MenuItem)
Layout.fillWidth: true
Layout.preferredHeight: row.implicitHeight + Appearance.padding.smaller * 2
anchors.left: parent.left
anchors.right: parent.right
clip: false
z: root.expanded ? expandedZ : -1
implicitHeight: row.implicitHeight + Appearance.padding.smaller * 2
opacity: shouldBeActive ? 1 : 0
scale: shouldBeActive ? 1 : 0.8
z: splitButton.menu.implicitHeight > 0 ? expandedZ : 1
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
RowLayout {
id: row
@@ -36,7 +49,6 @@ Item {
color: root.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.larger
text: root.label
z: root.expanded ? root.expandedZ : -1
}
CustomSplitButton {
@@ -44,14 +56,13 @@ Item {
enabled: root.enabled
type: CustomSplitButton.Filled
z: root.expanded ? root.expandedZ : -1
z: 2
menu.onItemSelected: item => {
root.selected(item);
splitButton.closeDropdown();
}
stateLayer.onClicked: {
splitButton.toggleDropdown();
splitButton.expanded = !splitButton.expanded;
}
}
}
+18 -16
View File
@@ -1,6 +1,6 @@
import QtQuick
import QtQuick.Templates
import QtQuick.Shapes
import QtQuick.Templates
import qs.Config
Switch {
@@ -12,38 +12,41 @@ Switch {
implicitWidth: implicitIndicatorWidth
indicator: CustomRect {
color: root.checked ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer)
implicitHeight: 13 + 7 * 2
color: root.checked && root.enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer)
implicitHeight: Appearance.font.size.medium + Appearance.padding.normal * 2
implicitWidth: implicitHeight * 1.7
radius: Appearance.rounding.full
CustomRect {
readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.3 : implicitHeight
readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.2 : implicitHeight
anchors.verticalCenter: parent.verticalCenter
color: root.checked ? DynamicColors.palette.m3onPrimary : DynamicColors.layer(DynamicColors.palette.m3outline, root.cLayer + 1)
implicitHeight: parent.implicitHeight - 10
color: root.checked && root.enabled ? DynamicColors.palette.m3onPrimary : DynamicColors.layer(DynamicColors.palette.m3outline, root.cLayer + 1)
implicitHeight: parent.implicitHeight - Appearance.padding.extraSmall
implicitWidth: nonAnimWidth
radius: Appearance.rounding.full
x: root.checked ? parent.implicitWidth - nonAnimWidth - 10 / 2 : 10 / 2
x: root.checked ? parent.implicitWidth - nonAnimWidth - Appearance.padding.extraSmall / 2 : Appearance.padding.extraSmall / 2
Behavior on implicitWidth {
Anim {
type: Anim.FastSpatial
}
}
Behavior on x {
Anim {
type: Anim.FastSpatial
}
}
CustomRect {
anchors.fill: parent
color: root.checked ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface
color: root.checked && root.enabled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface
opacity: root.pressed ? 0.1 : root.hovered ? 0.08 : 0
radius: parent.radius
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
}
@@ -63,14 +66,14 @@ Switch {
}
property point end2: {
if (root.pressed)
return Qt.point(width, height / 2);
return Qt.point(width * 0.8, height / 2);
if (root.checked)
return Qt.point(width * 0.85, height * 0.2);
return Qt.point(width * 0.85, height * 0.15);
}
property point start1: {
if (root.pressed)
return Qt.point(width * 0.1, height / 2);
return Qt.point(width * 0.2, height / 2);
if (root.checked)
return Qt.point(width * 0.15, height / 2);
return Qt.point(width * 0.15, height * 0.15);
@@ -88,7 +91,7 @@ Switch {
anchors.centerIn: parent
asynchronous: true
height: parent.implicitHeight - Appearance.padding.small * 2
height: parent.implicitHeight - Appearance.padding.larger
preferredRendererType: Shape.CurveRenderer
width: height
@@ -110,11 +113,11 @@ Switch {
}
ShapePath {
capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
capStyle: ShapePath.RoundCap
fillColor: "transparent"
startX: icon.start1.x
startY: icon.start1.y
strokeColor: root.checked ? DynamicColors.palette.m3primary : DynamicColors.palette.m3surfaceContainerHighest
strokeColor: root.checked && root.enabled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3surfaceContainerHighest
strokeWidth: Appearance.font.size.larger * 0.15
Behavior on strokeColor {
@@ -148,8 +151,7 @@ Switch {
}
component PropAnim: PropertyAnimation {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
easing.type: Easing.BezierSpline
duration: Appearance.anim.durations.expressiveFastSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
}
}
+1
View File
@@ -15,6 +15,7 @@ Text {
color: DynamicColors.palette.m3onSurface
font.family: Appearance.font.family.sans
font.pointSize: Appearance.font.size.normal
linkColor: DynamicColors.palette.m3onPrimaryFixedVariant
renderType: Text.NativeRendering
textFormat: Text.PlainText
+2 -1
View File
@@ -1,6 +1,6 @@
import qs.Config
import QtQuick
import QtQuick.Effects
import qs.Config
RectangularShadow {
property real dp: [0, 1, 3, 6, 8, 12][level]
@@ -13,6 +13,7 @@ RectangularShadow {
Behavior on dp {
Anim {
type: Anim.SlowEffects
}
}
}
+33
View File
@@ -0,0 +1,33 @@
import QtQuick
import QtQuick.Controls
import qs.Config
IconButton {
id: root
required property bool shouldBeVisible
opacity: 0
scale: 0
visible: root.scale > 0
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.small
}
}
Behavior on scale {
Anim {
}
}
onShouldBeVisibleChanged: {
if (root.shouldBeVisible) {
root.opacity = 1;
root.scale = 1;
} else {
root.opacity = 0;
root.scale = 0;
}
}
}
+5 -6
View File
@@ -41,12 +41,11 @@ CustomRect {
color: type === IconButton.Text ? "transparent" : disabled ? disabledColour : internalChecked ? activeColour : inactiveColour
implicitHeight: label.implicitHeight + padding * 2
implicitWidth: implicitHeight
radius: internalChecked ? 6 : implicitHeight / 2 * Math.min(1, 1)
radius: internalChecked ? 6 : (implicitHeight / 2 * Math.min(1, 1)) * Appearance.rounding.scale
Behavior on radius {
Anim {
id: radiusAnim
}
}
@@ -55,14 +54,14 @@ CustomRect {
StateLayer {
id: stateLayer
function onClicked(): void {
color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour
disabled: root.disabled
onClicked: {
if (root.toggle)
root.internalChecked = !root.internalChecked;
root.clicked();
}
color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour
disabled: root.disabled
}
MaterialIcon {
+2
View File
@@ -102,6 +102,7 @@ Item {
animate: root.animate
animateProp: "opacity"
color: root.color
font.pointSize: elideText.font.pointSize
text: elideText.text
}
@@ -111,6 +112,7 @@ Item {
animate: root.animate
animateProp: "opacity"
color: root.color
font.pointSize: elideText.font.pointSize
text: t1.text
x: t1.width + root.gap
}
+129 -69
View File
@@ -2,109 +2,169 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.Config
import qs.Drawers
Elevation {
MouseArea {
id: root
enum Side {
Top,
Bottom,
Left,
Right
}
property MenuItem active: items[0] ?? null
property int attachSideX: Menu.Right
property int attachSideY: Menu.Bottom
required property Item attachTo
property bool expanded
property list<MenuItem> items
property real marginX
property real marginY
property int thisSideX: Menu.Right
property int thisSideY: Menu.Top
signal itemSelected(item: MenuItem)
implicitHeight: root.expanded ? column.implicitHeight + Appearance.padding.small * 2 : 0
implicitWidth: Math.max(200, column.implicitWidth)
level: 2
opacity: root.expanded ? 1 : 0
radius: Appearance.rounding.normal
Behavior on implicitHeight {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
anchors.fill: parent
enabled: expanded
layer.enabled: opacity < 1
opacity: expanded ? 1 : 0
parent: {
const win = QsWindow.window;
const contentWin = win as Windows;
return contentWin ? contentWin.interactionWrapper : (win as QsWindow).contentItem;
}
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
type: Anim.DefaultEffects
}
}
CustomClippingRect {
anchors.fill: parent
color: DynamicColors.palette.m3surfaceContainer
radius: parent.radius
onClicked: expanded = false
ColumnLayout {
id: column
TransformWatcher {
id: watcher
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 5
a: root.parent
b: root.attachTo
}
Repeater {
model: root.items
Elevation {
id: menu
CustomRect {
id: item
implicitHeight: column.implicitHeight + column.anchors.margins * 2
implicitWidth: Math.max(200, column.implicitWidth + column.anchors.margins * 2)
level: 2
radius: Appearance.rounding.medium
x: {
watcher.transform;
const item = root.attachTo;
let off = root.attachSideX === Menu.Left ? 0 : item.width;
if (root.thisSideX === Menu.Right)
off -= width;
return item.mapToItem(root.parent, off, 0).x + root.marginX;
}
y: {
watcher.transform;
const item = root.attachTo;
let off = root.attachSideY === Menu.Top ? 0 : item.height;
if (root.thisSideY === Menu.Bottom)
off -= height;
return item.mapToItem(root.parent, 0, off).y + root.marginY;
}
readonly property bool active: modelData === root.active
required property int index
required property MenuItem modelData
transform: Scale {
origin.y: root.thisSideY === Menu.Bottom ? menu.height : 0
yScale: root.expanded ? 1 : 0.1
Layout.fillWidth: true
implicitHeight: menuOptionRow.implicitHeight + Appearance.padding.normal * 2
implicitWidth: menuOptionRow.implicitWidth + Appearance.padding.normal * 2
Behavior on yScale {
Anim {
}
}
}
CustomRect {
anchors.fill: parent
color: DynamicColors.palette.m3surfaceContainerLow
radius: parent.radius
ColumnLayout {
id: column
anchors.fill: parent
anchors.margins: Appearance.padding.extraSmall
spacing: Appearance.spacing.extraSmall
Repeater {
id: repeater
model: root.items
CustomRect {
anchors.fill: parent
anchors.leftMargin: Appearance.padding.small
anchors.rightMargin: Appearance.padding.small
color: Qt.alpha(DynamicColors.palette.m3secondaryContainer, active ? 1 : 0)
radius: Appearance.rounding.normal - Appearance.padding.small
id: item
readonly property bool active: modelData === root.active
required property int index
required property MenuItem modelData
Layout.fillWidth: true
color: Qt.alpha(DynamicColors.palette.m3tertiaryContainer, active ? 1 : 0)
implicitHeight: menuOptionRow.implicitHeight + Appearance.padding.larger * 2
implicitWidth: menuOptionRow.implicitWidth + Appearance.padding.larger * 2
radius: Appearance.rounding.small
Behavior on radius {
Anim {
}
}
StateLayer {
function onClicked(): void {
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurface
disabled: !root.expanded
onClicked: {
root.itemSelected(item.modelData);
root.active = item.modelData;
item.modelData.clicked();
root.expanded = false;
}
color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
disabled: !root.expanded
}
}
RowLayout {
id: menuOptionRow
anchors.fill: parent
anchors.margins: Appearance.padding.normal
spacing: Appearance.spacing.small
MaterialIcon {
Layout.alignment: Qt.AlignVCenter
color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurfaceVariant
text: item.modelData.icon
}
CustomText {
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
text: item.modelData.text
}
RowLayout {
id: menuOptionRow
Loader {
Layout.alignment: Qt.AlignVCenter
active: item.modelData.trailingIcon.length > 0
visible: active
anchors.fill: parent
anchors.margins: Appearance.padding.larger
spacing: Appearance.spacing.small
sourceComponent: MaterialIcon {
color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
text: item.modelData.trailingIcon
MaterialIcon {
Layout.alignment: Qt.AlignVCenter
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurfaceVariant
text: item.modelData.icon
}
CustomText {
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurface
text: item.modelData.text
}
Loader {
Layout.alignment: Qt.AlignVCenter
active: item.modelData.trailingIcon.length > 0
asynchronous: true
visible: active
sourceComponent: MaterialIcon {
color: item.active ? DynamicColors.palette.m3onTertiaryContainer : DynamicColors.palette.m3onSurfaceVariant
text: item.modelData.trailingIcon
}
}
}
}
+2
View File
@@ -29,6 +29,7 @@ Elevation {
level: root.expanded ? 2 : 0
radius: itemHeight / 2
visible: implicitHeight > 0
z: root.expanded ? 100 : 0
Behavior on implicitHeight {
Anim {
@@ -68,6 +69,7 @@ Elevation {
anchors.fill: parent
color: DynamicColors.palette.m3surfaceContainer
radius: parent.radius
z: root.z
// Main visible spinner: normal/outside text color
PathView {
+172 -67
View File
@@ -1,15 +1,53 @@
import qs.Config
import QtQuick
import QtQuick.Shapes
import ZShell
import ZShell.Components
import qs.Helpers
import qs.Config
MouseArea {
id: root
property color color: DynamicColors.palette.m3onSurface
property alias bottomLeftRadius: base.bottomLeftRadius
property alias bottomRightRadius: base.bottomRightRadius
property real circleRadius
property alias color: base.color
property bool disabled
property real radius: parent?.radius ?? 0
property alias rect: hoverLayer
readonly property real endRadius: {
const d1 = distSq(0, 0);
const d2 = distSq(width, 0);
const d3 = distSq(0, height);
const d4 = distSq(width, height);
return (Math.sqrt(Math.max(d1, d2, d3, d4)) + (shapeMorph ? 24 : 0)) * 1.3;
}
property real endRadiusAtPress
property bool manualPressOverride
property real pressX: width / 2
property real pressY: height / 2
property alias radius: base.radius
readonly property alias rect: base
property bool shapeMorph
property bool showHoverBackground: true
property real stateOpacity: containsMouse ? 0.08 : 0
property alias topLeftRadius: base.topLeftRadius
property alias topRightRadius: base.topRightRadius
function onClicked(): void {
function clamp(r: real): real {
return Math.max(0, Math.min(r, width / 2, height / 2));
}
function distSq(x: real, y: real): real {
return (pressX - x) ** 2 + (pressY - y) ** 2;
}
function press(x: real, y: real): void {
pressX = x;
pressY = y;
fadeAnim.complete();
circleRadius = 0;
circle.opacity = 0.1;
rippleAnim.restart();
endRadiusAtPress = endRadius;
}
anchors.fill: parent
@@ -17,79 +55,146 @@ MouseArea {
enabled: !disabled
hoverEnabled: true
onClicked: event => !disabled && onClicked(event)
onPressed: event => {
if (disabled)
return;
rippleAnim.x = event.x;
rippleAnim.y = event.y;
const dist = (ox, oy) => ox * ox + oy * oy;
rippleAnim.radius = Math.sqrt(Math.max(dist(event.x, event.y), dist(event.x, height - event.y), dist(width - event.x, event.y), dist(width - event.x, height - event.y)));
rippleAnim.restart();
Behavior on stateOpacity {
Anim {
type: Anim.DefaultEffects
}
}
SequentialAnimation {
onCircleRadiusChanged: {
if (!(pressed || manualPressOverride) && circleRadius > endRadiusAtPress * 0.99 && !fadeAnim.running)
fadeAnim.start();
}
onClicked: event => !disabled && onClicked(event)
onManualPressOverrideChanged: {
if (!(pressed || manualPressOverride) && circleRadius > endRadiusAtPress * 0.99 && !fadeAnim.running)
fadeAnim.start();
}
onPressed: e => press(e.x, e.y)
onPressedChanged: {
if (!(pressed || manualPressOverride) && !rippleAnim.running && circle.opacity > 0)
fadeAnim.start();
}
Anim {
id: rippleAnim
property real radius
property real x
property real y
PropertyAction {
property: "x"
target: ripple
value: rippleAnim.x
}
PropertyAction {
property: "y"
target: ripple
value: rippleAnim.y
}
PropertyAction {
property: "opacity"
target: ripple
value: 0.08
}
Anim {
easing.bezierCurve: MaterialEasing.standardDecel
from: 0
properties: "implicitWidth,implicitHeight"
target: ripple
to: rippleAnim.radius * 2
}
Anim {
property: "opacity"
target: ripple
to: 0
}
alwaysRunToEnd: true
duration: Appearance.anim.durations.expressiveSlowEffects * 2
easing.bezierCurve: Appearance.anim.curves.standard
property: "circleRadius"
target: root
to: root.endRadius
}
CustomClippingRect {
id: hoverLayer
Anim {
id: fadeAnim
property: "opacity"
target: circle
to: 0
type: Anim.SlowEffects
}
CustomRect {
id: base
anchors.fill: parent
border.pixelAligned: false
color: Qt.alpha(root.color, root.disabled ? 0 : root.pressed ? 0.1 : root.containsMouse ? 0.08 : 0)
radius: root.radius
bottomLeftRadius: root.parent?.bottomLeftRadius ?? radius ?? 0
bottomRightRadius: root.parent?.bottomRightRadius ?? radius ?? 0
color: DynamicColors.palette.m3onSurface
opacity: root.stateOpacity
// Pick up radius from parent if it has one (parent can be anything with radius props)
// qmllint disable missing-property
radius: root.parent?.radius ?? 0
topLeftRadius: root.parent?.topLeftRadius ?? radius ?? 0
topRightRadius: root.parent?.topRightRadius ?? radius ?? 0
// qmllint enable missing-property
}
CustomRect {
id: ripple
Shape {
id: circle
border.pixelAligned: false
color: root.color
opacity: 0
radius: Appearance.rounding.full
anchors.fill: parent
opacity: 0
preferredRendererType: Shape.CurveRenderer
transform: Translate {
x: -ripple.width / 2
y: -ripple.height / 2
ShapePath {
fillColor: base.color
startX: root.clamp(base.topLeftRadius)
startY: 0
strokeColor: "transparent"
strokeWidth: 0
fillGradient: RadialGradient {
centerRadius: root.circleRadius
centerX: root.pressX
centerY: root.pressY
focalX: centerX
focalY: centerY
GradientStop {
color: Qt.alpha(base.color, 1)
position: 0
}
GradientStop {
color: Qt.alpha(base.color, 1)
position: ZUtils.clamp(1 - 0.2 * root.endRadius / root.circleRadius, 0.01, 0.99)
}
GradientStop {
color: Qt.alpha(base.color, ZUtils.clamp((root.circleRadius / root.endRadius - 0.9) / 0.1, 0, 1))
position: 1
}
}
PathLine {
x: root.width - root.clamp(base.topRightRadius)
y: 0
}
PathArc {
radiusX: root.clamp(base.topRightRadius)
radiusY: root.clamp(base.topRightRadius)
relativeX: root.clamp(base.topRightRadius)
relativeY: root.clamp(base.topRightRadius)
}
PathLine {
x: root.width
y: root.height - root.clamp(base.bottomRightRadius)
}
PathArc {
radiusX: root.clamp(base.bottomRightRadius)
radiusY: root.clamp(base.bottomRightRadius)
relativeX: -root.clamp(base.bottomRightRadius)
relativeY: root.clamp(base.bottomRightRadius)
}
PathLine {
x: root.clamp(base.bottomLeftRadius)
y: root.height
}
PathArc {
radiusX: root.clamp(base.bottomLeftRadius)
radiusY: root.clamp(base.bottomLeftRadius)
relativeX: -root.clamp(base.bottomLeftRadius)
relativeY: -root.clamp(base.bottomLeftRadius)
}
PathLine {
x: 0
y: root.clamp(base.topLeftRadius)
}
PathArc {
radiusX: root.clamp(base.topLeftRadius)
radiusY: root.clamp(base.topLeftRadius)
x: root.clamp(base.topLeftRadius)
y: 0
}
}
}
+1 -2
View File
@@ -4,10 +4,9 @@ import Quickshell
Singleton {
readonly property AppearanceConf.Anim anim: Config.appearance.anim
readonly property AppearanceConf.Deform deform: Config.appearance.deform
readonly property AppearanceConf.FontStuff font: Config.appearance.font
readonly property AppearanceConf.Padding padding: Config.appearance.padding
// Literally just here to shorten accessing stuff :woe:
// Also kinda so I can keep accessing it with `Appearance.xxx` instead of `Conf.appearance.xxx`
readonly property AppearanceConf.Rounding rounding: Config.appearance.rounding
readonly property AppearanceConf.Spacing spacing: Config.appearance.spacing
readonly property AppearanceConf.Transparency transparency: Config.appearance.transparency
+24 -7
View File
@@ -3,6 +3,8 @@ import Quickshell.Io
JsonObject {
property Anim anim: Anim {
}
property Deform deform: Deform {
}
property FontStuff font: FontStuff {
}
property Padding padding: Padding {
@@ -26,9 +28,13 @@ JsonObject {
property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1]
property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1]
property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1]
property list<real> expressiveDefaultEffects: [0.34, 0.8, 0.34, 1, 1, 1]
property list<real> expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1, 1, 1]
property list<real> expressiveEffects: [0.34, 0.8, 0.34, 1, 1, 1]
property list<real> expressiveFastEffects: [0.31, 0.94, 0.34, 1, 1, 1]
property list<real> expressiveFastSpatial: [0.42, 1.67, 0.21, 0.9, 1, 1]
property list<real> expressiveSlowEffects: [0.34, 0.88, 0.34, 1, 1, 1]
property list<real> expressiveSlowSpatial: [0.39, 1.29, 0.35, 0.98, 1, 1]
property list<real> standard: [0.2, 0, 0, 1, 1, 1]
property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1]
property list<real> standardDecel: [0, 0, 0, 1, 1, 1]
@@ -36,13 +42,18 @@ JsonObject {
component AnimDurations: JsonObject {
property int expressiveDefaultSpatial: 500 * scale
property int expressiveEffects: 200 * scale
property int expressiveFastEffects: 150 * scale
property int expressiveFastSpatial: 350 * scale
property int expressiveSlowEffects: 300 * scale
property int extraLarge: 1000 * scale
property int large: 600 * scale
property int normal: 400 * scale
property real scale: 1
property int small: 200 * scale
}
component Deform: JsonObject {
property real scale: 1
}
component FontFamily: JsonObject {
property string clock: "Rubik"
property string material: "Material Symbols Rounded"
@@ -52,7 +63,8 @@ JsonObject {
component FontSize: JsonObject {
property int extraLarge: 28 * scale
property int large: 18 * scale
property int larger: 15 * scale
property int larger: 16 * scale
property int medium: 14 * scale
property int normal: 13 * scale
property real scale: 1
property int small: 11 * scale
@@ -65,28 +77,33 @@ JsonObject {
}
}
component Padding: JsonObject {
property int large: 15 * scale
property int extraLargeIncreased: 32 * scale
property int extraSmall: 4 * scale
property int large: 16 * scale
property int larger: 12 * scale
property int normal: 10 * scale
property int normal: 8 * scale
property real scale: 1
property int small: 5 * scale
property int smaller: 7 * scale
property int smallest: 2 * scale
}
component Rounding: JsonObject {
property int extraSmall: 4 * scale
property int full: 1000 * scale
property int large: 25 * scale
property int normal: 17 * scale
property int large: 24 * scale
property int medium: 16 * scale
property int normal: 18 * scale
property real scale: 1
property int small: 12 * scale
property int smallest: 8 * scale
}
component Spacing: JsonObject {
property int extraSmall: 4 * scale
property int large: 20 * scale
property int larger: 15 * scale
property int larger: 16 * scale
property int normal: 12 * scale
property real scale: 1
property int small: 7 * scale
property int small: 8 * scale
property int smaller: 10 * scale
}
component Transparency: JsonObject {
+7
View File
@@ -4,4 +4,11 @@ import qs.Config
JsonObject {
property bool enabled: true
property int wallFadeDuration: MaterialEasing.standardTime
property real alignX: 0.5
property real alignY: 0.5
property real zoom: 1.0
property real sourceClipX: 0
property real sourceClipY: 0
property real sourceClipW: 0
property real sourceClipH: 0
}
+10 -11
View File
@@ -8,10 +8,6 @@ JsonObject {
id: "workspaces",
enabled: true
},
{
id: "audio",
enabled: true
},
{
id: "media",
enabled: true
@@ -24,10 +20,6 @@ JsonObject {
id: "updates",
enabled: true
},
{
id: "dash",
enabled: true
},
{
id: "spacer",
enabled: true
@@ -41,12 +33,12 @@ JsonObject {
enabled: true
},
{
id: "tray",
id: "hyprsunset",
enabled: true
},
{
id: "upower",
enabled: false
id: "tray",
enabled: true
},
{
id: "network",
@@ -62,9 +54,13 @@ JsonObject {
},
]
property int height: 34
property bool hideWhenNotif: false
property Popouts popouts: Popouts {
}
property int rounding: 8
property int smoothing: 32
property Tray tray: Tray {
}
component Popouts: JsonObject {
property bool activeWindow: true
@@ -75,4 +71,7 @@ JsonObject {
property bool tray: true
property bool upower: true
}
component Tray: JsonObject {
property int trayIconSize: 24
}
}
+8
View File
@@ -1,5 +1,13 @@
import Quickshell.Io
JsonObject {
property Presets presets: Presets {
}
property string schemeType: "vibrant"
component Presets: JsonObject {
property string accent: ""
property string name: ""
property string variant: ""
}
}
+55 -7
View File
@@ -4,8 +4,6 @@ import Quickshell
import Quickshell.Io
import ZShell
import QtQuick
import qs.Helpers
import qs.Paths
Singleton {
id: root
@@ -23,6 +21,7 @@ Singleton {
property alias osd: adapter.osd
property alias overview: adapter.overview
property bool recentlySaved: false
property alias screenshot: adapter.screenshot
property alias services: adapter.services
property alias sidebar: adapter.sidebar
property alias utilities: adapter.utilities
@@ -48,6 +47,9 @@ Singleton {
padding: {
scale: appearance.padding.scale
},
deform: {
scale: appearance.deform.scale
},
font: {
family: {
sans: appearance.font.family.sans,
@@ -77,16 +79,28 @@ Singleton {
function serializeBackground(): var {
return {
wallFadeDuration: background.wallFadeDuration,
enabled: background.enabled
enabled: background.enabled,
alignX: background.alignX,
sourceClipX: background.sourceClipX,
sourceClipY: background.sourceClipY,
sourceClipW: background.sourceClipW,
sourceClipH: background.sourceClipH,
alignY: background.alignY,
zoom: background.zoom
};
}
function serializeBar(): var {
return {
autoHide: barConfig.autoHide,
hideWhenNotif: barConfig.hideWhenNotif,
rounding: barConfig.rounding,
border: barConfig.border,
smoothing: barConfig.smoothing,
height: barConfig.height,
tray: {
trayIconSize: barConfig.tray.trayIconSize
},
popouts: {
tray: barConfig.popouts.tray,
audio: barConfig.popouts.audio,
@@ -102,7 +116,12 @@ Singleton {
function serializeColors(): var {
return {
schemeType: colors.schemeType
schemeType: colors.schemeType,
presets: {
name: colors.presets.name,
variant: colors.presets.variant,
accent: colors.presets.accent
}
};
}
@@ -121,7 +140,8 @@ Singleton {
background: serializeBackground(),
launcher: serializeLauncher(),
colors: serializeColors(),
dock: serializeDock()
dock: serializeDock(),
screenshot: serializeScreenshot()
};
}
@@ -172,11 +192,16 @@ Singleton {
return {
logo: general.logo,
wallpaperPath: general.wallpaperPath,
username: general.username,
desktopIcons: general.desktopIcons,
dateFormat: general.dateFormat,
color: {
mode: general.color.mode,
smart: general.color.smart,
scheduleDark: general.color.scheduleDark,
scheduleHyprsunset: general.color.scheduleHyprsunset,
scheduleHyprsunsetStart: general.color.scheduleHyprsunsetStart,
hyprsunsetTemp: general.color.hyprsunsetTemp,
scheduleHyprsunsetEnd: general.color.scheduleHyprsunsetEnd,
schemeGeneration: general.color.schemeGeneration,
scheduleDarkStart: general.color.scheduleDarkStart,
scheduleDarkEnd: general.color.scheduleDarkEnd,
@@ -190,6 +215,10 @@ Singleton {
},
idle: {
timeouts: general.idle.timeouts
},
battery: {
popupThresholds: general.battery.popupThresholds,
critPerc: general.battery.critPerc
}
};
}
@@ -198,6 +227,7 @@ Singleton {
return {
maxAppsShown: launcher.maxAppsShown,
maxWallpapers: launcher.maxWallpapers,
uwsm: launcher.uwsm,
actionPrefix: launcher.actionPrefix,
specialPrefix: launcher.specialPrefix,
useFuzzy: {
@@ -221,6 +251,8 @@ Singleton {
return {
recolorLogo: lock.recolorLogo,
enableFprint: lock.enableFprint,
showNotifContent: lock.showNotifContent,
showNotifIcon: lock.showNotifIcon,
maxFprintTries: lock.maxFprintTries,
blurAmount: lock.blurAmount,
sizes: {
@@ -262,9 +294,24 @@ Singleton {
};
}
function serializeScreenshot(): var {
return {
enable_pp: screenshot.enable_pp,
mode: screenshot.mode,
radius: screenshot.radius,
shadow: screenshot.shadow,
rounding: screenshot.rounding,
shadow_blur: screenshot.shadow_blur,
shadow_color: screenshot.shadow_color,
shadow_offset_x: screenshot.shadow_offset_x,
shadow_offset_y: screenshot.shadow_offset_y
};
}
function serializeServices(): var {
return {
weatherLocation: services.weatherLocation,
updates: services.updates,
useFahrenheit: services.useFahrenheit,
ddcutilService: services.ddcutilService,
useTwelveHourClock: services.useTwelveHourClock,
@@ -317,7 +364,6 @@ Singleton {
ElapsedTimer {
id: timer
}
Timer {
@@ -415,6 +461,8 @@ Singleton {
}
property Overview overview: Overview {
}
property Screenshot screenshot: Screenshot {
}
property Services services: Services {
}
property SidebarConfig sidebar: SidebarConfig {
+12 -64
View File
@@ -29,9 +29,10 @@ Singleton {
readonly property alias wallLuminance: analyser.luminance
function alterColor(c: color, a: real, layer: int): color {
const luminance = getLuminance(c);
const initLuminance = getLuminance(c);
const luminance = Math.max(initLuminance, 0.001);
const offset = (!light || layer == 1 ? 1 : -layer / 2) * (light ? 0.2 : 0.3) * (1 - transparency.base) * (1 + wallLuminance * (light ? (layer == 1 ? 3 : 1) : 2.5));
const offset = (!light || layer == 1 ? 1 : -layer / 2) * (light ? 0.2 : 0.3) * (0.2 + 0.3 * (1 - transparency.base)) * (1 + wallLuminance * (light ? (layer == 1 ? 3 : 1) : 2.5));
const scale = (luminance + offset) / luminance;
const r = Math.max(0, Math.min(1, c.r * scale));
const g = Math.max(0, Math.min(1, c.g * scale));
@@ -84,6 +85,10 @@ Singleton {
Config.save();
}
function swapRG(c: color): color {
return Qt.rgba(c.g, c.r, c.b, c.a);
}
FileView {
path: "/etc/zshell-greeter/scheme.json"
watchChanges: true
@@ -95,69 +100,9 @@ Singleton {
ImageAnalyser {
id: analyser
source: WallpaperPath.currentWallpaperPath
source: WallpaperPath.lockscreenBg
}
component M3MaccchiatoPalette: QtObject {
property color m3background: "#131317"
property color m3error: "#ffb4ab"
property color m3errorContainer: "#93000a"
property color m3inverseOnSurface: "#303034"
property color m3inversePrimary: "#525b92"
property color m3inverseSurface: "#e4e1e7"
property color m3neutral_paletteKeyColor: "#77767b"
property color m3neutral_variant_paletteKeyColor: "#767680"
property color m3onBackground: "#e4e1e7"
property color m3onError: "#690005"
property color m3onErrorContainer: "#ffdad6"
property color m3onPrimary: "#232c60"
property color m3onPrimaryContainer: "#ffffff"
property color m3onPrimaryFixed: "#0b154b"
property color m3onPrimaryFixedVariant: "#3a4378"
property color m3onSecondary: "#2c2f44"
property color m3onSecondaryContainer: "#b1b3ce"
property color m3onSecondaryFixed: "#171a2e"
property color m3onSecondaryFixedVariant: "#42455c"
property color m3onSuccess: "#213528"
property color m3onSuccessContainer: "#D1E9D6"
property color m3onSurface: "#e4e1e7"
property color m3onSurfaceVariant: "#c6c5d1"
property color m3onTertiary: "#4c1f48"
property color m3onTertiaryContainer: "#000000"
property color m3onTertiaryFixed: "#340831"
property color m3onTertiaryFixedVariant: "#66365f"
property color m3outline: "#90909a"
property color m3outlineVariant: "#46464f"
property color m3primary: "#bac3ff"
property color m3primaryContainer: "#6a73ac"
property color m3primaryFixed: "#dee0ff"
property color m3primaryFixedDim: "#bac3ff"
property color m3primary_paletteKeyColor: "#6a73ac"
property color m3scrim: "#000000"
property color m3secondary: "#c3c5e0"
property color m3secondaryContainer: "#42455c"
property color m3secondaryFixed: "#dfe1fd"
property color m3secondaryFixedDim: "#c3c5e0"
property color m3secondary_paletteKeyColor: "#72758e"
property color m3shadow: "#000000"
property color m3success: "#B5CCBA"
property color m3successContainer: "#374B3E"
property color m3surface: "#131317"
property color m3surfaceBright: "#39393d"
property color m3surfaceContainer: "#1f1f23"
property color m3surfaceContainerHigh: "#2a2a2e"
property color m3surfaceContainerHighest: "#353438"
property color m3surfaceContainerLow: "#1b1b1f"
property color m3surfaceContainerLowest: "#0e0e12"
property color m3surfaceDim: "#131317"
property color m3surfaceTint: "#bac3ff"
property color m3surfaceVariant: "#46464f"
property color m3tertiary: "#f1b3e5"
property color m3tertiaryContainer: "#b77ead"
property color m3tertiaryFixed: "#ffd7f4"
property color m3tertiaryFixedDim: "#f1b3e5"
property color m3tertiary_paletteKeyColor: "#9b6592"
}
component M3Palette: QtObject {
property color m3background: "#191114"
property color m3error: "#ffb4ab"
@@ -279,8 +224,11 @@ Singleton {
readonly property color m3tertiary_paletteKeyColor: root.layer(root.palette.m3tertiary_paletteKeyColor)
}
component Transparency: QtObject {
readonly property real base: Appearance.transparency.base - (root.light ? 0.1 : 0)
readonly property real base: Math.max(0, Math.min(1, Appearance.transparency.base - (root.light ? 0.1 : 0)))
readonly property bool enabled: Appearance.transparency.enabled
readonly property real layers: Appearance.transparency.layers
onBaseChanged: debounceTimer.restart()
onEnabledChanged: debounceTimer.restart()
}
}
+19 -1
View File
@@ -4,13 +4,15 @@ import Quickshell
JsonObject {
property Apps apps: Apps {
}
property Battery battery: Battery {
}
property Color color: Color {
}
property string dateFormat: "ddd d MMM - hh:mm:ss"
property bool desktopIcons: false
property Idle idle: Idle {
}
property string logo: ""
property string username: ""
property string wallpaperPath: Quickshell.env("HOME") + "/Pictures/Wallpapers"
component Apps: JsonObject {
@@ -19,11 +21,27 @@ JsonObject {
property list<string> playback: ["mpv"]
property list<string> terminal: ["kitty"]
}
component Battery: JsonObject {
property int critPerc: 5
property list<var> popupThresholds: [
{
perc: 20,
name: qsTr("Low battery"),
message: qsTr("Battery is low"),
icon: "battery_android_frame_2"
},
]
}
component Color: JsonObject {
property int hyprsunsetTemp: 5000
property string mode: "dark"
property bool neovimColors: false
property bool scheduleDark: false
property int scheduleDarkEnd: 0
property int scheduleDarkStart: 0
property bool scheduleHyprsunset: false
property int scheduleHyprsunsetEnd: 0
property int scheduleHyprsunsetStart: 0
property bool schemeGeneration: true
property bool smart: false
}
+1
View File
@@ -91,6 +91,7 @@ JsonObject {
property string specialPrefix: "@"
property UseFuzzy useFuzzy: UseFuzzy {
}
property bool uwsm: true
component Sizes: JsonObject {
property int itemHeight: 50
+2
View File
@@ -5,6 +5,8 @@ JsonObject {
property bool enableFprint: true
property int maxFprintTries: 3
property bool recolorLogo: false
property bool showNotifContent: false
property bool showNotifIcon: true
property Sizes sizes: Sizes {
}
+13
View File
@@ -0,0 +1,13 @@
import Quickshell.Io
JsonObject {
property bool enable_pp: true
property string mode: "manual"
property real radius: 12.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 real shadow_offset_x: 5.0
property real shadow_offset_y: 5.0
}
+1
View File
@@ -14,6 +14,7 @@ JsonObject {
"to": "YT Music"
}
]
property bool updates: true
property bool useFahrenheit: false
property bool useTwelveHourClock: Qt.locale().timeFormat(Locale.ShortFormat).toLowerCase().includes("a")
property int visualizerBars: 30
+20 -20
View File
@@ -16,27 +16,14 @@ Scope {
property bool launching: false
property string promptMessage: ""
readonly property var selectedSession: sessionIndex >= 0 ? sessions[sessionIndex] : null
readonly property var selectedUser: Users.selectedUser
property int sessionIndex: sessions.length > 0 ? 0 : -1
property var sessions: []
readonly property string userFace: selectedUser ? selectedUser.face : ""
readonly property string username: Users.selectedUsername
// User handling - now uses the Users singleton
readonly property var users: Users.users
readonly property var selectedUser: Users.selectedUser
readonly property string username: Users.selectedUsername
readonly property string userFace: selectedUser ? selectedUser.face : ""
// User selection functions (delegate to Users singleton)
function selectUser(username: string): bool {
return Users.selectUser(username);
}
function selectNextUser(): void {
Users.selectNext();
}
function selectPreviousUser(): void {
Users.selectPrevious();
}
signal flashMsg
@@ -58,11 +45,11 @@ Scope {
event.accepted = true;
return;
}
if (event.text && !/[\r\n]/.test(event.text)) {
} else if (event.key === Qt.Key_Escape) {
buffer = "";
} else if (" abcdefghijklmnopqrstuvwxyz1234567890`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?".includes(event.text.toLowerCase())) {
// No illegal characters (you are insane if you use unicode in your password)
buffer += event.text;
event.accepted = true;
}
}
@@ -81,6 +68,19 @@ Scope {
Greetd.launch(selectedSession.command, [], true);
}
function selectNextUser(): void {
Users.selectNext();
}
function selectPreviousUser(): void {
Users.selectPrevious();
}
// User selection functions (delegate to Users singleton)
function selectUser(username: string): bool {
return Users.selectUser(username);
}
function submit(): void {
errorMessage = "";
+1 -1
View File
@@ -83,7 +83,7 @@ ColumnLayout {
radius: Appearance.rounding.normal - Appearance.padding.smaller
StateLayer {
function onClicked(): void {
onClicked: {
root.greeter.sessionIndex = index;
}
}
+1 -1
View File
@@ -84,7 +84,7 @@ ColumnLayout {
radius: Appearance.rounding.normal - Appearance.padding.smaller
StateLayer {
function onClicked(): void {
onClicked: {
root.greeter.selectUser(modelData.username);
}
}
+30 -84
View File
@@ -1,19 +1,13 @@
pragma Singleton
import Quickshell
import Quickshell.Io
import "../scripts/levendist.js" as Levendist
import qs.Helpers
import "../scripts/fuzzysort.js" as Fuzzy
import qs.Config
Singleton {
id: root
readonly property list<DesktopEntry> list: Array.from(DesktopEntries.applications.values).filter((app, index, self) => index === self.findIndex(t => (t.id === app.id)))
readonly property var preppedIcons: list.map(a => ({
name: Fuzzy.prepare(`${a.icon} `),
entry: a
}))
readonly property list<DesktopEntry> list: Array.from(DesktopEntries.applications.values)
readonly property var preppedNames: list.map(a => ({
name: Fuzzy.prepare(`${a.name} `),
entry: a
@@ -36,8 +30,7 @@ Singleton {
"replace": "system-lock-screen"
}
]
readonly property real scoreGapThreshold: 0.1
readonly property real scoreThreshold: 0.6
property real scoreThreshold: 0.2
property var substitutions: ({
"code-url-handler": "visual-studio-code",
"Code": "visual-studio-code",
@@ -45,64 +38,27 @@ Singleton {
"pavucontrol-qt": "pavucontrol",
"wps": "wps-office2019-kprometheus",
"wpsoffice": "wps-office2019-kprometheus",
"footclient": "foot"
"footclient": "foot",
"zen": "zen-browser"
})
function bestFuzzyEntry(search: string, preppedList: list<var>, key: string): var {
const results = Fuzzy.go(search, preppedList, {
key: key,
threshold: root.scoreThreshold,
limit: 2
signal reload
function fuzzyQuery(search: string): var {
return Fuzzy.go(search, preppedNames, {
all: true,
key: "name"
}).map(r => {
return r.obj.entry;
});
if (!results || results.length === 0)
return null;
const best = results[0];
const second = results.length > 1 ? results[1] : null;
if (second && (best.score - second.score) < root.scoreGapThreshold)
return null;
return best.obj.entry;
}
function fuzzyQuery(search: string, preppedList: list<var>): var {
const entry = bestFuzzyEntry(search, preppedList, "name");
return entry ? [entry] : [];
}
function getKebabNormalizedAppName(str: string): string {
return str.toLowerCase().replace(/\s+/g, "-");
}
function getReverseDomainNameAppName(str: string): string {
return str.split('.').slice(-1)[0];
}
function getUndescoreToKebabAppName(str: string): string {
return str.toLowerCase().replace(/_/g, "-");
}
function guessIcon(str) {
if (!str || str.length == 0)
return "image-missing";
if (iconExists(str))
return str;
const entry = DesktopEntries.byId(str);
if (entry)
return entry.icon;
const heuristicEntry = DesktopEntries.heuristicLookup(str);
if (heuristicEntry)
return heuristicEntry.icon;
if (substitutions[str])
return substitutions[str];
if (substitutions[str.toLowerCase()])
return substitutions[str.toLowerCase()];
for (let i = 0; i < regexSubstitutions.length; i++) {
const substitution = regexSubstitutions[i];
@@ -111,35 +67,25 @@ Singleton {
return replacedName;
}
const lowercased = str.toLowerCase();
if (iconExists(lowercased))
return lowercased;
if (iconExists(str))
return str;
const reverseDomainNameAppName = getReverseDomainNameAppName(str);
if (iconExists(reverseDomainNameAppName))
return reverseDomainNameAppName;
let guessStr = str;
guessStr = str.split('.').slice(-1)[0].toLowerCase();
if (iconExists(guessStr))
return guessStr;
guessStr = str.toLowerCase().replace(/\s+/g, "-");
if (iconExists(guessStr))
return guessStr;
const searchResults = root.fuzzyQuery(str);
if (searchResults.length > 0) {
const firstEntry = searchResults[0];
guessStr = firstEntry.icon;
if (iconExists(guessStr))
return guessStr;
}
const lowercasedDomainNameAppName = reverseDomainNameAppName.toLowerCase();
if (iconExists(lowercasedDomainNameAppName))
return lowercasedDomainNameAppName;
const kebabNormalizedGuess = getKebabNormalizedAppName(str);
if (iconExists(kebabNormalizedGuess))
return kebabNormalizedGuess;
const undescoreToKebabGuess = getUndescoreToKebabAppName(str);
if (iconExists(undescoreToKebabGuess))
return undescoreToKebabGuess;
const iconSearchResult = fuzzyQuery(str, preppedIcons);
if (iconSearchResult && iconExists(iconSearchResult.icon))
return iconSearchResult.icon;
const nameSearchResult = root.fuzzyQuery(str, preppedNames);
if (nameSearchResult && iconExists(nameSearchResult.icon))
return nameSearchResult.icon;
return "application-x-executable";
return str;
}
function iconExists(iconName) {
+34
View File
@@ -0,0 +1,34 @@
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
readonly property real timeToEmpty: UPower.displayDevice.timeToEmpty
readonly property real timeToFull: UPower.displayDevice.timeToFull
}
+1 -1
View File
@@ -232,7 +232,7 @@ Singleton {
}
function setBrightness(value: real): void {
value = Math.max(0, Math.min(1, value));
value = Math.max(Config.services.minBrightness, Math.min(1, value));
const rounded = Math.round(value * 100);
if (Math.round(brightness * 100) === rounded)
return;
+249
View File
@@ -0,0 +1,249 @@
pragma Singleton
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Config
import "../scripts/fuzzysort.js" as Fuzzy
Singleton {
id: root
property string cliphistBinary: "cliphist"
property string currentEntry: ""
property list<string> entries: []
property real pasteDelay: 0.05
readonly property var preparedEntries: entries.map(a => ({
name: Fuzzy.prepare(displayText(a)),
entry: a
}))
property string previewImageFile: "/tmp/qs-cliphist-preview.img"
property string previewImageSource: ""
property bool previewIsImage: false
property list<var> previewText: []
property int previewToken: 0
property real scoreThreshold: 0.2
function copy(entry): void {
Quickshell.execDetached(["bash", "-c", `printf '${shellSingleQuoteEscape(entry)}' | ${root.cliphistBinary} decode | wl-copy`]);
}
function deleteEntry(entry): void {
deleteProc.deleteEntry(entry);
}
function displayText(entry): string {
return entry.replace(/^\s*\d+\s+/, "");
}
function entryIsImage(entry): bool {
return !!(/^\d+\t\[\[.*binary data.*\d+x\d+.*\]\]$/.test(entry));
}
function escapeHtml(str): string {
return String(str).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
}
function fuzzyQuery(search: string): var {
if (search.trim() === "") {
return entries;
}
return Fuzzy.go(search, preparedEntries, {
all: true,
key: "name"
}).map(r => {
return r.obj.entry;
});
}
function paste(entry): void {
Quickshell.execDetached(["bash", "-c", `printf '${shellSingleQuoteEscape(entry)}' | ${root.cliphistBinary} decode | wl-copy && wl-paste`]);
}
function processPreviewLines(text: string): var {
if (text === "")
return [];
const lines = text.split("\n");
let minIndent = Infinity;
for (let i = 0; i < lines.length; i++) {
const m = lines[i].match(/^(\s*)/);
const indent = m ? m[1].length : 0;
if (lines[i].trim().length > 0 && indent < minIndent)
minIndent = indent;
}
if (minIndent === Infinity)
minIndent = 0;
const result = [];
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const indentMatch = line.match(/^(\s*)/);
const indentLen = indentMatch ? indentMatch[1].length : 0;
const stripped = line.slice(Math.min(minIndent, indentLen));
result.push({
line: i + 1,
text: stripped
});
}
return result;
}
function refresh(): void {
readProc.buffer = [];
readProc.running = true;
}
function refreshPreview(): void {
previewToken += 1;
const token = previewToken;
if (!currentEntry) {
previewText = [];
previewImageSource = "";
previewIsImage = false;
return;
}
previewImageSource = "";
previewIsImage = entryIsImage(currentEntry);
if (previewIsImage) {
previewImageProc.token = token;
previewImageProc.running = true;
} else {
previewTextProc.token = token;
previewTextProc.running = true;
}
}
function shellSingleQuoteEscape(str): string {
return String(str).replace(/'/g, "'\\''");
}
function wipe(): void {
wipeProc.running = true;
}
Process {
id: deleteProc
property string entry: ""
function deleteEntry(entry) {
deleteProc.entry = entry;
deleteProc.running = true;
deleteProc.entry = "";
}
command: ["bash", "-c", `echo '${root.shellSingleQuoteEscape(deleteProc.entry)}' | ${root.cliphistBinary} delete`]
onExited: (exitCode, exitStatus) => {
root.refresh();
}
}
Process {
id: wipeProc
command: [root.cliphistBinary, "wipe"]
onExited: (exitCode, exitStatus) => {
root.refresh();
}
}
Connections {
function onClipboardTextChanged() {
delayedUpdateTimer.restart();
}
target: Quickshell
}
Timer {
id: delayedUpdateTimer
interval: 50
repeat: false
onTriggered: {
root.refresh();
}
}
Process {
id: previewTextProc
property int token: 0
command: ["bash", "-c", `
printf '%s' '${root.shellSingleQuoteEscape(root.currentEntry)}' | ${root.cliphistBinary} decode
`]
running: false
stdout: StdioCollector {
onStreamFinished: {
if (previewTextProc.token !== root.previewToken)
return;
root.previewText = root.processPreviewLines(this.text);
}
}
}
Process {
id: previewImageProc
property int token: 0
command: ["bash", "-c", `
set -euo pipefail
tmp='${root.shellSingleQuoteEscape(root.previewImageFile)}'
printf '%s' '${root.shellSingleQuoteEscape(root.currentEntry)}' | ${root.cliphistBinary} decode > "$tmp"
`]
running: false
onExited: (exitCode, exitStatus) => {
if (token !== root.previewToken)
return;
if (exitCode !== 0) {
console.error("[Cliphist] image preview failed", exitCode, exitStatus);
return;
}
root.previewImageSource = "";
Qt.callLater(() => {
root.previewImageSource = `file://${root.previewImageFile}`;
});
}
}
Process {
id: readProc
property list<string> buffer: []
command: [root.cliphistBinary, "list"]
stdout: SplitParser {
onRead: line => {
readProc.buffer.push(line);
}
}
onExited: (exitCode, exitStatus) => {
if (exitCode === 0) {
root.entries = readProc.buffer;
} else {
console.error("[Cliphist] Failed to refresh with code", exitCode, "and status", exitStatus);
}
}
}
IpcHandler {
function update(): void {
root.refresh();
}
target: "cliphistService"
}
}
+63
View File
@@ -0,0 +1,63 @@
// FetchPresets.qml
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
Singleton {
id: root
property var parsedPresets: ({})
readonly property var presets: parsedPresets
property bool ready: false
function accents(presetName, variantName) {
const variant = parsedPresets[presetName]?.variants?.[variantName];
return variant?.accents ?? [];
}
function defaultAccent(presetName, variantName) {
const variant = parsedPresets[presetName]?.variants?.[variantName];
return variant?.default_accent ?? "";
}
function modes(presetName, variantName) {
const variant = parsedPresets[presetName]?.variants?.[variantName];
return variant?.modes ?? [];
}
function presetNames() {
return Object.keys(parsedPresets);
}
function variantNames(presetName) {
const preset = parsedPresets[presetName];
if (!preset || !preset.variants)
return [];
return Object.keys(preset.variants);
}
Process {
command: ["zshell-cli", "scheme", "list-presets", "--json"]
running: true
stdout: StdioCollector {
onStreamFinished: {
try {
const parsed = JSON.parse(text);
root.parsedPresets = parsed.presets ?? {};
root.ready = true;
} catch (e) {
console.error("Failed to parse presets JSON:", e);
}
}
}
}
}
+1 -1
View File
@@ -36,7 +36,7 @@ Singleton {
PersistentProperties {
id: props
property bool enabled: Hypr.options["animations:enabled"] === 0
property bool enabled: Hypr.options.animations.enabled === 0
reloadableId: "gamemode"
}
-1
View File
@@ -158,6 +158,5 @@ Singleton {
HyprExtras {
id: extras
}
}
+11 -59
View File
@@ -2,12 +2,13 @@ pragma Singleton
import Quickshell
import QtQuick
import ZShell.Services
import qs.Config
Singleton {
id: root
property bool enabled
readonly property bool enabled: service.enabled
readonly property int end: Config.general.color.scheduleHyprsunsetEnd
property bool manualToggle: false
readonly property int start: Config.general.color.scheduleHyprsunsetStart
@@ -17,69 +18,20 @@ Singleton {
if (!Config.general.color.scheduleHyprsunset)
return;
var now = new Date();
if (now.getHours() >= root.start || now.getHours() < root.end) {
root.startNightLight(root.temp);
} else {
root.stopNightLight();
}
}
function startNightLight(temp: int): void {
Quickshell.execDetached(["hyprctl", "hyprsunset", "temperature", `${temp}`]);
root.enabled = true;
}
function stopNightLight(): void {
Quickshell.execDetached(["hyprctl", "hyprsunset", "identity"]);
root.enabled = false;
service.apply();
}
function toggleNightLight(): void {
if (enabled)
stopNightLight();
else
startNightLight(temp);
service.manualToggle = true;
service.toggle();
}
onManualToggleChanged: {
if (root.manualToggle)
manualTimer.start();
}
HyprsunsetManager {
id: service
Timer {
id: manualTimer
interval: 60000 * 60
repeat: false
running: false
onTriggered: {
root.manualToggle = false;
}
}
Timer {
interval: 5000
repeat: true
running: true
triggeredOnStart: true
onTriggered: {
if (!Config.general.color.scheduleHyprsunset)
return;
if (root.manualToggle)
return;
var now = new Date();
if (now.getHours() >= root.start || now.getHours() < root.end) {
if (!root.enabled)
root.startNightLight(root.temp);
} else {
if (root.enabled)
root.stopNightLight();
}
}
activeAuto: Config.general.color.scheduleHyprsunset
endTime: root.end
startTime: root.start
temp: root.temp
}
}
+29 -5
View File
@@ -12,7 +12,7 @@ Singleton {
readonly property int darkEnd: Config.general.color.scheduleDarkEnd
readonly property int darkStart: Config.general.color.scheduleDarkStart
readonly property bool enabled: Config.general.color.scheduleDark
readonly property bool enabled: Config.general.color.scheduleDark && Config.general.color.schemeGeneration
function applyDarkMode() {
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--mode", "dark"]);
@@ -42,11 +42,24 @@ Singleton {
function checkStartup() {
if (!root.enabled)
return;
var now = new Date();
if (now.getHours() >= darkStart || now.getHours() < darkEnd) {
applyDarkMode();
var nowMinutes = now.getHours() * 60 + now.getMinutes();
var isDarkTime;
if (root.darkStart <= root.darkEnd) {
isDarkTime = (nowMinutes >= root.darkStart && nowMinutes < root.darkEnd);
} else {
applyLightMode();
isDarkTime = (nowMinutes >= root.darkStart || nowMinutes < root.darkEnd);
}
if (isDarkTime) {
if (DynamicColors.light)
root.applyDarkMode();
} else {
if (!DynamicColors.light)
root.applyLightMode();
}
}
@@ -60,8 +73,19 @@ Singleton {
onTriggered: {
if (!root.enabled)
return;
var now = new Date();
if (now.getHours() >= root.darkStart || now.getHours() < root.darkEnd) {
var nowMinutes = now.getHours() * 60 + now.getMinutes();
var isDarkTime;
if (root.darkStart <= root.darkEnd) {
isDarkTime = (nowMinutes >= root.darkStart && nowMinutes < root.darkEnd);
} else {
isDarkTime = (nowMinutes >= root.darkStart || nowMinutes < root.darkEnd);
}
if (isDarkTime) {
if (DynamicColors.light)
root.applyDarkMode();
} else {
+21 -1
View File
@@ -39,6 +39,21 @@ Singleton {
readonly property real uploadSpeed: _uploadSpeed
readonly property real uploadTotal: _uploadTotal
function resetSampling(): void {
netDevFile.reload();
const content = netDevFile.text();
if (!content)
return;
const data = root.parseNetDev(content);
const now = Date.now();
root._prevRxBytes = data.rx;
root._prevTxBytes = data.tx;
root._prevTimestamp = now;
}
function formatBytes(bytes: real): var {
// Handle negative or invalid values
if (bytes < 0 || isNaN(bytes) || !isFinite(bytes)) {
@@ -156,10 +171,15 @@ Singleton {
Timer {
interval: Config.dashboard.resourceUpdateInterval
repeat: true
running: root.refCount > 0
running: true
triggeredOnStart: true
onTriggered: {
if (root.refCount <= 0) {
root.resetSampling();
return;
}
netDevFile.reload();
const content = netDevFile.text();
if (!content)
+17 -3
View File
@@ -26,16 +26,23 @@ MouseArea {
return (bc.pinned - ac.pinned) || ((bc.fullscreen !== 0) - (ac.fullscreen !== 0)) || (bc.floating - ac.floating);
});
}
readonly property int cornerRadius: Hypr.options.decoration.rounding
property real ex: screen.width
property real ey: screen.height
required property LazyLoader loader
property bool onClient
property real realBorderWidth: onClient ? (Hypr.options["general:border_size"] ?? 1) : 2
property real realRounding: onClient ? (Hypr.options["decoration:rounding"] ?? 0) : 0
property real realBorderWidth: onClient ? (Hypr.options.general.border_size ?? 1) : 2
property real realRounding: onClient ? (Hypr.options.decoration.rounding ?? 0) : 0
property real rsx: Math.min(sx, ex)
property real rsy: Math.min(sy, ey)
readonly property real scaleRatio: Hypr.monitorFor(screen).scale
required property ShellScreen screen
property real sh: Math.abs(sy - ey)
readonly property color shadowColor: Hypr.options.decoration.shadow.color
readonly property bool shadowEnabled: Hypr.options.decoration.shadow.enabled
readonly property var shadowOffset: Hypr.options.decoration.shadow.offset
readonly property int shadowRange: Hypr.options.decoration.shadow.range
readonly property int shadowRenderPower: Hypr.options.decoration.shadow.render_power
property real ssx
property real ssy
property real sw: Math.abs(sx - ex)
@@ -66,7 +73,14 @@ MouseArea {
function save(): void {
const tmpfile = Qt.resolvedUrl(`/tmp/zshell-picker-${Quickshell.processId}-${Date.now()}.png`);
const cmd = Config.screenshot.enable_pp ? ["zshell-img-tools", "--image"] : ["swappy", "-f"];
const rounding = root.cornerRadius > 0;
const shadow_blur = root.shadowRange / root.shadowRenderPower;
const r = Math.floor(root.shadowColor.r * 256);
const g = Math.floor(root.shadowColor.g * 256);
const b = Math.floor(root.shadowColor.b * 256);
const a = Math.floor(root.shadowColor.a * 256);
const args = Config.screenshot.mode === "auto" ? ["--rounding", `${rounding}`, "--radius", root.cornerRadius, "--shadow", root.shadowEnabled, "--shadow-blur", `${shadow_blur}`, "--shadow-color", `${r},${g},${b},${a}`, "--shadow-offset-x", root.shadowOffset[0], "--shadow-offset-y", root.shadowOffset[1]] : [];
const cmd = Config.screenshot.enable_pp ? ["zshell-img-tools", "--scale", root.scaleRatio, ...args, "--image"] : ["swappy", "-f"];
ZShellIo.saveItem(screencopy, tmpfile, Qt.rect(Math.ceil(rsx), Math.ceil(rsy), Math.floor(sw), Math.floor(sh)), path => Quickshell.execDetached([...cmd, path]));
closeAnim.start();
}

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