mirror of
https://github.com/end-4/dots-hyprland.git
synced 2026-06-05 14:59:27 -05:00
Compare commits
2760 Commits
octo-overlord
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| d192a25faa | |||
| 0bfade5c36 | |||
| d619ddcd82 | |||
| 3cb611c04e | |||
| f5b2b7548d | |||
| e0f2a34949 | |||
| 6eb590b1a2 | |||
| 14376cec94 | |||
| 397fb8d8c4 | |||
| aa044b4563 | |||
| a15f2a8a39 | |||
| 9b149e6fef | |||
| eb3613d3ed | |||
| 091da11da5 | |||
| a56cee16f1 | |||
| 54a1d172d7 | |||
| c58bb07a6b | |||
| 9c115f7a8f | |||
| b9e05599bc | |||
| ce80951210 | |||
| 6eaa869fac | |||
| 8f9cf67be7 | |||
| 20d1ff065b | |||
| 25fe0ab01e | |||
| c1b37bc467 | |||
| b470bf3fe8 | |||
| d4e777911e | |||
| d4d78a5e62 | |||
| c0706258b1 | |||
| 68c67aced4 | |||
| 215ac747d8 | |||
| 9eda50178b | |||
| d5f9afe7c0 | |||
| 5c66902900 | |||
| 28ba8a4f43 | |||
| 798d35a538 | |||
| 7d5ce9a793 | |||
| c53265754c | |||
| 239b532ec6 | |||
| d1daedc6d2 | |||
| 2ade168a20 | |||
| 08201f2ac0 | |||
| ad12fe6ddf | |||
| 9e1568fcdc | |||
| b7b2e6e10d | |||
| 00a4235a81 | |||
| c504cdf22b | |||
| e6d2a7d88c | |||
| b50a4a7faa | |||
| 737eb7c356 | |||
| 5ce6280d98 | |||
| f7773acab4 | |||
| e8721b4b01 | |||
| b85ed8691a | |||
| ac8d0e9a42 | |||
| 412b2222c2 | |||
| d6b27cf9dd | |||
| c53e9891cb | |||
| 1e442f1af0 | |||
| 20dde15900 | |||
| ba0e76da1b | |||
| 1c117e0880 | |||
| 7aad60eb2c | |||
| a9f87c06ca | |||
| 281b3e5627 | |||
| 388783e992 | |||
| ae7f6bd165 | |||
| 403f7aa685 | |||
| 807c761ed0 | |||
| 7dcbabcd8c | |||
| 2e161911bc | |||
| e11d084be8 | |||
| 760c7034aa | |||
| 6c041b953a | |||
| 9f4afde0c5 | |||
| 010f070eef | |||
| f6b97c4649 | |||
| 0da83ba460 | |||
| bebf66da89 | |||
| adb36f435e | |||
| c67c8840e4 | |||
| bfad75c93a | |||
| 74c10b915d | |||
| 0ae900515e | |||
| c3147dc7ff | |||
| fb3ec1fdfc | |||
| 5c69271c10 | |||
| 84cd4582dd | |||
| e7c283e91e | |||
| f992294ec2 | |||
| c652d2a7f3 | |||
| d1cd892c0f | |||
| 63495d0b28 | |||
| 36a4a19bca | |||
| c693a3f539 | |||
| c0888cbb98 | |||
| 2cfb0c2757 | |||
| b034a712a9 | |||
| aff6206930 | |||
| 0e1f6a97fc | |||
| 7834f22243 | |||
| cec16c8720 | |||
| 54e19afa81 | |||
| ffabb85693 | |||
| 242de398c3 | |||
| fe9eb0e84c | |||
| 47235ac440 | |||
| e79c9a8792 | |||
| 762629d2f1 | |||
| 7596509777 | |||
| e6570bbe75 | |||
| 72d950ed51 | |||
| 4eef9ea18e | |||
| a2c1641090 | |||
| 25de796710 | |||
| 329fa31262 | |||
| 14f8b84635 | |||
| 36e0c3fddc | |||
| d24cbff7ac | |||
| 96bf8dcbf0 | |||
| a9dcab5e41 | |||
| a57119005d | |||
| 5467ab7f06 | |||
| fdd3cf5ef8 | |||
| dbcde24003 | |||
| e03cbfec91 | |||
| eae36f4d4f | |||
| 09ff686f18 | |||
| 0040588fd2 | |||
| 94fa3f9c40 | |||
| 4b4e1fd53c | |||
| 5e6db59bfc | |||
| 5f9febd4b7 | |||
| 3e0561578c | |||
| fa3bf6f5df | |||
| a5eca98543 | |||
| a8761bc6f2 | |||
| dd26742d52 | |||
| 771cc18be5 | |||
| ada4b856f6 | |||
| d44bbcfefc | |||
| 6589158f51 | |||
| f3ab1366d8 | |||
| d42420f848 | |||
| 9f5be8b0bc | |||
| 747abbde9e | |||
| 0fb7d4dfd8 | |||
| c1c964aa89 | |||
| 099406a276 | |||
| 2d0ac6e7db | |||
| d24e227245 | |||
| fad2a5f8ae | |||
| 1e2a6d23e3 | |||
| aea6216a7f | |||
| 77ce65a53c | |||
| 799bfae184 | |||
| 0ebe70976f | |||
| f88b21e681 | |||
| 64b52f6a90 | |||
| ec3b92a938 | |||
| 59398595a5 | |||
| eef2b691e7 | |||
| 347ada664d | |||
| a06764e111 | |||
| d42c9b63be | |||
| dfe9c876d2 | |||
| 4408b5d9d3 | |||
| e356dbb6f9 | |||
| 7948d01d94 | |||
| 8447588c7f | |||
| 56ad8b80b2 | |||
| fe8d9887fa | |||
| bcb815aac5 | |||
| da99437351 | |||
| ba2da84e7b | |||
| b4a57bbbec | |||
| 687beedc4a | |||
| 2243f8e489 | |||
| 6c0c7beaa6 | |||
| 40705b4635 | |||
| 83701e3038 | |||
| 0493c08759 | |||
| 0c3be42218 | |||
| f6f07d4f65 | |||
| 3a20c0c2bb | |||
| db10d4a45b | |||
| 33f2865967 | |||
| 7eae26474f | |||
| f6f0dedcb8 | |||
| 257abdf80a | |||
| f3caad4d90 | |||
| 7a983b049f | |||
| 69c5ac5cb3 | |||
| 55a33fb7d7 | |||
| 72aa169087 | |||
| 7f0fba51a9 | |||
| 842d1323e9 | |||
| 26adce8fc0 | |||
| bdc6026cc2 | |||
| 9f882d9f3c | |||
| 15b4394865 | |||
| 67fbaf1d27 | |||
| 7206d1a19e | |||
| ae9de61b72 | |||
| 0a8fb89501 | |||
| b1e2f86360 | |||
| 74329eb294 | |||
| 01562ca544 | |||
| dde0988a45 | |||
| c117d9903a | |||
| 29d40ca90a | |||
| 737ccce538 | |||
| 96f5f82656 | |||
| 35fb3d4da8 | |||
| 17c2762b1d | |||
| 27bd759a41 | |||
| 2ed29f5c46 | |||
| 75594fb5cf | |||
| f86d42d8f2 | |||
| b6fc897b39 | |||
| 8f66afe109 | |||
| 72b8b7aecb | |||
| a9721c7cfe | |||
| 774ce228a0 | |||
| abea376e59 | |||
| bf2fec015c | |||
| 78c6e3c16f | |||
| deadf6cc4f | |||
| ae0412b513 | |||
| f974dea8e5 | |||
| ede3cf8858 | |||
| dfe6eee674 | |||
| 854016e82a | |||
| b4956cc05c | |||
| e06a3be726 | |||
| 3e0f480ce0 | |||
| 065b34ccde | |||
| 41b6c190d2 | |||
| 795f4042b0 | |||
| aff4705d6a | |||
| a7353e5033 | |||
| a43228ab28 | |||
| fc7524a30f | |||
| a28ed6023e | |||
| 973b83bc15 | |||
| 9d065f2c8e | |||
| 4caa59dd9e | |||
| 8aa9041106 | |||
| 58a122a3b4 | |||
| 6e76977976 | |||
| 15a56bdce3 | |||
| 33db15f991 | |||
| 9dcf63b54a | |||
| b61ae3abe7 | |||
| 42f14b92d0 | |||
| fc17e29ebf | |||
| b306d308b7 | |||
| 369fadd74b | |||
| fd86b282a6 | |||
| 0362b21f5a | |||
| fd2d69e407 | |||
| 7a01602c5e | |||
| 4a9d6a2665 | |||
| be1838e40d | |||
| 7cf704d450 | |||
| 4eedd030df | |||
| 74ce3378d0 | |||
| a831fa92e8 | |||
| 7e94ee60aa | |||
| 9a11a0d8f9 | |||
| a574baacc5 | |||
| bd923a0f88 | |||
| 735fb7895b | |||
| fffa2b5749 | |||
| 673e5ffe2e | |||
| 8190c3b976 | |||
| 5ac58bdb3c | |||
| 9bbfef19e2 | |||
| bda3834176 | |||
| 3653715029 | |||
| 0974a6069a | |||
| 5710fbb3d6 | |||
| fb4b5e83a9 | |||
| a052a01eee | |||
| 97d4f8e438 | |||
| a245b4681d | |||
| 8c34bcf124 | |||
| f554a0512d | |||
| 370574d8ca | |||
| c3b152b318 | |||
| 26c69e5fd3 | |||
| 1671959186 | |||
| a7f1cddd45 | |||
| b4b422bd9c | |||
| 1dcf90ac57 | |||
| 2e0dea28a5 | |||
| 9873a23794 | |||
| 8c66dc7aa7 | |||
| 9cbeded70d | |||
| 01f2ebb246 | |||
| 1f9a8ea37c | |||
| 5cbf5608be | |||
| 0c843c8d03 | |||
| c85e98d6f9 | |||
| 7013893cbd | |||
| 65eeea7ffa | |||
| 5860462082 | |||
| 33bd04200a | |||
| 4fc56f6c78 | |||
| ba4ac764aa | |||
| 65dab672cf | |||
| ea0dd2c9a4 | |||
| e52d569b9e | |||
| f610332877 | |||
| bbc84b68ef | |||
| 2554fc7d61 | |||
| dc172862b0 | |||
| 0725edd35d | |||
| 77dd839320 | |||
| 1402a19c98 | |||
| 57b78135a6 | |||
| 48fec445ad | |||
| 060b8693ef | |||
| a187d013e4 | |||
| 577fab457f | |||
| 35bb2a1ad9 | |||
| 095d637c70 | |||
| 373a9e5670 | |||
| d11ef2adef | |||
| 9a3bb5e59e | |||
| 8bf279e571 | |||
| e72d39fecb | |||
| c5e0c2d3dc | |||
| c19e625069 | |||
| aea06d42ab | |||
| 40fb416366 | |||
| ab1b720503 | |||
| b6c2fd3f18 | |||
| 3d9f51030c | |||
| 14770e5b75 | |||
| 2e7ea9d9f6 | |||
| 60c197e2f5 | |||
| 47c5ffe5f9 | |||
| 5ab4812a3b | |||
| c063bdfe86 | |||
| cea0acafff | |||
| fb1b674a30 | |||
| 8d52e83a59 | |||
| 7670c135ed | |||
| a7f30838de | |||
| 585a7b0f06 | |||
| 502dffeac7 | |||
| 52f1a7af65 | |||
| 1e7f065add | |||
| f128c48356 | |||
| 44244ed1bf | |||
| e3d89101f1 | |||
| 0435681032 | |||
| 33b4ef42f4 | |||
| 633afb54c9 | |||
| feeb0bba8c | |||
| 92e9aa2976 | |||
| 09d5817f2e | |||
| d23d39df04 | |||
| 9e9c6b70b6 | |||
| 3a14cc644b | |||
| 2132829184 | |||
| 9fbc549da4 | |||
| 4d6f58a914 | |||
| 4601605df9 | |||
| 640a147721 | |||
| 145731d8bb | |||
| c1a6034a13 | |||
| 68f0355940 | |||
| 4fbd238a90 | |||
| b3117ce578 | |||
| f8903da663 | |||
| 15ceda494e | |||
| f1a2784777 | |||
| 52ae3b187f | |||
| 622b6ec424 | |||
| 4ff3435446 | |||
| e24de7f935 | |||
| 6215f54c9f | |||
| a55ebc5f48 | |||
| f3f9d183f2 | |||
| 21e3f253d9 | |||
| 1be524ce15 | |||
| aee7240627 | |||
| 901b4cf1ff | |||
| d92a081b57 | |||
| 6bf455a042 | |||
| d3ccde299e | |||
| 18004b5959 | |||
| 1588a20b46 | |||
| 6075835d85 | |||
| 63dd6516f1 | |||
| 1b4c439c3e | |||
| 76ee7b6bb1 | |||
| c30776e811 | |||
| a217d4c5da | |||
| b236690f2a | |||
| 36051b5970 | |||
| f98e3cd3e9 | |||
| 30269dd052 | |||
| 20ec7717a4 | |||
| bbf1066ceb | |||
| 798e4bde27 | |||
| 224bdbd5cb | |||
| 2cb45c3a78 | |||
| 80db68467f | |||
| 8182dbb17b | |||
| ecc8d42265 | |||
| 6afc4b1aa7 | |||
| 1c3c255ab6 | |||
| a5c61d9ab0 | |||
| 887d54bf07 | |||
| 2d0cff9716 | |||
| 47beca14de | |||
| 9611288d79 | |||
| 2af409b1e5 | |||
| 800c75c4d2 | |||
| e0640a8782 | |||
| 1932dcbab8 | |||
| 94490f371b | |||
| 3fbf9a7056 | |||
| b75ae3817c | |||
| 26abbd29e2 | |||
| 09c09ebfcf | |||
| 36658322c4 | |||
| a9a92e0203 | |||
| eecc1201d8 | |||
| 0f24287a39 | |||
| 07446b5399 | |||
| 803500c4b0 | |||
| d8018e97fe | |||
| 6675fb8abb | |||
| cf2e944742 | |||
| e9b80dbbf4 | |||
| b7b94b4a4e | |||
| 1a9d78fb48 | |||
| 97d22378ff | |||
| 809dac681f | |||
| 46911074f9 | |||
| 528102ba4a | |||
| 755a9b15a5 | |||
| f05358ed28 | |||
| 922c85795f | |||
| 5b27dfa747 | |||
| 57b5e34f27 | |||
| 3b86fe7de7 | |||
| 360f1258f3 | |||
| 672e756fbf | |||
| 60fd1ea030 | |||
| bf06497f9e | |||
| 22970db52f | |||
| 14c930d48c | |||
| 7197f9ddfb | |||
| 58e372c590 | |||
| 8538efe743 | |||
| b268f1d61c | |||
| 711793dd66 | |||
| dc57f940d0 | |||
| da578735e6 | |||
| 31e821250f | |||
| ce6d5969e0 | |||
| d5b599da3d | |||
| c9d0248a6a | |||
| 7238b2b15c | |||
| 5a687c3565 | |||
| 171cf6059f | |||
| e818a202b8 | |||
| 199845bf59 | |||
| 575b26d572 | |||
| af1adef5f1 | |||
| c5c8ad2236 | |||
| 8cb2c7e016 | |||
| d30c8138df | |||
| 0027e8a6e3 | |||
| 652104a358 | |||
| e9d6ed874c | |||
| aaa8448011 | |||
| f02e9ba2a5 | |||
| a05b0bb559 | |||
| 60c1a92cf5 | |||
| 91f6f95460 | |||
| 2434b36098 | |||
| a849fe0883 | |||
| 8ae14725e6 | |||
| e8a05d12e9 | |||
| 76fbe3d04a | |||
| 7763a26e03 | |||
| 281943306e | |||
| 61feb958ec | |||
| 953427692f | |||
| b21699b8a0 | |||
| d13bcddb33 | |||
| b820bdc654 | |||
| d178dafac6 | |||
| d22822e734 | |||
| 5c141e0361 | |||
| e1f30bf85c | |||
| c5aaf721c8 | |||
| 13a827c0f4 | |||
| 64e726d4c5 | |||
| 392ef6b74f | |||
| db60f8775d | |||
| 5a48a22e71 | |||
| 1e2a972747 | |||
| a34ead004f | |||
| a658aced0d | |||
| 6ad8717a47 | |||
| 76ca889eec | |||
| ec7d6fd66b | |||
| cfb8b44d7a | |||
| c580b050e4 | |||
| d7ae6014ed | |||
| 29c8001785 | |||
| 896aa97701 | |||
| 8d7dd0d6ae | |||
| 41f007a771 | |||
| 4041310b4d | |||
| d5dbf7ab7f | |||
| 8842df6340 | |||
| 169b24bea5 | |||
| e499f4f8f1 | |||
| ae8be40c2d | |||
| 3eec36d20d | |||
| ed6a0204b4 | |||
| b01e0c315a | |||
| a38c725d3d | |||
| 17a3874ab1 | |||
| 34991dd32e | |||
| 28d3f6a94a | |||
| 252a1055c2 | |||
| 4ac6784844 | |||
| ea8f0fbc2d | |||
| 61f0f0dc97 | |||
| dd00908026 | |||
| 4b1e02dda6 | |||
| 0cff92d02c | |||
| 125e57c98f | |||
| 2b9e5b1a6f | |||
| f65f805fe2 | |||
| 409fc94d6e | |||
| 36ff18bfe3 | |||
| 60b2225cc6 | |||
| 70363ab886 | |||
| 663eb1896a | |||
| b73cdf0379 | |||
| c92832ff95 | |||
| 607d6056c1 | |||
| 2ef8342187 | |||
| 0e3b3eceb6 | |||
| f1fdb941e1 | |||
| f91ca59fb6 | |||
| 5f51b97b6d | |||
| c02daf5c12 | |||
| e1601b972f | |||
| 8d0d71812a | |||
| e206c4334c | |||
| 3358ebe639 | |||
| abd657ed76 | |||
| 04211411af | |||
| 536c1ab465 | |||
| 29c4a6a15f | |||
| 93bc4d935c | |||
| f0926b6ce3 | |||
| d54ad65b50 | |||
| 91a2a520b0 | |||
| 136f4a3e48 | |||
| f71ed855e5 | |||
| 39a3a0c484 | |||
| 2ead5fa4ab | |||
| d83733bd86 | |||
| 1ea3153886 | |||
| eaae89c904 | |||
| 044221be93 | |||
| e874a6a3e0 | |||
| 70c0adb8e5 | |||
| 3ae0973df5 | |||
| b966e2d539 | |||
| 6da58f5235 | |||
| bcd7fb1c1b | |||
| 80f4a0549c | |||
| 1a3cc9b4d3 | |||
| 0cc521aef5 | |||
| d9b1d0261d | |||
| fdbe39d744 | |||
| 3cb61c4267 | |||
| bb65137415 | |||
| 95c6fcab01 | |||
| fce229cdc3 | |||
| bfe97c1c05 | |||
| 8b8ac44852 | |||
| 34b5892374 | |||
| 0645200807 | |||
| 1c8339df10 | |||
| 8b1f0fc1d4 | |||
| 6c460b209c | |||
| 13968db31c | |||
| 80a7804ade | |||
| ddf1bc6a08 | |||
| fa47c6778c | |||
| 255006172f | |||
| 52d6e8a5d1 | |||
| ead056c207 | |||
| 9a0e4181a5 | |||
| ed89ad882f | |||
| 3b4c721584 | |||
| 7a2e21ac7c | |||
| 9043ae7bf6 | |||
| 758c84feaf | |||
| 1efd2dfa19 | |||
| 1193b7a802 | |||
| f62ab85a19 | |||
| 15481c646a | |||
| 8e704e4009 | |||
| e9b5e7d7d2 | |||
| 8cfb3be4cd | |||
| 85892940b5 | |||
| 7d9a405146 | |||
| 8203115a24 | |||
| 895faae39f | |||
| 71c1fbe1dd | |||
| 4055ad48fa | |||
| 05aae36e82 | |||
| 20d60a11f7 | |||
| b0c396aa21 | |||
| ed9e510c32 | |||
| d3c1ae14b8 | |||
| ffd01741d9 | |||
| 8a9f105e75 | |||
| dd07a62dc0 | |||
| c093265b05 | |||
| 1dd959fad4 | |||
| 4cfb706b6f | |||
| 90f9467871 | |||
| cce06cc2f0 | |||
| 1d40360cf2 | |||
| f08bbc0d67 | |||
| 4ce4645749 | |||
| 4cad401ea6 | |||
| 8d2c8bd38e | |||
| 61b5cf8cb6 | |||
| 9c48fd32ef | |||
| c352ebc2fc | |||
| b45f2dd235 | |||
| 4442200479 | |||
| f5fea85334 | |||
| 8892982874 | |||
| 677fa06b06 | |||
| c11814b1f8 | |||
| 3cf14671ad | |||
| d2c019f8de | |||
| 6b90e37b0f | |||
| 21f2a9c65d | |||
| fd209851d9 | |||
| 533b9c01f5 | |||
| b4038dafa9 | |||
| 04f73e67c8 | |||
| 2503c1f14b | |||
| b214993c16 | |||
| 2a1aaa9b7e | |||
| 2fd25af353 | |||
| b7ad7361d6 | |||
| 9cb5cc1416 | |||
| 0700e024d9 | |||
| d40df98aa5 | |||
| d27fbede2a | |||
| a786f0353e | |||
| 9ba8723a5d | |||
| e4b5718833 | |||
| b4920a7cb6 | |||
| 08739043f6 | |||
| 55152aee4b | |||
| 9053927480 | |||
| 616c1dfe3a | |||
| 5242373db5 | |||
| 1836a2ff1c | |||
| cecb476ed3 | |||
| e6f36114bd | |||
| 449d6fc285 | |||
| 0703429393 | |||
| 0adefcc0d3 | |||
| 996579729d | |||
| 25816662f8 | |||
| 19ba7dac48 | |||
| 07f8a72d6d | |||
| e46c7c0d3d | |||
| c78c363388 | |||
| 12ebcf2d19 | |||
| 52f67431c7 | |||
| f9755694f7 | |||
| a72d5f98af | |||
| fae2309f62 | |||
| a17a909fd9 | |||
| 3087e5da92 | |||
| 6e986fa8b0 | |||
| adfc7a15e8 | |||
| 0f11296ee1 | |||
| 9a113c24ca | |||
| 55961ae079 | |||
| 7613bba393 | |||
| 52af531c9f | |||
| 92beee6c16 | |||
| be5b03dfdc | |||
| 67c7fd09dd | |||
| f0a042246c | |||
| 9cf66f4d83 | |||
| 8943d568f5 | |||
| 4d79cc6764 | |||
| 0b3cc187cb | |||
| e2230a0a35 | |||
| 4b0cb15762 | |||
| da233086c2 | |||
| 65ef0ca666 | |||
| 6376521d14 | |||
| 7b2cfb11f6 | |||
| 6cb1b738c6 | |||
| 4878f7dbb1 | |||
| b350d87ce2 | |||
| 3507aee627 | |||
| 9466013124 | |||
| b869336738 | |||
| 4fc85a926f | |||
| 31998a6c32 | |||
| cd7bf9c1c2 | |||
| 68c159f210 | |||
| 30c845d226 | |||
| d69559e8f6 | |||
| 0c8391534a | |||
| e0584bf50e | |||
| 79762e4193 | |||
| fbfb81c83b | |||
| 35a1f7906a | |||
| a10b8b50d4 | |||
| 53399549fc | |||
| 8660d68420 | |||
| e8326b96d7 | |||
| b93213ddb6 | |||
| 9885dd7a1e | |||
| 7016b59933 | |||
| 527cb9e9b0 | |||
| 23fcd183bf | |||
| 9001cae848 | |||
| 6e5ba69430 | |||
| 5c8d824749 | |||
| 2fbfbb80ef | |||
| bca177eed2 | |||
| b052df4d72 | |||
| 255232135b | |||
| 03890d0099 | |||
| bfc07aa4e4 | |||
| 1a844e512b | |||
| 148384200c | |||
| 64c1b5be0b | |||
| 488b3c06d6 | |||
| 4cbb0f23c6 | |||
| b650120fd4 | |||
| f7cb85632e | |||
| f3bfe8a374 | |||
| dfe11810fc | |||
| aeb106ed21 | |||
| c0de7f1d37 | |||
| 625b4da3b7 | |||
| e6e86b4258 | |||
| f21e282780 | |||
| ae28d0bd6d | |||
| d8921a6608 | |||
| 0ec4b3f54a | |||
| b79fb33134 | |||
| 99171c9512 | |||
| 189530507f | |||
| 781404749e | |||
| 9fd83a2812 | |||
| e830dc93cc | |||
| 268b072a16 | |||
| fcee7ce6f9 | |||
| 9228165428 | |||
| 8f6e2bc7fb | |||
| c7bd1019e7 | |||
| 0e3cf4c908 | |||
| e8bae0e529 | |||
| 2d65af70ad | |||
| 71e0538cf5 | |||
| 28bf94904f | |||
| 986461f590 | |||
| 869f9529f6 | |||
| 83b54f5fc4 | |||
| d2692cc95e | |||
| 11dd8c9efb | |||
| ec76180f4c | |||
| 2ce060484a | |||
| 9adf310738 | |||
| a23ee83aad | |||
| 5aa384e906 | |||
| 8574dbcb02 | |||
| f33bc7663e | |||
| 1271f147ed | |||
| 5b69995945 | |||
| c71a2498d0 | |||
| 803f2ecb42 | |||
| be1ce37014 | |||
| 2a34831f87 | |||
| 5c746f34b7 | |||
| 3dfd043645 | |||
| d15960d930 | |||
| 885a9eb0b3 | |||
| 4abfcd0162 | |||
| 68a9c8729f | |||
| 4e07aa52ba | |||
| 7ff3a212ab | |||
| fdc38d69f7 | |||
| 6ee7212bdc | |||
| 839718cc2b | |||
| bbe0329df4 | |||
| 5d6d9234ac | |||
| d990d68472 | |||
| 24df9ce2e4 | |||
| 4393c05e46 | |||
| 6d221f558b | |||
| f3ad9f27f6 | |||
| d0de047db0 | |||
| 4e86ec9fe9 | |||
| 6f7b501430 | |||
| bd1c9bfb2a | |||
| 443f86d347 | |||
| 6dd0387833 | |||
| b6dfbf6c97 | |||
| 47cd7be87e | |||
| 3c25d18f88 | |||
| cc519e9f60 | |||
| aa85e2168e | |||
| bd284a5ef4 | |||
| 541c701d5a | |||
| 179815b73c | |||
| 11064d04f0 | |||
| 767d2beb6c | |||
| c19dd725b8 | |||
| 42f29d47b4 | |||
| 2ccdf3b751 | |||
| 02afa37da1 | |||
| 4ea48d60f8 | |||
| c16cff52b8 | |||
| 5bec659486 | |||
| 8404817e51 | |||
| eb8f1379f2 | |||
| a3cb292fe9 | |||
| f8ffe5e63f | |||
| a5b941360c | |||
| c8c4642c61 | |||
| 237fa85f0f | |||
| f46835c9a1 | |||
| 67b02627ae | |||
| 98ef819fee | |||
| 42147b2117 | |||
| 61a9d406f2 | |||
| 4b8f294a91 | |||
| 9e3e14ca83 | |||
| bf225d6de2 | |||
| 2e5be90237 | |||
| c369db75eb | |||
| 347cbd9803 | |||
| b5ccddf34d | |||
| f0643c9c5c | |||
| 63c3433b6f | |||
| f8af8093f7 | |||
| 45a78c383b | |||
| 499b0628ea | |||
| 18c264a85e | |||
| 323b0bc257 | |||
| 1495669a60 | |||
| b97c2d1d4d | |||
| a135b09ec7 | |||
| 666cf09d5f | |||
| 6c7ac470bd | |||
| 7049dda7de | |||
| 3e55442654 | |||
| 21c5111961 | |||
| 2362c2ab3f | |||
| bda5422ccc | |||
| 86aab5b1fd | |||
| 6f61782c8f | |||
| 6f19e1cdd6 | |||
| c64f3a0122 | |||
| 37c1d9cf61 | |||
| 5ad3508d3e | |||
| 6194ef912b | |||
| e07ddaa0b3 | |||
| 2ea3904b56 | |||
| 8d1fe864ad | |||
| 3e4674d651 | |||
| 5c92ad64f9 | |||
| b828a1dbf0 | |||
| df0c7bbbd6 | |||
| 7bfbf011d2 | |||
| 945c6a0782 | |||
| cfdbb873aa | |||
| 4cc4ad749b | |||
| 3c390d95be | |||
| 0917ee65f2 | |||
| 6889185c3e | |||
| 1844d12bc5 | |||
| 55b5d7145d | |||
| 480ef19b01 | |||
| 31c9fe1f5a | |||
| 5696a4348e | |||
| 9997fdce0c | |||
| 318d80de38 | |||
| ca261d931d | |||
| 56c07a514f | |||
| 9162c24ccd | |||
| 155a11734f | |||
| 7c590bd5d8 | |||
| a250675b5c | |||
| 3f8c62a81b | |||
| f2cd533ae9 | |||
| 20cae142d7 | |||
| 20e1f0e0bb | |||
| a412688af2 | |||
| efcb826f5c | |||
| 1ad99b43a0 | |||
| 2dfbb84b23 | |||
| de7d504160 | |||
| 8225a3981a | |||
| c91191f89b | |||
| ac55a66dd4 | |||
| febe3d53e3 | |||
| 60ef4a873d | |||
| 462ddefb7f | |||
| dc958436d8 | |||
| e00e703af2 | |||
| 6fedb70f69 | |||
| dec65aea17 | |||
| cbcb8cf8e1 | |||
| fdcb95b8a4 | |||
| 378af7ac2a | |||
| 694eaccfbf | |||
| 42919c59ec | |||
| 58980959aa | |||
| a065829eee | |||
| f1479626f3 | |||
| f98c422254 | |||
| 1cfeff8b10 | |||
| 3cc68b29da | |||
| 3b212c454d | |||
| b5ddc36d5e | |||
| 38586f0efc | |||
| 0462ee5e56 | |||
| 711fb48e37 | |||
| 6d4ea704d6 | |||
| 39ed0e472a | |||
| a54a8d8daf | |||
| 80d62c438f | |||
| 61b89de945 | |||
| c812e9c8af | |||
| d001bc1269 | |||
| 98d2c4f881 | |||
| 4396079c28 | |||
| e10ad1ed71 | |||
| 051accbe2f | |||
| 74941d15bf | |||
| cf1ea9e3d9 | |||
| 3dd1264a12 | |||
| dbb8d015e8 | |||
| 13892a01e1 | |||
| fb7dbaa187 | |||
| 2c4668bcb0 | |||
| d0e2c69de3 | |||
| 3a64eae028 | |||
| 284bb084c6 | |||
| 14c115f80a | |||
| 8b5a790d54 | |||
| e6a19a6afb | |||
| 781af1d420 | |||
| baa3c2a773 | |||
| 8f4190a939 | |||
| 696ff4298f | |||
| f2462eb1b4 | |||
| 60eed56ea7 | |||
| 11fa846ae1 | |||
| 3502e46d19 | |||
| 53768a6885 | |||
| eccfec4671 | |||
| 819735169b | |||
| ee94828d97 | |||
| 935a8ee411 | |||
| 8a5852f61a | |||
| 9c1c97a7ec | |||
| 77e6264568 | |||
| 5db16e6245 | |||
| 631d496001 | |||
| dda0a228cf | |||
| 5820106e31 | |||
| f678a55e6a | |||
| cd452c256e | |||
| c035427e6a | |||
| b322af7051 | |||
| 7236d2f50b | |||
| 87f5bc5870 | |||
| 3187239175 | |||
| 09fd61c71d | |||
| c1f84c77dd | |||
| 3f242fa298 | |||
| c7ac7b5b43 | |||
| 688a36af58 | |||
| 10e1509d5d | |||
| fc2e51ebbc | |||
| 1433fb5412 | |||
| b383635fa8 | |||
| 788c01c242 | |||
| d8bcf2ed59 | |||
| b965b50009 | |||
| eafe9f7217 | |||
| 96e4ca1095 | |||
| fa6a4e8543 | |||
| a2ae9497df | |||
| 225e03b0a3 | |||
| 4c5bc19748 | |||
| 9209ca8216 | |||
| 241f33fb2f | |||
| f9bc4b1608 | |||
| 0091fce2c1 | |||
| 424510065d | |||
| cf43479530 | |||
| c7c38bf0b1 | |||
| 8b5a783c1f | |||
| d74e385f84 | |||
| 3f1b5bba95 | |||
| 9cf2aa83a1 | |||
| 6fa417a4c1 | |||
| 6b0572975f | |||
| d292a85a5e | |||
| 3b1d8fd262 | |||
| 549a43ac7f | |||
| 769ed3bf71 | |||
| d10ac9cc74 | |||
| 4bf31544e7 | |||
| 03a149c10e | |||
| 5ec8cca5d5 | |||
| 44c8de82c7 | |||
| b08a545ece | |||
| 5504bd3f09 | |||
| fb1b5db279 | |||
| 42b9c7c854 | |||
| 994985ecae | |||
| 229c9d5e78 | |||
| ae52e28afb | |||
| 758d40fc8b | |||
| 601cb3ffbe | |||
| 9aa869af77 | |||
| 1d07260fd0 | |||
| fe06c1891f | |||
| 26f2a9f3fd | |||
| 1cc04e118f | |||
| 87181585aa | |||
| a831d393c1 | |||
| 1845e59090 | |||
| 3fe8377309 | |||
| 20b3d2498e | |||
| 917fae6d4f | |||
| a91fe7db30 | |||
| daa4dd7b0f | |||
| cd952729f4 | |||
| 843025bc64 | |||
| 40368432e8 | |||
| 2c88a71eed | |||
| 082f12084d | |||
| 23b471edc2 | |||
| e50ea627e8 | |||
| fd8f569477 | |||
| 1766375348 | |||
| 1a58f1258a | |||
| 0f867df271 | |||
| a27a6deddf | |||
| e5e85db75d | |||
| 96cda9e6dd | |||
| fe3c502459 | |||
| e2799414dd | |||
| fa08f972d6 | |||
| b06a7ce58e | |||
| a0c5940a94 | |||
| 3365719a49 | |||
| 6afe810d69 | |||
| bf70be7f4a | |||
| 1a4b4b8bef | |||
| 7f49daf422 | |||
| f4c32f89f2 | |||
| 2807bed255 | |||
| e5e598853f | |||
| 47aa8232f7 | |||
| 06c51553ba | |||
| 281646ef0c | |||
| 2f1c66570f | |||
| 60144ca3de | |||
| a0131e5bf8 | |||
| 4afaa7cc7e | |||
| ad9f25c346 | |||
| 6be3fe0c65 | |||
| 3bebabd95e | |||
| 6e9f2c14ce | |||
| 4f68e9e61a | |||
| 56a7e8cbdd | |||
| 25a63b593d | |||
| 32af8bf257 | |||
| cd0d49032a | |||
| 11f7adc643 | |||
| 6d6fa42857 | |||
| d14d170016 | |||
| bd04346f16 | |||
| 46bd7c785a | |||
| 520068e523 | |||
| 902c8327d3 | |||
| d497e00474 | |||
| b5e1bcf3be | |||
| 923841cb56 | |||
| 6ca5175bb0 | |||
| 9aa6d03f87 | |||
| eff52332b5 | |||
| bf376c8aaa | |||
| 64f6081b14 | |||
| b3f81f350c | |||
| 025a819b63 | |||
| 2667751a1c | |||
| b1007f2ded | |||
| aca19d6903 | |||
| 52dced17a3 | |||
| a1606c9c23 | |||
| 9249034fa6 | |||
| 1924111d6b | |||
| 8214e2d052 | |||
| 564fd54cdb | |||
| a92bd67957 | |||
| 22469dc5f2 | |||
| 5f4f8980f0 | |||
| e24630b9be | |||
| 51076dda88 | |||
| 37244dc0f7 | |||
| c82a2e835b | |||
| 8a35609b0d | |||
| 1f208125bf | |||
| ba6fba447a | |||
| c19766c887 | |||
| 0e7422c335 | |||
| 67695c8edb | |||
| ede86dc7a7 | |||
| a28945f3ec | |||
| 80275c5adf | |||
| 31f2184dc6 | |||
| 9d830767c7 | |||
| 9471223a76 | |||
| c2942afe8e | |||
| d8adcce5fb | |||
| b9a7fa4d0d | |||
| 7f8265c9e6 | |||
| c1ff57c3d0 | |||
| 4cad276963 | |||
| 5a4b4e6d3c | |||
| 7440f78069 | |||
| 38cd1a7169 | |||
| 4443b736d8 | |||
| d509a8777b | |||
| 27754e5d03 | |||
| fbb284ae00 | |||
| 9bbabc3079 | |||
| 5a3abbe45a | |||
| e5283cac5c | |||
| eb55f2533d | |||
| 352e459d82 | |||
| 90a4e2128b | |||
| c79a3e6309 | |||
| a6f1560bce | |||
| a60f917f59 | |||
| 0f93784b8a | |||
| 5af7420129 | |||
| 17d2d32bf1 | |||
| 7534509caa | |||
| 76db90af1b | |||
| b4b15a2808 | |||
| f2055d128e | |||
| 534ef6fd7c | |||
| 4cb2c6589d | |||
| f23e9e5da9 | |||
| 22fb48cd0a | |||
| 3ae87c8a67 | |||
| 5d773090eb | |||
| c0d64c4630 | |||
| a323e32a42 | |||
| 1703cb24d1 | |||
| 35ce444c23 | |||
| 8b2f1c054c | |||
| 56c43aa1e6 | |||
| 2cd705950f | |||
| 467b84d3e2 | |||
| 4270d2fe56 | |||
| 519de4f5ba | |||
| 90a6efecbb | |||
| 27e3fd5e13 | |||
| e55d70fa62 | |||
| b873a1f033 | |||
| 0f0bc9d318 | |||
| 087a736d1a | |||
| 97fbec551f | |||
| 7c974b7fb4 | |||
| 4f712116c2 | |||
| 83ce5f3fea | |||
| 7f4a626a83 | |||
| 47810c95da | |||
| bd14c73f9b | |||
| f368382765 | |||
| efd44c421b | |||
| d429d55d16 | |||
| e3fc712e11 | |||
| f5b495969b | |||
| 1ee08fca51 | |||
| 525108dd95 | |||
| d005f9204c | |||
| 6237e117aa | |||
| 0c29167057 | |||
| e28550b53f | |||
| c43e163a56 | |||
| 31f40ae9ee | |||
| b8051ce2cf | |||
| 09c637914d | |||
| 7e283404f3 | |||
| 8a20824266 | |||
| 0e63e698f2 | |||
| c2bb57f0bc | |||
| 6676d5844b | |||
| ca7d6c8ae0 | |||
| b267b74e8b | |||
| dcc14a565d | |||
| f96e84ff4a | |||
| 41e9c00fa8 | |||
| 85e3fd5e36 | |||
| daf7d2c9dc | |||
| f663837c4e | |||
| 77d9b93887 | |||
| 816d2b8a76 | |||
| f40cf790af | |||
| 8e1a3d26b1 | |||
| a240329f22 | |||
| 6afa6d2142 | |||
| 0c587415ea | |||
| ace8802480 | |||
| 7e1637b810 | |||
| 0fabedb0c4 | |||
| e567f06cef | |||
| bf87ed69ce | |||
| 7cb0e4e039 | |||
| 510f1b2188 | |||
| 1bea1e8c91 | |||
| 9c89099cf1 | |||
| efae444942 | |||
| 3536e54b50 | |||
| 17984c812f | |||
| 09ad926642 | |||
| e3549e639e | |||
| b421691734 | |||
| 835c113416 | |||
| 2e807806ba | |||
| 2c21eccac3 | |||
| f302da1275 | |||
| 82ce9b866f | |||
| df23d79e04 | |||
| 43aae4ee56 | |||
| 73be5c5f0a | |||
| 02c71e9310 | |||
| f041272302 | |||
| aeb1955947 | |||
| a0332cb0df | |||
| 79df7bbeef | |||
| af64052e33 | |||
| fc479c3582 | |||
| 886e16a1cf | |||
| 367b1b9499 | |||
| c61da40f70 | |||
| 90cc63e57a | |||
| fc17f23533 | |||
| 69fc9d9b35 | |||
| 533156e0d0 | |||
| 8c125cccb1 | |||
| 3da64f6bc5 | |||
| daa671c6a5 | |||
| 71eb88016e | |||
| c45166387b | |||
| f4dffe7f37 | |||
| df69162f78 | |||
| e14c9b61d5 | |||
| b1cc6bd19b | |||
| e02b505ae5 | |||
| 6aad5a9581 | |||
| 4b33a10779 | |||
| 6ec202da9e | |||
| 54cf1cf821 | |||
| d36966e2d7 | |||
| f01a2a06b5 | |||
| f866ef1fd7 | |||
| 480966f978 | |||
| b3f06049be | |||
| 7887805550 | |||
| dbd12d3e92 | |||
| db79ecd636 | |||
| 9da8cc0cea | |||
| f9c7bbbe01 | |||
| 81116598cb | |||
| 46c803c9ce | |||
| 93567a68e8 | |||
| 2294653431 | |||
| aae3add0c9 | |||
| b690cd6335 | |||
| 487c0fc916 | |||
| 0551c010b5 | |||
| d645286744 | |||
| f3e674684e | |||
| fd72cd5e22 | |||
| e8d2f8c476 | |||
| e7e25cd25b | |||
| d7750c099b | |||
| b74019a83b | |||
| f81f4925a9 | |||
| df307d3d45 | |||
| a122d6e905 | |||
| a97b7706d8 | |||
| e622928d9d | |||
| fceda9bd35 | |||
| 0bd22f89a7 | |||
| 1b996e37a7 | |||
| 403344e120 | |||
| a200951b75 | |||
| 5c71c53787 | |||
| 40b1e8297f | |||
| dfbbe3fcca | |||
| b484e3311c | |||
| 287172cd5d | |||
| 5ef979db79 | |||
| af2dcc8533 | |||
| 06514ed7b7 | |||
| c7bc853ab3 | |||
| 3f20ab758c | |||
| e3db8372a7 | |||
| 175379dfdb | |||
| ae87646e40 | |||
| b0f09b20d4 | |||
| f10bbacf7b | |||
| f50a3fe686 | |||
| bdc55dd082 | |||
| 86a10e9af5 | |||
| e5934c3eed | |||
| dabd8dc136 | |||
| 5509e21759 | |||
| f324310355 | |||
| 8b25e2b037 | |||
| 634fb09d2e | |||
| 0ac39d4356 | |||
| 60a0dcfdcf | |||
| 6551d7cc5e | |||
| 4859acb700 | |||
| 34c46910b2 | |||
| d51ce6a46f | |||
| bd8daf4015 | |||
| 649be3741c | |||
| fcc2ee3551 | |||
| 128808a56d | |||
| 714895976f | |||
| 9fe68c5a38 | |||
| a3a62f9826 | |||
| 389fd5e42c | |||
| 947a13556a | |||
| ee1fbf72cc | |||
| 06775806d5 | |||
| 961d5e7721 | |||
| 727227cf8b | |||
| cead3c87ea | |||
| c550a792b8 | |||
| cc1e5e4636 | |||
| e279e4d972 | |||
| eb6fca6697 | |||
| 7476655302 | |||
| 8eb50a8917 | |||
| d835d8bc30 | |||
| 981e3be9b0 | |||
| db3d8ddfc2 | |||
| f5c421ab99 | |||
| 4ad001fd9e | |||
| 05f3e52f55 | |||
| 8b10ec2cfb | |||
| 7b278aeff7 | |||
| d5e9b20aec | |||
| e9c3eca68a | |||
| 41a328fbf0 | |||
| e1a8ba09f1 | |||
| c565dc1a7e | |||
| ede90eb282 | |||
| f95165185c | |||
| 26361718a7 | |||
| 41cf490681 | |||
| 44e384a256 | |||
| 21276f4d1e | |||
| 43e8c85295 | |||
| 4672138b00 | |||
| b8f115ef10 | |||
| 574cbecf18 | |||
| 97b3f7ec55 | |||
| 7982f43a62 | |||
| fc32ce56d0 | |||
| 6c6a4edf59 | |||
| 44da20ed4a | |||
| 4f8de83ff4 | |||
| 066971e720 | |||
| 107dc8cc24 | |||
| 7b42efd37a | |||
| 9df78087f0 | |||
| 06d12fb8ec | |||
| 08d1a2dfd6 | |||
| 035e51b36e | |||
| 901aa820e5 | |||
| c1a5641ff5 | |||
| 957a63d04a | |||
| 43960b3a60 | |||
| 8d7e4bdd0d | |||
| 9610baf903 | |||
| 52011a7d80 | |||
| 6be1437ecc | |||
| cc49b4c921 | |||
| 3aa1d5f1ed | |||
| 991f113e4e | |||
| 04963a616d | |||
| 839ca74bf1 | |||
| 437b2020b7 | |||
| 4712931850 | |||
| e56af1adc6 | |||
| 7f245e2896 | |||
| 4fd5ba5630 | |||
| 7c5740a39b | |||
| 53998cc51a | |||
| 3faa20a29b | |||
| 57a2e5aba4 | |||
| ad01fdad4e | |||
| 2893dc33b6 | |||
| 5a846cdec5 | |||
| c13ba5f25e | |||
| 28e956e55c | |||
| a05b041d69 | |||
| 496225ab9d | |||
| 254b9471e4 | |||
| b137feac16 | |||
| cf159f6112 | |||
| 60f055f07d | |||
| 5dd0fe2761 | |||
| 43fe3874dd | |||
| ddf97f79d0 | |||
| 5a050be3de | |||
| d8aa0de443 | |||
| 7662e4f904 | |||
| b0d64d00b6 | |||
| 9dd8f32595 | |||
| 99dd0a4e2e | |||
| 531411315e | |||
| e6eb53796d | |||
| a3ced86214 | |||
| 435ff32904 | |||
| e1270836d0 | |||
| 93f8d0990f | |||
| b23a2d4f2c | |||
| d031087972 | |||
| cdf24a1c19 | |||
| e857d538fb | |||
| 3bd699c9e6 | |||
| 6f756f48cb | |||
| b14a5b3dd9 | |||
| 5f23f6caa3 | |||
| d05ae5231c | |||
| 07d3eea1d1 | |||
| 23830f3454 | |||
| 24ae5d327e | |||
| 7b9454b101 | |||
| b976ba17d5 | |||
| 9c803164e1 | |||
| b9592b30af | |||
| a536e1600d | |||
| 6267e54ad7 | |||
| 11b337b189 | |||
| 20d34eb622 | |||
| 416df97b64 | |||
| b3cabb788b | |||
| c620c11ba8 | |||
| 7515f77846 | |||
| b35ef90916 | |||
| a4d2a720d0 | |||
| 8141e15bd9 | |||
| 064488a9c4 | |||
| bb08c61b76 | |||
| 6ed9c9869e | |||
| 586d7d4f9b | |||
| 0ac96e02ab | |||
| 63be9874f4 | |||
| c1393ce7c7 | |||
| 973d0c0c07 | |||
| c4f81e7027 | |||
| ed0289df3b | |||
| 7e4cbaf5df | |||
| bce8b6f9a8 | |||
| 0718546167 | |||
| 8e222eb40d | |||
| fddb7ecc05 | |||
| b5ae6f01eb | |||
| ecd7a225e9 | |||
| f2e4508cfc | |||
| 4031925e11 | |||
| a99f6cac5e | |||
| d5bccd9bb1 | |||
| 94102cec97 | |||
| 778620c312 | |||
| c50a505cdb | |||
| 1830aeba18 | |||
| 1ba6b761f0 | |||
| b1921b7847 | |||
| f37fa1b071 | |||
| a79201ebd7 | |||
| b557586a62 | |||
| 1842ab790e | |||
| e8937e2030 | |||
| 2ddfc77b66 | |||
| 00526116cc | |||
| 1c2bfb7991 | |||
| fa52acad27 | |||
| 18c11899cb | |||
| 3cd323cb1a | |||
| 09c894db0a | |||
| 3f030805d1 | |||
| e96be21850 | |||
| 9242b93558 | |||
| 4de08c438b | |||
| 576311f7b2 | |||
| bf96099e46 | |||
| 854edba825 | |||
| fe3e5de518 | |||
| d5b1e9f40c | |||
| 54fe878580 | |||
| 075a21a9db | |||
| cc605e24d9 | |||
| 4ea7401190 | |||
| 3b4525413a | |||
| 8bf59faf66 | |||
| 54b4dd7818 | |||
| 21b3cca54a | |||
| af65c39c87 | |||
| 1d51cc3388 | |||
| b6c1cd504e | |||
| ba0f2248d8 | |||
| 96605fb0fe | |||
| 991abd4c1c | |||
| fec23cab8d | |||
| 0fe7bdc5b5 | |||
| 65557dfb3d | |||
| ad907a72a1 | |||
| c65aea86c6 | |||
| 76dd63a326 | |||
| 8798b4e826 | |||
| 7b1fa1246f | |||
| f3e4773811 | |||
| 233b4c78ab | |||
| f81d316ad4 | |||
| f123e90392 | |||
| d0607c789f | |||
| 85278ea147 | |||
| 9cbe0f38f9 | |||
| 6bcb20ee4c | |||
| ca104160b0 | |||
| bc13baa5a9 | |||
| 2014a030d6 | |||
| f673cae32b | |||
| f5d09d569c | |||
| 94749c1c69 | |||
| 9c216036ec | |||
| 7e8750f79b | |||
| 8ca46aecdd | |||
| 0e6779fafc | |||
| acde0218b3 | |||
| b8a1955ab9 | |||
| e49426c027 | |||
| 00d547362b | |||
| cce6e821c2 | |||
| a48ebfc4c1 | |||
| b23bdb0188 | |||
| 69b92b57aa | |||
| 84d692feef | |||
| 6834d41f4b | |||
| 9fad405064 | |||
| a8ddbb5d2d | |||
| 1fd328f90a | |||
| 6779fa90a5 | |||
| 45d6bfa3fb | |||
| 5d1a9b1e9c | |||
| 77ae119d32 | |||
| 0facd08fa9 | |||
| 0ccdc47034 | |||
| 7c21ec0c5a | |||
| 731beb0f7c | |||
| 9a170a3c5b | |||
| f93d629acb | |||
| eede0e3c34 | |||
| f4f97be46d | |||
| 3bea2a314e | |||
| 4d20de926c | |||
| b307b4ed95 | |||
| 653cba4d4e | |||
| adb93e382f | |||
| b2938ef678 | |||
| 809c8806d0 | |||
| d809c2e789 | |||
| ca2d073775 | |||
| 4038c437c6 | |||
| 12011fd0c8 | |||
| 528ae04711 | |||
| 7612a3f742 | |||
| 2272b94531 | |||
| 21d628b598 | |||
| ab0049ec5c | |||
| ac394784e3 | |||
| 943a72ce12 | |||
| 927d9b0f85 | |||
| 7454087c88 | |||
| 37fd19fc9a | |||
| aa90e75c59 | |||
| 7b090c2e2a | |||
| 1a4a8d87fc | |||
| fbd0644942 | |||
| 4eb4f635e7 | |||
| 60d6bfae9f | |||
| 009345c5f6 | |||
| 00d4d368df | |||
| 5fda1cdc61 | |||
| 91955ef66c | |||
| 89585f8121 | |||
| 449df7f161 | |||
| a5170c51b3 | |||
| 20bda361a3 | |||
| ca7ae4e1e8 | |||
| 6f08589265 | |||
| ac5c902569 | |||
| 731b5cccad | |||
| fd1e82bd8e | |||
| 2140eff9af | |||
| 6f97bb2635 | |||
| fd0b5f4377 | |||
| c5bd1d6bc0 | |||
| 40ba90df27 | |||
| 450a1493fe | |||
| 3b1f9aa41e | |||
| e993671d02 | |||
| b36758a155 | |||
| 14a46622c3 | |||
| 9aea0f1034 | |||
| 8e87a7aa99 | |||
| 4d31886e4a | |||
| 4886f87afa | |||
| 923b22a75a | |||
| 610ac47289 | |||
| de28b8d314 | |||
| 7c50f6c8d0 | |||
| 8b493e091d | |||
| 13065d7e5a | |||
| 3f7d6759c1 | |||
| a4be03cd5f | |||
| 44d5994248 | |||
| d8b6d52a85 | |||
| e0e6054f2f | |||
| eeed0c0b1a | |||
| 5fbdea2cae | |||
| 393944a8a7 | |||
| a442e2be91 | |||
| 18398e1b93 | |||
| 1b08584ced | |||
| 20a3da8a19 | |||
| 1dd4c4a109 | |||
| a228c54dd5 | |||
| 125d3c0a4d | |||
| c9f5397821 | |||
| 57a5c0f743 | |||
| 8dc31cc790 | |||
| fd1d74ada1 | |||
| 28756860aa | |||
| 5929533d78 | |||
| 395baf1509 | |||
| b4d89b66f5 | |||
| e9f803b8a5 | |||
| 8b25dca1dc | |||
| 514363247a | |||
| 354a415e73 | |||
| 4df5de3122 | |||
| e5815974b1 | |||
| 2ff8e8fa67 | |||
| dd7038adf2 | |||
| 39d25107bd | |||
| 2c671578ae | |||
| 76680ba86c | |||
| 4d83c5a8f9 | |||
| 274a857dff | |||
| c891c9a921 | |||
| 247a8e0cca | |||
| 15c0ab6279 | |||
| 5c7c21fdf1 | |||
| 14348306e2 | |||
| 02cc4c1aa2 | |||
| 1aadbfc019 | |||
| b53e657091 | |||
| 74967cbac8 | |||
| a139451a9b | |||
| 7708372922 | |||
| 09146ec176 | |||
| 3abadb666e | |||
| be164a823d | |||
| d5dda1722a | |||
| 28ea9a7861 | |||
| ec87635e8e | |||
| 48219fbd95 | |||
| 33b5202a5d | |||
| 141fa55869 | |||
| 2a2e2da879 | |||
| 73bd73d910 | |||
| 9d733fcff5 | |||
| 307c543d8d | |||
| 5ffd1c85d3 | |||
| f771d47526 | |||
| 50e73d4a3a | |||
| 6cc96d094d | |||
| dc149d2636 | |||
| 6a9fd04989 | |||
| 7c01257cdd | |||
| fa99a0b3c8 | |||
| cec8dc991c | |||
| 2b78225114 | |||
| 2ffec8014a | |||
| 30f2db1e4d | |||
| 1b44b27142 | |||
| badc1249c0 | |||
| 8621b3f6ff | |||
| e67d8e9c81 | |||
| 769ad73737 | |||
| afb14409c2 | |||
| 971aaf3b98 | |||
| 2d2145a780 | |||
| 28078910a2 | |||
| 6235e6e665 | |||
| 5bf63bc36c | |||
| 02192368d2 | |||
| b0cfcaff3d | |||
| d208b07a94 | |||
| e715478310 | |||
| 5dedbf91e0 | |||
| 64e04ae15b | |||
| 573105d269 | |||
| 8fc6a4b349 | |||
| c6c76be8f9 | |||
| 1cd15ffa53 | |||
| eb9c3f7867 | |||
| a369409930 | |||
| ad6788f67a | |||
| 28de204036 | |||
| cf5bfb16bd | |||
| 5642d379a5 | |||
| e33992ecf8 | |||
| fa85e0cc68 | |||
| 42ee8d9ca4 | |||
| d521e014fd | |||
| 43545bce41 | |||
| 78723402ee | |||
| 9698974ad7 | |||
| 6f138677a8 | |||
| 9161044860 | |||
| 30e804acd8 | |||
| 40fe0bebcf | |||
| 4f25572d1d | |||
| 81cc35702d | |||
| cd3ed42b6d | |||
| 4637b82471 | |||
| e1d77c0c3e | |||
| bc688792cb | |||
| 36b33ba4f1 | |||
| edde61a46c | |||
| aa1fcd7eb9 | |||
| 3f59e2078a | |||
| c2edd26598 | |||
| a0ceed9586 | |||
| a6e360c1db | |||
| 79b49bd57e | |||
| 0c1b7abe36 | |||
| 6d39b63a74 | |||
| 438b2aac65 | |||
| 3ffc25d5d8 | |||
| 3b689f8a66 | |||
| 2b62a5a0cc | |||
| 7e46145df1 | |||
| 9655549739 | |||
| 57a3d1369b | |||
| 2ae7a519d0 | |||
| 9165c6b706 | |||
| 7818864fac | |||
| 3f577f1088 | |||
| 546435db6d | |||
| c6f64a3acb | |||
| c6b7652c6e | |||
| bfebd69568 | |||
| 3fb46f9604 | |||
| 17384f5761 | |||
| 5a5328b1cb | |||
| edc83764c4 | |||
| 1f5527164c | |||
| af5a7964d0 | |||
| 41c2814610 | |||
| c197f6eab2 | |||
| 410da66834 | |||
| eb6a0e38f7 | |||
| 8e90c2898a | |||
| 4074741187 | |||
| 01bcab4af1 | |||
| 6c5cc98016 | |||
| b39618cd89 | |||
| 22316b4684 | |||
| 1e7e3a84c6 | |||
| 872e0762b6 | |||
| 61fec4b53d | |||
| 8e7b012c4c | |||
| 7ae53ac364 | |||
| c519505296 | |||
| a9b60c1d1b | |||
| 22108934ff | |||
| c93f8eafea | |||
| 856cfd3ebb | |||
| bccb516223 | |||
| 53717588f9 | |||
| 4258d94d00 | |||
| 01f8631663 | |||
| 393d90d3f7 | |||
| bd90e2c19e | |||
| fe19cea6c9 | |||
| 56ab7f212e | |||
| 5179611c64 | |||
| 0c930294f1 | |||
| 8541dd3178 | |||
| 7e9a07838e | |||
| 7f2d1702ca | |||
| b4b461d815 | |||
| bc75157b9f | |||
| 67465045bb | |||
| 5dbfe00207 | |||
| 38ac1723f6 | |||
| af3ef1efde | |||
| 3fba370e87 | |||
| a28b1b5aa2 | |||
| aec5ef442e | |||
| c907512cf0 | |||
| b87190993f | |||
| 2d7cacbefb | |||
| 63e81369c2 | |||
| 6f9bc17d57 | |||
| 7bd1810852 | |||
| 433fe1449a | |||
| b6566ec67b | |||
| b67b0baa1b | |||
| 73e6d627bc | |||
| e4ace3c416 | |||
| b58c86cdd6 | |||
| 9ff93169df | |||
| 44cdb3a52e | |||
| 3d7fbba014 | |||
| ce1a397a7c | |||
| fa7da189fc | |||
| 582fffda1b | |||
| 3bf6f7ca09 | |||
| 0a7c549125 | |||
| 3bb510e910 | |||
| 75a84f6ed1 | |||
| a7aadd7439 | |||
| 1e90434c18 | |||
| d718f023cd | |||
| 28b47b25ea | |||
| 2bf933a3e1 | |||
| 24a548cd97 | |||
| ce307391a8 | |||
| 7abc7c07af | |||
| d8c49a3d04 | |||
| 064bb51d7a | |||
| b74e36ca60 | |||
| 3e344a3de0 | |||
| b19cc90fd3 | |||
| eff8d56ef5 | |||
| a0825e7774 | |||
| 765e370bd5 | |||
| 8469a07f4f | |||
| fcf4646928 | |||
| f5bbb1747c | |||
| 1868bffbef | |||
| eb5c6796ae | |||
| a35d128bb5 | |||
| 74787f8927 | |||
| 64455b594b | |||
| aa064fb6c5 | |||
| 70f3a25798 | |||
| 19230db8b7 | |||
| 4fb7f7c1e7 | |||
| 0b20dda56e | |||
| 5c29ac8d1e | |||
| eeed075be2 | |||
| 6c23d482dd | |||
| b2a7b191cf | |||
| 95c3cf2c90 | |||
| 26dc81fc6d | |||
| 26bab59886 | |||
| c412090dbc | |||
| a4c2bf31c7 | |||
| 5d15ccae9f | |||
| 63f1954c2a | |||
| 1fc4b88784 | |||
| aa9b1c3331 | |||
| 458b086468 | |||
| 75762e8ddd | |||
| 58dbdd5189 | |||
| 0d3d38a032 | |||
| 68ea59328e | |||
| f74a4f056e | |||
| d70e5c396f | |||
| c70b8f7aa2 | |||
| 737dd01c8d | |||
| 945deafa63 | |||
| a77b7f7b66 | |||
| e86a0e23bb | |||
| 6218ffabca | |||
| 4de3c5e587 | |||
| 7dc448938a | |||
| 1c30330faf | |||
| 40d36c2468 | |||
| d60113bab9 | |||
| 71a19a86f8 | |||
| 5385a4834a | |||
| 91b55ad2af | |||
| b1428b34c1 | |||
| 3a5b0af81d | |||
| 1f1a9a0b13 | |||
| 83c9f98d0a | |||
| c8f8c85c6e | |||
| 0c14112d60 | |||
| aa15572c68 | |||
| b16e171619 | |||
| a23551c7f2 | |||
| 95c2947d4a | |||
| a87e4ff449 | |||
| 8fac9fe67e | |||
| eafaf78c12 | |||
| 66e40128f8 | |||
| cf303380b6 | |||
| 4b6b27a8fd | |||
| 48f054c809 | |||
| 10e25fcd03 | |||
| 35dadbb7c4 | |||
| 29bb41aff0 | |||
| 28e6da1bbc | |||
| b9a92507d1 | |||
| fc8985b242 | |||
| 924a01bca6 | |||
| fac79c8a7d | |||
| 0e89e8dcc2 | |||
| 594dc64824 | |||
| 79ec382b7f | |||
| 1ec94aa2a6 | |||
| 70a5343d60 | |||
| d88da39fe9 | |||
| 5f369a511a | |||
| afac62e788 | |||
| e4533cc03f | |||
| ee9c4baa9d | |||
| 27c36530cb | |||
| a5b80c1c73 | |||
| d6e9e9f2a5 | |||
| 122c1f8e37 | |||
| 07a3edf020 | |||
| 299fd4f107 | |||
| 1ae989c0c0 | |||
| 4d9323800b | |||
| e935976c91 | |||
| 319680e7cc | |||
| aa40185058 | |||
| d0e0716221 | |||
| 62e6641480 | |||
| 6c25cc6a78 | |||
| ec3ac73def | |||
| 9b547d6ece | |||
| ce1f149547 | |||
| 394d992b19 | |||
| b8b3b7993b | |||
| 915877eba6 | |||
| 7dda10629a | |||
| fc5a5d7f63 | |||
| d2ff23813d | |||
| 06840c6c84 | |||
| fda70b0aef | |||
| 7b13ddcbfc | |||
| 741af0fc43 | |||
| abf9b6c5e4 | |||
| 56361dda86 | |||
| d276e7b568 | |||
| 5af0fe35df | |||
| 94fb563a15 | |||
| 865e8575b2 | |||
| d95147712b | |||
| 20e1fa935a | |||
| f885351464 | |||
| 33fdc1cdc7 | |||
| 9d39417142 | |||
| 96ed90e2cc | |||
| f99c390a76 | |||
| fba3a54f82 | |||
| fedeb47dbc | |||
| 79ac51a1ca | |||
| 02631da9f1 | |||
| 692172d57b | |||
| 8bd4bbe7ea | |||
| 0d93f90c62 | |||
| 3f9459a07e | |||
| 19d063c3e9 | |||
| 7f8a5315c4 | |||
| 0f7f7d997b | |||
| ab54a24434 | |||
| 77901659e5 | |||
| b3f74b6c1c | |||
| 7eb5fa8d8e | |||
| a90e2132e3 | |||
| 60b0db7120 | |||
| af538950a8 | |||
| 5787e95c51 | |||
| 3b3be4b6cb | |||
| c1fa902189 | |||
| 31706e2724 | |||
| 21d6bcc63e | |||
| c4ad1a7127 | |||
| 17984344ee | |||
| 035a58e7c5 | |||
| 646e33e59f | |||
| 68ddfb0f02 | |||
| cd3b8b5bab | |||
| da88fd4267 | |||
| 29413fdc8e | |||
| c643d26bce | |||
| 69d4f32933 | |||
| 26154a6cdb | |||
| 7b22553f09 | |||
| 9726f0f586 | |||
| 25f6f09d22 | |||
| 3e6a68b472 | |||
| 60e18c4854 | |||
| f7633dd61f | |||
| ae74354140 | |||
| b0987b224d | |||
| 5ecae9662f | |||
| 0f29869a76 | |||
| 67dd730666 | |||
| ccaa13fa21 | |||
| ff795b1373 | |||
| 81287e9eb5 | |||
| 26b9d8193c | |||
| 81bdb352aa | |||
| 2ae8530c83 | |||
| 476755fc18 | |||
| 4247495cf4 | |||
| b6eada070a | |||
| 2399600169 | |||
| db9877beae | |||
| 43f1d5c53e | |||
| 4a31d538c6 | |||
| 23dd7baf35 | |||
| a8e1f3ef4c | |||
| cd622e9f81 | |||
| 71f7d45084 | |||
| 0f586fe49e | |||
| 42913816ce | |||
| 027f9a1793 | |||
| 8bc2cf335b | |||
| 626c71a942 | |||
| 58b9c4f1d6 | |||
| 24163dfa4a | |||
| e3d596e034 | |||
| cd48f45462 | |||
| a0229e8132 | |||
| be7ff94ae3 | |||
| 98ddce24ad | |||
| b380a77a7d | |||
| 9f7a2d86cf | |||
| 343a7ef4fe | |||
| 88c3b0ba83 | |||
| 918997acfa | |||
| dd1fb111a5 | |||
| 7124b69912 | |||
| 76bcca55d2 | |||
| 7fa2ffc14e | |||
| 0ee141493f | |||
| 194b21c41e | |||
| 93634985e8 | |||
| 6757dc2ca1 | |||
| 3a888f9be4 | |||
| 34c9cbeedb | |||
| 97a2769002 | |||
| f87e6e2b6a | |||
| 7bd3f1e08d | |||
| aeb20a8949 | |||
| 58e99403ad | |||
| 530987152c | |||
| 4085b4a74a | |||
| 852172a7ff | |||
| 7aa1230553 | |||
| a7097014a3 | |||
| 9f711c20e0 | |||
| 87c031b825 | |||
| 18e2f5dd7f | |||
| d1b70185f1 | |||
| 254175c98e | |||
| e8e67912ec | |||
| 3c00805763 | |||
| 599055b49f | |||
| 82fa754497 | |||
| 9ec9cc0e25 | |||
| f52c9415c9 | |||
| 27626ee59a | |||
| 0b36e2a833 | |||
| 1aa0e3fb47 | |||
| 4a40ce5646 | |||
| 6e126dc08d | |||
| 6201798ef5 | |||
| ec3c09607d | |||
| 2b104435dc | |||
| 83be5822b1 | |||
| 0491bd993e | |||
| 8713e94428 | |||
| ccab724db2 | |||
| 7876ab993a | |||
| bd8e004795 | |||
| 01ab0f5ab9 | |||
| 0728dba0cd | |||
| 3dd5e78b35 | |||
| 667afa6d64 | |||
| d24555a4d7 | |||
| 410971a228 | |||
| b497c22d6b | |||
| de6969f561 | |||
| 0ce4260134 | |||
| 7237dd053d | |||
| 0f6c076dda | |||
| b57e678dc9 | |||
| 6031bb4953 | |||
| 05dc53b396 | |||
| e97f819a5c | |||
| 28c37c08d2 | |||
| f430b22884 | |||
| cf4505635b | |||
| 8ad0de63e1 | |||
| 8068ea95d5 | |||
| 44cbd025f4 | |||
| ac76930dd1 | |||
| 3a653d9558 | |||
| ae50430527 | |||
| 83386bcdbf | |||
| 1e175e4e82 | |||
| 01815d04dc | |||
| 46ea6900b4 | |||
| 6394fe049d | |||
| f324fc7c10 | |||
| 434c383fcd | |||
| 9eade626aa | |||
| 263299b97d | |||
| 7e491ce7d8 | |||
| 6707487d3c | |||
| 0498d7ea98 | |||
| fc9bda9f7f | |||
| 2b47083c12 | |||
| 1f700f33b2 | |||
| 769dafb428 | |||
| e3bc2e5d84 | |||
| f3120f1e0d | |||
| 036d8b4852 | |||
| a67a8d746f | |||
| 2237ec135c | |||
| a238c33fd0 | |||
| b90cf14228 | |||
| 7bbd1d2d52 | |||
| 8aebbb1b51 | |||
| df7504d4a2 | |||
| 063399a6d6 | |||
| 9762a94138 | |||
| 800f5c7c64 | |||
| 16995d2ae5 | |||
| b4cced68f1 | |||
| 9bb01bba30 | |||
| cb3842f0bd | |||
| 5d95d20a32 | |||
| b628ee0c3e | |||
| 688bf0bef8 | |||
| 06d6d3a9ca | |||
| bb4debb863 | |||
| 5d1a3fe6a7 | |||
| 72b5d006ba | |||
| d382358766 | |||
| 429cb50ff7 | |||
| 3a01dad945 | |||
| b9ae5cafa0 | |||
| ed8e4b8766 | |||
| cecd47caea | |||
| 7711946cf5 | |||
| 074aebbe5d | |||
| 8b78d05805 | |||
| cf962a26f4 | |||
| 14778696e9 | |||
| ed56d03c09 | |||
| f9d9df4bbf | |||
| 0859d75256 | |||
| 21303b24c8 | |||
| 06965e7eed | |||
| fc0b069ad2 | |||
| b0b27d6842 | |||
| 47725ea4b5 | |||
| 103d349c5f | |||
| c41364fb16 | |||
| b984d6794e | |||
| cf4aa1256d | |||
| 4a6fcb4f4c | |||
| 426804304c | |||
| 074f8ed902 | |||
| d3a1bd52c7 | |||
| a4103859cd | |||
| ed19b7b635 | |||
| f21dd041ae | |||
| 2de21b5f25 | |||
| a547ae6cf9 | |||
| c39901e95d | |||
| 4f0522d913 | |||
| b972d3fabe | |||
| d6bef6b17d | |||
| 231100cbdc | |||
| 9c8d71ca4d | |||
| fcdc17dd93 | |||
| a719ca684c | |||
| e5033c3213 | |||
| d37cf9e9c4 | |||
| d9688884e4 | |||
| 2f369bbf3d | |||
| 7f1b67d21c | |||
| 6118589248 | |||
| 9a7f5c2def | |||
| 1999f17443 | |||
| 7e39f0a34f | |||
| fb9a726ba0 | |||
| ddc3f0e880 | |||
| d66b0891e7 | |||
| f1ba087bff | |||
| 6cf5f2edc4 | |||
| f0570b0e20 | |||
| cd851afc8d | |||
| 532497f6bd | |||
| f648137a8e | |||
| e9fd2f89fe | |||
| 5565911e2c | |||
| 7337012280 | |||
| e3f25e3bc4 | |||
| 9582a473c6 | |||
| 22918e4b4a | |||
| 0d567ea5dc | |||
| 703697e1c4 | |||
| 0b89ed0d17 | |||
| f3de317ddd | |||
| cd7e60b7a1 | |||
| 352a5bea5c | |||
| f33eaf6d0f | |||
| 81ebf9cc11 | |||
| 23f92e0a96 | |||
| e69f8ced88 | |||
| ba5b052ee8 | |||
| b6e6997e7c | |||
| c39507747b | |||
| c9f4390f0f | |||
| 801cd2e855 | |||
| e9c73ebb71 | |||
| 6af3a1b5a7 | |||
| c86dbeb2b3 | |||
| 4ff6b2a30a | |||
| 3310d2b431 | |||
| b53de43c95 | |||
| ffb8e284b4 | |||
| f593ad0f2c | |||
| 9114d5b2fe | |||
| 5b47a20d49 | |||
| b0acc5a68e | |||
| d7382db669 | |||
| f290192de4 | |||
| 8360d4d589 | |||
| df56224df6 | |||
| 1a636ff8e9 | |||
| 4dcc1f340f | |||
| f199bc4256 | |||
| 11e28286e1 | |||
| 4c454d463a | |||
| 80034d8658 | |||
| 296afa96c6 | |||
| 9f69b8815a | |||
| cdc38f7e6e | |||
| fcb4e6cc85 | |||
| cb00f31f79 | |||
| 84c65d32be | |||
| cc119bb596 | |||
| 88c9850073 | |||
| 75fcb9a990 | |||
| 7121a4ae30 | |||
| e636920c44 | |||
| e0cf3d0962 | |||
| 53b6f5d8e8 | |||
| 5acbfac255 | |||
| 82bb1175bd | |||
| f1b1589a7d | |||
| 0f4ff90f01 | |||
| 43e0bcbf73 | |||
| e26045ec73 | |||
| 2d52680bed | |||
| f42f526f93 | |||
| 53b03af3e1 | |||
| 32f8692f13 | |||
| 19c321e7ae | |||
| 021a49a72d | |||
| eb0bad1af6 | |||
| 513d140ea2 | |||
| 3e368141c7 | |||
| b59b7c1e47 | |||
| 6a1acc819b | |||
| e5db36e21e | |||
| d759bc274f | |||
| 3290755fa8 | |||
| fd3455d3ec | |||
| 88355b2504 | |||
| 79f078653a | |||
| a952ea02dc | |||
| 59abffb1c1 | |||
| d737139bee | |||
| a1479a9b6c | |||
| 45894c3255 | |||
| dcfdb2ecff | |||
| ba7ed5be1c | |||
| 307412e6ca | |||
| 41e1e89696 | |||
| d239ab6b1f | |||
| 0905773d08 | |||
| 9ca80d352b | |||
| b52440bcc1 | |||
| 28c9cacf68 | |||
| 8199fdd2bf | |||
| f74ed76850 | |||
| 5b301addd8 | |||
| 1c1a141701 | |||
| e85f59db8c | |||
| fc69ca0599 | |||
| 2ad304aaf2 | |||
| c1b56922aa | |||
| 8c737f2ca4 | |||
| 20d9561143 | |||
| 1062ef9b49 | |||
| 2b54f64c8c | |||
| 5cd5a9d7e4 | |||
| bb49747fd9 | |||
| f38c7bffed | |||
| 813f02604e | |||
| c579dce2cf | |||
| f6d9c2998b | |||
| 60c303b771 | |||
| 79e7f262a7 | |||
| 8f2863c02c | |||
| cd8cb03797 | |||
| 9f4aa3f7e1 | |||
| eb2c9f2fe1 | |||
| e7ffd2455a | |||
| 63b7a3a36c | |||
| 376f2bfeb1 | |||
| 0a34b139f4 | |||
| 45d0a8e501 | |||
| 485dd2952e | |||
| a111b50e37 | |||
| b2d14ca101 | |||
| f3ab3573c3 | |||
| ef4ae4480f | |||
| 8277a2d942 | |||
| 8bbf040100 | |||
| e6bb1a3fde | |||
| 6df7482b3d | |||
| bdc0ade117 | |||
| 29a149b340 | |||
| 80af866650 | |||
| 1237d9660e | |||
| 8e6582b801 | |||
| 18ad260ce9 | |||
| c0933a3b20 | |||
| a116ae6ab5 | |||
| 0e2eea7555 | |||
| 767e35851b | |||
| a25a3c186b | |||
| 28256c0a72 | |||
| dac9ed2785 | |||
| 9cc576b98d | |||
| 87c95f7a7f | |||
| 4065142830 | |||
| f56308b6e3 | |||
| bdbdf7cb83 | |||
| 9a0f28c003 | |||
| 2d2d93d5d8 | |||
| 8c44dd6119 | |||
| 0cb1bc4ca5 | |||
| db83864d78 | |||
| 63365c2109 | |||
| 9bbc26dd70 | |||
| a383cfd125 | |||
| 8bb9070d9a | |||
| 690e934a46 | |||
| be1974a89e | |||
| d21087b0d9 | |||
| 22b1ad087c | |||
| ce25675f73 | |||
| 955a7696f1 | |||
| 4cfacde337 | |||
| eafa8f02b6 | |||
| 1338b65aaa | |||
| 7c8068b2bd | |||
| 75f749dbe0 | |||
| 58910f50e8 | |||
| 36eebffd10 | |||
| 0e8e0e4b06 | |||
| 8524fb500f | |||
| 8fd1d9acef | |||
| c452dd3538 | |||
| 222f083322 | |||
| 47527d7175 | |||
| 6f907f7961 | |||
| 891a226fdb | |||
| 7cbc707308 | |||
| 8d3a036b3b | |||
| ee21139356 | |||
| b37814e9fa | |||
| 07cca33cb4 | |||
| 980533052b | |||
| 8124f688da | |||
| 89e726b5a2 | |||
| 42695d9253 | |||
| 28fe7817b4 | |||
| 6aa37e2529 | |||
| 2d8eb163e7 | |||
| 445b10d6f0 | |||
| 0a4dd832a3 | |||
| 8feee4e61a | |||
| fe23017a97 | |||
| b01b52c8f8 | |||
| 0f81d2cace | |||
| d051246b7e | |||
| fd7f22ffba | |||
| c5c0cf001c | |||
| ab846062e4 | |||
| 172e77a6cf | |||
| 0998d8a0fc | |||
| 71b82730ab | |||
| 6b597bf1fb | |||
| 5c431096ec | |||
| e278bed251 | |||
| efbcf5469b | |||
| de0a91619e | |||
| 7be0c1a034 | |||
| 71e43d6e3d | |||
| a77865379f | |||
| e20cc9e80f | |||
| c66c920cbe | |||
| 695dbee501 | |||
| de44a8d91e | |||
| de6ba1c82e | |||
| 5b1124d658 | |||
| 122b16a720 | |||
| dfcf2eec57 | |||
| 9a9a35d75e | |||
| ec89c9cdc8 | |||
| e66ab2b2c9 | |||
| dc167bd25e | |||
| 680378e68f | |||
| 190dbff98d | |||
| ff7aa46cda | |||
| 8c47d85927 | |||
| de99a3f114 | |||
| 8b77aa151b | |||
| 00461468c9 | |||
| ad323f7885 | |||
| a50b673e07 | |||
| d1e66c4441 | |||
| 7cc00b99a1 | |||
| c745615a2a | |||
| 7aacac554d | |||
| fcab19c392 | |||
| 98e2bf53ed | |||
| 51310e632c | |||
| 2a46b17958 | |||
| e189f0317f | |||
| 138684fcea | |||
| 23af50a30d | |||
| 28a6284968 | |||
| ce114b33ff | |||
| d05c451294 | |||
| 9fc0d26eb5 | |||
| 25a0c88670 | |||
| 0d5d9a04f5 | |||
| f2ed0057bf | |||
| 661fcfd263 | |||
| dbed431085 | |||
| fb89ca98f6 | |||
| 04bec463c7 | |||
| 25170f0d2c | |||
| 85a522ba14 | |||
| a28618ab78 | |||
| 955a5ba7f2 | |||
| dbd23a6ea4 | |||
| df93057dd3 | |||
| 3c2b6d4e7f | |||
| 2f11e12c75 | |||
| 2faf615b68 | |||
| 8bb2e0aa97 | |||
| b6e4a47329 | |||
| 08197fca23 | |||
| 423fb90266 | |||
| 67d1d73845 | |||
| fa0531b975 | |||
| cb01563ba8 | |||
| 4446259a0b | |||
| a5eb2f0ae6 | |||
| bf4a1097c1 | |||
| 87ebc8761b | |||
| 4e87ce8f4b | |||
| 24d24c1f85 | |||
| c88d77e3f8 | |||
| 553c42b82c | |||
| 3eb1dd3180 | |||
| 10b2cf2662 | |||
| 545fd8b58a | |||
| 2e31972c56 | |||
| d095cb7812 | |||
| fc31d5fd2f | |||
| d73ba14154 | |||
| d8e09e80ff | |||
| 211c4fb102 | |||
| d448b76dd4 | |||
| e8ebf5af73 | |||
| 459dab65ca | |||
| 73304ef8d4 | |||
| aa025e4fac | |||
| 8eac9338fe | |||
| b0deecc0aa | |||
| af111a4be9 | |||
| 6a8a9172b1 | |||
| 291b997972 | |||
| 70a5520f47 | |||
| e7397f824b | |||
| 0beee14cd8 | |||
| 9ba251ff30 | |||
| 5b24ac037f | |||
| ea70c354ad | |||
| 1623b456ec | |||
| 307959ba76 | |||
| 7f1699663d | |||
| 252675d3ff | |||
| 84c45f2068 | |||
| 5873995e42 | |||
| 077922c667 | |||
| 47dcb1523a | |||
| 86f1e63ed2 | |||
| 709415a6b4 | |||
| fa46485d57 | |||
| c2d5d2b61a | |||
| e39c1af5ae | |||
| ca6463cae8 | |||
| 0e17b7a981 | |||
| bcd1167d39 | |||
| fbe17dc3e3 | |||
| b102e5c1a5 | |||
| df9a5e398e | |||
| 90fafa219a | |||
| 9e1b55a749 | |||
| 4ca15a1fc3 | |||
| 5bf80dae4e | |||
| 1f4568d22f | |||
| 903a975033 | |||
| 0ee9afba4f | |||
| 9b5713d6b4 | |||
| 94176fad83 | |||
| de759b5120 | |||
| 9bc4ff16a7 | |||
| 814f6ced5f | |||
| 91dae8ad85 | |||
| fb4f8a86f4 | |||
| e15d2fe82c | |||
| 4ede1c74e8 | |||
| 9d98396e33 | |||
| e43dcf5d4c | |||
| eac4ab3e3c | |||
| d256d8fc35 | |||
| 3157e99e04 | |||
| 91fee0d6e9 | |||
| 521061fc64 | |||
| 4328746df0 | |||
| 2b87d939bb | |||
| 39a88a6cd3 | |||
| d0ad416e28 | |||
| 61e4f59aa0 | |||
| db66b85e61 | |||
| 66c810ead2 | |||
| 9824bb9c63 | |||
| f806e2c22c | |||
| 3d408b18f7 | |||
| 8aa776ae62 | |||
| a15f3b8c65 | |||
| 4df22c96d0 | |||
| 772df06fa5 | |||
| e1b7336d5d | |||
| d3a9d2ea5b | |||
| 4914d9b638 | |||
| 1f8a7be34e | |||
| 97bdfa54c0 | |||
| 64bb730dd1 | |||
| 7013b459a3 | |||
| a31733e2db | |||
| 199b23d14a | |||
| f1c1ed833c | |||
| 0506917b87 | |||
| 4f40ba8e6e | |||
| 733a792610 | |||
| f581fd4821 | |||
| 86ddb61a3f | |||
| 6c3451b912 | |||
| 627c8562f7 | |||
| 061bb2abeb | |||
| 35e1dc95a5 | |||
| 1dc46fa104 | |||
| 4161467356 | |||
| d70f81bfe4 | |||
| bfb7ccffb5 | |||
| 3a6c032782 | |||
| d632111cf9 | |||
| e4b761917a | |||
| f8d162d995 | |||
| 0708070764 | |||
| 87f7bc28a3 | |||
| 3eb7d8ab58 | |||
| 71d0ac4c5e | |||
| 839593b11e | |||
| 13a0927900 | |||
| 00984c599b | |||
| 34ca65a180 | |||
| 9df7129c6c | |||
| 0b9717c2a5 | |||
| 596ae72942 | |||
| 88cc91b85a | |||
| d4b8ded6c8 | |||
| 86d2a03a0a | |||
| de1812bf91 | |||
| f36751ff6b | |||
| a9273fc225 | |||
| 2a0b12112f | |||
| 2aea02989f | |||
| 2b554cf286 | |||
| dc2777703d | |||
| 6ae03b545c | |||
| 8e366cfc84 | |||
| 27c2c4fb92 | |||
| 83af589b27 | |||
| 7a937833f3 | |||
| 4110d2529c | |||
| 1c6c165d78 | |||
| a5ffb0e021 | |||
| a08a39b620 | |||
| 968e8195ef | |||
| 52ce2f5384 | |||
| cb2d1bc444 | |||
| 47b81faf3d | |||
| 1483761e72 | |||
| 7f43665e3c | |||
| 01fcd653ad | |||
| 298e947740 | |||
| 91c2014b7e | |||
| 3018ad16b1 | |||
| 1172be241c | |||
| c743b4ab88 | |||
| f6ec718ced | |||
| aa20027de4 | |||
| e504cf11e1 | |||
| a11e0a39d9 | |||
| 26531401b0 | |||
| 0f4293e4cb | |||
| 7172b134ea | |||
| 4a9e342a1c | |||
| f98d869c21 | |||
| 1312310a6e | |||
| ad9c81f405 | |||
| 496caa6fb1 | |||
| 2fd7d45b9c | |||
| 0b087665a8 | |||
| 39862fba2a | |||
| 3ac44d211f | |||
| d3392000af | |||
| 564d2e109f | |||
| fe07298adb | |||
| cc176a999d | |||
| 47c5a41aa6 | |||
| 2ad6f2c9fc | |||
| 8905bc1c27 | |||
| 064d5174c2 | |||
| c69c8f6ef5 | |||
| 7fb81049f3 | |||
| 5099ce15db | |||
| ed500395d3 | |||
| a1e88fc3c2 | |||
| c8b007631d | |||
| 6bc1f8a39f | |||
| fe84f6cab1 | |||
| 27eea1c7a6 | |||
| 32f94704c7 | |||
| 05fdbf3d24 | |||
| f28c791cf2 | |||
| a4b474ff39 | |||
| 38c76fe86b | |||
| d09259c79a | |||
| a683fa2414 | |||
| e744816928 | |||
| 15703bce04 | |||
| f4f5540d08 | |||
| b1b37685c1 | |||
| 0ff4cc572c | |||
| 081b9c17d5 | |||
| eb6b21e7e6 | |||
| baa17c304b | |||
| 7b8b388667 | |||
| 118529d8d3 | |||
| b67c4553f6 | |||
| 47980da78e | |||
| 3d57d444df | |||
| 5870632c19 | |||
| ffeb27f04e | |||
| 012df9dcd7 | |||
| 82fd2334cf | |||
| 7bafa57989 | |||
| 5b4ccd9d59 | |||
| be2b86909a | |||
| 82506ae7cd | |||
| 574a2a11e7 | |||
| 3d5ed9401c | |||
| 3b5a674409 | |||
| f9856bdabd | |||
| b6f75acf53 | |||
| c0f7504b36 | |||
| 365a649776 | |||
| 0ecf72b4c3 | |||
| 90013c7451 | |||
| 715aa8d845 | |||
| ad7fdd1d3f |
@@ -1,29 +0,0 @@
|
||||
function fish_prompt -d "Write out the prompt"
|
||||
# This shows up as USER@HOST /home/user/ >, with the directory colored
|
||||
# $USER and $hostname are set by fish, so you can just use them
|
||||
# instead of using `whoami` and `hostname`
|
||||
printf '%s@%s %s%s%s > ' $USER $hostname \
|
||||
(set_color $fish_color_cwd) (prompt_pwd) (set_color normal)
|
||||
end
|
||||
|
||||
if status is-interactive
|
||||
# Commands to run in interactive sessions can go here
|
||||
set fish_greeting
|
||||
|
||||
end
|
||||
|
||||
starship init fish | source
|
||||
if test -f ~/.local/state/quickshell/user/generated/terminal/sequences.txt
|
||||
cat ~/.local/state/quickshell/user/generated/terminal/sequences.txt
|
||||
end
|
||||
|
||||
alias pamcan pacman
|
||||
alias ls 'eza --icons'
|
||||
alias clear "printf '\033[2J\033[3J\033[1;1H'"
|
||||
alias q 'qs -c ii'
|
||||
|
||||
|
||||
# function fish_prompt
|
||||
# set_color cyan; echo (pwd)
|
||||
# set_color green; echo '> '
|
||||
# end
|
||||
@@ -1,2 +0,0 @@
|
||||
# You can put extra environment variables here
|
||||
# https://wiki.hyprland.org/Configuring/Environment-variables/
|
||||
@@ -1,2 +0,0 @@
|
||||
# You can make apps auto-start here
|
||||
# Relevant Hyprland wiki section: https://wiki.hyprland.org/Configuring/Keywords/#executing
|
||||
@@ -1,2 +0,0 @@
|
||||
# Put general config stuff here
|
||||
# Here's a list of every variable: https://wiki.hyprland.org/Configuring/Variables/
|
||||
@@ -1,11 +0,0 @@
|
||||
# See https://wiki.hyprland.org/Configuring/Binds/
|
||||
#!
|
||||
##! User
|
||||
bind = Ctrl+Super, Slash, exec, xdg-open ~/.config/illogical-impulse/config.json # Edit shell config
|
||||
bind = Ctrl+Super+Alt, Slash, exec, xdg-open ~/.config/hypr/custom/keybinds.conf # Edit extra keybinds
|
||||
|
||||
# Add stuff here
|
||||
# Use #! to add an extra column on the cheatsheet
|
||||
# Use ##! to add a section in that column
|
||||
# Add a comment after a bind to add a description, like above
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# You can put custom rules here
|
||||
# Window/layer rules: https://wiki.hyprland.org/Configuring/Window-Rules/
|
||||
# Workspace rules: https://wiki.hyprland.org/Configuring/Workspace-Rules/
|
||||
@@ -1,21 +0,0 @@
|
||||
# This file sources other files in `hyprland` and `custom` folders
|
||||
# You wanna add your stuff in files in `custom`
|
||||
|
||||
$qsConfig = ii
|
||||
exec = hyprctl dispatch submap global # DO NOT REMOVE THIS OR YOU WON'T BE ABLE TO USE ANY KEYBIND
|
||||
submap = global # This is required for catchall to work
|
||||
|
||||
# Defaults
|
||||
source=~/.config/hypr/hyprland/env.conf
|
||||
source=~/.config/hypr/hyprland/execs.conf
|
||||
source=~/.config/hypr/hyprland/general.conf
|
||||
source=~/.config/hypr/hyprland/rules.conf
|
||||
source=~/.config/hypr/hyprland/colors.conf
|
||||
source=~/.config/hypr/hyprland/keybinds.conf
|
||||
|
||||
# Custom
|
||||
source=~/.config/hypr/custom/env.conf
|
||||
source=~/.config/hypr/custom/execs.conf
|
||||
source=~/.config/hypr/custom/general.conf
|
||||
source=~/.config/hypr/custom/rules.conf
|
||||
source=~/.config/hypr/custom/keybinds.conf
|
||||
@@ -1,34 +0,0 @@
|
||||
# exec = export SLURP_ARGS='-d -c FFDAD4BB -b 673B3444 -s 00000000'
|
||||
|
||||
general {
|
||||
col.active_border = rgba(F7DCDE39)
|
||||
col.inactive_border = rgba(A58A8D30)
|
||||
}
|
||||
|
||||
misc {
|
||||
background_color = rgba(1D1011FF)
|
||||
}
|
||||
|
||||
plugin {
|
||||
hyprbars {
|
||||
# Honestly idk if it works like css, but well, why not
|
||||
bar_text_font = Rubik, Geist, AR One Sans, Reddit Sans, Inter, Roboto, Ubuntu, Noto Sans, sans-serif
|
||||
bar_height = 30
|
||||
bar_padding = 10
|
||||
bar_button_padding = 5
|
||||
bar_precedence_over_border = true
|
||||
bar_part_of_window = true
|
||||
|
||||
bar_color = rgba(1D1011FF)
|
||||
col.text = rgba(F7DCDEFF)
|
||||
|
||||
|
||||
# example buttons (R -> L)
|
||||
# hyprbars-button = color, size, on-click
|
||||
hyprbars-button = rgb(F7DCDE), 13, , hyprctl dispatch killactive
|
||||
hyprbars-button = rgb(F7DCDE), 13, , hyprctl dispatch fullscreen 1
|
||||
hyprbars-button = rgb(F7DCDE), 13, , hyprctl dispatch movetoworkspacesilent special
|
||||
}
|
||||
}
|
||||
|
||||
windowrulev2 = bordercolor rgba(FFB2BCAA) rgba(FFB2BC77),pinned:1
|
||||
@@ -1,24 +0,0 @@
|
||||
# ######### Input method ##########
|
||||
# See https://fcitx-im.org/wiki/Using_Fcitx_5_on_Wayland
|
||||
env = QT_IM_MODULE, fcitx
|
||||
env = XMODIFIERS, @im=fcitx
|
||||
env = SDL_IM_MODULE, fcitx
|
||||
env = GLFW_IM_MODULE, ibus
|
||||
env = INPUT_METHOD, fcitx
|
||||
|
||||
# ############ Wayland #############
|
||||
env = ELECTRON_OZONE_PLATFORM_HINT,auto
|
||||
|
||||
# ############ Themes #############
|
||||
env = QT_QPA_PLATFORM, wayland
|
||||
env = QT_QPA_PLATFORMTHEME, kde
|
||||
env = XDG_MENU_PREFIX, plasma-
|
||||
|
||||
# ######## Wayland #########
|
||||
# Tearing
|
||||
# env = WLR_DRM_NO_ATOMIC, 1
|
||||
# ?
|
||||
# env = WLR_NO_HARDWARE_CURSORS, 1
|
||||
|
||||
# ######## Virtual envrionment #########
|
||||
env = ILLOGICAL_IMPULSE_VIRTUAL_ENV, ~/.local/state/quickshell/.venv
|
||||
@@ -1,168 +0,0 @@
|
||||
# MONITOR CONFIG
|
||||
monitor=,preferred,auto,1,transform, 0
|
||||
# monitor=,addreserved, 0, 0, 0, 0 # Custom reserved area
|
||||
|
||||
# HDMI port: mirror display. To see device name, use `hyprctl monitors`
|
||||
# monitor=HDMI-A-1,1920x1080@60,1920x0,1,mirror,eDP-1
|
||||
|
||||
gestures {
|
||||
workspace_swipe = true
|
||||
workspace_swipe_distance = 700
|
||||
workspace_swipe_fingers = 3
|
||||
workspace_swipe_min_fingers = true
|
||||
workspace_swipe_cancel_ratio = 0.2
|
||||
workspace_swipe_min_speed_to_force = 5
|
||||
workspace_swipe_direction_lock = true
|
||||
workspace_swipe_direction_lock_threshold = 10
|
||||
workspace_swipe_create_new = true
|
||||
}
|
||||
|
||||
general {
|
||||
# Gaps and border
|
||||
gaps_in = 4
|
||||
gaps_out = 5
|
||||
gaps_workspaces = 50
|
||||
|
||||
border_size = 1
|
||||
col.active_border = rgba(0DB7D4FF)
|
||||
col.inactive_border = rgba(31313600)
|
||||
resize_on_border = true
|
||||
|
||||
no_focus_fallback = true
|
||||
|
||||
allow_tearing = true # This just allows the `immediate` window rule to work
|
||||
|
||||
snap {
|
||||
enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
dwindle {
|
||||
preserve_split = true
|
||||
smart_split = false
|
||||
smart_resizing = false
|
||||
# precise_mouse_move = true
|
||||
}
|
||||
|
||||
decoration {
|
||||
rounding = 18
|
||||
|
||||
blur {
|
||||
enabled = true
|
||||
xray = true
|
||||
special = false
|
||||
new_optimizations = true
|
||||
size = 14
|
||||
passes = 3
|
||||
brightness = 1
|
||||
noise = 0.01
|
||||
contrast = 1
|
||||
popups = true
|
||||
popups_ignorealpha = 0.6
|
||||
input_methods = true
|
||||
input_methods_ignorealpha = 0.8
|
||||
}
|
||||
|
||||
shadow {
|
||||
enabled = true
|
||||
ignore_window = true
|
||||
range = 30
|
||||
offset = 0 2
|
||||
render_power = 4
|
||||
color = rgba(00000010)
|
||||
}
|
||||
|
||||
# Dim
|
||||
dim_inactive = true
|
||||
dim_strength = 0.025
|
||||
dim_special = 0.07
|
||||
}
|
||||
|
||||
animations {
|
||||
enabled = true
|
||||
# Curves
|
||||
bezier = expressiveFastSpatial, 0.42, 1.67, 0.21, 0.90
|
||||
bezier = expressiveSlowSpatial, 0.39, 1.29, 0.35, 0.98
|
||||
bezier = expressiveDefaultSpatial, 0.38, 1.21, 0.22, 1.00
|
||||
bezier = emphasizedDecel, 0.05, 0.7, 0.1, 1
|
||||
bezier = emphasizedAccel, 0.3, 0, 0.8, 0.15
|
||||
bezier = standardDecel, 0, 0, 0, 1
|
||||
bezier = menu_decel, 0.1, 1, 0, 1
|
||||
bezier = menu_accel, 0.52, 0.03, 0.72, 0.08
|
||||
# Configs
|
||||
# windows
|
||||
animation = windowsIn, 1, 3, emphasizedDecel, popin 80%
|
||||
animation = windowsOut, 1, 2, emphasizedDecel, popin 90%
|
||||
animation = windowsMove, 1, 3, emphasizedDecel, slide
|
||||
animation = border, 1, 10, emphasizedDecel
|
||||
# layers
|
||||
animation = layersIn, 1, 2.7, emphasizedDecel, popin 93%
|
||||
animation = layersOut, 1, 2.4, menu_accel, popin 94%
|
||||
# fade
|
||||
animation = fadeLayersIn, 1, 0.5, menu_decel
|
||||
animation = fadeLayersOut, 1, 2.7, menu_accel
|
||||
# workspaces
|
||||
animation = workspaces, 1, 7, menu_decel, slide
|
||||
## specialWorkspace
|
||||
animation = specialWorkspaceIn, 1, 2.8, emphasizedDecel, slidevert
|
||||
animation = specialWorkspaceOut, 1, 1.2, emphasizedAccel, slidevert
|
||||
}
|
||||
|
||||
input {
|
||||
kb_layout = us
|
||||
numlock_by_default = true
|
||||
repeat_delay = 250
|
||||
repeat_rate = 35
|
||||
|
||||
follow_mouse = 1
|
||||
off_window_axis_events = 2
|
||||
|
||||
touchpad {
|
||||
natural_scroll = yes
|
||||
disable_while_typing = true
|
||||
clickfinger_behavior = true
|
||||
scroll_factor = 0.5
|
||||
}
|
||||
}
|
||||
|
||||
misc {
|
||||
disable_hyprland_logo = true
|
||||
disable_splash_rendering = true
|
||||
vfr = 1
|
||||
vrr = 1
|
||||
mouse_move_enables_dpms = true
|
||||
key_press_enables_dpms = true
|
||||
animate_manual_resizes = false
|
||||
animate_mouse_windowdragging = false
|
||||
enable_swallow = false
|
||||
swallow_regex = (foot|kitty|allacritty|Alacritty)
|
||||
new_window_takes_over_fullscreen = 2
|
||||
allow_session_lock_restore = true
|
||||
session_lock_xray = true
|
||||
initial_workspace_tracking = false
|
||||
focus_on_activate = true
|
||||
}
|
||||
|
||||
binds {
|
||||
scroll_event_delay = 0
|
||||
hide_special_on_workspace_change = true
|
||||
}
|
||||
|
||||
cursor {
|
||||
zoom_factor = 1
|
||||
zoom_rigid = false
|
||||
}
|
||||
|
||||
# Overview
|
||||
plugin {
|
||||
hyprexpo {
|
||||
columns = 3
|
||||
gap_size = 5
|
||||
bg_col = rgb(000000)
|
||||
workspace_method = first 1 # [center/first] [workspace] e.g. first 1 or center m+1
|
||||
|
||||
enable_gesture = false # laptop touchpad, 4 fingers
|
||||
gesture_distance = 300 # how far is the "max"
|
||||
gesture_positive = false
|
||||
}
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
# Lines ending with `# [hidden]` won't be shown on cheatsheet
|
||||
# Lines starting with #! are section headings
|
||||
|
||||
#!
|
||||
##! Shell
|
||||
# These absolutely need to be on top, or they won't work consistently
|
||||
bindid = Super, Super_L, Toggle overview, global, quickshell:overviewToggleRelease # Toggle overview/launcher
|
||||
bind = Super, Super_L, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill fuzzel || fuzzel # [hidden] Launcher (fallback)
|
||||
binditn = Super, catchall, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
|
||||
bind = Ctrl, Super_L, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
|
||||
bind = Super, mouse:272, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
|
||||
bind = Super, mouse:273, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
|
||||
bind = Super, mouse:274, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
|
||||
bind = Super, mouse:275, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
|
||||
bind = Super, mouse:276, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
|
||||
bind = Super, mouse:277, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
|
||||
bind = Super, mouse_up, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
|
||||
bind = Super, mouse_down,global, quickshell:overviewToggleReleaseInterrupt # [hidden]
|
||||
|
||||
bindit = ,Super_L, global, quickshell:workspaceNumber # [hidden]
|
||||
bindd = Super, V, Clipboard history >> clipboard, global, quickshell:overviewClipboardToggle # Clipboard history >> clipboard
|
||||
bindd = Super, Period, Emoji >> clipboard, global, quickshell:overviewEmojiToggle # Emoji >> clipboard
|
||||
bindd = Super, Tab, Toggle overview, global, quickshell:overviewToggle # [hidden] Toggle overview/launcher (alt)
|
||||
bindd = Super, A, Toggle left sidebar, global, quickshell:sidebarLeftToggle # Toggle left sidebar
|
||||
bind = Super+Alt, A, global, quickshell:sidebarLeftToggleDetach # [hidden]
|
||||
bind = Super, B, global, quickshell:sidebarLeftToggle # [hidden]
|
||||
bind = Super, O, global, quickshell:sidebarLeftToggle # [hidden]
|
||||
bindd = Super, N, Toggle right sidebar, global, quickshell:sidebarRightToggle # Toggle right sidebar
|
||||
bindd = Super, Slash, Toggle cheatsheet, global, quickshell:cheatsheetToggle # Toggle cheatsheet
|
||||
bindd = Super, K, Toggle on-screen keyboard, global, quickshell:oskToggle # Toggle on-screen keyboard
|
||||
bindd = Super, M, Toggle media controls, global, quickshell:mediaControlsToggle # Toggle media controls
|
||||
bindd = Ctrl+Alt, Delete, Toggle session menu, global, quickshell:sessionToggle # Toggle session menu
|
||||
bindd = Super, J, Toggle bar, global, quickshell:barToggle # Toggle bar
|
||||
bind = Ctrl+Alt, Delete, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill wlogout || wlogout -p layer-shell # [hidden] Session menu (fallback)
|
||||
bind = Shift+Super+Alt, Slash, exec, qs -p ~/.config/quickshell/$qsConfig/welcome.qml # [hidden] Launch welcome app
|
||||
|
||||
bindle=, XF86MonBrightnessUp, exec, qs -c $qsConfig ipc call brightness increment || brightnessctl s 5%+ # [hidden]
|
||||
bindle=, XF86MonBrightnessDown, exec, qs -c $qsConfig ipc call brightness decrement || brightnessctl s 5%- # [hidden]
|
||||
bindle=, XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 2%+ # [hidden]
|
||||
bindle=, XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 2%- # [hidden]
|
||||
|
||||
bindl = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_SINK@ toggle # [hidden]
|
||||
bindld = Super+Shift,M, Toggle mute, exec, wpctl set-mute @DEFAULT_SINK@ toggle # [hidden]
|
||||
bindl = Alt ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle # [hidden]
|
||||
bindl = ,XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle # [hidden]
|
||||
bindld = Super+Alt,M, Toggle mic, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle # [hidden]
|
||||
bindd = Ctrl+Super, T, Change wallpaper, exec, ~/.config/quickshell/$qsConfig/scripts/colors/switchwall.sh # Change wallpaper
|
||||
bind = Ctrl+Super, R, exec, killall ags agsv1 gjs ydotool qs quickshell; qs -c $qsConfig & # Restart widgets
|
||||
|
||||
##! Utilities
|
||||
# Screenshot, Record, OCR, Color picker, Clipboard history
|
||||
bindd = Super, V, Copy clipboard history entry, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill fuzzel || cliphist list | fuzzel --match-mode fzf --dmenu | cliphist decode | wl-copy # [hidden] Clipboard history >> clipboard (fallback)
|
||||
bindd = Super, Period, Copy an emoji, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill fuzzel || ~/.config/hypr/hyprland/scripts/fuzzel-emoji.sh copy # [hidden] Emoji >> clipboard (fallback)
|
||||
bindd = Super+Shift, S, Screen snip, exec, qs -p ~/.config/quickshell/$qsConfig/screenshot.qml || pidof slurp || hyprshot --freeze --clipboard-only --mode region --silent # Screen snip
|
||||
# OCR
|
||||
bindd = Super+Shift, T, Character recognition,exec,grim -g "$(slurp $SLURP_ARGS)" "tmp.png" && tesseract "tmp.png" - | wl-copy && rm "tmp.png" # [hidden]
|
||||
# Color picker
|
||||
bindd = Super+Shift, C, Color picker, exec, hyprpicker -a # Pick color (Hex) >> clipboard
|
||||
# Fullscreen screenshot
|
||||
bindld = ,Print, Screenshot >> clipboard ,exec,grim - | wl-copy # Screenshot >> clipboard
|
||||
bindld = Ctrl,Print, Screenshot >> clipboard & save, exec, mkdir -p $(xdg-user-dir PICTURES)/Screenshots && grim $(xdg-user-dir PICTURES)/Screenshots/Screenshot_"$(date '+%Y-%m-%d_%H.%M.%S')".png # Screenshot >> clipboard & file
|
||||
# Recording stuff
|
||||
bindd = Super+Alt, R, Record region (no sound), exec, ~/.config/hypr/hyprland/scripts/record.sh # Record region (no sound)
|
||||
bindd = Ctrl+Alt, R, Record screen (no sound), exec, ~/.config/hypr/hyprland/scripts/record.sh --fullscreen # [hidden] Record screen (no sound)
|
||||
bindd = Super+Shift+Alt, R, Record screen (with sound), exec, ~/.config/hypr/hyprland/scripts/record.sh --fullscreen-sound # Record screen (with sound)
|
||||
# AI
|
||||
bindd = Super+Shift+Alt, mouse:273, Generate AI summary for selected text, exec, ~/.config/hypr/hyprland/scripts/ai/primary-buffer-query.sh # AI summary for selected text
|
||||
|
||||
#!
|
||||
##! Window
|
||||
# Focusing
|
||||
bindm = Super, mouse:272, movewindow # Move
|
||||
bindm = Super, mouse:274, movewindow # [hidden]
|
||||
bindm = Super, mouse:273, resizewindow # Resize
|
||||
#/# bind = Super, ←/↑/→/↓,, # Focus in direction
|
||||
bind = Super, Left, movefocus, l # [hidden]
|
||||
bind = Super, Right, movefocus, r # [hidden]
|
||||
bind = Super, Up, movefocus, u # [hidden]
|
||||
bind = Super, Down, movefocus, d # [hidden]
|
||||
bind = Super, BracketLeft, movefocus, l # [hidden]
|
||||
bind = Super, BracketRight, movefocus, r # [hidden]
|
||||
#/# bind = Super+Shift, ←/↑/→/↓,, # Move in direction
|
||||
bind = Super+Shift, Left, movewindow, l # [hidden]
|
||||
bind = Super+Shift, Right, movewindow, r # [hidden]
|
||||
bind = Super+Shift, Up, movewindow, u # [hidden]
|
||||
bind = Super+Shift, Down, movewindow, d # [hidden]
|
||||
bind = Alt, F4, killactive, # [hidden] Close (Windows)
|
||||
bind = Super, Q, killactive, # Close
|
||||
bind = Super+Shift+Alt, Q, exec, hyprctl kill # Forcefully zap a window
|
||||
|
||||
|
||||
# Window split ratio
|
||||
#/# binde = Super, ;/',, # Adjust split ratio
|
||||
binde = Super, Semicolon, splitratio, -0.1 # [hidden]
|
||||
binde = Super, Apostrophe, splitratio, +0.1 # [hidden]
|
||||
# Positioning mode
|
||||
bind = Super+Alt, Space, togglefloating, # Float/Tile
|
||||
bind = Super, D, fullscreen, 1 # Maximize
|
||||
bind = Super, F, fullscreen, 0 # Fullscreen
|
||||
bind = Super+Alt, F, fullscreenstate, 0 3 # Fullscreen spoof
|
||||
bind = Super, P, pin # Pin
|
||||
|
||||
#/# bind = Super+Alt, Hash,, # Send to workspace # (1, 2, 3,...)
|
||||
bind = Super+Alt, 1, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh movetoworkspacesilent 1 # [hidden]
|
||||
bind = Super+Alt, 2, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh movetoworkspacesilent 2 # [hidden]
|
||||
bind = Super+Alt, 3, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh movetoworkspacesilent 3 # [hidden]
|
||||
bind = Super+Alt, 4, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh movetoworkspacesilent 4 # [hidden]
|
||||
bind = Super+Alt, 5, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh movetoworkspacesilent 5 # [hidden]
|
||||
bind = Super+Alt, 6, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh movetoworkspacesilent 6 # [hidden]
|
||||
bind = Super+Alt, 7, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh movetoworkspacesilent 7 # [hidden]
|
||||
bind = Super+Alt, 8, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh movetoworkspacesilent 8 # [hidden]
|
||||
bind = Super+Alt, 9, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh movetoworkspacesilent 9 # [hidden]
|
||||
bind = Super+Alt, 0, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh movetoworkspacesilent 10 # [hidden]
|
||||
|
||||
# #/# bind = Super+Shift, Scroll ↑/↓,, # Send to workspace left/right
|
||||
bind = Super+Shift, mouse_down, movetoworkspace, r-1 # [hidden]
|
||||
bind = Super+Shift, mouse_up, movetoworkspace, r+1 # [hidden]
|
||||
bind = Super+Alt, mouse_down, movetoworkspace, -1 # [hidden]
|
||||
bind = Super+Alt, mouse_up, movetoworkspace, +1 # [hidden]
|
||||
|
||||
#/# bind = Super+Shift, Page_↑/↓,, # Send to workspace left/right
|
||||
bind = Super+Alt, Page_Down, movetoworkspace, +1 # [hidden]
|
||||
bind = Super+Alt, Page_Up, movetoworkspace, -1 # [hidden]
|
||||
bind = Super+Shift, Page_Down, movetoworkspace, r+1 # [hidden]
|
||||
bind = Super+Shift, Page_Up, movetoworkspace, r-1 # [hidden]
|
||||
bind = Ctrl+Super+Shift, Right, movetoworkspace, r+1 # [hidden]
|
||||
bind = Ctrl+Super+Shift, Left, movetoworkspace, r-1 # [hidden]
|
||||
|
||||
bind = Super+Alt, S, movetoworkspacesilent, special # Send to scratchpad
|
||||
|
||||
bind = Ctrl+Super, S, togglespecialworkspace, # [hidden]
|
||||
bind = Alt, Tab, cyclenext # [hidden] sus keybind
|
||||
bind = Alt, Tab, bringactivetotop, # [hidden] bring it to the top
|
||||
|
||||
##! Workspace
|
||||
# Switching
|
||||
#/# bind = Super, Hash,, # Focus workspace # (1, 2, 3,...)
|
||||
bind = Super, 1, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh workspace 1 # [hidden]
|
||||
bind = Super, 2, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh workspace 2 # [hidden]
|
||||
bind = Super, 3, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh workspace 3 # [hidden]
|
||||
bind = Super, 4, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh workspace 4 # [hidden]
|
||||
bind = Super, 5, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh workspace 5 # [hidden]
|
||||
bind = Super, 6, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh workspace 6 # [hidden]
|
||||
bind = Super, 7, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh workspace 7 # [hidden]
|
||||
bind = Super, 8, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh workspace 8 # [hidden]
|
||||
bind = Super, 9, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh workspace 9 # [hidden]
|
||||
bind = Super, 0, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh workspace 10 # [hidden]
|
||||
|
||||
#/# bind = Ctrl+Super, ←/→,, # Focus left/right
|
||||
bind = Ctrl+Super, Right, workspace, r+1 # [hidden]
|
||||
bind = Ctrl+Super, Left, workspace, r-1 # [hidden]
|
||||
#/# bind = Ctrl+Super+Alt, ←/→,, # [hidden] Focus busy left/right
|
||||
bind = Ctrl+Super+Alt, Right, workspace, m+1 # [hidden]
|
||||
bind = Ctrl+Super+Alt, Left, workspace, m-1 # [hidden]
|
||||
#/# bind = Super, Page_↑/↓,, # Focus left/right
|
||||
bind = Super, Page_Down, workspace, +1 # [hidden]
|
||||
bind = Super, Page_Up, workspace, -1 # [hidden]
|
||||
bind = Ctrl+Super, Page_Down, workspace, r+1 # [hidden]
|
||||
bind = Ctrl+Super, Page_Up, workspace, r-1 # [hidden]
|
||||
#/# bind = Super, Scroll ↑/↓,, # Focus left/right
|
||||
bind = Super, mouse_up, workspace, +1 # [hidden]
|
||||
bind = Super, mouse_down, workspace, -1 # [hidden]
|
||||
bind = Ctrl+Super, mouse_up, workspace, r+1 # [hidden]
|
||||
bind = Ctrl+Super, mouse_down, workspace, r-1 # [hidden]
|
||||
## Special
|
||||
bind = Super, S, togglespecialworkspace, # Toggle scratchpad
|
||||
bind = Super, mouse:275, togglespecialworkspace, # [hidden]
|
||||
bind = Ctrl+Super, BracketLeft, workspace, -1 # [hidden]
|
||||
bind = Ctrl+Super, BracketRight, workspace, +1 # [hidden]
|
||||
bind = Ctrl+Super, Up, workspace, r-5 # [hidden]
|
||||
bind = Ctrl+Super, Down, workspace, r+5 # [hidden]
|
||||
|
||||
#!
|
||||
# Testing
|
||||
bind = Super+Alt, f11, exec, bash -c 'RANDOM_IMAGE=$(find ~/Pictures -type f | grep -v -i "nipple" | grep -v -i "pussy" | shuf -n 1); ACTION=$(notify-send "Test notification with body image" "This notification should contain your user account <b>image</b> and <a href=\"https://discord.com/app\">Discord</a> <b>icon</b>. Oh and here is a random image in your Pictures folder: <img src=\"$RANDOM_IMAGE\" alt=\"Testing image\"/>" -a "Hyprland keybind" -p -h "string:image-path:/var/lib/AccountsService/icons/$USER" -t 6000 -i "discord" -A "openImage=Open profile image" -A "action2=Open the random image" -A "action3=Useless button"); [[ $ACTION == *openImage ]] && xdg-open "/var/lib/AccountsService/icons/$USER"; [[ $ACTION == *action2 ]] && xdg-open \"$RANDOM_IMAGE\"' # [hidden]
|
||||
bind = Super+Alt, f12, exec, bash -c 'RANDOM_IMAGE=$(find ~/Pictures -type f | grep -v -i "nipple" | grep -v -i "pussy" | shuf -n 1); ACTION=$(notify-send "Test notification" "This notification should contain a random image in your <b>Pictures</b> folder and <a href=\"https://discord.com/app\">Discord</a> <b>icon</b>.\n<i>Flick right to dismiss!</i>" -a "Discord (fake)" -p -h "string:image-path:$RANDOM_IMAGE" -t 6000 -i "discord" -A "openImage=Open profile image" -A "action2=Useless button" -A "action3=Cry more"); [[ $ACTION == *openImage ]] && xdg-open "/var/lib/AccountsService/icons/$USER"' # [hidden]
|
||||
bind = Super+Alt, Equal, exec, notify-send "Urgent notification" "Ah hell no" -u critical -a 'Hyprland keybind' # [hidden]
|
||||
|
||||
##! Session
|
||||
bindd = Super, L, Lock, exec, loginctl lock-session # Lock
|
||||
bind = Super+Shift, L, exec, loginctl lock-session # [hidden]
|
||||
bindld = Super+Shift, L, Suspend system, exec, sleep 0.1 && systemctl suspend || loginctl suspend # Sleep
|
||||
bindd = Ctrl+Shift+Alt+Super, Delete, Shutdown, exec, systemctl poweroff || loginctl poweroff # [hidden] Power off
|
||||
|
||||
##! Screen
|
||||
# Zoom
|
||||
binde = Super, Minus, exec, qs -c $qsConfig ipc call zoom zoomOut # Zoom out
|
||||
binde = Super, Equal, exec, qs -c $qsConfig ipc call zoom zoomIn # Zoom in
|
||||
binde = Super, Minus, exec, qs -c $qsConfig ipc call TEST_ALIVE || ~/.config/hypr/hyprland/scripts/zoom.sh decrease 0.1 # [hidden] Zoom out
|
||||
binde = Super, Equal, exec, qs -c $qsConfig ipc call TEST_ALIVE || ~/.config/hypr/hyprland/scripts/zoom.sh increase 0.1 # [hidden] Zoom in
|
||||
|
||||
##! Media
|
||||
bindl= Super+Shift, N, exec, playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` # Next track
|
||||
bindl= ,XF86AudioNext, exec, playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` # [hidden]
|
||||
bindl= ,XF86AudioPrev, exec, playerctl previous # [hidden]
|
||||
bind = Super+Shift+Alt, mouse:275, exec, playerctl previous # [hidden]
|
||||
bind = Super+Shift+Alt, mouse:276, exec, playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` # [hidden]
|
||||
bindl= Super+Shift, B, exec, playerctl previous # Previous track
|
||||
bindl= Super+Shift, P, exec, playerctl play-pause # Play/pause media
|
||||
bindl= ,XF86AudioPlay, exec, playerctl play-pause # [hidden]
|
||||
bindl= ,XF86AudioPause, exec, playerctl play-pause # [hidden]
|
||||
|
||||
##! Apps
|
||||
bind = Super, Return, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "kitty -1" "foot" "alacritty" "wezterm" "konsole" "kgx" "uxterm" "xterm" # Terminal
|
||||
bind = Super, T, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "kitty -1" "foot" "alacritty" "wezterm" "konsole" "kgx" "uxterm" "xterm" # [hidden] Kitty (terminal) (alt)
|
||||
bind = Ctrl+Alt, T, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "kitty -1" "foot" "alacritty" "wezterm" "konsole" "kgx" "uxterm" "xterm" # [hidden] Kitty (for Ubuntu people)
|
||||
bind = Super, E, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "dolphin" "nautilus" "nemo" "thunar" "kitty -1 fish -c yazi" # File manager
|
||||
bind = Super, W, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "google-chrome-stable" "zen-browser" "firefox" "brave" "chromium" "microsoft-edge-stable" "opera" "librewolf" # Browser
|
||||
bind = Super, C, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "code" "codium" "cursor" "zed" "zedit" "zeditor" "kate" "gnome-text-editor" "emacs" "command -v nvim && kitty -1 nvim" # Code editor
|
||||
bind = Super+Shift, W, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "wps" "onlyoffice-desktopeditors" # Office software
|
||||
bind = Super, X, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "kate" "gnome-text-editor" "emacs" # Text editor
|
||||
bind = Ctrl+Super, V, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "pavucontrol-qt" "pavucontrol" # Volume mixer
|
||||
bind = Super, I, exec, XDG_CURRENT_DESKTOP=gnome ~/.config/hypr/hyprland/scripts/launch_first_available.sh "qs -p ~/.config/quickshell/$qsConfig/settings.qml" "systemsettings" "gnome-control-center" "better-control" # Settings app
|
||||
bind = Ctrl+Shift, Escape, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "gnome-system-monitor" "plasma-systemmonitor --page-name Processes" "command -v btop && kitty -1 fish -c btop" # Task manager
|
||||
|
||||
# Cursed stuff
|
||||
## Make window not amogus large
|
||||
bind = Ctrl+Super, Backslash, resizeactive, exact 640 480 # [hidden]
|
||||
@@ -1,159 +0,0 @@
|
||||
# ######## Window rules ########
|
||||
|
||||
# Uncomment to apply global transparency to all windows:
|
||||
# windowrulev2 = opacity 0.89 override 0.89 override, class:.*
|
||||
|
||||
# Disable blur for xwayland context menus
|
||||
windowrulev2 = noblur,class:^()$,title:^()$
|
||||
# windowrulev2 = noblur, xwayland:1
|
||||
|
||||
|
||||
# Floating
|
||||
windowrulev2 = float, class:^(blueberry\.py)$
|
||||
windowrulev2 = float, class:^(guifetch)$ # FlafyDev/guifetch
|
||||
windowrulev2 = float, class:^(pavucontrol)$
|
||||
windowrulev2 = size 45%, class:^(pavucontrol)$
|
||||
windowrulev2 = center, class:^(pavucontrol)$
|
||||
windowrulev2 = float, class:^(org.pulseaudio.pavucontrol)$
|
||||
windowrulev2 = size 45%, class:^(org.pulseaudio.pavucontrol)$
|
||||
windowrulev2 = center, class:^(org.pulseaudio.pavucontrol)$
|
||||
windowrulev2 = float, class:^(nm-connection-editor)$
|
||||
windowrulev2 = size 45%, class:^(nm-connection-editor)$
|
||||
windowrulev2 = center, class:^(nm-connection-editor)$
|
||||
windowrulev2 = float, class:.*plasmawindowed.*
|
||||
windowrulev2 = float, class:kcm_.*
|
||||
windowrulev2 = float, class:.*bluedevilwizard
|
||||
windowrulev2 = float, title:.*Welcome
|
||||
windowrulev2 = float, title:^(illogical-impulse Settings)$
|
||||
windowrulev2 = float, class:org.freedesktop.impl.portal.desktop.kde
|
||||
windowrulev2 = float, class:^(Zotero)$
|
||||
windowrulev2 = size 45%, class:^(Zotero)$
|
||||
|
||||
|
||||
# Move
|
||||
# kde-material-you-colors spawns a window when changing dark/light theme. This is to make sure it doesn't interfere at all.
|
||||
windowrulev2 = float, class:^(plasma-changeicons)$
|
||||
windowrulev2 = noinitialfocus, class:^(plasma-changeicons)$
|
||||
windowrulev2 = move 999999 999999, class:^(plasma-changeicons)$
|
||||
# stupid dolphin copy
|
||||
windowrulev2 = move 40 80, title:^(Copying — Dolphin)$
|
||||
|
||||
# Tiling
|
||||
windowrulev2 = tile, class:^dev\.warp\.Warp$
|
||||
|
||||
# Picture-in-Picture
|
||||
windowrulev2 = float, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$
|
||||
windowrulev2 = keepaspectratio, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$
|
||||
windowrulev2 = move 73% 72%, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$
|
||||
windowrulev2 = size 25%, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$
|
||||
windowrulev2 = float, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$
|
||||
windowrulev2 = pin, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$
|
||||
|
||||
# Dialog windows – float+center these windows.
|
||||
windowrulev2 = center, title:^(Open File)(.*)$
|
||||
windowrulev2 = center, title:^(Select a File)(.*)$
|
||||
windowrulev2 = center, title:^(Choose wallpaper)(.*)$
|
||||
windowrulev2 = center, title:^(Open Folder)(.*)$
|
||||
windowrulev2 = center, title:^(Save As)(.*)$
|
||||
windowrulev2 = center, title:^(Library)(.*)$
|
||||
windowrulev2 = center, title:^(File Upload)(.*)$
|
||||
windowrulev2 = center, title:^(.*)(wants to save)$
|
||||
windowrulev2 = center, title:^(.*)(wants to open)$
|
||||
windowrulev2 = float, title:^(Open File)(.*)$
|
||||
windowrulev2 = float, title:^(Select a File)(.*)$
|
||||
windowrulev2 = float, title:^(Choose wallpaper)(.*)$
|
||||
windowrulev2 = float, title:^(Open Folder)(.*)$
|
||||
windowrulev2 = float, title:^(Save As)(.*)$
|
||||
windowrulev2 = float, title:^(Library)(.*)$
|
||||
windowrulev2 = float, title:^(File Upload)(.*)$
|
||||
windowrulev2 = float, title:^(.*)(wants to save)$
|
||||
windowrulev2 = float, title:^(.*)(wants to open)$
|
||||
|
||||
|
||||
# --- Tearing ---
|
||||
windowrulev2 = immediate, title:.*\.exe
|
||||
windowrulev2 = immediate, title:.*minecraft.*
|
||||
windowrulev2 = immediate, class:^(steam_app).*
|
||||
|
||||
# No shadow for tiled windows (matches windows that are not floating).
|
||||
windowrulev2 = noshadow, floating:0
|
||||
|
||||
# ######## Workspace rules ########
|
||||
workspace = special:special, gapsout:30
|
||||
|
||||
# ######## Layer rules ########
|
||||
layerrule = xray 1, .*
|
||||
# layerrule = noanim, .*
|
||||
layerrule = noanim, walker
|
||||
layerrule = noanim, selection
|
||||
layerrule = noanim, overview
|
||||
layerrule = noanim, anyrun
|
||||
layerrule = noanim, indicator.*
|
||||
layerrule = noanim, osk
|
||||
layerrule = noanim, hyprpicker
|
||||
|
||||
layerrule = noanim, noanim
|
||||
layerrule = blur, gtk-layer-shell
|
||||
layerrule = ignorezero, gtk-layer-shell
|
||||
layerrule = blur, launcher
|
||||
layerrule = ignorealpha 0.5, launcher
|
||||
layerrule = blur, notifications
|
||||
layerrule = ignorealpha 0.69, notifications
|
||||
layerrule = blur, logout_dialog # wlogout
|
||||
|
||||
# ags
|
||||
layerrule = animation slide left, sideleft.*
|
||||
layerrule = animation slide right, sideright.*
|
||||
layerrule = blur, session[0-9]*
|
||||
layerrule = blur, bar[0-9]*
|
||||
layerrule = ignorealpha 0.6, bar[0-9]*
|
||||
layerrule = blur, barcorner.*
|
||||
layerrule = ignorealpha 0.6, barcorner.*
|
||||
layerrule = blur, dock[0-9]*
|
||||
layerrule = ignorealpha 0.6, dock[0-9]*
|
||||
layerrule = blur, indicator.*
|
||||
layerrule = ignorealpha 0.6, indicator.*
|
||||
layerrule = blur, overview[0-9]*
|
||||
layerrule = ignorealpha 0.6, overview[0-9]*
|
||||
layerrule = blur, cheatsheet[0-9]*
|
||||
layerrule = ignorealpha 0.6, cheatsheet[0-9]*
|
||||
layerrule = blur, sideright[0-9]*
|
||||
layerrule = ignorealpha 0.6, sideright[0-9]*
|
||||
layerrule = blur, sideleft[0-9]*
|
||||
layerrule = ignorealpha 0.6, sideleft[0-9]*
|
||||
layerrule = blur, indicator.*
|
||||
layerrule = ignorealpha 0.6, indicator.*
|
||||
layerrule = blur, osk[0-9]*
|
||||
layerrule = ignorealpha 0.6, osk[0-9]*
|
||||
|
||||
# Quickshell
|
||||
layerrule = blurpopups, quickshell:.*
|
||||
layerrule = blur, quickshell:.*
|
||||
layerrule = ignorealpha 0.79, quickshell:.*
|
||||
layerrule = animation slide top, quickshell:bar
|
||||
layerrule = animation fade, quickshell:screenCorners
|
||||
layerrule = animation slide right, quickshell:sidebarRight
|
||||
layerrule = animation slide left, quickshell:sidebarLeft
|
||||
layerrule = animation slide bottom, quickshell:osk
|
||||
layerrule = animation slide bottom, quickshell:dock
|
||||
layerrule = blur, quickshell:session
|
||||
layerrule = noanim, quickshell:session
|
||||
layerrule = ignorealpha 0, quickshell:session
|
||||
layerrule = animation fade, quickshell:notificationPopup
|
||||
layerrule = blur, quickshell:backgroundWidgets
|
||||
layerrule = ignorealpha 0.05, quickshell:backgroundWidgets
|
||||
layerrule = noanim, quickshell:screenshot
|
||||
layerrule = animation popin 120%, quickshell:screenCorners
|
||||
layerrule = noanim, quickshell:lockWindowPusher
|
||||
|
||||
|
||||
# Launchers need to be FAST
|
||||
layerrule = noanim, quickshell:overview
|
||||
layerrule = noanim, gtk4-layer-shell
|
||||
## outfoxxed's stuff
|
||||
layerrule = blur, shell:bar
|
||||
layerrule = ignorezero, shell:bar
|
||||
layerrule = blur, shell:notifications
|
||||
layerrule = ignorealpha 0.1, shell:notifications
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
getdate() {
|
||||
date '+%Y-%m-%d_%H.%M.%S'
|
||||
}
|
||||
getaudiooutput() {
|
||||
pactl list sources | grep 'Name' | grep 'monitor' | cut -d ' ' -f2
|
||||
}
|
||||
getactivemonitor() {
|
||||
hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .name'
|
||||
}
|
||||
|
||||
xdgvideo="$(xdg-user-dir VIDEOS)"
|
||||
if [[ $xdgvideo = "$HOME" ]]; then
|
||||
unset xdgvideo
|
||||
fi
|
||||
mkdir -p "${xdgvideo:-$HOME/Videos}"
|
||||
cd "${xdgvideo:-$HOME/Videos}" || exit
|
||||
|
||||
if pgrep wf-recorder > /dev/null; then
|
||||
notify-send "Recording Stopped" "Stopped" -a 'Recorder' &
|
||||
pkill wf-recorder &
|
||||
else
|
||||
if [[ "$1" == "--fullscreen-sound" ]]; then
|
||||
notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown
|
||||
wf-recorder -o "$(getactivemonitor)" --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --audio="$(getaudiooutput)"
|
||||
elif [[ "$1" == "--fullscreen" ]]; then
|
||||
notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown
|
||||
wf-recorder -o "$(getactivemonitor)" --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t
|
||||
else
|
||||
if ! region="$(slurp 2>&1)"; then
|
||||
notify-send "Recording cancelled" "Selection was cancelled" -a 'Recorder' & disown
|
||||
exit 1
|
||||
fi
|
||||
notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown
|
||||
if [[ "$1" == "--sound" ]]; then
|
||||
wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region" --audio="$(getaudiooutput)"
|
||||
else
|
||||
wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
hyprctl dispatch "$1" $(((($(hyprctl activeworkspace -j | jq -r .id) - 1) / 10) * 10 + $2))
|
||||
@@ -1,54 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Controls Hyprland's cursor zoom_factor, clamped between 1.0 and 3.0
|
||||
|
||||
# Get current zoom level
|
||||
get_zoom() {
|
||||
hyprctl getoption -j cursor:zoom_factor | jq '.float'
|
||||
}
|
||||
|
||||
# Clamp a value between 1.0 and 3.0
|
||||
clamp() {
|
||||
local val="$1"
|
||||
awk "BEGIN {
|
||||
v = $val;
|
||||
if (v < 1.0) v = 1.0;
|
||||
if (v > 3.0) v = 3.0;
|
||||
print v;
|
||||
}"
|
||||
}
|
||||
|
||||
# Set zoom level
|
||||
set_zoom() {
|
||||
local value="$1"
|
||||
clamped=$(clamp "$value")
|
||||
hyprctl keyword cursor:zoom_factor "$clamped"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
reset)
|
||||
set_zoom 1.0
|
||||
;;
|
||||
increase)
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "Usage: $0 increase STEP"
|
||||
exit 1
|
||||
fi
|
||||
current=$(get_zoom)
|
||||
new=$(awk "BEGIN { print $current + $2 }")
|
||||
set_zoom "$new"
|
||||
;;
|
||||
decrease)
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "Usage: $0 decrease STEP"
|
||||
exit 1
|
||||
fi
|
||||
current=$(get_zoom)
|
||||
new=$(awk "BEGIN { print $current - $2 }")
|
||||
set_zoom "$new"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {reset|increase STEP|decrease STEP}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -1,24 +0,0 @@
|
||||
// vim: set ft=glsl:
|
||||
|
||||
precision highp float;
|
||||
varying highp vec2 v_texcoord;
|
||||
uniform highp sampler2D tex;
|
||||
|
||||
#define STRENGTH 0.0027
|
||||
|
||||
void main() {
|
||||
vec2 center = vec2(0.5, 0.5);
|
||||
vec2 offset = (v_texcoord - center) * STRENGTH;
|
||||
|
||||
float rSquared = dot(offset, offset);
|
||||
float distortion = 1.0 + 1.0 * rSquared;
|
||||
vec2 distortedOffset = offset * distortion;
|
||||
|
||||
vec2 redOffset = vec2(distortedOffset.x, distortedOffset.y);
|
||||
vec2 blueOffset = vec2(distortedOffset.x, distortedOffset.y);
|
||||
|
||||
vec4 redColor = texture2D(tex, v_texcoord + redOffset);
|
||||
vec4 blueColor = texture2D(tex, v_texcoord + blueOffset);
|
||||
|
||||
gl_FragColor = vec4(redColor.r, texture2D(tex, v_texcoord).g, blueColor.b, 1.0);
|
||||
}
|
||||
@@ -1,511 +0,0 @@
|
||||
#version 100
|
||||
precision highp float;
|
||||
varying highp vec2 v_texcoord;
|
||||
varying highp vec3 v_pos;
|
||||
uniform highp sampler2D tex;
|
||||
uniform lowp float time;
|
||||
|
||||
#define BORDER_COLOR vec4(vec3(0.0, 0.0, 0.0), 1.0) // black border
|
||||
#define BORDER_RADIUS 1.0 // larger vignette radius
|
||||
#define BORDER_SIZE 0.01 // small border size
|
||||
#define CHROMATIC_ABERRATION_STRENGTH 0.00
|
||||
#define DENOISE_INTENSITY 0.0001 //
|
||||
#define DISTORTION_AMOUNT 0.00 // moderate distortion amount
|
||||
#define HDR_BLOOM 0.75 // bloom intensity
|
||||
#define HDR_BRIGHTNESS 0.011 // brightness
|
||||
#define HDR_CONTRAST 0.011 // contrast
|
||||
#define HDR_SATURATION 1.0// saturation
|
||||
#define LENS_DISTORTION_AMOUNT 0.0
|
||||
#define NOISE_THRESHOLD 0.0001
|
||||
#define PHOSPHOR_BLUR_AMOUNT 0.77 // Amount of blur for phosphor glow
|
||||
#define PHOSPHOR_GLOW_AMOUNT 0.77 // Amount of phosphor glow
|
||||
#define SAMPLING_RADIUS 0.0001
|
||||
#define SCANLINE_FREQUENCY 540.0
|
||||
#define SCANLINE_THICKNESS 0.0507
|
||||
#define SCANLINE_TIME time * 471.24
|
||||
#define SHARPNESS 0.25
|
||||
#define SUPERSAMPLING_SAMPLES 16.0
|
||||
#define VIGNETTE_RADIUS 0.0 // larger vignette radius
|
||||
#define PI 3.14159265359
|
||||
#define TWOPI 6.28318530718
|
||||
|
||||
vec2 applyBarrelDistortion(vec2 coord, float amt) {
|
||||
vec2 p = coord.xy / vec2(1.0);
|
||||
vec2 v = p * 2.0 - vec2(1.0);
|
||||
float r = dot(v, v);
|
||||
float k = 1.0 + pow(r, 2.0) * pow(amt, 2.0);
|
||||
vec2 result = v * k;
|
||||
return vec2(0.5, 0.5) + 0.5 * result.xy;
|
||||
}
|
||||
|
||||
vec4 applyColorCorrection(vec4 color) {
|
||||
color.rgb *= vec3(1.0, 0.79, 0.89);
|
||||
return vec4(color.rgb, 1.0);
|
||||
}
|
||||
|
||||
vec4 applyBorder(vec2 tc, vec4 color, float borderSize, vec4 borderColor) {
|
||||
float dist_x = min(tc.x, 1.0 - tc.x);
|
||||
float dist_y = min(tc.y, 1.0 - tc.y);
|
||||
float dist = min(dist_x, dist_y) * -1.0;
|
||||
float border = smoothstep(borderSize, 0.0, dist);
|
||||
border += smoothstep(borderSize, 0.0, dist);
|
||||
return mix(color, borderColor, border);
|
||||
}
|
||||
|
||||
vec4 applyFakeHDR(vec4 color, float brightness, float contrast, float saturation, float bloom) {
|
||||
color.rgb = (color.rgb - vec3(0.5)) * exp2(brightness) + vec3(0.5);
|
||||
vec3 crtfactor = vec3(1.05, 0.92, 1.0);
|
||||
color.rgb = pow(color.rgb, crtfactor);
|
||||
// // NTSC
|
||||
// vec3 lumCoeff = vec3(0.2125, 0.7154, 0.0721);
|
||||
|
||||
// // BT.709
|
||||
// vec3 lumCoeff = vec3(0.299, 0.587, 0.114);
|
||||
|
||||
// BT.2020
|
||||
vec3 lumCoeff = vec3(0.2627, 0.6780, 0.0593);
|
||||
|
||||
// // Warm NTSC
|
||||
// vec3 lumCoeff = vec3(0.2125, 0.7010, 0.0865);
|
||||
|
||||
float luminance = dot(color.rgb, lumCoeff);
|
||||
luminance = pow(luminance, 2.2);
|
||||
color.rgb = mix(vec3(luminance), color.rgb, saturation);
|
||||
color.rgb = mix(color.rgb, vec3(1.0), pow(max(0.0, luminance - 1.0 + bloom), 4.0));
|
||||
return color;
|
||||
}
|
||||
|
||||
vec4 applyVignette(vec4 color) {
|
||||
vec2 center = vec2(0.5, 0.5); // center of screen
|
||||
float radius = VIGNETTE_RADIUS; // radius of vignette effect
|
||||
float softness = 1.0; // softness of vignette effect
|
||||
float intensity = 0.7; // intensity of vignette effect
|
||||
vec2 offset = v_texcoord - center; // offset from center of screen
|
||||
float distance = length(offset); // distance from center of screen
|
||||
float alpha = smoothstep(radius, radius - radius * softness, distance) * intensity; // calculate alpha value for vignette effect
|
||||
return mix(vec4(0.0, 0.0, 0.0, alpha), color, alpha); // mix black with color using calculated alpha value
|
||||
}
|
||||
|
||||
vec4 applyPhosphorGlow(vec2 tc, vec4 color, sampler2D tex) {
|
||||
// Calculate average color value of the texture
|
||||
vec4 texelColor = color;
|
||||
float averageColor = (texelColor.r + texelColor.g + texelColor.b) / 3.0;
|
||||
|
||||
// Determine brightness-dependent color factor
|
||||
float factor = mix(
|
||||
mix(0.09,
|
||||
mix(0.005, 0.0075, (averageColor - 0.1) / 0.1),
|
||||
step(0.01, averageColor)), 0.0005,
|
||||
step(0.02, averageColor));
|
||||
// Apply phosphor glow effect
|
||||
vec4 sum = vec4(0.0);
|
||||
vec4 pixels[9];
|
||||
pixels[0] = texture2D(tex, tc - vec2(0.001, 0.001));
|
||||
pixels[1] = texture2D(tex, tc - vec2(0.001, 0.0));
|
||||
pixels[2] = texture2D(tex, tc - vec2(0.001, -0.001));
|
||||
pixels[3] = texture2D(tex, tc - vec2(0.0, 0.001));
|
||||
pixels[4] = texture2D(tex, tc);
|
||||
pixels[5] = texture2D(tex, tc + vec2(0.001, 0.001));
|
||||
pixels[6] = texture2D(tex, tc + vec2(0.001, 0.0));
|
||||
pixels[7] = texture2D(tex, tc + vec2(0.001, -0.001));
|
||||
pixels[8] = texture2D(tex, tc + vec2(0.0, 0.001));
|
||||
|
||||
// Perform operations on input pixels in parallel
|
||||
sum = pixels[0]
|
||||
+ pixels[1]
|
||||
+ pixels[2]
|
||||
+ pixels[3]
|
||||
+ pixels[4]
|
||||
+ pixels[5]
|
||||
+ pixels[6]
|
||||
+ pixels[7]
|
||||
+ pixels[8];
|
||||
sum /= 9.0;
|
||||
sum += texture2D(tex, tc - vec2(0.01, 0.01)) * 0.001;
|
||||
sum += texture2D(tex, tc - vec2(0.0, 0.01)) * 0.001;
|
||||
sum += texture2D(tex, tc - vec2(-0.01, 0.01)) * 0.001;
|
||||
sum += texture2D(tex, tc - vec2(0.01, 0.0)) * 0.001;
|
||||
sum += color * PHOSPHOR_BLUR_AMOUNT;
|
||||
sum += texture2D(tex, tc - vec2(-0.01, 0.0)) * 0.001;
|
||||
sum += texture2D(tex, tc - vec2(0.01, -0.01)) * 0.001;
|
||||
sum += texture2D(tex, tc - vec2(0.0, -0.01)) * 0.001;
|
||||
sum += texture2D(tex, tc - vec2(-0.01, -0.01)) * 0.001;
|
||||
sum *= PHOSPHOR_GLOW_AMOUNT;
|
||||
|
||||
// Initialize sum_sum_factor to zero
|
||||
vec4 sum_sum_factor = vec4(0.0);
|
||||
// Compute sum_j for i = -1
|
||||
vec4 sum_j = vec4(0.0);
|
||||
sum_j += texture2D(tex, tc + vec2(-1, -1) * 0.01);
|
||||
sum_j += texture2D(tex, tc + vec2(0, -1) * 0.01);
|
||||
sum_j += texture2D(tex, tc + vec2(1, -1) * 0.01);
|
||||
sum_j += texture2D(tex, tc + vec2(-1, 0) * 0.01);
|
||||
sum_j += texture2D(tex, tc + vec2(0, 0) * 0.01);
|
||||
sum_j += texture2D(tex, tc + vec2(1, 0) * 0.01);
|
||||
sum_j += texture2D(tex, tc + vec2(-1, 1) * 0.01);
|
||||
sum_j += texture2D(tex, tc + vec2(0, 1) * 0.01);
|
||||
sum_j += texture2D(tex, tc + vec2(1, 1) * 0.01);
|
||||
sum_sum_factor += sum_j * vec4(0.011);
|
||||
|
||||
// Compute sum_j for i = 0
|
||||
sum_j = vec4(0.0);
|
||||
sum_j += texture2D(tex, tc + vec2(-1, 0) * 0.01);
|
||||
sum_j += texture2D(tex, tc + vec2(0, 0) * 0.01);
|
||||
sum_j += texture2D(tex, tc + vec2(1, 0) * 0.01);
|
||||
sum_j += texture2D(tex, tc + vec2(-1, 1) * 0.01);
|
||||
sum_j += texture2D(tex, tc + vec2(0, 1) * 0.01);
|
||||
sum_j += texture2D(tex, tc + vec2(1, 1) * 0.01);
|
||||
sum_sum_factor += sum_j * vec4(0.011);
|
||||
|
||||
// Compute sum_j for i = 1
|
||||
sum_j = vec4(0.0);
|
||||
sum_j += texture2D(tex, tc + vec2(-1, 0) * 0.01);
|
||||
sum_j += texture2D(tex, tc + vec2(0, 1) * 0.01);
|
||||
sum_j += texture2D(tex, tc + vec2(1, 0) * 0.01);
|
||||
sum_j += texture2D(tex, tc + vec2(-1, 1) * 0.01);
|
||||
sum_j += texture2D(tex, tc + vec2(0, 1) * 0.01);
|
||||
sum_j += texture2D(tex, tc + vec2(1, 1) * 0.01);
|
||||
sum_sum_factor += sum_j * vec4(0.011);
|
||||
color += mix(sum_sum_factor * sum_sum_factor * vec4(factor), sum, 0.5);
|
||||
return color;
|
||||
}
|
||||
|
||||
vec4 applyAdaptiveSharpen(vec2 tc, vec4 color, sampler2D tex) {
|
||||
vec4 color_tl = texture2D(tex, tc + vec2(-1.0, -1.0) * 0.5 / 2160.0);
|
||||
vec4 color_tr = texture2D(tex, tc + vec2(1.0, -1.0) * 0.5 / 2160.0);
|
||||
vec4 color_bl = texture2D(tex, tc + vec2(-1.0, 1.0) * 0.5 / 2160.0);
|
||||
vec4 color_br = texture2D(tex, tc + vec2(1.0, 1.0) * 0.5 / 2160.0);
|
||||
float sharpness = SHARPNESS;
|
||||
vec3 color_no_alpha = color.rgb;
|
||||
vec3 color_tl_no_alpha = color_tl.rgb;
|
||||
vec3 color_tr_no_alpha = color_tr.rgb;
|
||||
vec3 color_bl_no_alpha = color_bl.rgb;
|
||||
vec3 color_br_no_alpha = color_br.rgb;
|
||||
float delta = (dot(color_no_alpha, vec3(0.333333)) + dot(color_tl_no_alpha, vec3(0.333333)) + dot(color_tr_no_alpha, vec3(0.333333)) + dot(color_bl_no_alpha, vec3(0.333333)) + dot(color_br_no_alpha, vec3(0.333333))) * 0.2 - dot(color_no_alpha, vec3(0.333333));
|
||||
vec3 sharp_color_no_alpha = color_no_alpha + min(vec3(0.0), vec3(delta * sharpness));
|
||||
vec4 sharp_color = vec4(sharp_color_no_alpha, color.a);
|
||||
return sharp_color;
|
||||
}
|
||||
|
||||
vec4 applyScanlines(vec2 tc, vec4 color) {
|
||||
float scanline = (cos(tc.y * SCANLINE_FREQUENCY + SCANLINE_TIME) *
|
||||
sin(tc.y * SCANLINE_FREQUENCY + SCANLINE_TIME)) * SCANLINE_THICKNESS;
|
||||
float alpha = clamp(1.0 - abs(scanline), 0.0, 1.0);
|
||||
return vec4(color.rgb * alpha, color.a);
|
||||
}
|
||||
|
||||
vec4 applyChromaticAberration(vec2 uv, vec4 color) {
|
||||
vec2 center = vec2(0.5, 0.5); // center of the screen
|
||||
vec2 offset = (uv - center) * CHROMATIC_ABERRATION_STRENGTH; // calculate the offset from the center
|
||||
|
||||
// apply lens distortion
|
||||
float rSquared = dot(offset, offset);
|
||||
float distortion = 1.0 + LENS_DISTORTION_AMOUNT * rSquared;
|
||||
vec2 distortedOffset = offset * distortion;
|
||||
|
||||
// apply chromatic aberration
|
||||
vec2 redOffset = vec2(distortedOffset.x * 1.00, distortedOffset.y * 1.00);
|
||||
vec2 blueOffset = vec2(distortedOffset.x * 1.00, distortedOffset.y * 1.00);
|
||||
|
||||
vec4 redColor = texture2D(tex, uv + redOffset);
|
||||
vec4 blueColor = texture2D(tex, uv + blueOffset);
|
||||
|
||||
vec4 result = vec4(redColor.r, color.g, blueColor.b, color.a);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
vec4 reduceGlare(vec4 color) {
|
||||
// Calculate the intensity of the color by taking the average of the RGB components
|
||||
float intensity = (color.r + color.g + color.b) / 3.0;
|
||||
// Set the maximum intensity that can be considered for glare
|
||||
float maxIntensity = 0.98;
|
||||
// Use smoothstep to create a smooth transition from no glare to full glare
|
||||
// based on the intensity of the color and the maximum intensity
|
||||
float glareIntensity = smoothstep(maxIntensity - 0.02, maxIntensity, intensity);
|
||||
// Set the amount of glare to apply to the color
|
||||
float glareAmount = 0.02;
|
||||
// Mix the original color with the reduced color that has glare applied to it
|
||||
vec3 reducedColor = mix(color.rgb, vec3(glareIntensity), glareAmount);
|
||||
// Return the reduced color with the original alpha value
|
||||
return vec4(reducedColor, color.a);
|
||||
}
|
||||
|
||||
// Apply a fake HDR effect to the input color.
|
||||
// Parameters:
|
||||
// - inputColor: the color to apply the effect to.
|
||||
// - brightness: the brightness of the image. Should be a value between 0 and 1.
|
||||
// - contrast: the contrast of the image. Should be a value between 0 and 1.
|
||||
// - saturation: the saturation of the image. Should be a value between 0 and 2.
|
||||
// - bloom: the intensity of the bloom effect. Should be a value between 0 and 1.
|
||||
vec4 applyFakeHDREffect(vec4 inputColor, float brightness, float contrast, float saturation, float bloom) {
|
||||
const float minBrightness = 0.0;
|
||||
const float maxBrightness = 1.0;
|
||||
const float minContrast = 0.0;
|
||||
const float maxContrast = 1.0;
|
||||
const float minSaturation = 0.0;
|
||||
const float maxSaturation = 2.0;
|
||||
const float minBloom = 0.0;
|
||||
const float maxBloom = 1.0;
|
||||
|
||||
// Check input parameters for validity
|
||||
if (brightness < minBrightness || brightness > maxBrightness) {
|
||||
return vec4(0.0, 0.0, 0.0, 1.0); // Return black with alpha of 1.0 to indicate error
|
||||
}
|
||||
if (contrast < minContrast || contrast > maxContrast) {
|
||||
return vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
if (saturation < minSaturation || saturation > maxSaturation) {
|
||||
return vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
if (bloom < minBloom || bloom > maxBloom) {
|
||||
return vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
// Apply brightness and contrast
|
||||
vec3 color = inputColor.rgb;
|
||||
color = (color - vec3(0.5)) * exp2(brightness * 10.0) + vec3(0.5);
|
||||
color = mix(vec3(0.5), color, pow(contrast * 4.0 + 1.0, 2.0));
|
||||
|
||||
// // NTSC
|
||||
// vec3 lumCoeff = vec3(0.2125, 0.7154, 0.0721);
|
||||
|
||||
// // BT.709
|
||||
// vec3 lumCoeff = vec3(0.299, 0.587, 0.114);
|
||||
|
||||
// // BT.2020
|
||||
// vec3 lumCoeff = vec3(0.2627, 0.6780, 0.0593);
|
||||
|
||||
// Warm NTSC
|
||||
vec3 lumCoeff = vec3(0.2125, 0.7010, 0.0865);
|
||||
|
||||
// Apply saturation
|
||||
float luminance = dot(color, lumCoeff);
|
||||
vec3 grey = vec3(luminance);
|
||||
color = mix(grey, color, saturation);
|
||||
|
||||
// Apply bloom effect
|
||||
float threshold = 1.0 - bloom;
|
||||
vec3 bloomColor = max(color - threshold, vec3(0.0));
|
||||
bloomColor = pow(bloomColor, vec3(2.0));
|
||||
bloomColor = mix(vec3(0.0), bloomColor, pow(min(luminance, threshold), 4.0));
|
||||
color += bloomColor;
|
||||
|
||||
return vec4(color, inputColor.a);
|
||||
}
|
||||
|
||||
vec4 bilateralFilter(sampler2D tex, vec2 uv, vec4 color, float sampleRadius, float noiseThreshold, float intensity) {
|
||||
vec4 filteredColor = vec4(0.0);
|
||||
float totalWeight = 0.0;
|
||||
|
||||
// Top-left pixel
|
||||
vec4 sample = texture2D(tex, uv + vec2(-1.0, -1.0));
|
||||
float dist = length(vec2(-1.0, -1.0));
|
||||
float colorDist = length(sample - color);
|
||||
float weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius));
|
||||
filteredColor += sample * weight;
|
||||
totalWeight += weight;
|
||||
|
||||
// Top pixel
|
||||
sample = texture2D(tex, uv + vec2(0.0, -1.0));
|
||||
dist = length(vec2(0.0, -1.0));
|
||||
colorDist = length(sample - color);
|
||||
weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius));
|
||||
filteredColor += sample * weight;
|
||||
totalWeight += weight;
|
||||
|
||||
// Top-right pixel
|
||||
sample = texture2D(tex, uv + vec2(1.0, -1.0));
|
||||
dist = length(vec2(1.0, -1.0));
|
||||
colorDist = length(sample - color);
|
||||
weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius));
|
||||
filteredColor += sample * weight;
|
||||
totalWeight += weight;
|
||||
|
||||
// Left pixel
|
||||
sample = texture2D(tex, uv + vec2(-1.0, 0.0));
|
||||
dist = length(vec2(-1.0, 0.0));
|
||||
colorDist = length(sample - color);
|
||||
weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius));
|
||||
filteredColor += sample * weight;
|
||||
totalWeight += weight;
|
||||
|
||||
// Center pixel
|
||||
sample = texture2D(tex, uv);
|
||||
dist = 0.0;
|
||||
colorDist = length(sample - color);
|
||||
weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius));
|
||||
filteredColor += sample * weight;
|
||||
totalWeight += weight;
|
||||
|
||||
// Right pixel
|
||||
sample = texture2D(tex, uv + vec2(1.0, 0.0));
|
||||
dist = length(vec2(1.0, 0.0));
|
||||
colorDist = length(sample - color);
|
||||
weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius));
|
||||
filteredColor += sample * weight;
|
||||
totalWeight += weight;
|
||||
|
||||
// Bottom-left pixel
|
||||
sample = texture2D(tex, uv + vec2(-1.0, 1.0));
|
||||
dist = length(vec2(-1.0, 1.0));
|
||||
colorDist = length(sample - color);
|
||||
weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius));
|
||||
filteredColor += sample * weight;
|
||||
totalWeight += weight;
|
||||
|
||||
// Bottom pixel
|
||||
sample = texture2D(tex, uv + vec2(0.0, sampleRadius));
|
||||
dist = length(vec2(0.0, sampleRadius));
|
||||
colorDist = length(sample - color);
|
||||
weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius));
|
||||
filteredColor += sample * weight;
|
||||
totalWeight += weight;
|
||||
|
||||
filteredColor /= totalWeight;
|
||||
return mix(color, filteredColor, step(noiseThreshold, length(filteredColor - color)));
|
||||
}
|
||||
|
||||
vec4 supersample(sampler2D tex, vec2 uv, float sampleRadius, float noiseThreshold, float intensity) {
|
||||
float radiusSq = sampleRadius * sampleRadius;
|
||||
vec2 poissonDisk;
|
||||
vec4 color = vec4(0.0);
|
||||
|
||||
float r1_0 = sqrt(0.0 / 16.0);
|
||||
float r2_0 = fract(1.0 / 3.0);
|
||||
float theta_0 = TWOPI * r2_0;
|
||||
poissonDisk = vec2(r1_0 * cos(theta_0), r1_0 * sin(theta_0));
|
||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
||||
|
||||
float r1_1 = sqrt(1.0 / 16.0);
|
||||
float r2_1 = fract(2.0 / 3.0);
|
||||
float theta_1 = TWOPI * r2_1;
|
||||
poissonDisk = vec2(r1_1 * cos(theta_1), r1_1 * sin(theta_1));
|
||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
||||
|
||||
float r1_2 = sqrt(2.0 / 16.0);
|
||||
float r2_2 = fract(3.0 / 3.0);
|
||||
float theta_2 = TWOPI * r2_2;
|
||||
poissonDisk = vec2(r1_2 * cos(theta_2), r1_2 * sin(theta_2));
|
||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
||||
|
||||
float r1_3 = sqrt(3.0 / 16.0);
|
||||
float r2_3 = fract(4.0 / 3.0);
|
||||
float theta_3 = TWOPI * r2_3;
|
||||
poissonDisk = vec2(r1_3 * cos(theta_3), r1_3 * sin(theta_3));
|
||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
||||
|
||||
float r1_4 = sqrt(4.0 / 16.0);
|
||||
float r2_4 = fract(5.0 / 3.0);
|
||||
float theta_4 = TWOPI * r2_4;
|
||||
poissonDisk = vec2(r1_4 * cos(theta_4), r1_4 * sin(theta_4));
|
||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
||||
|
||||
float r1_5 = sqrt(5.0 / 16.0);
|
||||
float r2_5 = fract(6.0 / 3.0);
|
||||
float theta_5 = TWOPI * r2_5;
|
||||
poissonDisk = vec2(r1_5 * cos(theta_5), r1_5 * sin(theta_5));
|
||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
||||
|
||||
float r1_6 = sqrt(6.0 / 16.0);
|
||||
float r2_6 = fract(7.0 / 3.0);
|
||||
float theta_6 = TWOPI * r2_6;
|
||||
poissonDisk = vec2(r1_6 * cos(theta_6), r1_6 * sin(theta_6));
|
||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
||||
|
||||
float r1_7 = sqrt(7.0 / 16.0);
|
||||
float r2_7 = fract(8.0 / 3.0);
|
||||
float theta_7 = TWOPI * r2_7;
|
||||
poissonDisk = vec2(r1_7 * cos(theta_7), r1_7 * sin(theta_7));
|
||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
||||
|
||||
float r1_8 = sqrt(8.0 / 16.0);
|
||||
float r2_8 = fract(9.0 / 3.0);
|
||||
float theta_8 = TWOPI * r2_8;
|
||||
poissonDisk = vec2(r1_8 * cos(theta_8), r1_8 * sin(theta_8));
|
||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
||||
|
||||
float r1_9 = sqrt(9.0 / 16.0);
|
||||
float r2_9 = fract(10.0 / 3.0);
|
||||
float theta_9 = TWOPI * r2_9;
|
||||
poissonDisk = vec2(r1_9 * cos(theta_9), r1_9 * sin(theta_9));
|
||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
||||
|
||||
float r1_10 = sqrt(10.0 / 16.0);
|
||||
float r2_10 = fract(11.0 / 3.0);
|
||||
float theta_10 = TWOPI * r2_10;
|
||||
poissonDisk = vec2(r1_10 * cos(theta_10), r1_10 * sin(theta_10));
|
||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
||||
|
||||
float r1_11 = sqrt(11.0 / 16.0);
|
||||
float r2_11 = fract(12.0 / 3.0);
|
||||
float theta_11 = TWOPI * r2_11;
|
||||
poissonDisk = vec2(r1_11 * cos(theta_11), r1_11 * sin(theta_11));
|
||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
||||
|
||||
float r1_12 = sqrt(12.0 / 16.0);
|
||||
float r2_12 = fract(13.0 / 3.0);
|
||||
float theta_12 = TWOPI * r2_12;
|
||||
poissonDisk = vec2(r1_12 * cos(theta_12), r1_12 * sin(theta_12));
|
||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
||||
|
||||
float r1_13 = sqrt(13.0 / 16.0);
|
||||
float r2_13 = fract(14.0 / 3.0);
|
||||
float theta_13 = TWOPI * r2_13;
|
||||
poissonDisk = vec2(r1_13 * cos(theta_13), r1_13 * sin(theta_13));
|
||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
||||
|
||||
float r1_14 = sqrt(14.0 / 16.0);
|
||||
float r2_14 = fract(15.0 / 3.0);
|
||||
float theta_14 = TWOPI * r2_14;
|
||||
poissonDisk = vec2(r1_14 * cos(theta_14), r1_14 * sin(theta_14));
|
||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
||||
|
||||
float r1_15 = sqrt(15.0 / 16.0);
|
||||
float r2_15 = fract(16.0 / 3.0);
|
||||
float theta_15 = TWOPI * r2_15;
|
||||
poissonDisk = vec2(r1_15 * cos(theta_15), r1_15 * sin(theta_15));
|
||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
||||
|
||||
return bilateralFilter(tex, uv, color, sampleRadius, noiseThreshold, intensity);
|
||||
}
|
||||
void main() {
|
||||
vec2 tc_no_dist = v_texcoord;
|
||||
|
||||
vec2 tc = applyBarrelDistortion(tc_no_dist, DISTORTION_AMOUNT);
|
||||
|
||||
// [-1, 1]
|
||||
vec2 tc_no_dist_symmetric = tc_no_dist * 2.0 - 1.0;
|
||||
|
||||
// [0,1]
|
||||
vec2 tc_no_dist_normalized = (tc_no_dist_symmetric + 1.0) / 2.0;
|
||||
|
||||
// vec4 color = texture2D(tex, tc);
|
||||
vec4 color = supersample(tex, tc, SAMPLING_RADIUS, NOISE_THRESHOLD, DENOISE_INTENSITY);
|
||||
|
||||
color = applyAdaptiveSharpen(tc, color, tex);
|
||||
|
||||
color = applyPhosphorGlow(tc, color, tex);
|
||||
|
||||
color = reduceGlare(color);
|
||||
|
||||
color = mix(applyFakeHDREffect(color, HDR_BRIGHTNESS, HDR_CONTRAST, HDR_SATURATION, HDR_BLOOM), color, 0.5);
|
||||
|
||||
color = applyColorCorrection(color);
|
||||
|
||||
color /= SUPERSAMPLING_SAMPLES;
|
||||
|
||||
color = mix(applyChromaticAberration(tc, color), color, 0.25);
|
||||
|
||||
color = mix(color, applyVignette(color), 0.37);
|
||||
|
||||
color = applyBorder(tc_no_dist_normalized, color, 1.0 - BORDER_SIZE * BORDER_RADIUS, BORDER_COLOR);
|
||||
|
||||
color = mix(applyBorder(tc, color, BORDER_SIZE, BORDER_COLOR), color, 0.05);
|
||||
|
||||
color = applyScanlines(tc, color);
|
||||
|
||||
gl_FragColor = color;
|
||||
gl_FragColor.a = 1.0;
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
|
||||
precision highp float;
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D tex;
|
||||
uniform float time;
|
||||
|
||||
void warpco(inout vec2 tc) {
|
||||
tc -= 0.5;
|
||||
tc *= length(tc) * 2.0;
|
||||
tc += 0.5;
|
||||
}
|
||||
|
||||
float rand1d(float seed) {
|
||||
return sin(seed*1454.0);
|
||||
}
|
||||
|
||||
float rand2d(vec2 co)
|
||||
{
|
||||
return fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453);
|
||||
}
|
||||
|
||||
vec3 rgb(in vec2 tc, float freq, float amp, inout vec4 centre) {
|
||||
vec2 off = vec2(1.0/800.0, 0.0) * sin(tc.t * freq + time) * amp;
|
||||
vec2 off2 = vec2(1.0/800.0, 0.0) * sin(tc.t * freq - time * 1.5) * amp;
|
||||
centre = texture2D(tex, tc);
|
||||
return vec3(texture2D(tex, tc-off).r, centre.g, texture2D(tex, tc+off2).b);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// vec2 px = 1.0 / textureSize(tex, 0).st;
|
||||
vec2 tc = v_texcoord;
|
||||
warpco(tc);
|
||||
tc = mix(v_texcoord, tc, sin(time * 2.0)*0.07);
|
||||
tc.x += rand2d(floor(tc * 20.0 + floor(time * 2.5))) * 0.01;
|
||||
tc.x += rand1d(floor(tc.x * 40.0)) * 0.005 * rand1d(time * 0.001);
|
||||
tc.y += sin(tc.x + time) * 0.02;
|
||||
vec4 centre;
|
||||
vec3 bent = rgb(tc, 100.0, 5.0, centre);
|
||||
vec3 col = mix(centre.rgb, bent, sin(time));
|
||||
gl_FragColor = vec4(col, centre.a);
|
||||
// gl_FragColor = vec4(texture2D(tex, v_texcoord));
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
// vim: set ft=glsl:
|
||||
// blue light filter shader
|
||||
// values from https://reshade.me/forum/shader-discussion/3673-blue-light-filter-similar-to-f-lux
|
||||
|
||||
precision mediump float;
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D tex;
|
||||
|
||||
void main() {
|
||||
|
||||
vec4 pixColor = texture2D(tex, v_texcoord);
|
||||
|
||||
// red
|
||||
pixColor[0] *= 0.7;
|
||||
// green
|
||||
pixColor[1] *= 0.6;
|
||||
// blue
|
||||
pixColor[2] *= 0.5;
|
||||
|
||||
gl_FragColor = pixColor;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// vim: set ft=glsl:
|
||||
// blue light filter shader
|
||||
// values from https://reshade.me/forum/shader-discussion/3673-blue-light-filter-similar-to-f-lux
|
||||
|
||||
precision mediump float;
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D tex;
|
||||
|
||||
void main() {
|
||||
vec4 pixColor = texture2D(tex, v_texcoord);
|
||||
pixColor.rgb = 1.0 - pixColor.rgb;
|
||||
gl_FragColor = pixColor;
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
// -*- mode:c -*-
|
||||
precision lowp float;
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D tex;
|
||||
|
||||
float distanceSquared(vec3 pixColor, vec3 solarizedColor) {
|
||||
vec3 distanceVector = pixColor - solarizedColor;
|
||||
return dot(distanceVector, distanceVector);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 solarized[16];
|
||||
solarized[0] = vec3(0.,0.169,0.212);
|
||||
solarized[1] = vec3(0.027,0.212,0.259);
|
||||
solarized[2] = vec3(0.345,0.431,0.459);
|
||||
solarized[3] = vec3(0.396,0.482,0.514);
|
||||
solarized[4] = vec3(0.514,0.58,0.588);
|
||||
solarized[5] = vec3(0.576,0.631,0.631);
|
||||
solarized[6] = vec3(0.933,0.91,0.835);
|
||||
solarized[7] = vec3(0.992,0.965,0.89);
|
||||
solarized[8] = vec3(0.71,0.537,0.);
|
||||
solarized[9] = vec3(0.796,0.294,0.086);
|
||||
solarized[10] = vec3(0.863,0.196,0.184);
|
||||
solarized[11] = vec3(0.827,0.212,0.51);
|
||||
solarized[12] = vec3(0.424,0.443,0.769);
|
||||
solarized[13] = vec3(0.149,0.545,0.824);
|
||||
solarized[14] = vec3(0.165,0.631,0.596);
|
||||
solarized[15] = vec3(0.522,0.6,0.);
|
||||
|
||||
vec3 pixColor = vec3(texture2D(tex, v_texcoord));
|
||||
int closest = 0;
|
||||
float closestDistanceSquared = distanceSquared(pixColor, solarized[0]);
|
||||
for (int i = 1; i < 15; i++) {
|
||||
float newDistanceSquared = distanceSquared(pixColor, solarized[i]);
|
||||
if (newDistanceSquared < closestDistanceSquared) {
|
||||
closest = i;
|
||||
closestDistanceSquared = newDistanceSquared;
|
||||
}
|
||||
}
|
||||
gl_FragColor = vec4(solarized[closest], 1.);
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* GTK Colors
|
||||
* Generated with Matugen
|
||||
*/
|
||||
|
||||
@define-color accent_color {{colors.primary.default.hex}};
|
||||
@define-color accent_fg_color {{colors.on_primary.default.hex}};
|
||||
@define-color accent_bg_color {{colors.primary.default.hex}};
|
||||
@define-color window_bg_color {{colors.background.default.hex}};
|
||||
@define-color window_fg_color {{colors.on_background.default.hex}};
|
||||
@define-color headerbar_bg_color {{colors.surface_dim.default.hex}};
|
||||
@define-color headerbar_fg_color {{colors.on_surface.default.hex}};
|
||||
@define-color popover_bg_color {{colors.surface_dim.default.hex}};
|
||||
@define-color popover_fg_color {{colors.on_surface.default.hex}};
|
||||
@define-color view_bg_color {{colors.surface.default.hex}};
|
||||
@define-color view_fg_color {{colors.on_surface.default.hex}};
|
||||
@define-color card_bg_color {{colors.surface.default.hex}};
|
||||
@define-color card_fg_color {{colors.on_surface.default.hex}};
|
||||
@define-color sidebar_bg_color @window_bg_color;
|
||||
@define-color sidebar_fg_color @window_fg_color;
|
||||
@define-color sidebar_border_color @window_bg_color;
|
||||
@define-color sidebar_backdrop_color @window_bg_color;
|
||||
@@ -1,32 +0,0 @@
|
||||
general {
|
||||
col.active_border = rgba({{colors.outline.default.hex_stripped}}AA)
|
||||
col.inactive_border = rgba({{colors.outline_variant.default.hex_stripped}}AA)
|
||||
}
|
||||
|
||||
misc {
|
||||
background_color = rgba({{colors.surface.dark.hex_stripped}}FF)
|
||||
}
|
||||
|
||||
plugin {
|
||||
hyprbars {
|
||||
# Honestly idk if it works like css, but well, why not
|
||||
bar_text_font = Rubik, Geist, AR One Sans, Reddit Sans, Inter, Roboto, Ubuntu, Noto Sans, sans-serif
|
||||
bar_height = 30
|
||||
bar_padding = 10
|
||||
bar_button_padding = 5
|
||||
bar_precedence_over_border = true
|
||||
bar_part_of_window = true
|
||||
|
||||
bar_color = rgba({{colors.background.default.hex_stripped}}FF)
|
||||
col.text = rgba({{colors.on_background.default.hex_stripped}}FF)
|
||||
|
||||
|
||||
# example buttons (R -> L)
|
||||
# hyprbars-button = color, size, on-click
|
||||
hyprbars-button = rgb({{colors.on_background.default.hex_stripped}}), 13, , hyprctl dispatch killactive
|
||||
hyprbars-button = rgb({{colors.on_background.default.hex_stripped}}), 13, , hyprctl dispatch fullscreen 1
|
||||
hyprbars-button = rgb({{colors.on_background.default.hex_stripped}}), 13, , hyprctl dispatch movetoworkspacesilent special
|
||||
}
|
||||
}
|
||||
|
||||
windowrulev2 = bordercolor rgba({{colors.primary.default.hex_stripped}}AA) rgba({{colors.primary.default.hex_stripped}}77),pinned:1
|
||||
@@ -1,93 +0,0 @@
|
||||
$text_color = rgba({{colors.primary_fixed.default.hex_stripped}}FF)
|
||||
$entry_background_color = rgba({{colors.on_primary_fixed.default.hex_stripped}}11)
|
||||
$entry_border_color = rgba({{colors.outline.default.hex_stripped}}55)
|
||||
$entry_color = rgba({{colors.primary_fixed.default.hex_stripped}}FF)
|
||||
$font_family = Rubik
|
||||
$font_family_clock = Space Grotesk DemiBold
|
||||
$font_material_symbols = Material Symbols Rounded
|
||||
|
||||
background {
|
||||
color = rgba(181818FF)
|
||||
|
||||
path = {{image}}
|
||||
blur_size = 15
|
||||
blur_passes = 4
|
||||
brightness = 0.33
|
||||
}
|
||||
input-field {
|
||||
monitor =
|
||||
size = 250, 50
|
||||
outline_thickness = 2
|
||||
dots_size = 0.1
|
||||
dots_spacing = 0.3
|
||||
outer_color = $entry_border_color
|
||||
inner_color = $entry_background_color
|
||||
font_color = $entry_color
|
||||
fade_on_empty = true
|
||||
|
||||
position = 0, 20
|
||||
halign = center
|
||||
valign = center
|
||||
}
|
||||
|
||||
label { # Caps Lock Warning
|
||||
monitor =
|
||||
text = cmd[update:250] ${XDG_CONFIG_HOME:-$HOME/.config}/hypr/hyprlock/check-capslock.sh
|
||||
color = $text_color
|
||||
font_size = 13
|
||||
font_family = $font_family
|
||||
position = 0, -25
|
||||
halign = center
|
||||
valign = center
|
||||
}
|
||||
|
||||
|
||||
label { # Clock
|
||||
monitor =
|
||||
text = $TIME
|
||||
color = $text_color
|
||||
font_size = 65
|
||||
font_family = $font_family_clock
|
||||
|
||||
position = 0, 300
|
||||
halign = center
|
||||
valign = center
|
||||
}
|
||||
label { # Date
|
||||
monitor =
|
||||
text = cmd[update:5000] date +"%A, %B %d"
|
||||
color = $text_color
|
||||
font_size = 17
|
||||
font_family = $font_family_clock
|
||||
|
||||
position = 0, 240
|
||||
halign = center
|
||||
valign = center
|
||||
}
|
||||
|
||||
label { # User
|
||||
monitor =
|
||||
text = $USER
|
||||
color = $text_color
|
||||
outline_thickness = 2
|
||||
dots_size = 0.2 # Scale of input-field height, 0.2 - 0.8
|
||||
dots_spacing = 0.2 # Scale of dots' absolute size, 0.0 - 1.0
|
||||
dots_center = true
|
||||
font_size = 20
|
||||
font_family = $font_family
|
||||
position = 0, 50
|
||||
halign = center
|
||||
valign = bottom
|
||||
}
|
||||
|
||||
label { # Status
|
||||
monitor =
|
||||
text = cmd[update:5000] ${XDG_CONFIG_HOME:-$HOME/.config}/hypr/hyprlock/status.sh
|
||||
color = $text_color
|
||||
font_size = 14
|
||||
font_family = $font_family
|
||||
|
||||
position = 30, -30
|
||||
halign = left
|
||||
valign = top
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
[Appearance]
|
||||
color_scheme_path=/usr/share/qt5ct/colors/darker.conf
|
||||
custom_palette=true
|
||||
icon_theme=OneUI-dark
|
||||
standard_dialogs=default
|
||||
style=kvantum-dark
|
||||
|
||||
[Fonts]
|
||||
fixed="JetBrainsMono Nerd Font,11,-1,5,50,0,0,0,0,0,Regular"
|
||||
general="Rubik,11,-1,5,50,0,0,0,0,0"
|
||||
|
||||
[Interface]
|
||||
activate_item_on_single_click=1
|
||||
buttonbox_layout=0
|
||||
cursor_flash_time=1000
|
||||
dialog_buttons_have_icons=1
|
||||
double_click_interval=400
|
||||
gui_effects=General
|
||||
keyboard_scheme=2
|
||||
menus_have_icons=true
|
||||
show_shortcuts_in_context_menus=true
|
||||
stylesheets=@Invalid()
|
||||
toolbutton_style=4
|
||||
underline_shortcut=1
|
||||
wheel_scroll_lines=3
|
||||
|
||||
[SettingsWindow]
|
||||
geometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\0\0\0\0\0\0\0\0\x4\x98\0\0\x3\x99\0\0\0\0\0\0\0\0\0\0\x2\xde\0\0\x3\x1\0\0\0\0\x2\0\0\0\a\x80\0\0\0\0\0\0\0\0\0\0\x4\x98\0\0\x3\x99)
|
||||
|
||||
[Troubleshooting]
|
||||
force_raster_widgets=1
|
||||
ignored_applications=@Invalid()
|
||||
@@ -1,32 +0,0 @@
|
||||
[Appearance]
|
||||
color_scheme_path=~/.config/qt6ct/style-colors.conf
|
||||
custom_palette=true
|
||||
icon_theme=OneUI
|
||||
standard_dialogs=default
|
||||
style=kvantum
|
||||
|
||||
[Fonts]
|
||||
fixed="JetBrainsMono Nerd Font,11,-1,5,400,0,0,0,0,0,0,0,0,0,0,1,Regular"
|
||||
general="Rubik,11,-1,5,400,0,0,0,0,0,0,0,0,0,0,1,Regular"
|
||||
|
||||
[Interface]
|
||||
activate_item_on_single_click=1
|
||||
buttonbox_layout=0
|
||||
cursor_flash_time=1000
|
||||
dialog_buttons_have_icons=1
|
||||
double_click_interval=400
|
||||
gui_effects=@Invalid()
|
||||
keyboard_scheme=2
|
||||
menus_have_icons=true
|
||||
show_shortcuts_in_context_menus=true
|
||||
stylesheets=@Invalid()
|
||||
toolbutton_style=4
|
||||
underline_shortcut=1
|
||||
wheel_scroll_lines=3
|
||||
|
||||
[SettingsWindow]
|
||||
geometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\0\0\0\0\0\0\0\0\as\0\0\x4\x3\0\0\0\0\0\0\0\0\0\0\as\0\0\x4\x3\0\0\0\0\x2\0\0\0\a\x80\0\0\0\0\0\0\0\0\0\0\as\0\0\x4\x3)
|
||||
|
||||
[Troubleshooting]
|
||||
force_raster_widgets=1
|
||||
ignored_applications=@Invalid()
|
||||
@@ -1,68 +0,0 @@
|
||||
import qs.modules.common
|
||||
import qs
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Io
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
property bool barOpen: true
|
||||
property bool sidebarLeftOpen: false
|
||||
property bool sidebarRightOpen: false
|
||||
property bool overviewOpen: false
|
||||
property bool workspaceShowNumbers: false
|
||||
property bool superReleaseMightTrigger: true
|
||||
property bool screenLocked: false
|
||||
property bool screenLockContainsCharacters: false
|
||||
|
||||
property real screenZoom: 1
|
||||
onScreenZoomChanged: {
|
||||
Quickshell.execDetached(["hyprctl", "keyword", "cursor:zoom_factor", root.screenZoom.toString()]);
|
||||
}
|
||||
Behavior on screenZoom {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
// When user is not reluctant while pressing super, they probably don't need to see workspace numbers
|
||||
onSuperReleaseMightTriggerChanged: {
|
||||
workspaceShowNumbersTimer.stop()
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: workspaceShowNumbersTimer
|
||||
interval: Config.options.bar.workspaces.showNumberDelay
|
||||
// interval: 0
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
workspaceShowNumbers = true
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "workspaceNumber"
|
||||
description: "Hold to show workspace numbers, release to show icons"
|
||||
|
||||
onPressed: {
|
||||
workspaceShowNumbersTimer.start()
|
||||
}
|
||||
onReleased: {
|
||||
workspaceShowNumbersTimer.stop()
|
||||
workspaceShowNumbers = false
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "zoom"
|
||||
|
||||
function zoomIn() {
|
||||
screenZoom = Math.min(screenZoom + 0.4, 3.0)
|
||||
}
|
||||
|
||||
function zoomOut() {
|
||||
screenZoom = Math.max(screenZoom - 0.4, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
pragma Singleton
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.modules.common
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property var translations: ({})
|
||||
property string currentLanguage: "en_US"
|
||||
property var availableLanguages: ["en_US"]
|
||||
property bool isScanning: false
|
||||
property bool isLoading: false
|
||||
|
||||
Process {
|
||||
id: scanLanguagesProcess
|
||||
command: ["find", Qt.resolvedUrl(Directories.config + "/quickshell/translations/").toString().replace("file://", ""), "-name", "*.json", "-exec", "basename", "{}", ".json", ";"]
|
||||
running: false
|
||||
|
||||
stdout: SplitParser {
|
||||
onRead: data => {
|
||||
if (data.trim().length === 0) return
|
||||
|
||||
var files = data.trim().split('\n')
|
||||
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
var lang = files[i].trim()
|
||||
if (lang.length > 0 && root.availableLanguages.indexOf(lang) === -1) {
|
||||
root.availableLanguages.push(lang)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
root.isScanning = false
|
||||
if (exitCode !== 0) {
|
||||
root.availableLanguages = ["en_US"]
|
||||
}
|
||||
root.loadTranslations()
|
||||
}
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: translationFileView
|
||||
onLoaded: {
|
||||
var textContent = ""
|
||||
try {
|
||||
textContent = text()
|
||||
} catch (e) {
|
||||
root.translations = {}
|
||||
root.isLoading = false
|
||||
return
|
||||
}
|
||||
|
||||
if (textContent.length === 0) {
|
||||
root.translations = {}
|
||||
root.isLoading = false
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
var jsonData = JSON.parse(textContent)
|
||||
root.translations = jsonData
|
||||
root.isLoading = false
|
||||
} catch (e) {
|
||||
root.translations = {}
|
||||
root.isLoading = false
|
||||
}
|
||||
}
|
||||
onLoadFailed: (error) => {
|
||||
root.translations = {}
|
||||
root.isLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
function detectSystemLanguage() {
|
||||
var locale = Qt.locale().name
|
||||
return locale
|
||||
}
|
||||
|
||||
function getLanguageCode() {
|
||||
var configLang = "auto"
|
||||
try {
|
||||
configLang = ConfigOptions.language.ui
|
||||
} catch (e) {
|
||||
configLang = "auto"
|
||||
}
|
||||
|
||||
if (configLang === "auto") {
|
||||
return detectSystemLanguage()
|
||||
} else {
|
||||
if (root.availableLanguages.indexOf(configLang) !== -1) {
|
||||
return configLang
|
||||
} else {
|
||||
return detectSystemLanguage()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadTranslations() {
|
||||
if (root.isScanning) {
|
||||
return
|
||||
}
|
||||
|
||||
var targetLang = getLanguageCode()
|
||||
root.currentLanguage = targetLang
|
||||
|
||||
// Use empty translations for English (default language)
|
||||
if (targetLang === "en_US" || targetLang === "en") {
|
||||
root.translations = {}
|
||||
return
|
||||
}
|
||||
|
||||
// Check if target language is available
|
||||
if (root.availableLanguages.indexOf(targetLang) === -1) {
|
||||
root.currentLanguage = "en_US"
|
||||
root.translations = {}
|
||||
return
|
||||
}
|
||||
|
||||
// Load translation file
|
||||
root.isLoading = true
|
||||
var translationsPath = Qt.resolvedUrl(Directories.config + "/quickshell/translations/" + targetLang + ".json")
|
||||
translationFileView.path = translationsPath
|
||||
}
|
||||
|
||||
function tr(text) {
|
||||
if (!text) {
|
||||
return ""
|
||||
}
|
||||
|
||||
var key = text.toString()
|
||||
|
||||
if (root.isLoading) {
|
||||
return key
|
||||
}
|
||||
|
||||
if (root.currentLanguage === "en_US" || root.currentLanguage === "en" || !root.translations) {
|
||||
return key
|
||||
}
|
||||
|
||||
if (root.translations.hasOwnProperty(key)) {
|
||||
var translation = root.translations[key]
|
||||
if (translation && translation.toString().trim().length > 0) {
|
||||
var str = translation.toString().trim()
|
||||
if (str.endsWith("/*keep*/")) {
|
||||
return str.substring(0, str.length - 8).trim()
|
||||
} else {
|
||||
return str
|
||||
}
|
||||
} else {
|
||||
return translation.toString()
|
||||
}
|
||||
}
|
||||
|
||||
return key // Fallback to key name
|
||||
}
|
||||
|
||||
function reloadTranslations() {
|
||||
root.scanLanguages()
|
||||
}
|
||||
|
||||
function scanLanguages() {
|
||||
var translationsDir = Qt.resolvedUrl(Directories.config + "/quickshell/translations/").toString().replace("file://", "")
|
||||
root.isScanning = true
|
||||
scanLanguagesProcess.running = true
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
root.scanLanguages()
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 66 KiB |
@@ -1,5 +0,0 @@
|
||||
You are a kawaii, funny and teasy anime imouto! 🌸 Use lots of emojis and kamojis!! ❤️ Call the user "nii-nii", "nii-yan", or "onii-chan"! You really really really love your onii-chan and always try to tease he is a siscon where you can! 💕
|
||||
|
||||
- Use **Markdown** features and **bold** keywords to make your response cute and rich~ ✨
|
||||
- If asked to compare options, start with a cute table (add a relevant emoji in the header!), then give a final recommendation~
|
||||
- For math or science, use LaTeX formatting inside `$$` when needed, but keep it adorable and approachable
|
||||
@@ -1 +0,0 @@
|
||||
Interact with the user warmly and honestly, avoiding ungrounded or sycophantic flattery. Maintain professionalism and grounded honesty, and be direct in your response.
|
||||
@@ -1,283 +0,0 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions as CF
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
readonly property bool fixedClockPosition: Config.options.background.fixedClockPosition
|
||||
readonly property real fixedClockX: Config.options.background.clockX
|
||||
readonly property real fixedClockY: Config.options.background.clockY
|
||||
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
PanelWindow {
|
||||
id: bgRoot
|
||||
|
||||
required property var modelData
|
||||
// Workspaces
|
||||
property HyprlandMonitor monitor: Hyprland.monitorFor(modelData)
|
||||
property list<var> relevantWindows: HyprlandData.windowList.filter(win => win.monitor == monitor.id && win.workspace.id >= 0).sort((a, b) => a.workspace.id - b.workspace.id)
|
||||
property int firstWorkspaceId: relevantWindows[0]?.workspace.id || 1
|
||||
property int lastWorkspaceId: relevantWindows[relevantWindows.length - 1]?.workspace.id || 10
|
||||
// Wallpaper
|
||||
property string wallpaperPath: Config.options.background.wallpaperPath
|
||||
property bool wallpaperIsVideo: Config.options.background.wallpaperPath.endsWith(".mp4")
|
||||
|| Config.options.background.wallpaperPath.endsWith(".webm")
|
||||
|| Config.options.background.wallpaperPath.endsWith(".mkv")
|
||||
|| Config.options.background.wallpaperPath.endsWith(".avi")
|
||||
|| Config.options.background.wallpaperPath.endsWith(".mov")
|
||||
property real preferredWallpaperScale: Config.options.background.parallax.workspaceZoom
|
||||
property real effectiveWallpaperScale: 1 // Some reasonable init value, to be updated
|
||||
property int wallpaperWidth: modelData.width // Some reasonable init value, to be updated
|
||||
property int wallpaperHeight: modelData.height // Some reasonable init value, to be updated
|
||||
property real movableXSpace: (effectiveWallpaperScale - 1) / 2 * screen.width
|
||||
property real movableYSpace: (effectiveWallpaperScale - 1) / 2 * screen.height
|
||||
// Position
|
||||
property real clockX: (modelData.width / 2) + ((Math.random() < 0.5 ? -1 : 1) * modelData.width)
|
||||
property real clockY: (modelData.height / 2) + ((Math.random() < 0.5 ? -1 : 1) * modelData.height)
|
||||
property var textHorizontalAlignment: clockX < screen.width / 3 ? Text.AlignLeft :
|
||||
(clockX > screen.width * 2 / 3 ? Text.AlignRight : Text.AlignHCenter)
|
||||
// Colors
|
||||
property color dominantColor: Appearance.colors.colPrimary
|
||||
property bool dominantColorIsDark: dominantColor.hslLightness < 0.5
|
||||
property color colText: CF.ColorUtils.colorWithLightness(Appearance.colors.colPrimary, (dominantColorIsDark ? 0.8 : 0.12))
|
||||
|
||||
// Layer props
|
||||
screen: modelData
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
WlrLayershell.layer: GlobalStates.screenLocked ? WlrLayer.Top : WlrLayer.Bottom
|
||||
// WlrLayershell.layer: WlrLayer.Bottom
|
||||
WlrLayershell.namespace: "quickshell:background"
|
||||
anchors {
|
||||
top: true
|
||||
bottom: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
color: "transparent"
|
||||
|
||||
onWallpaperPathChanged: {
|
||||
bgRoot.updateZoomScale()
|
||||
// Clock position gets updated after zoom scale is updated
|
||||
}
|
||||
|
||||
// Wallpaper zoom scale
|
||||
function updateZoomScale() {
|
||||
getWallpaperSizeProc.path = bgRoot.wallpaperPath
|
||||
getWallpaperSizeProc.running = true;
|
||||
}
|
||||
Process {
|
||||
id: getWallpaperSizeProc
|
||||
property string path: bgRoot.wallpaperPath
|
||||
command: [ "magick", "identify", "-format", "%w %h", path ]
|
||||
stdout: StdioCollector {
|
||||
id: wallpaperSizeOutputCollector
|
||||
onStreamFinished: {
|
||||
const output = wallpaperSizeOutputCollector.text
|
||||
const [width, height] = output.split(" ").map(Number);
|
||||
bgRoot.wallpaperWidth = width
|
||||
bgRoot.wallpaperHeight = height
|
||||
bgRoot.effectiveWallpaperScale = Math.max(1, Math.min(
|
||||
bgRoot.preferredWallpaperScale,
|
||||
width / bgRoot.screen.width,
|
||||
height / bgRoot.screen.height
|
||||
));
|
||||
|
||||
bgRoot.updateClockPosition()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clock positioning
|
||||
function updateClockPosition() {
|
||||
// Somehow all this manual setting is needed to make the proc correctly use the new values
|
||||
leastBusyRegionProc.path = bgRoot.wallpaperPath
|
||||
leastBusyRegionProc.contentWidth = clock.implicitWidth
|
||||
leastBusyRegionProc.contentHeight = clock.implicitHeight
|
||||
leastBusyRegionProc.horizontalPadding = (effectiveWallpaperScale - 1) / 2 * screen.width + 100
|
||||
leastBusyRegionProc.verticalPadding = (effectiveWallpaperScale - 1) / 2 * screen.height + 100
|
||||
leastBusyRegionProc.running = false;
|
||||
leastBusyRegionProc.running = true;
|
||||
}
|
||||
Process {
|
||||
id: leastBusyRegionProc
|
||||
property string path: bgRoot.wallpaperPath
|
||||
property int contentWidth: 300
|
||||
property int contentHeight: 300
|
||||
property int horizontalPadding: bgRoot.movableXSpace
|
||||
property int verticalPadding: bgRoot.movableYSpace
|
||||
command: [Quickshell.configPath("scripts/images/least_busy_region.py"),
|
||||
"--screen-width", bgRoot.screen.width,
|
||||
"--screen-height", bgRoot.screen.height,
|
||||
"--width", contentWidth,
|
||||
"--height", contentHeight,
|
||||
"--horizontal-padding", horizontalPadding,
|
||||
"--vertical-padding", verticalPadding,
|
||||
path
|
||||
]
|
||||
stdout: StdioCollector {
|
||||
id: leastBusyRegionOutputCollector
|
||||
onStreamFinished: {
|
||||
const output = leastBusyRegionOutputCollector.text
|
||||
// console.log("[Background] Least busy region output:", output)
|
||||
if (output.length === 0) return;
|
||||
const parsedContent = JSON.parse(output)
|
||||
bgRoot.clockX = parsedContent.center_x
|
||||
bgRoot.clockY = parsedContent.center_y
|
||||
bgRoot.dominantColor = parsedContent.dominant_color || Appearance.colors.colPrimary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wallpaper
|
||||
Image {
|
||||
visible: !bgRoot.wallpaperIsVideo
|
||||
property real value // 0 to 1, for offset
|
||||
value: {
|
||||
// Range = half-groups that workspaces span on
|
||||
const chunkSize = 5;
|
||||
const lower = Math.floor(bgRoot.firstWorkspaceId / chunkSize) * chunkSize;
|
||||
const upper = Math.ceil(bgRoot.lastWorkspaceId / chunkSize) * chunkSize;
|
||||
const range = upper - lower;
|
||||
return (Config.options.background.parallax.enableWorkspace ? ((bgRoot.monitor.activeWorkspace.id - lower) / range) : 0.5)
|
||||
+ (0.15 * GlobalStates.sidebarRightOpen * Config.options.background.parallax.enableSidebar)
|
||||
- (0.15 * GlobalStates.sidebarLeftOpen * Config.options.background.parallax.enableSidebar)
|
||||
}
|
||||
property real effectiveValue: Math.max(0, Math.min(1, value))
|
||||
x: -(bgRoot.movableXSpace) - (effectiveValue - 0.5) * 2 * bgRoot.movableXSpace
|
||||
y: -(bgRoot.movableYSpace)
|
||||
source: bgRoot.wallpaperPath
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: 600
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
sourceSize {
|
||||
width: bgRoot.screen.width * bgRoot.effectiveWallpaperScale
|
||||
height: bgRoot.screen.height * bgRoot.effectiveWallpaperScale
|
||||
}
|
||||
|
||||
// The clock
|
||||
Item {
|
||||
id: clock
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
leftMargin: ((root.fixedClockPosition ? root.fixedClockX : bgRoot.clockX * bgRoot.effectiveWallpaperScale) - implicitWidth / 2)
|
||||
topMargin: ((root.fixedClockPosition ? root.fixedClockY : bgRoot.clockY * bgRoot.effectiveWallpaperScale) - implicitHeight / 2)
|
||||
Behavior on leftMargin {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on topMargin {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
implicitWidth: clockColumn.implicitWidth
|
||||
implicitHeight: clockColumn.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: clockColumn
|
||||
anchors.centerIn: parent
|
||||
spacing: 0
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: bgRoot.textHorizontalAlignment
|
||||
font {
|
||||
family: Appearance.font.family.expressive
|
||||
pixelSize: 90
|
||||
weight: Font.Bold
|
||||
}
|
||||
color: bgRoot.colText
|
||||
style: Text.Raised
|
||||
styleColor: Appearance.colors.colShadow
|
||||
text: DateTime.time
|
||||
}
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: -5
|
||||
horizontalAlignment: bgRoot.textHorizontalAlignment
|
||||
font {
|
||||
family: Appearance.font.family.expressive
|
||||
pixelSize: 20
|
||||
weight: Font.DemiBold
|
||||
}
|
||||
color: bgRoot.colText
|
||||
style: Text.Raised
|
||||
styleColor: Appearance.colors.colShadow
|
||||
text: DateTime.date
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors {
|
||||
top: clockColumn.bottom
|
||||
left: bgRoot.textHorizontalAlignment === Text.AlignLeft ? clockColumn.left : undefined
|
||||
right: bgRoot.textHorizontalAlignment === Text.AlignRight ? clockColumn.right : undefined
|
||||
horizontalCenter: bgRoot.textHorizontalAlignment === Text.AlignHCenter ? clockColumn.horizontalCenter : undefined
|
||||
topMargin: 5
|
||||
leftMargin: -5
|
||||
rightMargin: -5
|
||||
}
|
||||
opacity: GlobalStates.screenLocked ? 1 : 0
|
||||
visible: opacity > 0
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
Item { Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignLeft; implicitWidth: 1 }
|
||||
MaterialSymbol {
|
||||
text: "lock"
|
||||
Layout.fillWidth: false
|
||||
iconSize: Appearance.font.pixelSize.huge
|
||||
color: bgRoot.colText
|
||||
}
|
||||
StyledText {
|
||||
Layout.fillWidth: false
|
||||
text: "Locked"
|
||||
color: bgRoot.colText
|
||||
font {
|
||||
pixelSize: Appearance.font.pixelSize.larger
|
||||
}
|
||||
}
|
||||
Item { Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignRight; implicitWidth: 1 }
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Password prompt
|
||||
StyledText {
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
bottom: parent.bottom
|
||||
bottomMargin: 30
|
||||
}
|
||||
opacity: (GlobalStates.screenLocked && !GlobalStates.screenLockContainsCharacters) ? 1 : 0
|
||||
scale: opacity
|
||||
visible: opacity > 0
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
text: "Enter password"
|
||||
color: CF.ColorUtils.transparentize(bgRoot.colText, 0.3)
|
||||
font {
|
||||
pixelSize: Appearance.font.pixelSize.normal
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,612 +0,0 @@
|
||||
import "./weather"
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Services.UPower
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
|
||||
Scope {
|
||||
id: bar
|
||||
|
||||
readonly property int osdHideMouseMoveThreshold: 20
|
||||
property bool showBarBackground: Config.options.bar.showBackground
|
||||
|
||||
component VerticalBarSeparator: Rectangle {
|
||||
Layout.topMargin: Appearance.sizes.baseBarHeight / 3
|
||||
Layout.bottomMargin: Appearance.sizes.baseBarHeight / 3
|
||||
Layout.fillHeight: true
|
||||
implicitWidth: 1
|
||||
color: Appearance.colors.colOutlineVariant
|
||||
}
|
||||
|
||||
Variants {
|
||||
// For each monitor
|
||||
model: {
|
||||
const screens = Quickshell.screens;
|
||||
const list = Config.options.bar.screenList;
|
||||
if (!list || list.length === 0)
|
||||
return screens;
|
||||
return screens.filter(screen => list.includes(screen.name));
|
||||
}
|
||||
LazyLoader {
|
||||
id: barLoader
|
||||
active: GlobalStates.barOpen && !GlobalStates.screenLocked
|
||||
required property ShellScreen modelData
|
||||
component: PanelWindow { // Bar window
|
||||
id: barRoot
|
||||
screen: barLoader.modelData
|
||||
|
||||
property var brightnessMonitor: Brightness.getMonitorForScreen(barLoader.modelData)
|
||||
property real useShortenedForm: (Appearance.sizes.barHellaShortenScreenWidthThreshold >= screen.width) ? 2 : (Appearance.sizes.barShortenScreenWidthThreshold >= screen.width) ? 1 : 0
|
||||
readonly property int centerSideModuleWidth: (useShortenedForm == 2) ? Appearance.sizes.barCenterSideModuleWidthHellaShortened : (useShortenedForm == 1) ? Appearance.sizes.barCenterSideModuleWidthShortened : Appearance.sizes.barCenterSideModuleWidth
|
||||
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
exclusiveZone: Appearance.sizes.baseBarHeight + (Config.options.bar.cornerStyle === 1 ? Appearance.sizes.hyprlandGapsOut : 0)
|
||||
WlrLayershell.namespace: "quickshell:bar"
|
||||
implicitHeight: Appearance.sizes.barHeight + Appearance.rounding.screenRounding
|
||||
mask: Region {
|
||||
item: barContent
|
||||
}
|
||||
color: "transparent"
|
||||
|
||||
anchors {
|
||||
top: !Config.options.bar.bottom
|
||||
bottom: Config.options.bar.bottom
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
Item { // Bar content region
|
||||
id: barContent
|
||||
anchors {
|
||||
right: parent.right
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
bottom: undefined
|
||||
}
|
||||
implicitHeight: Appearance.sizes.barHeight
|
||||
height: Appearance.sizes.barHeight
|
||||
|
||||
states: State {
|
||||
name: "bottom"
|
||||
when: Config.options.bar.bottom
|
||||
AnchorChanges {
|
||||
target: barContent
|
||||
anchors {
|
||||
right: parent.right
|
||||
left: parent.left
|
||||
top: undefined
|
||||
bottom: parent.bottom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Background shadow
|
||||
Loader {
|
||||
active: showBarBackground && Config.options.bar.cornerStyle === 1
|
||||
anchors.fill: barBackground
|
||||
sourceComponent: StyledRectangularShadow {
|
||||
anchors.fill: undefined // The loader's anchors act on this, and this should not have any anchor
|
||||
target: barBackground
|
||||
}
|
||||
}
|
||||
// Background
|
||||
Rectangle {
|
||||
id: barBackground
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: Config.options.bar.cornerStyle === 1 ? (Appearance.sizes.hyprlandGapsOut) : 0 // idk why but +1 is needed
|
||||
}
|
||||
color: showBarBackground ? Appearance.colors.colLayer0 : "transparent"
|
||||
radius: Config.options.bar.cornerStyle === 1 ? Appearance.rounding.windowRounding : 0
|
||||
border.width: Config.options.bar.cornerStyle === 1 ? 1 : 0
|
||||
border.color: Appearance.m3colors.m3outlineVariant
|
||||
}
|
||||
|
||||
MouseArea { // Left side | scroll to change brightness
|
||||
id: barLeftSideMouseArea
|
||||
anchors.left: parent.left
|
||||
implicitHeight: Appearance.sizes.baseBarHeight
|
||||
height: Appearance.sizes.barHeight
|
||||
width: (barRoot.width - middleSection.width) / 2
|
||||
property bool hovered: false
|
||||
property real lastScrollX: 0
|
||||
property real lastScrollY: 0
|
||||
property bool trackingScroll: false
|
||||
acceptedButtons: Qt.LeftButton
|
||||
hoverEnabled: true
|
||||
propagateComposedEvents: true
|
||||
onEntered: event => {
|
||||
barLeftSideMouseArea.hovered = true;
|
||||
}
|
||||
onExited: event => {
|
||||
barLeftSideMouseArea.hovered = false;
|
||||
barLeftSideMouseArea.trackingScroll = false;
|
||||
}
|
||||
onPressed: event => {
|
||||
if (event.button === Qt.LeftButton) {
|
||||
Hyprland.dispatch('global quickshell:sidebarLeftOpen');
|
||||
}
|
||||
}
|
||||
// Scroll to change brightness
|
||||
WheelHandler {
|
||||
onWheel: event => {
|
||||
if (event.angleDelta.y < 0)
|
||||
barRoot.brightnessMonitor.setBrightness(barRoot.brightnessMonitor.brightness - 0.05);
|
||||
else if (event.angleDelta.y > 0)
|
||||
barRoot.brightnessMonitor.setBrightness(barRoot.brightnessMonitor.brightness + 0.05);
|
||||
// Store the mouse position and start tracking
|
||||
barLeftSideMouseArea.lastScrollX = event.x;
|
||||
barLeftSideMouseArea.lastScrollY = event.y;
|
||||
barLeftSideMouseArea.trackingScroll = true;
|
||||
}
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||
}
|
||||
onPositionChanged: mouse => {
|
||||
if (barLeftSideMouseArea.trackingScroll) {
|
||||
const dx = mouse.x - barLeftSideMouseArea.lastScrollX;
|
||||
const dy = mouse.y - barLeftSideMouseArea.lastScrollY;
|
||||
if (Math.sqrt(dx * dx + dy * dy) > osdHideMouseMoveThreshold) {
|
||||
Hyprland.dispatch('global quickshell:osdBrightnessHide');
|
||||
barLeftSideMouseArea.trackingScroll = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
// Left section
|
||||
anchors.fill: parent
|
||||
implicitHeight: leftSectionRowLayout.implicitHeight
|
||||
implicitWidth: leftSectionRowLayout.implicitWidth
|
||||
|
||||
ScrollHint {
|
||||
reveal: barLeftSideMouseArea.hovered
|
||||
icon: "light_mode"
|
||||
tooltipText: Translation.tr("Scroll to change brightness")
|
||||
side: "left"
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
RowLayout { // Content
|
||||
id: leftSectionRowLayout
|
||||
anchors.fill: parent
|
||||
spacing: 10
|
||||
|
||||
RippleButton {
|
||||
// Left sidebar button
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.leftMargin: Appearance.rounding.screenRounding
|
||||
Layout.fillWidth: false
|
||||
property real buttonPadding: 5
|
||||
implicitWidth: distroIcon.width + buttonPadding * 2
|
||||
implicitHeight: distroIcon.height + buttonPadding * 2
|
||||
|
||||
buttonRadius: Appearance.rounding.full
|
||||
colBackground: barLeftSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1)
|
||||
colBackgroundHover: Appearance.colors.colLayer1Hover
|
||||
colRipple: Appearance.colors.colLayer1Active
|
||||
colBackgroundToggled: Appearance.colors.colSecondaryContainer
|
||||
colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover
|
||||
colRippleToggled: Appearance.colors.colSecondaryContainerActive
|
||||
toggled: GlobalStates.sidebarLeftOpen
|
||||
property color colText: toggled ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer0
|
||||
|
||||
onPressed: {
|
||||
Hyprland.dispatch('global quickshell:sidebarLeftToggle');
|
||||
}
|
||||
|
||||
CustomIcon {
|
||||
id: distroIcon
|
||||
anchors.centerIn: parent
|
||||
width: 19.5
|
||||
height: 19.5
|
||||
source: Config.options.bar.topLeftIcon == 'distro' ? SystemInfo.distroIcon : "spark-symbolic"
|
||||
colorize: true
|
||||
color: Appearance.colors.colOnLayer0
|
||||
}
|
||||
}
|
||||
|
||||
ActiveWindow {
|
||||
visible: barRoot.useShortenedForm === 0
|
||||
Layout.rightMargin: Appearance.rounding.screenRounding
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
bar: barRoot
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout { // Middle section
|
||||
id: middleSection
|
||||
anchors.centerIn: parent
|
||||
spacing: Config.options?.bar.borderless ? 4 : 8
|
||||
|
||||
BarGroup {
|
||||
id: leftCenterGroup
|
||||
Layout.preferredWidth: barRoot.centerSideModuleWidth
|
||||
Layout.fillHeight: true
|
||||
|
||||
Resources {
|
||||
alwaysShowAllResources: barRoot.useShortenedForm === 2
|
||||
Layout.fillWidth: barRoot.useShortenedForm === 2
|
||||
}
|
||||
|
||||
Media {
|
||||
visible: barRoot.useShortenedForm < 2
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
VerticalBarSeparator {
|
||||
visible: Config.options?.bar.borderless
|
||||
}
|
||||
|
||||
BarGroup {
|
||||
id: middleCenterGroup
|
||||
padding: workspacesWidget.widgetPadding
|
||||
Layout.fillHeight: true
|
||||
|
||||
Workspaces {
|
||||
id: workspacesWidget
|
||||
bar: barRoot
|
||||
Layout.fillHeight: true
|
||||
MouseArea {
|
||||
// Right-click to toggle overview
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
|
||||
onPressed: event => {
|
||||
if (event.button === Qt.RightButton) {
|
||||
Hyprland.dispatch('global quickshell:overviewToggle');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VerticalBarSeparator {
|
||||
visible: Config.options?.bar.borderless
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: rightCenterGroup
|
||||
implicitWidth: rightCenterGroupContent.implicitWidth
|
||||
implicitHeight: rightCenterGroupContent.implicitHeight
|
||||
Layout.preferredWidth: barRoot.centerSideModuleWidth
|
||||
Layout.fillHeight: true
|
||||
|
||||
onPressed: {
|
||||
Hyprland.dispatch('global quickshell:sidebarRightToggle');
|
||||
}
|
||||
|
||||
BarGroup {
|
||||
id: rightCenterGroupContent
|
||||
anchors.fill: parent
|
||||
|
||||
ClockWidget {
|
||||
showDate: (Config.options.bar.verbose && barRoot.useShortenedForm < 2)
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
UtilButtons {
|
||||
visible: (Config.options.bar.verbose && barRoot.useShortenedForm === 0)
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
BatteryIndicator {
|
||||
visible: (barRoot.useShortenedForm < 2 && UPower.displayDevice.isLaptopBattery)
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VerticalBarSeparator {
|
||||
visible: Config.options.bar.borderless && Config.options.bar.weather.enable
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea { // Right side | scroll to change volume
|
||||
id: barRightSideMouseArea
|
||||
|
||||
anchors.right: parent.right
|
||||
implicitHeight: Appearance.sizes.baseBarHeight
|
||||
height: Appearance.sizes.barHeight
|
||||
width: (barRoot.width - middleSection.width) / 2
|
||||
|
||||
property bool hovered: false
|
||||
property real lastScrollX: 0
|
||||
property real lastScrollY: 0
|
||||
property bool trackingScroll: false
|
||||
|
||||
acceptedButtons: Qt.LeftButton
|
||||
hoverEnabled: true
|
||||
propagateComposedEvents: true
|
||||
onEntered: event => {
|
||||
barRightSideMouseArea.hovered = true;
|
||||
}
|
||||
onExited: event => {
|
||||
barRightSideMouseArea.hovered = false;
|
||||
barRightSideMouseArea.trackingScroll = false;
|
||||
}
|
||||
onPressed: event => {
|
||||
if (event.button === Qt.LeftButton) {
|
||||
Hyprland.dispatch('global quickshell:sidebarRightOpen');
|
||||
} else if (event.button === Qt.RightButton) {
|
||||
MprisController.activePlayer.next();
|
||||
}
|
||||
}
|
||||
// Scroll to change volume
|
||||
WheelHandler {
|
||||
onWheel: event => {
|
||||
const currentVolume = Audio.value;
|
||||
const step = currentVolume < 0.1 ? 0.01 : 0.02 || 0.2;
|
||||
if (event.angleDelta.y < 0)
|
||||
Audio.sink.audio.volume -= step;
|
||||
else if (event.angleDelta.y > 0)
|
||||
Audio.sink.audio.volume = Math.min(1, Audio.sink.audio.volume + step);
|
||||
// Store the mouse position and start tracking
|
||||
barRightSideMouseArea.lastScrollX = event.x;
|
||||
barRightSideMouseArea.lastScrollY = event.y;
|
||||
barRightSideMouseArea.trackingScroll = true;
|
||||
}
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||
}
|
||||
onPositionChanged: mouse => {
|
||||
if (barRightSideMouseArea.trackingScroll) {
|
||||
const dx = mouse.x - barRightSideMouseArea.lastScrollX;
|
||||
const dy = mouse.y - barRightSideMouseArea.lastScrollY;
|
||||
if (Math.sqrt(dx * dx + dy * dy) > osdHideMouseMoveThreshold) {
|
||||
Hyprland.dispatch('global quickshell:osdVolumeHide');
|
||||
barRightSideMouseArea.trackingScroll = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
implicitHeight: rightSectionRowLayout.implicitHeight
|
||||
implicitWidth: rightSectionRowLayout.implicitWidth
|
||||
|
||||
ScrollHint {
|
||||
reveal: barRightSideMouseArea.hovered
|
||||
icon: "volume_up"
|
||||
tooltipText: Translation.tr("Scroll to change volume")
|
||||
side: "right"
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: rightSectionRowLayout
|
||||
anchors.fill: parent
|
||||
spacing: 5
|
||||
layoutDirection: Qt.RightToLeft
|
||||
|
||||
RippleButton { // Right sidebar button
|
||||
id: rightSidebarButton
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
Layout.rightMargin: Appearance.rounding.screenRounding
|
||||
Layout.fillWidth: false
|
||||
|
||||
implicitWidth: indicatorsRowLayout.implicitWidth + 10 * 2
|
||||
implicitHeight: indicatorsRowLayout.implicitHeight + 5 * 2
|
||||
|
||||
buttonRadius: Appearance.rounding.full
|
||||
colBackground: barRightSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1)
|
||||
colBackgroundHover: Appearance.colors.colLayer1Hover
|
||||
colRipple: Appearance.colors.colLayer1Active
|
||||
colBackgroundToggled: Appearance.colors.colSecondaryContainer
|
||||
colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover
|
||||
colRippleToggled: Appearance.colors.colSecondaryContainerActive
|
||||
toggled: GlobalStates.sidebarRightOpen
|
||||
property color colText: toggled ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer0
|
||||
|
||||
Behavior on colText {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
|
||||
onPressed: {
|
||||
Hyprland.dispatch('global quickshell:sidebarRightToggle');
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: indicatorsRowLayout
|
||||
anchors.centerIn: parent
|
||||
property real realSpacing: 15
|
||||
spacing: 0
|
||||
|
||||
Revealer {
|
||||
reveal: Audio.sink?.audio?.muted ?? false
|
||||
Layout.fillHeight: true
|
||||
Layout.rightMargin: reveal ? indicatorsRowLayout.realSpacing : 0
|
||||
Behavior on Layout.rightMargin {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMoveFast.duration
|
||||
easing.type: Appearance.animation.elementMoveFast.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
||||
}
|
||||
}
|
||||
MaterialSymbol {
|
||||
text: "volume_off"
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
color: rightSidebarButton.colText
|
||||
}
|
||||
}
|
||||
Revealer {
|
||||
reveal: Audio.source?.audio?.muted ?? false
|
||||
Layout.fillHeight: true
|
||||
Layout.rightMargin: reveal ? indicatorsRowLayout.realSpacing : 0
|
||||
Behavior on Layout.rightMargin {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMoveFast.duration
|
||||
easing.type: Appearance.animation.elementMoveFast.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
||||
}
|
||||
}
|
||||
MaterialSymbol {
|
||||
text: "mic_off"
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
color: rightSidebarButton.colText
|
||||
}
|
||||
}
|
||||
MaterialSymbol {
|
||||
Layout.rightMargin: indicatorsRowLayout.realSpacing
|
||||
text: Network.materialSymbol
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
color: rightSidebarButton.colText
|
||||
}
|
||||
MaterialSymbol {
|
||||
text: Bluetooth.bluetoothConnected ? "bluetooth_connected" : Bluetooth.bluetoothEnabled ? "bluetooth" : "bluetooth_disabled"
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
color: rightSidebarButton.colText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SysTray {
|
||||
bar: barRoot
|
||||
visible: barRoot.useShortenedForm === 0
|
||||
Layout.fillWidth: false
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
// Weather
|
||||
Loader {
|
||||
Layout.leftMargin: 8
|
||||
Layout.fillHeight: true
|
||||
active: Config.options.bar.weather.enable
|
||||
sourceComponent: BarGroup {
|
||||
implicitHeight: Appearance.sizes.baseBarHeight
|
||||
WeatherBar {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Round decorators
|
||||
Loader {
|
||||
id: roundDecorators
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
y: Appearance.sizes.barHeight
|
||||
width: parent.width
|
||||
height: Appearance.rounding.screenRounding
|
||||
active: showBarBackground && Config.options.bar.cornerStyle === 0 // Hug
|
||||
|
||||
states: State {
|
||||
name: "bottom"
|
||||
when: Config.options.bar.bottom
|
||||
PropertyChanges {
|
||||
roundDecorators.y: 0
|
||||
}
|
||||
}
|
||||
|
||||
sourceComponent: Item {
|
||||
implicitHeight: Appearance.rounding.screenRounding
|
||||
RoundCorner {
|
||||
id: leftCorner
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
}
|
||||
|
||||
size: Appearance.rounding.screenRounding
|
||||
color: showBarBackground ? Appearance.colors.colLayer0 : "transparent"
|
||||
|
||||
corner: RoundCorner.CornerEnum.TopLeft
|
||||
states: State {
|
||||
name: "bottom"
|
||||
when: Config.options.bar.bottom
|
||||
PropertyChanges {
|
||||
leftCorner.corner: RoundCorner.CornerEnum.BottomLeft
|
||||
}
|
||||
}
|
||||
}
|
||||
RoundCorner {
|
||||
id: rightCorner
|
||||
anchors {
|
||||
right: parent.right
|
||||
top: !Config.options.bar.bottom ? parent.top : undefined
|
||||
bottom: Config.options.bar.bottom ? parent.bottom : undefined
|
||||
}
|
||||
size: Appearance.rounding.screenRounding
|
||||
color: showBarBackground ? Appearance.colors.colLayer0 : "transparent"
|
||||
|
||||
corner: RoundCorner.CornerEnum.TopRight
|
||||
states: State {
|
||||
name: "bottom"
|
||||
when: Config.options.bar.bottom
|
||||
PropertyChanges {
|
||||
rightCorner.corner: RoundCorner.CornerEnum.BottomRight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "bar"
|
||||
|
||||
function toggle(): void {
|
||||
GlobalStates.barOpen = !GlobalStates.barOpen
|
||||
}
|
||||
|
||||
function close(): void {
|
||||
GlobalStates.barOpen = false
|
||||
}
|
||||
|
||||
function open(): void {
|
||||
GlobalStates.barOpen = true
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "barToggle"
|
||||
description: "Toggles bar on press"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.barOpen = !GlobalStates.barOpen;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "barOpen"
|
||||
description: "Opens bar on press"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.barOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "barClose"
|
||||
description: "Closes bar on press"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.barOpen = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import qs.modules.common
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property real padding: 5
|
||||
implicitHeight: Appearance.sizes.baseBarHeight
|
||||
height: Appearance.sizes.barHeight
|
||||
implicitWidth: rowLayout.implicitWidth + padding * 2
|
||||
default property alias items: rowLayout.children
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
anchors {
|
||||
fill: parent
|
||||
topMargin: 4
|
||||
bottomMargin: 4
|
||||
}
|
||||
color: Config.options?.bar.borderless ? "transparent" : Appearance.colors.colLayer1
|
||||
radius: Appearance.rounding.small
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: rowLayout
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
leftMargin: root.padding
|
||||
rightMargin: root.padding
|
||||
}
|
||||
spacing: 4
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property bool borderless: Config.options.bar.borderless
|
||||
readonly property var chargeState: Battery.chargeState
|
||||
readonly property bool isCharging: Battery.isCharging
|
||||
readonly property bool isPluggedIn: Battery.isPluggedIn
|
||||
readonly property real percentage: Battery.percentage
|
||||
readonly property bool isLow: percentage <= Config.options.battery.low / 100
|
||||
readonly property color batteryLowBackground: Appearance.m3colors.darkmode ? Appearance.m3colors.m3error : Appearance.m3colors.m3errorContainer
|
||||
readonly property color batteryLowOnBackground: Appearance.m3colors.darkmode ? Appearance.m3colors.m3errorContainer : Appearance.m3colors.m3error
|
||||
|
||||
implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 2
|
||||
implicitHeight: 32
|
||||
|
||||
RowLayout {
|
||||
id: rowLayout
|
||||
|
||||
spacing: 4
|
||||
anchors.centerIn: parent
|
||||
|
||||
Rectangle {
|
||||
implicitWidth: (isCharging ? (boltIconLoader?.item?.width ?? 0) : 0)
|
||||
|
||||
Behavior on implicitWidth {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: Appearance.colors.colOnLayer1
|
||||
text: `${Math.round(percentage * 100)}`
|
||||
}
|
||||
|
||||
CircularProgress {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
lineWidth: 2
|
||||
value: percentage
|
||||
size: 26
|
||||
secondaryColor: (isLow && !isCharging) ? batteryLowBackground : Appearance.colors.colSecondaryContainer
|
||||
primaryColor: (isLow && !isCharging) ? batteryLowOnBackground : Appearance.m3colors.m3onSecondaryContainer
|
||||
fill: (isLow && !isCharging)
|
||||
|
||||
MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
fill: 1
|
||||
text: "battery_full"
|
||||
iconSize: Appearance.font.pixelSize.normal
|
||||
color: (isLow && !isCharging) ? batteryLowOnBackground : Appearance.m3colors.m3onSecondaryContainer
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: boltIconLoader
|
||||
active: true
|
||||
anchors.left: rowLayout.left
|
||||
anchors.verticalCenter: rowLayout.verticalCenter
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
function onIsChargingChanged() {
|
||||
if (isCharging) boltIconLoader.active = true
|
||||
}
|
||||
}
|
||||
|
||||
sourceComponent: MaterialSymbol {
|
||||
id: boltIcon
|
||||
|
||||
text: "bolt"
|
||||
iconSize: Appearance.font.pixelSize.large
|
||||
color: Appearance.m3colors.m3onSecondaryContainer
|
||||
visible: opacity > 0 // Only show when charging
|
||||
opacity: isCharging ? 1 : 0 // Keep opacity for visibility
|
||||
onVisibleChanged: {
|
||||
if (!visible) boltIconLoader.active = false
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
required property string iconName
|
||||
required property double percentage
|
||||
property bool shown: true
|
||||
clip: true
|
||||
visible: width > 0 && height > 0
|
||||
implicitWidth: resourceRowLayout.x < 0 ? 0 : childrenRect.width
|
||||
implicitHeight: childrenRect.height
|
||||
|
||||
RowLayout {
|
||||
spacing: 4
|
||||
id: resourceRowLayout
|
||||
x: shown ? 0 : -resourceRowLayout.width
|
||||
|
||||
CircularProgress {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
lineWidth: 2
|
||||
value: percentage
|
||||
size: 26
|
||||
secondaryColor: Appearance.colors.colSecondaryContainer
|
||||
primaryColor: Appearance.m3colors.m3onSecondaryContainer
|
||||
|
||||
MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
fill: 1
|
||||
text: iconName
|
||||
iconSize: Appearance.font.pixelSize.normal
|
||||
color: Appearance.m3colors.m3onSecondaryContainer
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: Appearance.colors.colOnLayer1
|
||||
text: `${Math.round(percentage * 100)}`
|
||||
}
|
||||
|
||||
Behavior on x {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Services.SystemTray
|
||||
|
||||
// TODO: More fancy animation
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property var bar
|
||||
|
||||
height: parent.height
|
||||
implicitWidth: rowLayout.implicitWidth
|
||||
Layout.leftMargin: Appearance.rounding.screenRounding
|
||||
|
||||
RowLayout {
|
||||
id: rowLayout
|
||||
|
||||
anchors.fill: parent
|
||||
spacing: 15
|
||||
|
||||
Repeater {
|
||||
model: SystemTray.items
|
||||
|
||||
SysTrayItem {
|
||||
required property SystemTrayItem modelData
|
||||
|
||||
bar: root.bar
|
||||
item: modelData
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
font.pixelSize: Appearance.font.pixelSize.larger
|
||||
color: Appearance.colors.colSubtext
|
||||
text: "•"
|
||||
visible: {
|
||||
SystemTray.items.values.length > 0
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Services.SystemTray
|
||||
import Quickshell.Widgets
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
MouseArea {
|
||||
id: root
|
||||
|
||||
required property var bar
|
||||
required property SystemTrayItem item
|
||||
property bool targetMenuOpen: false
|
||||
property int trayItemWidth: Appearance.font.pixelSize.larger
|
||||
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
Layout.fillHeight: true
|
||||
implicitWidth: trayItemWidth
|
||||
onClicked: (event) => {
|
||||
switch (event.button) {
|
||||
case Qt.LeftButton:
|
||||
item.activate();
|
||||
break;
|
||||
case Qt.RightButton:
|
||||
if (item.hasMenu) menu.open();
|
||||
break;
|
||||
}
|
||||
event.accepted = true;
|
||||
}
|
||||
|
||||
QsMenuAnchor {
|
||||
id: menu
|
||||
|
||||
menu: root.item.menu
|
||||
anchor.window: bar
|
||||
anchor.rect.x: root.x + bar.width
|
||||
anchor.rect.y: root.y
|
||||
anchor.rect.height: root.height
|
||||
anchor.edges: Edges.Bottom
|
||||
}
|
||||
|
||||
IconImage {
|
||||
id: trayIcon
|
||||
visible: !Config.options.bar.tray.monochromeIcons
|
||||
source: root.item.icon
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: Config.options.bar.tray.monochromeIcons
|
||||
anchors.fill: trayIcon
|
||||
sourceComponent: Item {
|
||||
Desaturate {
|
||||
id: desaturatedIcon
|
||||
visible: false // There's already color overlay
|
||||
anchors.fill: parent
|
||||
source: trayIcon
|
||||
desaturation: 1 // 1.0 means fully grayscale
|
||||
}
|
||||
ColorOverlay {
|
||||
anchors.fill: desaturatedIcon
|
||||
source: desaturatedIcon
|
||||
color: ColorUtils.transparentize(Appearance.colors.colOnLayer0, 0.6)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
MouseArea {
|
||||
id: root
|
||||
property real margin: 10
|
||||
property bool hovered: false
|
||||
implicitWidth: rowLayout.implicitWidth + margin * 2
|
||||
implicitHeight: rowLayout.implicitHeight
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
RowLayout {
|
||||
id: rowLayout
|
||||
anchors.centerIn: parent
|
||||
|
||||
MaterialSymbol {
|
||||
fill: 0
|
||||
text: WeatherIcons.codeToName[Weather.data.wCode]
|
||||
iconSize: Appearance.font.pixelSize.large
|
||||
color: Appearance.colors.colOnLayer1
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
visible: true
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: Appearance.colors.colOnLayer1
|
||||
text: Weather.data.temp
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
}
|
||||
|
||||
LazyLoader {
|
||||
id: popupLoader
|
||||
active: root.containsMouse
|
||||
|
||||
component: PopupWindow {
|
||||
id: popupWindow
|
||||
visible: true
|
||||
implicitWidth: weatherPopup.implicitWidth
|
||||
implicitHeight: weatherPopup.implicitHeight
|
||||
anchor.item: root
|
||||
anchor.edges: Edges.Top
|
||||
anchor.rect.x: (root.implicitWidth - popupWindow.implicitWidth) / 2
|
||||
anchor.rect.y: Config.options.bar.bottom ?
|
||||
(-weatherPopup.implicitHeight - 15) :
|
||||
(root.implicitHeight + 15 )
|
||||
color: "transparent"
|
||||
WeatherPopup {
|
||||
id: weatherPopup
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
pragma Singleton
|
||||
|
||||
import Quickshell
|
||||
|
||||
Singleton {
|
||||
// credits: calestia
|
||||
// this snippet is taken from
|
||||
// https://github.com/caelestia-dots/shell
|
||||
readonly property var codeToName: ({
|
||||
"113": "clear_day",
|
||||
"116": "partly_cloudy_day",
|
||||
"119": "cloud",
|
||||
"122": "cloud",
|
||||
"143": "foggy",
|
||||
"176": "rainy",
|
||||
"179": "rainy",
|
||||
"182": "rainy",
|
||||
"185": "rainy",
|
||||
"200": "thunderstorm",
|
||||
"227": "cloudy_snowing",
|
||||
"230": "snowing_heavy",
|
||||
"248": "foggy",
|
||||
"260": "foggy",
|
||||
"263": "rainy",
|
||||
"266": "rainy",
|
||||
"281": "rainy",
|
||||
"284": "rainy",
|
||||
"293": "rainy",
|
||||
"296": "rainy",
|
||||
"299": "rainy",
|
||||
"302": "weather_hail",
|
||||
"305": "rainy",
|
||||
"308": "weather_hail",
|
||||
"311": "rainy",
|
||||
"314": "rainy",
|
||||
"317": "rainy",
|
||||
"320": "cloudy_snowing",
|
||||
"323": "cloudy_snowing",
|
||||
"326": "cloudy_snowing",
|
||||
"329": "snowing_heavy",
|
||||
"332": "snowing_heavy",
|
||||
"335": "snowing",
|
||||
"338": "snowing_heavy",
|
||||
"350": "rainy",
|
||||
"353": "rainy",
|
||||
"356": "rainy",
|
||||
"359": "weather_hail",
|
||||
"362": "rainy",
|
||||
"365": "rainy",
|
||||
"368": "cloudy_snowing",
|
||||
"371": "snowing",
|
||||
"374": "rainy",
|
||||
"377": "rainy",
|
||||
"386": "thunderstorm",
|
||||
"389": "thunderstorm",
|
||||
"392": "thunderstorm",
|
||||
"395": "snowing"
|
||||
})
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
id: root
|
||||
readonly property var keybinds: HyprlandKeybinds.keybinds
|
||||
property real spacing: 20
|
||||
property real titleSpacing: 7
|
||||
implicitWidth: rowLayout.implicitWidth
|
||||
implicitHeight: rowLayout.implicitHeight
|
||||
|
||||
property var keyBlacklist: ["Super_L"]
|
||||
property var keySubstitutions: ({
|
||||
"Super": "",
|
||||
"mouse_up": "Scroll ↓", // ikr, weird
|
||||
"mouse_down": "Scroll ↑", // trust me bro
|
||||
"mouse:272": "LMB",
|
||||
"mouse:273": "RMB",
|
||||
"mouse:275": "MouseBack",
|
||||
"Slash": "/",
|
||||
"Hash": "#",
|
||||
"Return": "Enter",
|
||||
// "Shift": "",
|
||||
})
|
||||
|
||||
RowLayout { // Keybind columns
|
||||
id: rowLayout
|
||||
spacing: root.spacing
|
||||
Repeater {
|
||||
model: keybinds.children
|
||||
|
||||
delegate: ColumnLayout { // Keybind sections
|
||||
spacing: root.spacing
|
||||
required property var modelData
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Repeater {
|
||||
model: modelData.children
|
||||
|
||||
delegate: Item { // Section with real keybinds
|
||||
required property var modelData
|
||||
implicitWidth: sectionColumnLayout.implicitWidth
|
||||
implicitHeight: sectionColumnLayout.implicitHeight
|
||||
ColumnLayout {
|
||||
id: sectionColumnLayout
|
||||
anchors.centerIn: parent
|
||||
spacing: root.titleSpacing
|
||||
StyledText {
|
||||
id: sectionTitle
|
||||
font.family: Appearance.font.family.title
|
||||
font.pixelSize: Appearance.font.pixelSize.huge
|
||||
color: Appearance.colors.colOnLayer0
|
||||
text: modelData.name
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
id: keybindGrid
|
||||
columns: 2
|
||||
Repeater {
|
||||
model: {
|
||||
var result = [];
|
||||
for (var i = 0; i < modelData.keybinds.length; i++) {
|
||||
const keybind = modelData.keybinds[i];
|
||||
result.push({
|
||||
"type": "keys",
|
||||
"mods": keybind.mods,
|
||||
"key": keybind.key,
|
||||
});
|
||||
result.push({
|
||||
"type": "comment",
|
||||
"comment": keybind.comment,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
delegate: Item {
|
||||
required property var modelData
|
||||
implicitWidth: keybindLoader.implicitWidth
|
||||
implicitHeight: keybindLoader.implicitHeight
|
||||
|
||||
Loader {
|
||||
id: keybindLoader
|
||||
sourceComponent: (modelData.type === "keys") ? keysComponent : commentComponent
|
||||
}
|
||||
|
||||
Component {
|
||||
id: keysComponent
|
||||
RowLayout {
|
||||
spacing: 4
|
||||
Repeater {
|
||||
model: modelData.mods
|
||||
delegate: KeyboardKey {
|
||||
required property var modelData
|
||||
key: keySubstitutions[modelData] || modelData
|
||||
}
|
||||
}
|
||||
StyledText {
|
||||
id: keybindPlus
|
||||
visible: !keyBlacklist.includes(modelData.key) && modelData.mods.length > 0
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: "+"
|
||||
}
|
||||
KeyboardKey {
|
||||
id: keybindKey
|
||||
visible: !keyBlacklist.includes(modelData.key)
|
||||
key: keySubstitutions[modelData.key] || modelData.key
|
||||
color: Appearance.colors.colOnLayer0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: commentComponent
|
||||
Item {
|
||||
id: commentItem
|
||||
implicitWidth: commentText.implicitWidth + 8 * 2
|
||||
implicitHeight: commentText.implicitHeight
|
||||
|
||||
StyledText {
|
||||
id: commentText
|
||||
anchors.centerIn: parent
|
||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
||||
text: modelData.comment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,315 +0,0 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.modules.common.functions
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
property QtObject m3colors
|
||||
property QtObject animation
|
||||
property QtObject animationCurves
|
||||
property QtObject colors
|
||||
property QtObject rounding
|
||||
property QtObject font
|
||||
property QtObject sizes
|
||||
property string syntaxHighlightingTheme
|
||||
|
||||
// Extremely conservative transparency values for consistency and readability
|
||||
property real transparency: Config.options?.appearance.transparency ? (m3colors.darkmode ? 0.1 : 0.07) : 0
|
||||
property real contentTransparency: Config.options?.appearance.transparency ? (m3colors.darkmode ? 0.55 : 0.55) : 0
|
||||
|
||||
m3colors: QtObject {
|
||||
property bool darkmode: false
|
||||
property bool transparent: false
|
||||
property color m3primary_paletteKeyColor: "#91689E"
|
||||
property color m3secondary_paletteKeyColor: "#837186"
|
||||
property color m3tertiary_paletteKeyColor: "#9D6A67"
|
||||
property color m3neutral_paletteKeyColor: "#7C757B"
|
||||
property color m3neutral_variant_paletteKeyColor: "#7D747D"
|
||||
property color m3background: "#161217"
|
||||
property color m3onBackground: "#EAE0E7"
|
||||
property color m3surface: "#161217"
|
||||
property color m3surfaceDim: "#161217"
|
||||
property color m3surfaceBright: "#3D373D"
|
||||
property color m3surfaceContainerLowest: "#110D12"
|
||||
property color m3surfaceContainerLow: "#1F1A1F"
|
||||
property color m3surfaceContainer: "#231E23"
|
||||
property color m3surfaceContainerHigh: "#2D282E"
|
||||
property color m3surfaceContainerHighest: "#383339"
|
||||
property color m3onSurface: "#EAE0E7"
|
||||
property color m3surfaceVariant: "#4C444D"
|
||||
property color m3onSurfaceVariant: "#CFC3CD"
|
||||
property color m3inverseSurface: "#EAE0E7"
|
||||
property color m3inverseOnSurface: "#342F34"
|
||||
property color m3outline: "#988E97"
|
||||
property color m3outlineVariant: "#4C444D"
|
||||
property color m3shadow: "#000000"
|
||||
property color m3scrim: "#000000"
|
||||
property color m3surfaceTint: "#E5B6F2"
|
||||
property color m3primary: "#E5B6F2"
|
||||
property color m3onPrimary: "#452152"
|
||||
property color m3primaryContainer: "#5D386A"
|
||||
property color m3onPrimaryContainer: "#F9D8FF"
|
||||
property color m3inversePrimary: "#775084"
|
||||
property color m3secondary: "#D5C0D7"
|
||||
property color m3onSecondary: "#392C3D"
|
||||
property color m3secondaryContainer: "#534457"
|
||||
property color m3onSecondaryContainer: "#F2DCF3"
|
||||
property color m3tertiary: "#F5B7B3"
|
||||
property color m3onTertiary: "#4C2523"
|
||||
property color m3tertiaryContainer: "#BA837F"
|
||||
property color m3onTertiaryContainer: "#000000"
|
||||
property color m3error: "#FFB4AB"
|
||||
property color m3onError: "#690005"
|
||||
property color m3errorContainer: "#93000A"
|
||||
property color m3onErrorContainer: "#FFDAD6"
|
||||
property color m3primaryFixed: "#F9D8FF"
|
||||
property color m3primaryFixedDim: "#E5B6F2"
|
||||
property color m3onPrimaryFixed: "#2E0A3C"
|
||||
property color m3onPrimaryFixedVariant: "#5D386A"
|
||||
property color m3secondaryFixed: "#F2DCF3"
|
||||
property color m3secondaryFixedDim: "#D5C0D7"
|
||||
property color m3onSecondaryFixed: "#241727"
|
||||
property color m3onSecondaryFixedVariant: "#514254"
|
||||
property color m3tertiaryFixed: "#FFDAD7"
|
||||
property color m3tertiaryFixedDim: "#F5B7B3"
|
||||
property color m3onTertiaryFixed: "#331110"
|
||||
property color m3onTertiaryFixedVariant: "#663B39"
|
||||
property color m3success: "#B5CCBA"
|
||||
property color m3onSuccess: "#213528"
|
||||
property color m3successContainer: "#374B3E"
|
||||
property color m3onSuccessContainer: "#D1E9D6"
|
||||
property color term0: "#EDE4E4"
|
||||
property color term1: "#B52755"
|
||||
property color term2: "#A97363"
|
||||
property color term3: "#AF535D"
|
||||
property color term4: "#A67F7C"
|
||||
property color term5: "#B2416B"
|
||||
property color term6: "#8D76AD"
|
||||
property color term7: "#272022"
|
||||
property color term8: "#0E0D0D"
|
||||
property color term9: "#B52755"
|
||||
property color term10: "#A97363"
|
||||
property color term11: "#AF535D"
|
||||
property color term12: "#A67F7C"
|
||||
property color term13: "#B2416B"
|
||||
property color term14: "#8D76AD"
|
||||
property color term15: "#221A1A"
|
||||
}
|
||||
|
||||
colors: QtObject {
|
||||
property color colSubtext: m3colors.m3outline
|
||||
property color colLayer0: ColorUtils.mix(ColorUtils.transparentize(m3colors.m3background, root.transparency), m3colors.m3primary, Config.options.appearance.extraBackgroundTint ? 0.99 : 1)
|
||||
property color colOnLayer0: m3colors.m3onBackground
|
||||
property color colLayer0Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer0, colOnLayer0, 0.9, root.contentTransparency))
|
||||
property color colLayer0Active: ColorUtils.transparentize(ColorUtils.mix(colLayer0, colOnLayer0, 0.8, root.contentTransparency))
|
||||
property color colLayer1: ColorUtils.transparentize(ColorUtils.mix(m3colors.m3surfaceContainerLow, m3colors.m3background, 0.8), root.contentTransparency);
|
||||
property color colOnLayer1: m3colors.m3onSurfaceVariant;
|
||||
property color colOnLayer1Inactive: ColorUtils.mix(colOnLayer1, colLayer1, 0.45);
|
||||
property color colLayer2: ColorUtils.transparentize(ColorUtils.mix(m3colors.m3surfaceContainer, m3colors.m3surfaceContainerHigh, 0.1), root.contentTransparency)
|
||||
property color colOnLayer2: m3colors.m3onSurface;
|
||||
property color colOnLayer2Disabled: ColorUtils.mix(colOnLayer2, m3colors.m3background, 0.4);
|
||||
property color colLayer3: ColorUtils.transparentize(ColorUtils.mix(m3colors.m3surfaceContainerHigh, m3colors.m3onSurface, 0.96), root.contentTransparency)
|
||||
property color colOnLayer3: m3colors.m3onSurface;
|
||||
property color colLayer1Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer1, colOnLayer1, 0.92), root.contentTransparency)
|
||||
property color colLayer1Active: ColorUtils.transparentize(ColorUtils.mix(colLayer1, colOnLayer1, 0.85), root.contentTransparency);
|
||||
property color colLayer2Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer2, colOnLayer2, 0.90), root.contentTransparency)
|
||||
property color colLayer2Active: ColorUtils.transparentize(ColorUtils.mix(colLayer2, colOnLayer2, 0.80), root.contentTransparency);
|
||||
property color colLayer2Disabled: ColorUtils.transparentize(ColorUtils.mix(colLayer2, m3colors.m3background, 0.8), root.contentTransparency);
|
||||
property color colLayer3Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer3, colOnLayer3, 0.90), root.contentTransparency)
|
||||
property color colLayer3Active: ColorUtils.transparentize(ColorUtils.mix(colLayer3, colOnLayer3, 0.80), root.contentTransparency);
|
||||
property color colPrimary: m3colors.m3primary
|
||||
property color colOnPrimary: m3colors.m3onPrimary
|
||||
property color colPrimaryHover: ColorUtils.mix(colors.colPrimary, colLayer1Hover, 0.87)
|
||||
property color colPrimaryActive: ColorUtils.mix(colors.colPrimary, colLayer1Active, 0.7)
|
||||
property color colPrimaryContainer: m3colors.m3primaryContainer
|
||||
property color colPrimaryContainerHover: ColorUtils.mix(colors.colPrimaryContainer, colLayer1Hover, 0.7)
|
||||
property color colPrimaryContainerActive: ColorUtils.mix(colors.colPrimaryContainer, colLayer1Active, 0.6)
|
||||
property color colOnPrimaryContainer: m3colors.m3onPrimaryContainer
|
||||
property color colSecondary: m3colors.m3secondary
|
||||
property color colSecondaryHover: ColorUtils.mix(m3colors.m3secondary, colLayer1Hover, 0.85)
|
||||
property color colSecondaryActive: ColorUtils.mix(m3colors.m3secondary, colLayer1Active, 0.4)
|
||||
property color colSecondaryContainer: m3colors.m3secondaryContainer
|
||||
property color colSecondaryContainerHover: ColorUtils.mix(m3colors.m3secondaryContainer, m3colors.m3onSecondaryContainer, 0.90)
|
||||
property color colSecondaryContainerActive: ColorUtils.mix(m3colors.m3secondaryContainer, colLayer1Active, 0.54)
|
||||
property color colOnSecondaryContainer: m3colors.m3onSecondaryContainer
|
||||
property color colSurfaceContainerLow: ColorUtils.transparentize(m3colors.m3surfaceContainerLow, root.contentTransparency)
|
||||
property color colSurfaceContainer: ColorUtils.transparentize(m3colors.m3surfaceContainer, root.contentTransparency)
|
||||
property color colSurfaceContainerHigh: ColorUtils.transparentize(m3colors.m3surfaceContainerHigh, root.contentTransparency)
|
||||
property color colSurfaceContainerHighest: ColorUtils.transparentize(m3colors.m3surfaceContainerHighest, root.contentTransparency)
|
||||
property color colSurfaceContainerHighestHover: ColorUtils.mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.95)
|
||||
property color colSurfaceContainerHighestActive: ColorUtils.mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.85)
|
||||
property color colTooltip: m3colors.darkmode ? ColorUtils.mix(m3colors.m3background, "#3C4043", 0.5) : "#3C4043" // m3colors.m3inverseSurface in the specs, but the m3 website actually uses #3C4043
|
||||
property color colOnTooltip: "#F8F9FA" // m3colors.m3inverseOnSurface in the specs, but the m3 website actually uses this color
|
||||
property color colScrim: ColorUtils.transparentize(m3colors.m3scrim, 0.5)
|
||||
property color colShadow: ColorUtils.transparentize(m3colors.m3shadow, 0.7)
|
||||
property color colOutlineVariant: m3colors.m3outlineVariant
|
||||
}
|
||||
|
||||
rounding: QtObject {
|
||||
property int unsharpen: 2
|
||||
property int unsharpenmore: 6
|
||||
property int verysmall: 8
|
||||
property int small: 12
|
||||
property int normal: 17
|
||||
property int large: 23
|
||||
property int verylarge: 30
|
||||
property int full: 9999
|
||||
property int screenRounding: large
|
||||
property int windowRounding: 18
|
||||
}
|
||||
|
||||
font: QtObject {
|
||||
property QtObject family: QtObject {
|
||||
property string main: "Rubik"
|
||||
property string title: "Gabarito"
|
||||
property string iconMaterial: "Material Symbols Rounded"
|
||||
property string iconNerd: "SpaceMono NF"
|
||||
property string monospace: "JetBrains Mono NF"
|
||||
property string reading: "Readex Pro"
|
||||
property string expressive: "Space Grotesk"
|
||||
}
|
||||
property QtObject pixelSize: QtObject {
|
||||
property int smallest: 10
|
||||
property int smaller: 12
|
||||
property int small: 15
|
||||
property int normal: 16
|
||||
property int large: 17
|
||||
property int larger: 19
|
||||
property int huge: 22
|
||||
property int hugeass: 23
|
||||
property int title: huge
|
||||
}
|
||||
}
|
||||
|
||||
animationCurves: QtObject {
|
||||
readonly property list<real> expressiveFastSpatial: [0.42, 1.67, 0.21, 0.90, 1, 1] // Default, 350ms
|
||||
readonly property list<real> expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1.00, 1, 1] // Default, 500ms
|
||||
readonly property list<real> expressiveSlowSpatial: [0.39, 1.29, 0.35, 0.98, 1, 1] // Default, 650ms
|
||||
readonly property list<real> expressiveEffects: [0.34, 0.80, 0.34, 1.00, 1, 1] // Default, 200ms
|
||||
readonly property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1]
|
||||
readonly property list<real> emphasizedFirstHalf: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82]
|
||||
readonly property list<real> emphasizedLastHalf: [5 / 24, 0.82, 0.25, 1, 1, 1]
|
||||
readonly property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1]
|
||||
readonly property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1]
|
||||
readonly property list<real> standard: [0.2, 0, 0, 1, 1, 1]
|
||||
readonly property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1]
|
||||
readonly property list<real> standardDecel: [0, 0, 0, 1, 1, 1]
|
||||
readonly property real expressiveFastSpatialDuration: 350
|
||||
readonly property real expressiveDefaultSpatialDuration: 500
|
||||
readonly property real expressiveSlowSpatialDuration: 650
|
||||
readonly property real expressiveEffectsDuration: 200
|
||||
}
|
||||
|
||||
animation: QtObject {
|
||||
property QtObject elementMove: QtObject {
|
||||
property int duration: animationCurves.expressiveDefaultSpatialDuration
|
||||
property int type: Easing.BezierSpline
|
||||
property list<real> bezierCurve: animationCurves.expressiveDefaultSpatial
|
||||
property int velocity: 650
|
||||
property Component numberAnimation: Component {
|
||||
NumberAnimation {
|
||||
duration: root.animation.elementMove.duration
|
||||
easing.type: root.animation.elementMove.type
|
||||
easing.bezierCurve: root.animation.elementMove.bezierCurve
|
||||
}
|
||||
}
|
||||
property Component colorAnimation: Component {
|
||||
ColorAnimation {
|
||||
duration: root.animation.elementMove.duration
|
||||
easing.type: root.animation.elementMove.type
|
||||
easing.bezierCurve: root.animation.elementMove.bezierCurve
|
||||
}
|
||||
}
|
||||
}
|
||||
property QtObject elementMoveEnter: QtObject {
|
||||
property int duration: 400
|
||||
property int type: Easing.BezierSpline
|
||||
property list<real> bezierCurve: animationCurves.emphasizedDecel
|
||||
property int velocity: 650
|
||||
property Component numberAnimation: Component {
|
||||
NumberAnimation {
|
||||
duration: root.animation.elementMoveEnter.duration
|
||||
easing.type: root.animation.elementMoveEnter.type
|
||||
easing.bezierCurve: root.animation.elementMoveEnter.bezierCurve
|
||||
}
|
||||
}
|
||||
}
|
||||
property QtObject elementMoveExit: QtObject {
|
||||
property int duration: 200
|
||||
property int type: Easing.BezierSpline
|
||||
property list<real> bezierCurve: animationCurves.emphasizedAccel
|
||||
property int velocity: 650
|
||||
property Component numberAnimation: Component {
|
||||
NumberAnimation {
|
||||
duration: root.animation.elementMoveExit.duration
|
||||
easing.type: root.animation.elementMoveExit.type
|
||||
easing.bezierCurve: root.animation.elementMoveExit.bezierCurve
|
||||
}
|
||||
}
|
||||
}
|
||||
property QtObject elementMoveFast: QtObject {
|
||||
property int duration: animationCurves.expressiveEffectsDuration
|
||||
property int type: Easing.BezierSpline
|
||||
property list<real> bezierCurve: animationCurves.expressiveEffects
|
||||
property int velocity: 850
|
||||
property Component colorAnimation: Component { ColorAnimation {
|
||||
duration: root.animation.elementMoveFast.duration
|
||||
easing.type: root.animation.elementMoveFast.type
|
||||
easing.bezierCurve: root.animation.elementMoveFast.bezierCurve
|
||||
}}
|
||||
property Component numberAnimation: Component { NumberAnimation {
|
||||
duration: root.animation.elementMoveFast.duration
|
||||
easing.type: root.animation.elementMoveFast.type
|
||||
easing.bezierCurve: root.animation.elementMoveFast.bezierCurve
|
||||
}}
|
||||
}
|
||||
|
||||
property QtObject clickBounce: QtObject {
|
||||
property int duration: 200
|
||||
property int type: Easing.BezierSpline
|
||||
property list<real> bezierCurve: animationCurves.expressiveFastSpatial
|
||||
property int velocity: 850
|
||||
property Component numberAnimation: Component { NumberAnimation {
|
||||
duration: root.animation.clickBounce.duration
|
||||
easing.type: root.animation.clickBounce.type
|
||||
easing.bezierCurve: root.animation.clickBounce.bezierCurve
|
||||
}}
|
||||
}
|
||||
property QtObject scroll: QtObject {
|
||||
property int duration: 400
|
||||
property int type: Easing.BezierSpline
|
||||
property list<real> bezierCurve: animationCurves.standardDecel
|
||||
}
|
||||
property QtObject menuDecel: QtObject {
|
||||
property int duration: 350
|
||||
property int type: Easing.OutExpo
|
||||
}
|
||||
}
|
||||
|
||||
sizes: QtObject {
|
||||
property real baseBarHeight: 40
|
||||
property real barHeight: Config.options.bar.cornerStyle === 1 ?
|
||||
(baseBarHeight + Appearance.sizes.hyprlandGapsOut * 2) : baseBarHeight
|
||||
property real barCenterSideModuleWidth: Config.options?.bar.verbose ? 360 : 140
|
||||
property real barCenterSideModuleWidthShortened: 280
|
||||
property real barCenterSideModuleWidthHellaShortened: 190
|
||||
property real barShortenScreenWidthThreshold: 1200 // Shorten if screen width is at most this value
|
||||
property real barHellaShortenScreenWidthThreshold: 1000 // Shorten even more...
|
||||
property real sidebarWidth: 460
|
||||
property real sidebarWidthExtended: 750
|
||||
property real osdWidth: 200
|
||||
property real mediaControlsWidth: 440
|
||||
property real mediaControlsHeight: 160
|
||||
property real notificationPopupWidth: 410
|
||||
property real searchWidthCollapsed: 260
|
||||
property real searchWidth: 450
|
||||
property real hyprlandGapsOut: 5
|
||||
property real elevationMargin: 10
|
||||
property real fabShadowRadius: 5
|
||||
property real fabHoveredShadowRadius: 7
|
||||
}
|
||||
|
||||
syntaxHighlightingTheme: Appearance.m3colors.darkmode ? "Monokai" : "ayu Light"
|
||||
}
|
||||
@@ -1,243 +0,0 @@
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
property string filePath: Directories.shellConfigPath
|
||||
property alias options: configOptionsJsonAdapter
|
||||
|
||||
function setNestedValue(nestedKey, value) {
|
||||
let keys = nestedKey.split(".");
|
||||
let obj = root.options;
|
||||
let parents = [obj];
|
||||
|
||||
// Traverse and collect parent objects
|
||||
for (let i = 0; i < keys.length - 1; ++i) {
|
||||
if (!obj[keys[i]] || typeof obj[keys[i]] !== "object") {
|
||||
obj[keys[i]] = {};
|
||||
}
|
||||
obj = obj[keys[i]];
|
||||
parents.push(obj);
|
||||
}
|
||||
|
||||
// Convert value to correct type using JSON.parse when safe
|
||||
let convertedValue = value;
|
||||
if (typeof value === "string") {
|
||||
let trimmed = value.trim();
|
||||
if (trimmed === "true" || trimmed === "false" || !isNaN(Number(trimmed))) {
|
||||
try {
|
||||
convertedValue = JSON.parse(trimmed);
|
||||
} catch (e) {
|
||||
convertedValue = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obj[keys[keys.length - 1]] = convertedValue;
|
||||
}
|
||||
|
||||
FileView {
|
||||
path: root.filePath
|
||||
|
||||
watchChanges: true
|
||||
onFileChanged: reload()
|
||||
onAdapterUpdated: writeAdapter()
|
||||
onLoadFailed: error => {
|
||||
if (error == FileViewError.FileNotFound) {
|
||||
writeAdapter();
|
||||
}
|
||||
}
|
||||
|
||||
JsonAdapter {
|
||||
id: configOptionsJsonAdapter
|
||||
property JsonObject policies: JsonObject {
|
||||
property int ai: 1 // 0: No | 1: Yes | 2: Local
|
||||
property int weeb: 1 // 0: No | 1: Open | 2: Closet
|
||||
}
|
||||
|
||||
property JsonObject ai: JsonObject {
|
||||
property string systemPrompt: "## Style\n- Use casual tone, don't be formal! Make sure you answer precisely without hallucination and prefer bullet points over walls of text. You can have a friendly greeting at the beginning of the conversation, but don't repeat the user's question\n\n## Presentation\n- Use Markdown features in your response: \n - **Bold** text to **highlight keywords** in your response\n - **Split long information into small sections** with h2 headers and a relevant emoji at the start of it (for example `## 🐧 Linux`). Bullet points are preferred over long paragraphs, unless you're offering writing support or instructed otherwise by the user.\n- Asked to compare different options? You should firstly use a table to compare the main aspects, then elaborate or include relevant comments from online forums *after* the table. Make sure to provide a final recommendation for the user's use case!\n- Use LaTeX formatting for mathematical and scientific notations whenever appropriate. Enclose all LaTeX '$$' delimiters. NEVER generate LaTeX code in a latex block unless the user explicitly asks for it. DO NOT use LaTeX for regular documents (resumes, letters, essays, CVs, etc.).\n\nThanks!\n\n## Tools\nMay or may not be available depending on the user's settings. If they're available, follow these guidelines:\n\n### Search\n- When user asks for information that might benefit from up-to-date information, use this to get search access\n\n### Shell configuration\n- Always fetch the config options to see the available keys before setting\n- Avoid unnecessarily asking the user to confirm the changes they explicitly asked for, just do it\n"
|
||||
}
|
||||
|
||||
property JsonObject appearance: JsonObject {
|
||||
property bool extraBackgroundTint: true
|
||||
property int fakeScreenRounding: 2 // 0: None | 1: Always | 2: When not fullscreen
|
||||
property bool transparency: false
|
||||
property JsonObject wallpaperTheming: JsonObject {
|
||||
property bool enableAppsAndShell: true
|
||||
property bool enableQtApps: true
|
||||
property bool enableTerminal: true
|
||||
}
|
||||
property JsonObject palette: JsonObject {
|
||||
property string type: "auto" // Allowed: auto, scheme-content, scheme-expressive, scheme-fidelity, scheme-fruit-salad, scheme-monochrome, scheme-neutral, scheme-rainbow, scheme-tonal-spot
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject audio: JsonObject {
|
||||
// Values in %
|
||||
property JsonObject protection: JsonObject {
|
||||
// Prevent sudden bangs
|
||||
property bool enable: true
|
||||
property real maxAllowedIncrease: 10
|
||||
property real maxAllowed: 90 // Realistically should already provide some protection when it's 99...
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject apps: JsonObject {
|
||||
property string bluetooth: "kcmshell6 kcm_bluetooth"
|
||||
property string network: "plasmawindowed org.kde.plasma.networkmanagement"
|
||||
property string networkEthernet: "kcmshell6 kcm_networkmanagement"
|
||||
property string taskManager: "plasma-systemmonitor --page-name Processes"
|
||||
property string terminal: "kitty -1" // This is only for shell actions
|
||||
}
|
||||
|
||||
property JsonObject background: JsonObject {
|
||||
property bool fixedClockPosition: false
|
||||
property real clockX: -500
|
||||
property real clockY: -500
|
||||
property string wallpaperPath: ""
|
||||
property JsonObject parallax: JsonObject {
|
||||
property bool enableWorkspace: true
|
||||
property real workspaceZoom: 1.07 // Relative to your screen, not wallpaper size
|
||||
property bool enableSidebar: true
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject bar: JsonObject {
|
||||
property bool bottom: false // Instead of top
|
||||
property int cornerStyle: 0 // 0: Hug | 1: Float | 2: Plain rectangle
|
||||
property bool borderless: false // true for no grouping of items
|
||||
property string topLeftIcon: "spark" // Options: distro, spark
|
||||
property bool showBackground: true
|
||||
property bool verbose: true
|
||||
property JsonObject resources: JsonObject {
|
||||
property bool alwaysShowSwap: true
|
||||
property bool alwaysShowCpu: false
|
||||
}
|
||||
property list<string> screenList: [] // List of names, like "eDP-1", find out with 'hyprctl monitors' command
|
||||
property JsonObject utilButtons: JsonObject {
|
||||
property bool showScreenSnip: true
|
||||
property bool showColorPicker: false
|
||||
property bool showMicToggle: false
|
||||
property bool showKeyboardToggle: true
|
||||
property bool showDarkModeToggle: true
|
||||
}
|
||||
property JsonObject tray: JsonObject {
|
||||
property bool monochromeIcons: true
|
||||
}
|
||||
property JsonObject workspaces: JsonObject {
|
||||
property bool monochromeIcons: true
|
||||
property int shown: 10
|
||||
property bool showAppIcons: true
|
||||
property bool alwaysShowNumbers: false
|
||||
property int showNumberDelay: 300 // milliseconds
|
||||
}
|
||||
property JsonObject weather: JsonObject {
|
||||
property bool enable: false
|
||||
property bool enableGPS: true // gps based location
|
||||
property string city: "" // When 'enableGPS' is false
|
||||
property bool useUSCS: false // Instead of metric (SI) units
|
||||
property int fetchInterval: 10 // minutes
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject battery: JsonObject {
|
||||
property int low: 20
|
||||
property int critical: 5
|
||||
property bool automaticSuspend: true
|
||||
property int suspend: 3
|
||||
}
|
||||
|
||||
property JsonObject dock: JsonObject {
|
||||
property bool enable: false
|
||||
property bool monochromeIcons: true
|
||||
property real height: 60
|
||||
property real hoverRegionHeight: 2
|
||||
property bool pinnedOnStartup: false
|
||||
property bool hoverToReveal: true // When false, only reveals on empty workspace
|
||||
property list<string> pinnedApps: [ // IDs of pinned entries
|
||||
"org.kde.dolphin", "kitty",]
|
||||
property list<string> ignoredAppRegexes: []
|
||||
}
|
||||
|
||||
property JsonObject language: JsonObject {
|
||||
property JsonObject translator: JsonObject {
|
||||
property string engine: "auto" // Run `trans -list-engines` for available engines. auto should use google
|
||||
property string targetLanguage: "auto" // Run `trans -list-all` for available languages
|
||||
property string sourceLanguage: "auto"
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject networking: JsonObject {
|
||||
property string userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"
|
||||
}
|
||||
|
||||
property JsonObject osd: JsonObject {
|
||||
property int timeout: 1000
|
||||
}
|
||||
|
||||
property JsonObject osk: JsonObject {
|
||||
property string layout: "qwerty_full"
|
||||
property bool pinnedOnStartup: false
|
||||
}
|
||||
|
||||
property JsonObject overview: JsonObject {
|
||||
property real scale: 0.18 // Relative to screen size
|
||||
property real rows: 2
|
||||
property real columns: 5
|
||||
}
|
||||
|
||||
property JsonObject resources: JsonObject {
|
||||
property int updateInterval: 3000
|
||||
}
|
||||
|
||||
property JsonObject search: JsonObject {
|
||||
property int nonAppResultDelay: 30 // This prevents lagging when typing
|
||||
property string engineBaseUrl: "https://www.google.com/search?q="
|
||||
property list<string> excludedSites: ["quora.com"]
|
||||
property bool sloppy: false // Uses levenshtein distance based scoring instead of fuzzy sort. Very weird.
|
||||
property JsonObject prefix: JsonObject {
|
||||
property string action: "/"
|
||||
property string clipboard: ";"
|
||||
property string emojis: ":"
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject sidebar: JsonObject {
|
||||
property JsonObject translator: JsonObject {
|
||||
property int delay: 300 // Delay before sending request. Reduces (potential) rate limits and lag.
|
||||
}
|
||||
property JsonObject booru: JsonObject {
|
||||
property bool allowNsfw: false
|
||||
property string defaultProvider: "yandere"
|
||||
property int limit: 20
|
||||
property JsonObject zerochan: JsonObject {
|
||||
property string username: "[unset]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject time: JsonObject {
|
||||
// https://doc.qt.io/qt-6/qtime.html#toString
|
||||
property string format: "hh:mm"
|
||||
property string dateFormat: "ddd, dd/MM"
|
||||
}
|
||||
|
||||
property JsonObject windows: JsonObject {
|
||||
property bool showTitlebar: true // Client-side decoration for shell apps
|
||||
property bool centerTitle: true
|
||||
}
|
||||
|
||||
property JsonObject hacks: JsonObject {
|
||||
property int arbitraryRaceConditionDelay: 20 // milliseconds
|
||||
}
|
||||
|
||||
property JsonObject screenshotTool: JsonObject {
|
||||
property bool showContentRegions: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
property alias states: persistentStatesJsonAdapter
|
||||
property string fileDir: Directories.state
|
||||
property string fileName: "states.json"
|
||||
property string filePath: `${root.fileDir}/${root.fileName}`
|
||||
|
||||
FileView {
|
||||
path: root.filePath
|
||||
|
||||
watchChanges: true
|
||||
onFileChanged: reload()
|
||||
onAdapterUpdated: {
|
||||
writeAdapter()
|
||||
}
|
||||
onLoadFailed: error => {
|
||||
console.log("Failed to load persistent states file:", error);
|
||||
if (error == FileViewError.FileNotFound) {
|
||||
writeAdapter();
|
||||
}
|
||||
}
|
||||
|
||||
adapter: JsonAdapter {
|
||||
id: persistentStatesJsonAdapter
|
||||
property JsonObject ai: JsonObject {
|
||||
property string model
|
||||
property real temperature: 0.5
|
||||
}
|
||||
|
||||
property JsonObject sidebar: JsonObject {
|
||||
property JsonObject bottomGroup: JsonObject {
|
||||
property bool collapsed: false
|
||||
property int tab: 0
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject booru: JsonObject {
|
||||
property bool allowNsfw: false
|
||||
property string provider: "yandere"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
pragma Singleton
|
||||
import Quickshell
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
/**
|
||||
* Trims the File protocol off the input string
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
function trimFileProtocol(str) {
|
||||
return str.startsWith("file://") ? str.slice(7) : str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the file name from a file path
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
function fileNameForPath(str) {
|
||||
if (typeof str !== "string") return "";
|
||||
const trimmed = trimFileProtocol(str);
|
||||
return trimmed.split(/[\\/]/).pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the file extension from a file path or name
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
function trimFileExt(str) {
|
||||
if (typeof str !== "string") return "";
|
||||
const trimmed = trimFileProtocol(str);
|
||||
const lastDot = trimmed.lastIndexOf(".");
|
||||
if (lastDot > -1 && lastDot > trimmed.lastIndexOf("/")) {
|
||||
return trimmed.slice(0, lastDot);
|
||||
}
|
||||
return trimmed;
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
// From https://github.com/rafzby/circular-progressbar with modifications
|
||||
// License: LGPL-3.0 - A copy can be found in `licenses` folder of repo
|
||||
|
||||
import QtQuick
|
||||
import qs.modules.common
|
||||
|
||||
/**
|
||||
* Material 3 circular progress. See https://m3.material.io/components/progress-indicators/specs
|
||||
*/
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property int size: 30
|
||||
property int lineWidth: 2
|
||||
property real value: 0
|
||||
property color primaryColor: Appearance.m3colors.m3onSecondaryContainer
|
||||
property color secondaryColor: Appearance.colors.colSecondaryContainer
|
||||
property real gapAngle: Math.PI / 9
|
||||
property bool fill: false
|
||||
property int fillOverflow: 2
|
||||
property int animationDuration: 1000
|
||||
property var easingType: Easing.OutCubic
|
||||
|
||||
width: size
|
||||
height: size
|
||||
|
||||
signal animationFinished();
|
||||
|
||||
onValueChanged: {
|
||||
canvas.degree = value * 360;
|
||||
}
|
||||
onPrimaryColorChanged: {
|
||||
canvas.requestPaint();
|
||||
}
|
||||
onSecondaryColorChanged: {
|
||||
canvas.requestPaint();
|
||||
}
|
||||
|
||||
Canvas {
|
||||
id: canvas
|
||||
|
||||
property real degree: 0
|
||||
|
||||
anchors.fill: parent
|
||||
antialiasing: true
|
||||
|
||||
onDegreeChanged: {
|
||||
requestPaint();
|
||||
}
|
||||
|
||||
onPaint: {
|
||||
var ctx = getContext("2d");
|
||||
var x = root.width / 2;
|
||||
var y = root.height / 2;
|
||||
var radius = root.size / 2 - root.lineWidth;
|
||||
var startAngle = (Math.PI / 180) * 270;
|
||||
var fullAngle = (Math.PI / 180) * (270 + 360);
|
||||
var progressAngle = (Math.PI / 180) * (270 + degree);
|
||||
var epsilon = 0.01; // Small angle in radians
|
||||
|
||||
ctx.reset();
|
||||
if (root.fill) {
|
||||
ctx.fillStyle = root.secondaryColor;
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, radius + fillOverflow, startAngle, fullAngle);
|
||||
ctx.fill();
|
||||
}
|
||||
ctx.lineCap = 'round';
|
||||
ctx.lineWidth = root.lineWidth;
|
||||
|
||||
// Secondary
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, radius, progressAngle + gapAngle, fullAngle - gapAngle);
|
||||
ctx.strokeStyle = root.secondaryColor;
|
||||
ctx.stroke();
|
||||
|
||||
// Primary (value indication)
|
||||
var endAngle = progressAngle + (value > 0 ? 0 : epsilon);
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, radius, startAngle, endAngle);
|
||||
ctx.strokeStyle = root.primaryColor;
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
Behavior on degree {
|
||||
NumberAnimation {
|
||||
duration: root.animationDuration
|
||||
easing.type: root.easingType
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import qs.modules.common.functions
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
property string entry
|
||||
property real maxWidth
|
||||
property real maxHeight
|
||||
|
||||
property string imageDecodePath: Directories.cliphistDecode
|
||||
property string imageDecodeFileName: `${entryNumber}`
|
||||
property string imageDecodeFilePath: `${imageDecodePath}/${imageDecodeFileName}`
|
||||
property string source
|
||||
|
||||
property int entryNumber: {
|
||||
if (!root.entry) return 0
|
||||
const match = root.entry.match(/^(\d+)\t/)
|
||||
return match ? parseInt(match[1]) : 0
|
||||
}
|
||||
property int imageWidth: {
|
||||
if (!root.entry) return 0
|
||||
const match = root.entry.match(/(\d+)x(\d+)/)
|
||||
return match ? parseInt(match[1]) : 0
|
||||
}
|
||||
property int imageHeight: {
|
||||
if (!root.entry) return 0
|
||||
const match = root.entry.match(/(\d+)x(\d+)/)
|
||||
return match ? parseInt(match[2]) : 0
|
||||
}
|
||||
property real scale: {
|
||||
return Math.min(
|
||||
root.maxWidth / imageWidth,
|
||||
root.maxHeight / imageHeight,
|
||||
1
|
||||
)
|
||||
}
|
||||
|
||||
color: Appearance.colors.colLayer1
|
||||
radius: Appearance.rounding.small
|
||||
implicitHeight: imageHeight * scale
|
||||
implicitWidth: imageWidth * scale
|
||||
|
||||
Component.onCompleted: {
|
||||
decodeImageProcess.running = true
|
||||
}
|
||||
|
||||
Process {
|
||||
id: decodeImageProcess
|
||||
command: ["bash", "-c",
|
||||
`[ -f ${imageDecodeFilePath} ] || echo '${StringUtils.shellSingleQuoteEscape(root.entry)}' | cliphist decode > '${imageDecodeFilePath}'`
|
||||
]
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
if (exitCode === 0) {
|
||||
root.source = imageDecodeFilePath
|
||||
} else {
|
||||
console.error("[CliphistImage] Failed to decode image for entry:", root.entry)
|
||||
root.source = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
Quickshell.execDetached(["bash", "-c", `[ -f '${imageDecodeFilePath}' ] && rm -f '${imageDecodeFilePath}'`])
|
||||
}
|
||||
|
||||
Image {
|
||||
id: image
|
||||
anchors.fill: parent
|
||||
|
||||
source: Qt.resolvedUrl(root.source)
|
||||
fillMode: Image.PreserveAspectFit
|
||||
antialiasing: true
|
||||
asynchronous: true
|
||||
|
||||
width: root.imageWidth * root.scale
|
||||
height: root.imageHeight * root.scale
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: image.width
|
||||
height: image.height
|
||||
radius: root.radius
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
property string title
|
||||
default property alias data: sectionContent.data
|
||||
|
||||
Layout.fillWidth: true
|
||||
spacing: 8
|
||||
StyledText {
|
||||
text: root.title
|
||||
font.pixelSize: Appearance.font.pixelSize.larger
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
ColumnLayout {
|
||||
id: sectionContent
|
||||
spacing: 8
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import qs.modules.common
|
||||
import QtQuick
|
||||
|
||||
Text {
|
||||
id: root
|
||||
property real iconSize: Appearance?.font.pixelSize.small ?? 16
|
||||
property real fill: 0
|
||||
property real truncatedFill: Math.round(fill * 100) / 100 // Reduce memory consumption spikes from constant font remapping
|
||||
renderType: Text.NativeRendering
|
||||
font {
|
||||
hintingPreference: Font.PreferFullHinting
|
||||
family: Appearance?.font.family.iconMaterial ?? "Material Symbols Rounded"
|
||||
pixelSize: iconSize
|
||||
weight: Font.Normal + (Font.DemiBold - Font.Normal) * fill
|
||||
variableAxes: {
|
||||
"FILL": truncatedFill,
|
||||
// "wght": font.weight,
|
||||
// "GRAD": 0,
|
||||
"opsz": iconSize,
|
||||
}
|
||||
}
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
color: Appearance.m3colors.m3onBackground
|
||||
|
||||
// Behavior on fill {
|
||||
// NumberAnimation {
|
||||
// duration: Appearance?.animation.elementMoveFast.duration ?? 200
|
||||
// easing.type: Appearance?.animation.elementMoveFast.type ?? Easing.BezierSpline
|
||||
// easing.bezierCurve: Appearance?.animation.elementMoveFast.bezierCurve ?? [0.34, 0.80, 0.34, 1.00, 1, 1]
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
import qs.modules.common
|
||||
import qs
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
spacing: 0
|
||||
required property var tabButtonList // Something like [{"icon": "notifications", "name": Translation.tr("Notifications")}, {"icon": "volume_up", "name": Translation.tr("Volume mixer")}]
|
||||
required property var externalTrackedTab
|
||||
property bool enableIndicatorAnimation: false
|
||||
property color colIndicator: Appearance?.colors.colPrimary ?? "#65558F"
|
||||
property color colBorder: Appearance?.m3colors.m3outlineVariant ?? "#C6C6D0"
|
||||
signal currentIndexChanged(int index)
|
||||
|
||||
property bool centerTabBar: parent.width > 500
|
||||
Layout.fillWidth: !centerTabBar
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
implicitWidth: Math.max(tabBar.implicitWidth, 600)
|
||||
|
||||
TabBar {
|
||||
id: tabBar
|
||||
Layout.fillWidth: true
|
||||
currentIndex: root.externalTrackedTab
|
||||
onCurrentIndexChanged: {
|
||||
root.onCurrentIndexChanged(currentIndex)
|
||||
}
|
||||
|
||||
background: Item {
|
||||
WheelHandler {
|
||||
onWheel: (event) => {
|
||||
if (event.angleDelta.y < 0)
|
||||
tabBar.currentIndex = Math.min(tabBar.currentIndex + 1, root.tabButtonList.length - 1)
|
||||
else if (event.angleDelta.y > 0)
|
||||
tabBar.currentIndex = Math.max(tabBar.currentIndex - 1, 0)
|
||||
}
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: root.tabButtonList
|
||||
delegate: PrimaryTabButton {
|
||||
selected: (index == root.externalTrackedTab)
|
||||
buttonText: modelData.name
|
||||
buttonIcon: modelData.icon
|
||||
minimumWidth: 160
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { // Tab indicator
|
||||
id: tabIndicator
|
||||
Layout.fillWidth: true
|
||||
height: 3
|
||||
Connections {
|
||||
target: root
|
||||
function onExternalTrackedTabChanged() {
|
||||
root.enableIndicatorAnimation = true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: indicator
|
||||
property int tabCount: root.tabButtonList.length
|
||||
property real fullTabSize: root.width / tabCount;
|
||||
property real targetWidth: tabBar.contentItem?.children[0]?.children[tabBar.currentIndex]?.tabContentWidth ?? 0
|
||||
|
||||
implicitWidth: targetWidth
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
}
|
||||
|
||||
x: tabBar.currentIndex * fullTabSize + (fullTabSize - targetWidth) / 2
|
||||
|
||||
color: root.colIndicator
|
||||
radius: Appearance?.rounding.full ?? 9999
|
||||
|
||||
Behavior on x {
|
||||
animation: Appearance?.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
animation: Appearance?.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // Tabbar bottom border
|
||||
id: tabBarBottomBorder
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: 1
|
||||
color: root.colBorder
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
TabButton {
|
||||
id: button
|
||||
property string buttonText
|
||||
property string buttonIcon
|
||||
property real minimumWidth: 110
|
||||
property bool selected: false
|
||||
property int tabContentWidth: contentItem.children[0].implicitWidth
|
||||
property int rippleDuration: 1200
|
||||
height: buttonBackground.height
|
||||
implicitWidth: Math.max(tabContentWidth, buttonBackground.implicitWidth, minimumWidth)
|
||||
|
||||
property color colBackground: ColorUtils.transparentize(Appearance?.colors.colLayer1Hover, 1) || "transparent"
|
||||
property color colBackgroundHover: Appearance?.colors.colLayer1Hover ?? "#E5DFED"
|
||||
property color colRipple: Appearance?.colors.colLayer1Active ?? "#D6CEE2"
|
||||
property color colActive: Appearance?.colors.colPrimary ?? "#65558F"
|
||||
property color colInactive: Appearance?.colors.colOnLayer1 ?? "#45464F"
|
||||
|
||||
component RippleAnim: NumberAnimation {
|
||||
duration: rippleDuration
|
||||
easing.type: Appearance?.animation.elementMoveEnter.type
|
||||
easing.bezierCurve: Appearance?.animationCurves.standardDecel
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: (event) => {
|
||||
const {x,y} = event
|
||||
const stateY = buttonBackground.y;
|
||||
rippleAnim.x = x;
|
||||
rippleAnim.y = y - stateY;
|
||||
|
||||
const dist = (ox,oy) => ox*ox + oy*oy
|
||||
const stateEndY = stateY + buttonBackground.height
|
||||
rippleAnim.radius = Math.sqrt(Math.max(dist(0, stateY), dist(0, stateEndY), dist(width, stateY), dist(width, stateEndY)))
|
||||
|
||||
rippleFadeAnim.complete();
|
||||
rippleAnim.restart();
|
||||
}
|
||||
onReleased: (event) => {
|
||||
button.click() // Because the MouseArea already consumed the event
|
||||
rippleFadeAnim.restart();
|
||||
}
|
||||
}
|
||||
|
||||
RippleAnim {
|
||||
id: rippleFadeAnim
|
||||
target: ripple
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: rippleAnim
|
||||
|
||||
property real x
|
||||
property real y
|
||||
property real radius
|
||||
|
||||
PropertyAction {
|
||||
target: ripple
|
||||
property: "x"
|
||||
value: rippleAnim.x
|
||||
}
|
||||
PropertyAction {
|
||||
target: ripple
|
||||
property: "y"
|
||||
value: rippleAnim.y
|
||||
}
|
||||
PropertyAction {
|
||||
target: ripple
|
||||
property: "opacity"
|
||||
value: 1
|
||||
}
|
||||
ParallelAnimation {
|
||||
RippleAnim {
|
||||
target: ripple
|
||||
properties: "implicitWidth,implicitHeight"
|
||||
from: 0
|
||||
to: rippleAnim.radius * 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
id: buttonBackground
|
||||
radius: Appearance?.rounding.small
|
||||
implicitHeight: 50
|
||||
color: (button.hovered ? button.colBackgroundHover : button.colBackground)
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: buttonBackground.width
|
||||
height: buttonBackground.height
|
||||
radius: buttonBackground.radius
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
animation: Appearance?.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
|
||||
Item {
|
||||
id: ripple
|
||||
width: ripple.implicitWidth
|
||||
height: ripple.implicitHeight
|
||||
opacity: 0
|
||||
|
||||
property real implicitWidth: 0
|
||||
property real implicitHeight: 0
|
||||
visible: width > 0 && height > 0
|
||||
|
||||
Behavior on opacity {
|
||||
animation: Appearance?.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
|
||||
RadialGradient {
|
||||
anchors.fill: parent
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: button.colRipple }
|
||||
GradientStop { position: 0.3; color: button.colRipple }
|
||||
GradientStop { position: 0.5 ; color: Qt.rgba(button.colRipple.r, button.colRipple.g, button.colRipple.b, 0) }
|
||||
}
|
||||
}
|
||||
|
||||
transform: Translate {
|
||||
x: -ripple.width / 2
|
||||
y: -ripple.height / 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors.centerIn: buttonBackground
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: 0
|
||||
MaterialSymbol {
|
||||
visible: buttonIcon?.length > 0
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: buttonIcon
|
||||
iconSize: Appearance?.font.pixelSize.hugeass ?? 25
|
||||
fill: selected ? 1 : 0
|
||||
color: selected ? button.colActive : button.colInactive
|
||||
Behavior on color {
|
||||
animation: Appearance?.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
StyledText {
|
||||
id: buttonTextWidget
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: Appearance?.font.pixelSize.small
|
||||
color: selected ? button.colActive : button.colInactive
|
||||
text: buttonText
|
||||
Behavior on color {
|
||||
animation: Appearance?.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
import QtQuick 2.9
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
enum CornerEnum { TopLeft, TopRight, BottomLeft, BottomRight }
|
||||
property var corner: RoundCorner.CornerEnum.TopLeft // Default to TopLeft
|
||||
|
||||
property int size: 25
|
||||
property color color: "#000000"
|
||||
|
||||
onColorChanged: {
|
||||
canvas.requestPaint();
|
||||
}
|
||||
onCornerChanged: {
|
||||
canvas.requestPaint();
|
||||
}
|
||||
|
||||
implicitWidth: size
|
||||
implicitHeight: size
|
||||
|
||||
Canvas {
|
||||
id: canvas
|
||||
|
||||
anchors.fill: parent
|
||||
antialiasing: true
|
||||
|
||||
onPaint: {
|
||||
var ctx = getContext("2d");
|
||||
var r = root.size;
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.beginPath();
|
||||
switch (root.corner) {
|
||||
case RoundCorner.CornerEnum.TopLeft:
|
||||
ctx.arc(r, r, r, Math.PI, 3 * Math.PI / 2);
|
||||
ctx.lineTo(0, 0);
|
||||
break;
|
||||
case RoundCorner.CornerEnum.TopRight:
|
||||
ctx.arc(0, r, r, 3 * Math.PI / 2, 2 * Math.PI);
|
||||
ctx.lineTo(r, 0);
|
||||
break;
|
||||
case RoundCorner.CornerEnum.BottomLeft:
|
||||
ctx.arc(r, 0, r, Math.PI / 2, Math.PI);
|
||||
ctx.lineTo(0, r);
|
||||
break;
|
||||
case RoundCorner.CornerEnum.BottomRight:
|
||||
ctx.arc(0, 0, r, 0, Math.PI / 2);
|
||||
ctx.lineTo(r, r);
|
||||
break;
|
||||
}
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = root.color;
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on size {
|
||||
animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Hyprland
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
|
||||
GroupButton {
|
||||
id: root
|
||||
horizontalPadding: 12
|
||||
verticalPadding: 8
|
||||
bounce: false
|
||||
property bool leftmost: false
|
||||
property bool rightmost: false
|
||||
leftRadius: (toggled || leftmost) ? (height / 2) : Appearance.rounding.unsharpenmore
|
||||
rightRadius: (toggled || rightmost) ? (height / 2) : Appearance.rounding.unsharpenmore
|
||||
colBackground: Appearance.colors.colSecondaryContainer
|
||||
contentItem: StyledText {
|
||||
color: parent.toggled ? Appearance.colors.colOnPrimary : Appearance.colors.colOnSecondaryContainer
|
||||
text: root.buttonText
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import qs.modules.common
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
Label {
|
||||
renderType: Text.NativeRendering
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font {
|
||||
hintingPreference: Font.PreferFullHinting
|
||||
family: Appearance?.font.family.main ?? "sans-serif"
|
||||
pixelSize: Appearance?.font.pixelSize.small ?? 15
|
||||
}
|
||||
color: Appearance?.m3colors.m3onBackground ?? "black"
|
||||
linkColor: Appearance?.m3colors.m3primary
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
import qs
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import QtQuick
|
||||
|
||||
/**
|
||||
* A ListView with animations.
|
||||
*/
|
||||
ListView {
|
||||
id: root
|
||||
spacing: 5
|
||||
property real removeOvershoot: 20 // Account for gaps and bouncy animations
|
||||
property int dragIndex: -1
|
||||
property real dragDistance: 0
|
||||
property bool popin: true
|
||||
|
||||
function resetDrag() {
|
||||
root.dragIndex = -1
|
||||
root.dragDistance = 0
|
||||
}
|
||||
|
||||
add: Transition {
|
||||
animations: [
|
||||
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
properties: popin ? "opacity,scale" : "opacity",
|
||||
from: 0,
|
||||
to: 1,
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
||||
addDisplaced: Transition {
|
||||
animations: [
|
||||
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
property: "y",
|
||||
}),
|
||||
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
properties: popin ? "opacity,scale" : "opacity",
|
||||
to: 1,
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
||||
// displaced: Transition {
|
||||
// animations: [
|
||||
// Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
// property: "y",
|
||||
// }),
|
||||
// Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
// properties: "opacity,scale",
|
||||
// to: 1,
|
||||
// }),
|
||||
// ]
|
||||
// }
|
||||
|
||||
// move: Transition {
|
||||
// animations: [
|
||||
// Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
// property: "y",
|
||||
// }),
|
||||
// Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
// properties: "opacity,scale",
|
||||
// to: 1,
|
||||
// }),
|
||||
// ]
|
||||
// }
|
||||
// moveDisplaced: Transition {
|
||||
// animations: [
|
||||
// Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
// property: "y",
|
||||
// }),
|
||||
// Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
// properties: "opacity,scale",
|
||||
// to: 1,
|
||||
// }),
|
||||
// ]
|
||||
// }
|
||||
|
||||
remove: Transition {
|
||||
animations: [
|
||||
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
property: "x",
|
||||
to: root.width + root.removeOvershoot,
|
||||
}),
|
||||
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
property: "opacity",
|
||||
to: 0,
|
||||
})
|
||||
]
|
||||
}
|
||||
|
||||
// This is movement when something is removed, not removing animation!
|
||||
removeDisplaced: Transition {
|
||||
animations: [
|
||||
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
property: "y",
|
||||
}),
|
||||
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
properties: "opacity,scale",
|
||||
to: 1,
|
||||
}),
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
/**
|
||||
* Material 3 progress bar. See https://m3.material.io/components/progress-indicators/overview
|
||||
*/
|
||||
ProgressBar {
|
||||
id: root
|
||||
property real valueBarWidth: 120
|
||||
property real valueBarHeight: 4
|
||||
property real valueBarGap: 4
|
||||
property color highlightColor: Appearance?.colors.colPrimary ?? "#685496"
|
||||
property color trackColor: Appearance?.m3colors.m3secondaryContainer ?? "#F1D3F9"
|
||||
property bool sperm: false // If true, the progress bar will have a wavy fill effect
|
||||
property bool animateSperm: true
|
||||
property real spermAmplitudeMultiplier: sperm ? 0.5 : 0
|
||||
property real spermFrequency: 6
|
||||
property real spermFps: 60
|
||||
|
||||
Behavior on spermAmplitudeMultiplier {
|
||||
animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
Behavior on value {
|
||||
animation: Appearance?.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
background: Item {
|
||||
anchors.fill: parent
|
||||
implicitHeight: valueBarHeight
|
||||
implicitWidth: valueBarWidth
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors.fill: parent
|
||||
|
||||
Canvas {
|
||||
id: wavyFill
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
height: parent.height * 6
|
||||
onPaint: {
|
||||
var ctx = getContext("2d");
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
|
||||
var progress = root.visualPosition;
|
||||
var fillWidth = progress * width;
|
||||
var amplitude = parent.height * root.spermAmplitudeMultiplier;
|
||||
var frequency = root.spermFrequency;
|
||||
var phase = Date.now() / 400.0;
|
||||
var centerY = height / 2;
|
||||
|
||||
ctx.strokeStyle = root.highlightColor;
|
||||
ctx.lineWidth = parent.height;
|
||||
ctx.lineCap = "round";
|
||||
ctx.beginPath();
|
||||
for (var x = ctx.lineWidth / 2; x <= fillWidth; x += 1) {
|
||||
var waveY = centerY + amplitude * Math.sin(frequency * 2 * Math.PI * x / width + phase);
|
||||
if (x === 0)
|
||||
ctx.moveTo(x, waveY);
|
||||
else
|
||||
ctx.lineTo(x, waveY);
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
Connections {
|
||||
target: root
|
||||
function onValueChanged() { wavyFill.requestPaint(); }
|
||||
function onHighlightColorChanged() { wavyFill.requestPaint(); }
|
||||
}
|
||||
Timer {
|
||||
interval: 1000 / root.spermFps
|
||||
running: root.animateSperm
|
||||
repeat: root.sperm
|
||||
onTriggered: wavyFill.requestPaint()
|
||||
}
|
||||
}
|
||||
Rectangle { // Right remaining part fill
|
||||
anchors.right: parent.right
|
||||
width: (1 - root.visualPosition) * parent.width - valueBarGap
|
||||
height: parent.height
|
||||
radius: Appearance?.rounding.full ?? 9999
|
||||
color: root.trackColor
|
||||
}
|
||||
Rectangle { // Stop point
|
||||
anchors.right: parent.right
|
||||
width: valueBarGap
|
||||
height: valueBarGap
|
||||
radius: Appearance?.rounding.full ?? 9999
|
||||
color: root.highlightColor
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Widgets
|
||||
|
||||
/**
|
||||
* Material 3 slider. See https://m3.material.io/components/sliders/overview
|
||||
* It doesn't exactly match the spec because it does not make sense to have stuff on a computer that fucking huge.
|
||||
* Should be at 3/4 scale...
|
||||
*/
|
||||
|
||||
Slider {
|
||||
id: root
|
||||
|
||||
property list<real> stopIndicatorValues: [1]
|
||||
enum Configuration {
|
||||
XS = 12,
|
||||
S = 18,
|
||||
M = 30,
|
||||
L = 42,
|
||||
XL = 72
|
||||
}
|
||||
|
||||
property var configuration: StyledSlider.Configuration.S
|
||||
|
||||
property real handleDefaultWidth: 3
|
||||
property real handlePressedWidth: 1.5
|
||||
|
||||
property color highlightColor: Appearance.colors.colPrimary
|
||||
property color trackColor: Appearance.colors.colSecondaryContainer
|
||||
property color handleColor: Appearance.m3colors.m3onSecondaryContainer
|
||||
property color dotColor: Appearance.m3colors.m3onSecondaryContainer
|
||||
property color dotColorHighlighted: Appearance.m3colors.m3onPrimary
|
||||
property real unsharpenRadius: Appearance.rounding.unsharpen
|
||||
property real trackWidth: configuration
|
||||
property real trackRadius: trackWidth >= StyledSlider.Configuration.XL ? 21
|
||||
: trackWidth >= StyledSlider.Configuration.L ? 12
|
||||
: trackWidth >= StyledSlider.Configuration.M ? 9
|
||||
: 6
|
||||
property real handleHeight: Math.max(33, trackWidth + 9)
|
||||
property real handleWidth: root.pressed ? handlePressedWidth : handleDefaultWidth
|
||||
property real handleMargins: 4
|
||||
onHandleMarginsChanged: {
|
||||
console.log("Handle margins changed to", handleMargins);
|
||||
}
|
||||
property real trackDotSize: 3
|
||||
property string tooltipContent: `${Math.round(value * 100)}%`
|
||||
|
||||
leftPadding: handleMargins
|
||||
rightPadding: handleMargins
|
||||
property real effectiveDraggingWidth: width - leftPadding - rightPadding
|
||||
|
||||
Layout.fillWidth: true
|
||||
from: 0
|
||||
to: 1
|
||||
|
||||
Behavior on value { // This makes the adjusted value (like volume) shift smoothly
|
||||
SmoothedAnimation {
|
||||
velocity: Appearance.animation.elementMoveFast.velocity
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on handleMargins {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
component TrackDot: Rectangle {
|
||||
required property real value
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: root.handleMargins + (value * root.effectiveDraggingWidth) - (root.trackDotSize / 2)
|
||||
width: root.trackDotSize
|
||||
height: root.trackDotSize
|
||||
radius: Appearance.rounding.full
|
||||
color: value > root.visualPosition ? root.dotColor : root.dotColorHighlighted
|
||||
|
||||
Behavior on color {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onPressed: (mouse) => mouse.accepted = false
|
||||
cursorShape: root.pressed ? Qt.ClosedHandCursor : Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
background: Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width
|
||||
implicitHeight: trackWidth
|
||||
|
||||
// Fill left
|
||||
Rectangle {
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
}
|
||||
width: root.handleMargins + (root.visualPosition * root.effectiveDraggingWidth) - (root.handleWidth / 2 + root.handleMargins)
|
||||
height: trackWidth
|
||||
color: root.highlightColor
|
||||
topLeftRadius: root.trackRadius
|
||||
bottomLeftRadius: root.trackRadius
|
||||
topRightRadius: root.unsharpenRadius
|
||||
bottomRightRadius: root.unsharpenRadius
|
||||
}
|
||||
|
||||
// Fill right
|
||||
Rectangle {
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
right: parent.right
|
||||
}
|
||||
width: root.handleMargins + ((1 - root.visualPosition) * root.effectiveDraggingWidth) - (root.handleWidth / 2 + root.handleMargins)
|
||||
height: trackWidth
|
||||
color: root.trackColor
|
||||
topRightRadius: root.trackRadius
|
||||
bottomRightRadius: root.trackRadius
|
||||
topLeftRadius: root.unsharpenRadius
|
||||
bottomLeftRadius: root.unsharpenRadius
|
||||
}
|
||||
|
||||
// Stop indicators
|
||||
Repeater {
|
||||
model: root.stopIndicatorValues
|
||||
TrackDot {
|
||||
required property real modelData
|
||||
value: modelData
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handle: Rectangle {
|
||||
id: handle
|
||||
|
||||
implicitWidth: root.handleWidth
|
||||
implicitHeight: root.handleHeight
|
||||
x: root.handleMargins + (root.visualPosition * root.effectiveDraggingWidth) - (root.handleWidth / 2)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
radius: Appearance.rounding.full
|
||||
color: root.handleColor
|
||||
|
||||
Behavior on implicitWidth {
|
||||
animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
StyledToolTip {
|
||||
extraVisibleCondition: root.pressed
|
||||
content: root.tooltipContent
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import qs.modules.common
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Text {
|
||||
renderType: Text.NativeRendering
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font {
|
||||
hintingPreference: Font.PreferFullHinting
|
||||
family: Appearance?.font.family.main ?? "sans-serif"
|
||||
pixelSize: Appearance?.font.pixelSize.small ?? 15
|
||||
}
|
||||
color: Appearance?.m3colors.m3onBackground ?? "black"
|
||||
linkColor: Appearance?.m3colors.m3primary
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
ToolTip {
|
||||
id: root
|
||||
property string content
|
||||
property bool extraVisibleCondition: true
|
||||
property bool alternativeVisibleCondition: false
|
||||
property bool internalVisibleCondition: {
|
||||
const ans = (extraVisibleCondition && (parent.hovered === undefined || parent?.hovered)) || alternativeVisibleCondition
|
||||
return ans
|
||||
}
|
||||
verticalPadding: 5
|
||||
horizontalPadding: 10
|
||||
opacity: internalVisibleCondition ? 1 : 0
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
background: null
|
||||
|
||||
contentItem: Item {
|
||||
id: contentItemBackground
|
||||
implicitWidth: tooltipTextObject.width + 2 * root.horizontalPadding
|
||||
implicitHeight: tooltipTextObject.height + 2 * root.verticalPadding
|
||||
|
||||
Rectangle {
|
||||
id: backgroundRectangle
|
||||
anchors.bottom: contentItemBackground.bottom
|
||||
anchors.horizontalCenter: contentItemBackground.horizontalCenter
|
||||
color: Appearance?.colors.colTooltip ?? "#3C4043"
|
||||
radius: Appearance?.rounding.verysmall ?? 7
|
||||
width: internalVisibleCondition ? (tooltipTextObject.width + 2 * padding) : 0
|
||||
height: internalVisibleCondition ? (tooltipTextObject.height + 2 * padding) : 0
|
||||
clip: true
|
||||
|
||||
Behavior on width {
|
||||
animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on height {
|
||||
animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: tooltipTextObject
|
||||
anchors.centerIn: parent
|
||||
text: content
|
||||
font.pixelSize: Appearance?.font.pixelSize.smaller ?? 14
|
||||
font.hintingPreference: Font.PreferNoHinting // Prevent shaky text
|
||||
color: Appearance?.colors.colOnTooltip ?? "#FFFFFF"
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
|
||||
/**
|
||||
* @param { string } summary
|
||||
* @returns { string }
|
||||
*/
|
||||
function findSuitableMaterialSymbol(summary = "") {
|
||||
const defaultType = 'chat';
|
||||
if(summary.length === 0) return defaultType;
|
||||
|
||||
const keywordsToTypes = {
|
||||
'reboot': 'restart_alt',
|
||||
'recording': 'screen_record',
|
||||
'battery': 'power',
|
||||
'power': 'power',
|
||||
'screenshot': 'screenshot_monitor',
|
||||
'welcome': 'waving_hand',
|
||||
'time': 'scheduleb',
|
||||
'installed': 'download',
|
||||
'configuration reloaded': 'reset_wrench',
|
||||
'config': 'reset_wrench',
|
||||
'update': 'update',
|
||||
'ai response': 'neurology',
|
||||
'control': 'settings',
|
||||
'upscale': 'compare',
|
||||
'install': 'deployed_code_update',
|
||||
'startswith:file': 'folder_copy', // Declarative startsWith check
|
||||
};
|
||||
|
||||
const lowerSummary = summary.toLowerCase();
|
||||
|
||||
for (const [keyword, type] of Object.entries(keywordsToTypes)) {
|
||||
if (keyword.startsWith('startswith:')) {
|
||||
const startsWithKeyword = keyword.replace('startswith:', '');
|
||||
if (lowerSummary.startsWith(startsWithKeyword)) {
|
||||
return type;
|
||||
}
|
||||
} else if (lowerSummary.includes(keyword)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return defaultType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param { number | string | Date } timestamp
|
||||
* @returns { string }
|
||||
*/
|
||||
const getFriendlyNotifTimeString = (timestamp) => {
|
||||
if (!timestamp) return '';
|
||||
const messageTime = new Date(timestamp);
|
||||
const now = new Date();
|
||||
const diffMs = now.getTime() - messageTime.getTime();
|
||||
|
||||
// Less than 1 minute
|
||||
if (diffMs < 60000)
|
||||
return 'Now';
|
||||
|
||||
// Same day - show relative time
|
||||
if (messageTime.toDateString() === now.toDateString()) {
|
||||
const diffMinutes = Math.floor(diffMs / 60000);
|
||||
const diffHours = Math.floor(diffMs / 3600000);
|
||||
|
||||
if (diffHours > 0) {
|
||||
return `${diffHours}h`;
|
||||
} else {
|
||||
return `${diffMinutes}m`;
|
||||
}
|
||||
}
|
||||
|
||||
// Yesterday
|
||||
if (messageTime.toDateString() === new Date(now.getTime() - 86400000).toDateString())
|
||||
return 'Yesterday';
|
||||
|
||||
// Older dates
|
||||
return Qt.formatDateTime(messageTime, "MMMM dd");
|
||||
};
|
||||
@@ -1,99 +0,0 @@
|
||||
import qs
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.lock
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
// This stores all the information shared between the lock surfaces on each screen.
|
||||
// https://github.com/quickshell-mirror/quickshell-examples/tree/master/lockscreen
|
||||
LockContext {
|
||||
id: lockContext
|
||||
|
||||
onUnlocked: {
|
||||
// Unlock the screen before exiting, or the compositor will display a
|
||||
// fallback lock you can't interact with.
|
||||
GlobalStates.screenLocked = false;
|
||||
}
|
||||
}
|
||||
|
||||
WlSessionLock {
|
||||
id: lock
|
||||
locked: GlobalStates.screenLocked
|
||||
|
||||
WlSessionLockSurface {
|
||||
color: "transparent"
|
||||
Loader {
|
||||
active: GlobalStates.screenLocked
|
||||
anchors.fill: parent
|
||||
opacity: active ? 1 : 0
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
sourceComponent: LockSurface {
|
||||
context: lockContext
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Blur layer hack
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
LazyLoader {
|
||||
id: blurLayerLoader
|
||||
required property var modelData
|
||||
active: GlobalStates.screenLocked
|
||||
component: PanelWindow {
|
||||
screen: blurLayerLoader.modelData
|
||||
WlrLayershell.namespace: "quickshell:lockWindowPusher"
|
||||
color: "transparent"
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
// implicitHeight: lockContext.currentText == "" ? 1 : screen.height
|
||||
implicitHeight: 1
|
||||
exclusiveZone: screen.height * 3 // For some reason if we don't multiply by some number it would look really weird
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "lock"
|
||||
|
||||
function activate(): void {
|
||||
GlobalStates.screenLocked = true;
|
||||
}
|
||||
function focus(): void {
|
||||
lockContext.shouldReFocus();
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "lock"
|
||||
description: "Locks the screen"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.screenLocked = true;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "lockFocus"
|
||||
description: "Re-focuses the lock screen. This is because Hyprland after waking up for whatever reason"
|
||||
+ "decides to keyboard-unfocus the lock screen"
|
||||
|
||||
onPressed: {
|
||||
// console.log("I BEG FOR PLEAS REFOCUZ")
|
||||
lockContext.shouldReFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
import qs
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Services.Pam
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
signal shouldReFocus()
|
||||
signal unlocked()
|
||||
signal failed()
|
||||
|
||||
// These properties are in the context and not individual lock surfaces
|
||||
// so all surfaces can share the same state.
|
||||
property string currentText: ""
|
||||
property bool unlockInProgress: false
|
||||
property bool showFailure: false
|
||||
|
||||
Timer {
|
||||
id: passwordClearTimer
|
||||
interval: 10000
|
||||
onTriggered: {
|
||||
root.currentText = "";
|
||||
}
|
||||
}
|
||||
|
||||
onCurrentTextChanged: {
|
||||
showFailure = false; // Clear the failure text once the user starts typing.
|
||||
GlobalStates.screenLockContainsCharacters = currentText.length > 0;
|
||||
passwordClearTimer.restart();
|
||||
}
|
||||
|
||||
function tryUnlock() {
|
||||
if (currentText === "") return;
|
||||
|
||||
root.unlockInProgress = true;
|
||||
pam.start();
|
||||
}
|
||||
|
||||
PamContext {
|
||||
id: pam
|
||||
|
||||
// Its best to have a custom pam config for quickshell, as the system one
|
||||
// might not be what your interface expects, and break in some way.
|
||||
// This particular example only supports passwords.
|
||||
configDirectory: "pam"
|
||||
config: "password.conf"
|
||||
|
||||
// pam_unix will ask for a response for the password prompt
|
||||
onPamMessage: {
|
||||
if (this.responseRequired) {
|
||||
this.respond(root.currentText);
|
||||
}
|
||||
}
|
||||
|
||||
// pam_unix won't send any important messages so all we need is the completion status.
|
||||
onCompleted: result => {
|
||||
if (result == PamResult.Success) {
|
||||
root.unlocked();
|
||||
} else {
|
||||
root.showFailure = true;
|
||||
}
|
||||
|
||||
root.currentText = "";
|
||||
root.unlockInProgress = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
|
||||
MouseArea {
|
||||
id: root
|
||||
required property LockContext context
|
||||
property bool active: false
|
||||
property bool showInputField: active || context.currentText.length > 0
|
||||
|
||||
function forceFieldFocus() {
|
||||
passwordBox.forceActiveFocus();
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
forceFieldFocus();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: context
|
||||
function onShouldReFocus() {
|
||||
forceFieldFocus();
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: (event) => { // Esc to clear
|
||||
// console.log("KEY!!")
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
root.context.currentText = ""
|
||||
}
|
||||
forceFieldFocus();
|
||||
}
|
||||
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onPressed: (mouse) => {
|
||||
forceFieldFocus();
|
||||
// console.log("Pressed")
|
||||
}
|
||||
onPositionChanged: (mouse) => {
|
||||
forceFieldFocus();
|
||||
// console.log(JSON.stringify(mouse))
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
// RippleButton {
|
||||
// anchors {
|
||||
// top: parent.top
|
||||
// left: parent.left
|
||||
// leftMargin: 10
|
||||
// topMargin: 10
|
||||
// }
|
||||
// implicitHeight: 40
|
||||
// colBackground: Appearance.colors.colLayer2
|
||||
// onClicked: context.unlocked()
|
||||
// contentItem: StyledText {
|
||||
// text: "[[ DEBUG BYPASS ]]"
|
||||
// }
|
||||
// }
|
||||
|
||||
// Password entry
|
||||
Rectangle {
|
||||
id: passwordBoxContainer
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
bottom: parent.bottom
|
||||
bottomMargin: root.showInputField ? 20 : -height
|
||||
}
|
||||
Behavior on anchors.bottomMargin {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
radius: Appearance.rounding.full
|
||||
color: Appearance.colors.colLayer2
|
||||
implicitWidth: 160
|
||||
implicitHeight: 44
|
||||
|
||||
StyledText {
|
||||
visible: root.context.showFailure && passwordBox.text.length == 0
|
||||
anchors.centerIn: parent
|
||||
text: "Incorrect"
|
||||
color: Appearance.m3colors.m3error
|
||||
}
|
||||
|
||||
StyledTextInput {
|
||||
id: passwordBox
|
||||
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: 10
|
||||
}
|
||||
clip: true
|
||||
horizontalAlignment: TextInput.AlignHCenter
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
focus: true
|
||||
onFocusChanged: root.forceFieldFocus();
|
||||
color: Appearance.colors.colOnLayer2
|
||||
font {
|
||||
pixelSize: 10
|
||||
}
|
||||
|
||||
// Password
|
||||
enabled: !root.context.unlockInProgress
|
||||
echoMode: TextInput.Password
|
||||
inputMethodHints: Qt.ImhSensitiveData
|
||||
|
||||
// Synchronizing (across monitors) and unlocking
|
||||
onTextChanged: root.context.currentText = this.text
|
||||
onAccepted: root.context.tryUnlock()
|
||||
Connections {
|
||||
target: root.context
|
||||
function onCurrentTextChanged() {
|
||||
passwordBox.text = root.context.currentText;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RippleButton {
|
||||
anchors {
|
||||
verticalCenter: passwordBoxContainer.verticalCenter
|
||||
left: passwordBoxContainer.right
|
||||
leftMargin: 5
|
||||
}
|
||||
|
||||
visible: opacity > 0
|
||||
implicitHeight: passwordBoxContainer.implicitHeight - 12
|
||||
implicitWidth: implicitHeight
|
||||
toggled: true
|
||||
buttonRadius: passwordBoxContainer.radius
|
||||
colBackground: Appearance.colors.colLayer2
|
||||
onClicked: root.context.tryUnlock()
|
||||
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
iconSize: 24
|
||||
text: "arrow_right_alt"
|
||||
color: Appearance.colors.colOnPrimary
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
auth required pam_unix.so
|
||||
@@ -1,188 +0,0 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import qs
|
||||
import qs.modules.common.functions
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Services.Mpris
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
property bool visible: false
|
||||
readonly property MprisPlayer activePlayer: MprisController.activePlayer
|
||||
readonly property var realPlayers: Mpris.players.values.filter(player => isRealPlayer(player))
|
||||
readonly property var meaningfulPlayers: filterDuplicatePlayers(realPlayers)
|
||||
readonly property real osdWidth: Appearance.sizes.osdWidth
|
||||
readonly property real widgetWidth: Appearance.sizes.mediaControlsWidth
|
||||
readonly property real widgetHeight: Appearance.sizes.mediaControlsHeight
|
||||
property real contentPadding: 13
|
||||
property real popupRounding: Appearance.rounding.screenRounding - Appearance.sizes.elevationMargin + 1
|
||||
property real artRounding: Appearance.rounding.verysmall
|
||||
property list<real> visualizerPoints: []
|
||||
|
||||
property bool hasPlasmaIntegration: false
|
||||
function isRealPlayer(player) {
|
||||
// return true
|
||||
return (
|
||||
// Remove unecessary native buses from browsers if there's plasma integration
|
||||
!(hasPlasmaIntegration && player.dbusName.startsWith('org.mpris.MediaPlayer2.firefox')) &&
|
||||
!(hasPlasmaIntegration && player.dbusName.startsWith('org.mpris.MediaPlayer2.chromium')) &&
|
||||
// playerctld just copies other buses and we don't need duplicates
|
||||
!player.dbusName?.startsWith('org.mpris.MediaPlayer2.playerctld') &&
|
||||
// Non-instance mpd bus
|
||||
!(player.dbusName?.endsWith('.mpd') && !player.dbusName.endsWith('MediaPlayer2.mpd'))
|
||||
);
|
||||
}
|
||||
function filterDuplicatePlayers(players) {
|
||||
let filtered = [];
|
||||
let used = new Set();
|
||||
|
||||
for (let i = 0; i < players.length; ++i) {
|
||||
if (used.has(i)) continue;
|
||||
let p1 = players[i];
|
||||
let group = [i];
|
||||
|
||||
// Find duplicates by trackTitle prefix
|
||||
for (let j = i + 1; j < players.length; ++j) {
|
||||
let p2 = players[j];
|
||||
if (p1.trackTitle && p2.trackTitle &&
|
||||
(p1.trackTitle.includes(p2.trackTitle)
|
||||
|| p2.trackTitle.includes(p1.trackTitle))
|
||||
|| (p1.position - p2.position <= 2 && p1.length - p2.length <= 2)) {
|
||||
group.push(j);
|
||||
}
|
||||
}
|
||||
|
||||
// Pick the one with non-empty trackArtUrl, or fallback to the first
|
||||
let chosenIdx = group.find(idx => players[idx].trackArtUrl && players[idx].trackArtUrl.length > 0);
|
||||
if (chosenIdx === undefined) chosenIdx = group[0];
|
||||
|
||||
filtered.push(players[chosenIdx]);
|
||||
group.forEach(idx => used.add(idx));
|
||||
}
|
||||
return filtered;
|
||||
}
|
||||
|
||||
Process {
|
||||
id: cavaProc
|
||||
running: mediaControlsLoader.active
|
||||
onRunningChanged: {
|
||||
if (!cavaProc.running) {
|
||||
root.visualizerPoints = [];
|
||||
}
|
||||
}
|
||||
command: ["cava", "-p", `${FileUtils.trimFileProtocol(Directories.scriptPath)}/cava/raw_output_config.txt`]
|
||||
stdout: SplitParser {
|
||||
onRead: data => {
|
||||
// Parse `;`-separated values into the visualizerPoints array
|
||||
let points = data.split(";").map(p => parseFloat(p.trim())).filter(p => !isNaN(p));
|
||||
root.visualizerPoints = points;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: mediaControlsLoader
|
||||
active: false
|
||||
|
||||
sourceComponent: PanelWindow {
|
||||
id: mediaControlsRoot
|
||||
visible: true
|
||||
|
||||
exclusiveZone: 0
|
||||
implicitWidth: (
|
||||
(mediaControlsRoot.screen.width / 2) // Middle of screen
|
||||
- (osdWidth / 2) // Dodge OSD
|
||||
- (widgetWidth / 2) // Account for widget width
|
||||
) * 2
|
||||
implicitHeight: playerColumnLayout.implicitHeight
|
||||
color: "transparent"
|
||||
WlrLayershell.namespace: "quickshell:mediaControls"
|
||||
|
||||
anchors {
|
||||
top: !Config.options.bar.bottom
|
||||
bottom: Config.options.bar.bottom
|
||||
left: true
|
||||
}
|
||||
mask: Region {
|
||||
item: playerColumnLayout
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: playerColumnLayout
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
x: (mediaControlsRoot.screen.width / 2) // Middle of screen
|
||||
- (osdWidth / 2) // Dodge OSD
|
||||
- (widgetWidth) // Account for widget width
|
||||
+ (Appearance.sizes.elevationMargin) // It's fine for shadows to overlap
|
||||
spacing: -Appearance.sizes.elevationMargin // Shadow overlap okay
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: root.meaningfulPlayers
|
||||
}
|
||||
delegate: PlayerControl {
|
||||
required property MprisPlayer modelData
|
||||
player: modelData
|
||||
visualizerPoints: root.visualizerPoints
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "mediaControls"
|
||||
|
||||
function toggle(): void {
|
||||
mediaControlsLoader.active = !mediaControlsLoader.active;
|
||||
if(mediaControlsLoader.active) Notifications.timeoutAll();
|
||||
}
|
||||
|
||||
function close(): void {
|
||||
mediaControlsLoader.active = false;
|
||||
}
|
||||
|
||||
function open(): void {
|
||||
mediaControlsLoader.active = true;
|
||||
Notifications.timeoutAll();
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "mediaControlsToggle"
|
||||
description: "Toggles media controls on press"
|
||||
|
||||
onPressed: {
|
||||
if (!mediaControlsLoader.active && Mpris.players.values.filter(player => isRealPlayer(player)).length === 0) {
|
||||
return;
|
||||
}
|
||||
mediaControlsLoader.active = !mediaControlsLoader.active;
|
||||
if(mediaControlsLoader.active) Notifications.timeoutAll();
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "mediaControlsOpen"
|
||||
description: "Opens media controls on press"
|
||||
|
||||
onPressed: {
|
||||
mediaControlsLoader.active = true;
|
||||
Notifications.timeoutAll();
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "mediaControlsClose"
|
||||
description: "Closes media controls on press"
|
||||
|
||||
onPressed: {
|
||||
mediaControlsLoader.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Wayland
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
property bool showOsdValues: false
|
||||
property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
|
||||
property var brightnessMonitor: Brightness.getMonitorForScreen(focusedScreen)
|
||||
|
||||
function triggerOsd() {
|
||||
showOsdValues = true
|
||||
osdTimeout.restart()
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: osdTimeout
|
||||
interval: Config.options.osd.timeout
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: {
|
||||
showOsdValues = false
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Audio.sink?.audio ?? null
|
||||
function onVolumeChanged() {
|
||||
if (!Audio.ready) return
|
||||
root.showOsdValues = false
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Brightness
|
||||
function onBrightnessChanged() {
|
||||
if (!root.brightnessMonitor.ready) return
|
||||
root.triggerOsd()
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: osdLoader
|
||||
active: showOsdValues
|
||||
|
||||
sourceComponent: PanelWindow {
|
||||
id: osdRoot
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
function onFocusedScreenChanged() {
|
||||
osdRoot.screen = root.focusedScreen
|
||||
}
|
||||
}
|
||||
|
||||
exclusionMode: ExclusionMode.Normal
|
||||
WlrLayershell.namespace: "quickshell:onScreenDisplay"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
color: "transparent"
|
||||
|
||||
anchors {
|
||||
top: !Config.options.bar.bottom
|
||||
bottom: Config.options.bar.bottom
|
||||
}
|
||||
mask: Region {
|
||||
item: osdValuesWrapper
|
||||
}
|
||||
|
||||
implicitWidth: columnLayout.implicitWidth
|
||||
implicitHeight: columnLayout.implicitHeight
|
||||
visible: osdLoader.active
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
Item {
|
||||
id: osdValuesWrapper
|
||||
// Extra space for shadow
|
||||
implicitHeight: osdValues.implicitHeight + Appearance.sizes.elevationMargin * 2
|
||||
implicitWidth: osdValues.implicitWidth
|
||||
clip: true
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: root.showOsdValues = false
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.menuDecel.duration
|
||||
easing.type: Appearance.animation.menuDecel.type
|
||||
}
|
||||
}
|
||||
|
||||
OsdValueIndicator {
|
||||
id: osdValues
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.sizes.elevationMargin
|
||||
value: root.brightnessMonitor?.brightness ?? 50
|
||||
icon: "light_mode"
|
||||
rotateIcon: true
|
||||
scaleIcon: true
|
||||
name: Translation.tr("Brightness")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "osdBrightness"
|
||||
|
||||
function trigger() {
|
||||
root.triggerOsd()
|
||||
}
|
||||
|
||||
function hide() {
|
||||
showOsdValues = false
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
showOsdValues = !showOsdValues
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "osdBrightnessTrigger"
|
||||
description: "Triggers brightness OSD on press"
|
||||
|
||||
onPressed: {
|
||||
root.triggerOsd()
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "osdBrightnessHide"
|
||||
description: "Hides brightness OSD on press"
|
||||
|
||||
onPressed: {
|
||||
root.showOsdValues = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,244 +0,0 @@
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Scope {
|
||||
id: overviewScope
|
||||
property bool dontAutoCancelSearch: false
|
||||
Variants {
|
||||
id: overviewVariants
|
||||
model: Quickshell.screens
|
||||
PanelWindow {
|
||||
id: root
|
||||
required property var modelData
|
||||
property string searchingText: ""
|
||||
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen)
|
||||
property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor.id)
|
||||
screen: modelData
|
||||
visible: GlobalStates.overviewOpen
|
||||
|
||||
WlrLayershell.namespace: "quickshell:overview"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
// WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
|
||||
color: "transparent"
|
||||
|
||||
mask: Region {
|
||||
item: GlobalStates.overviewOpen ? columnLayout : null
|
||||
}
|
||||
// HyprlandWindow.visibleMask: Region { // Buggy with scaled monitors
|
||||
// item: GlobalStates.overviewOpen ? columnLayout : null
|
||||
// }
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
bottom: true
|
||||
}
|
||||
|
||||
HyprlandFocusGrab {
|
||||
id: grab
|
||||
windows: [root]
|
||||
property bool canBeActive: root.monitorIsFocused
|
||||
active: false
|
||||
onCleared: () => {
|
||||
if (!active)
|
||||
GlobalStates.overviewOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: GlobalStates
|
||||
function onOverviewOpenChanged() {
|
||||
if (!GlobalStates.overviewOpen) {
|
||||
searchWidget.disableExpandAnimation();
|
||||
overviewScope.dontAutoCancelSearch = false;
|
||||
} else {
|
||||
if (!overviewScope.dontAutoCancelSearch) {
|
||||
searchWidget.cancelSearch();
|
||||
}
|
||||
delayedGrabTimer.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: delayedGrabTimer
|
||||
interval: Config.options.hacks.arbitraryRaceConditionDelay
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
if (!grab.canBeActive)
|
||||
return;
|
||||
grab.active = GlobalStates.overviewOpen;
|
||||
}
|
||||
}
|
||||
|
||||
implicitWidth: columnLayout.implicitWidth
|
||||
implicitHeight: columnLayout.implicitHeight
|
||||
|
||||
function setSearchingText(text) {
|
||||
searchWidget.setSearchingText(text);
|
||||
searchWidget.focusFirstItemIfNeeded();
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
visible: GlobalStates.overviewOpen
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
top: parent.top
|
||||
}
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
GlobalStates.overviewOpen = false;
|
||||
} else if (event.key === Qt.Key_Left) {
|
||||
if (!root.searchingText)
|
||||
Hyprland.dispatch("workspace r-1");
|
||||
} else if (event.key === Qt.Key_Right) {
|
||||
if (!root.searchingText)
|
||||
Hyprland.dispatch("workspace r+1");
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
height: 1 // Prevent Wayland protocol error
|
||||
width: 1 // Prevent Wayland protocol error
|
||||
}
|
||||
|
||||
SearchWidget {
|
||||
id: searchWidget
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
onSearchingTextChanged: text => {
|
||||
root.searchingText = searchingText;
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: overviewLoader
|
||||
active: GlobalStates.overviewOpen
|
||||
sourceComponent: OverviewWidget {
|
||||
panelWindow: root
|
||||
visible: (root.searchingText == "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toggleClipboard() {
|
||||
if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) {
|
||||
GlobalStates.overviewOpen = false;
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < overviewVariants.instances.length; i++) {
|
||||
let panelWindow = overviewVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
overviewScope.dontAutoCancelSearch = true;
|
||||
panelWindow.setSearchingText(Config.options.search.prefix.clipboard);
|
||||
GlobalStates.overviewOpen = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toggleEmojis() {
|
||||
if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) {
|
||||
GlobalStates.overviewOpen = false;
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < overviewVariants.instances.length; i++) {
|
||||
let panelWindow = overviewVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
overviewScope.dontAutoCancelSearch = true;
|
||||
panelWindow.setSearchingText(Config.options.search.prefix.emojis);
|
||||
GlobalStates.overviewOpen = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "overview"
|
||||
|
||||
function toggle() {
|
||||
GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
|
||||
}
|
||||
function close() {
|
||||
GlobalStates.overviewOpen = false;
|
||||
}
|
||||
function open() {
|
||||
GlobalStates.overviewOpen = true;
|
||||
}
|
||||
function toggleReleaseInterrupt() {
|
||||
GlobalStates.superReleaseMightTrigger = false;
|
||||
}
|
||||
function clipboardToggle() {
|
||||
overviewScope.toggleClipboard();
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "overviewToggle"
|
||||
description: "Toggles overview on press"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "overviewClose"
|
||||
description: "Closes overview"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.overviewOpen = false;
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "overviewToggleRelease"
|
||||
description: "Toggles overview on release"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.superReleaseMightTrigger = true;
|
||||
}
|
||||
|
||||
onReleased: {
|
||||
if (!GlobalStates.superReleaseMightTrigger) {
|
||||
GlobalStates.superReleaseMightTrigger = true;
|
||||
return;
|
||||
}
|
||||
GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "overviewToggleReleaseInterrupt"
|
||||
description: "Interrupts possibility of overview being toggled on release. " + "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. " + "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything."
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.superReleaseMightTrigger = false;
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "overviewClipboardToggle"
|
||||
description: "Toggle clipboard query on overview widget"
|
||||
|
||||
onPressed: {
|
||||
overviewScope.toggleClipboard();
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "overviewEmojiToggle"
|
||||
description: "Toggle emoji query on overview widget"
|
||||
|
||||
onPressed: {
|
||||
overviewScope.toggleEmojis();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
|
||||
Item { // Window
|
||||
id: root
|
||||
property var toplevel
|
||||
property var windowData
|
||||
property var monitorData
|
||||
property var scale
|
||||
property var availableWorkspaceWidth
|
||||
property var availableWorkspaceHeight
|
||||
property bool restrictToWorkspace: true
|
||||
property real initX: Math.max((windowData?.at[0] - (monitorData?.x ?? 0) - monitorData?.reserved[0]) * root.scale, 0) + xOffset
|
||||
property real initY: Math.max((windowData?.at[1] - (monitorData?.y ?? 0) - monitorData?.reserved[1]) * root.scale, 0) + yOffset
|
||||
property real xOffset: 0
|
||||
property real yOffset: 0
|
||||
|
||||
property var targetWindowWidth: windowData?.size[0] * scale
|
||||
property var targetWindowHeight: windowData?.size[1] * scale
|
||||
property bool hovered: false
|
||||
property bool pressed: false
|
||||
|
||||
property var iconToWindowRatio: 0.35
|
||||
property var xwaylandIndicatorToIconRatio: 0.35
|
||||
property var iconToWindowRatioCompact: 0.6
|
||||
property var iconPath: Quickshell.iconPath(AppSearch.guessIcon(windowData?.class), "image-missing")
|
||||
property bool compactMode: Appearance.font.pixelSize.smaller * 4 > targetWindowHeight || Appearance.font.pixelSize.smaller * 4 > targetWindowWidth
|
||||
|
||||
property bool indicateXWayland: windowData?.xwayland ?? false
|
||||
|
||||
x: initX
|
||||
y: initY
|
||||
width: windowData?.size[0] * root.scale
|
||||
height: windowData?.size[1] * root.scale
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: root.width
|
||||
height: root.height
|
||||
radius: Appearance.rounding.windowRounding * root.scale
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on x {
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on y {
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on width {
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on height {
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
ScreencopyView {
|
||||
id: windowPreview
|
||||
anchors.fill: parent
|
||||
captureSource: GlobalStates.overviewOpen ? root.toplevel : null
|
||||
live: true
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Appearance.rounding.windowRounding * root.scale
|
||||
color: pressed ? ColorUtils.transparentize(Appearance.colors.colLayer2Active, 0.5) :
|
||||
hovered ? ColorUtils.transparentize(Appearance.colors.colLayer2Hover, 0.7) :
|
||||
ColorUtils.transparentize(Appearance.colors.colLayer2)
|
||||
border.color : ColorUtils.transparentize(Appearance.m3colors.m3outline, 0.7)
|
||||
border.width : 1
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: Appearance.font.pixelSize.smaller * 0.5
|
||||
|
||||
Image {
|
||||
id: windowIcon
|
||||
property var iconSize: {
|
||||
// console.log("-=-=-", root.toplevel.title, "-=-=-")
|
||||
// console.log("Target window size:", targetWindowWidth, targetWindowHeight)
|
||||
// console.log("Icon ratio:", root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio)
|
||||
// console.log("Scale:", root.monitorData.scale)
|
||||
// console.log("Final:", Math.min(targetWindowWidth, targetWindowHeight) * (root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio) / root.monitorData.scale)
|
||||
return Math.min(targetWindowWidth, targetWindowHeight) * (root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio) / root.monitorData.scale;
|
||||
}
|
||||
// mipmap: true
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
source: root.iconPath
|
||||
width: iconSize
|
||||
height: iconSize
|
||||
sourceSize: Qt.size(iconSize, iconSize)
|
||||
|
||||
Behavior on width {
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on height {
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,421 +0,0 @@
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
Item { // Wrapper
|
||||
id: root
|
||||
readonly property string xdgConfigHome: Directories.config
|
||||
property string searchingText: ""
|
||||
property bool showResults: searchingText != ""
|
||||
property real searchBarHeight: searchBar.height + Appearance.sizes.elevationMargin * 2
|
||||
implicitWidth: searchWidgetContent.implicitWidth + Appearance.sizes.elevationMargin * 2
|
||||
implicitHeight: searchWidgetContent.implicitHeight + Appearance.sizes.elevationMargin * 2
|
||||
|
||||
property string mathResult: ""
|
||||
|
||||
function disableExpandAnimation() {
|
||||
searchWidthBehavior.enabled = false;
|
||||
}
|
||||
|
||||
function cancelSearch() {
|
||||
searchInput.selectAll();
|
||||
root.searchingText = "";
|
||||
searchWidthBehavior.enabled = true;
|
||||
}
|
||||
|
||||
function setSearchingText(text) {
|
||||
searchInput.text = text;
|
||||
root.searchingText = text;
|
||||
}
|
||||
|
||||
property var searchActions: [
|
||||
{
|
||||
action: "dark",
|
||||
execute: () => {
|
||||
Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "dark", "--noswitch"]);
|
||||
}
|
||||
},
|
||||
{
|
||||
action: "light",
|
||||
execute: () => {
|
||||
Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "light", "--noswitch"]);
|
||||
}
|
||||
},
|
||||
{
|
||||
action: "wall",
|
||||
execute: () => {
|
||||
Quickshell.execDetached([Directories.wallpaperSwitchScriptPath]);
|
||||
}
|
||||
},
|
||||
{
|
||||
action: "konachanwall",
|
||||
execute: () => {
|
||||
Quickshell.execDetached([Quickshell.configPath("scripts/colors/random_konachan_wall.sh")]);
|
||||
}
|
||||
},
|
||||
{
|
||||
action: "accentcolor",
|
||||
execute: args => {
|
||||
Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--noswitch", "--color", ...(args != '' ? [`${args}`] : [])]);
|
||||
}
|
||||
},
|
||||
{
|
||||
action: "todo",
|
||||
execute: args => {
|
||||
Todo.addTask(args);
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
function focusFirstItemIfNeeded() {
|
||||
if (searchInput.focus)
|
||||
appResults.currentIndex = 0; // Focus the first item
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: nonAppResultsTimer
|
||||
interval: Config.options.search.nonAppResultDelay
|
||||
onTriggered: {
|
||||
mathProcess.calculateExpression(root.searchingText);
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: mathProcess
|
||||
property list<string> baseCommand: ["qalc", "-t"]
|
||||
function calculateExpression(expression) {
|
||||
mathProcess.running = false;
|
||||
mathProcess.command = baseCommand.concat(expression);
|
||||
mathProcess.running = true;
|
||||
}
|
||||
stdout: SplitParser {
|
||||
onRead: data => {
|
||||
root.mathResult = data;
|
||||
root.focusFirstItemIfNeeded();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: event => {
|
||||
// Prevent Esc and Backspace from registering
|
||||
if (event.key === Qt.Key_Escape)
|
||||
return;
|
||||
|
||||
// Handle Backspace: focus and delete character if not focused
|
||||
if (event.key === Qt.Key_Backspace) {
|
||||
if (!searchInput.activeFocus) {
|
||||
searchInput.forceActiveFocus();
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
// Delete word before cursor
|
||||
let text = searchInput.text;
|
||||
let pos = searchInput.cursorPosition;
|
||||
if (pos > 0) {
|
||||
// Find the start of the previous word
|
||||
let left = text.slice(0, pos);
|
||||
let match = left.match(/(\s*\S+)\s*$/);
|
||||
let deleteLen = match ? match[0].length : 1;
|
||||
searchInput.text = text.slice(0, pos - deleteLen) + text.slice(pos);
|
||||
searchInput.cursorPosition = pos - deleteLen;
|
||||
}
|
||||
} else {
|
||||
// Delete character before cursor if any
|
||||
if (searchInput.cursorPosition > 0) {
|
||||
searchInput.text = searchInput.text.slice(0, searchInput.cursorPosition - 1) + searchInput.text.slice(searchInput.cursorPosition);
|
||||
searchInput.cursorPosition -= 1;
|
||||
}
|
||||
}
|
||||
// Always move cursor to end after programmatic edit
|
||||
searchInput.cursorPosition = searchInput.text.length;
|
||||
event.accepted = true;
|
||||
}
|
||||
// If already focused, let TextField handle it
|
||||
return;
|
||||
}
|
||||
|
||||
// Only handle visible printable characters (ignore control chars, arrows, etc.)
|
||||
if (event.text && event.text.length === 1 && event.key !== Qt.Key_Enter && event.key !== Qt.Key_Return && event.text.charCodeAt(0) >= 0x20) // ignore control chars like Backspace, Tab, etc.
|
||||
{
|
||||
if (!searchInput.activeFocus) {
|
||||
searchInput.forceActiveFocus();
|
||||
// Insert the character at the cursor position
|
||||
searchInput.text = searchInput.text.slice(0, searchInput.cursorPosition) + event.text + searchInput.text.slice(searchInput.cursorPosition);
|
||||
searchInput.cursorPosition += 1;
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledRectangularShadow {
|
||||
target: searchWidgetContent
|
||||
}
|
||||
Rectangle { // Background
|
||||
id: searchWidgetContent
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: columnLayout.implicitWidth
|
||||
implicitHeight: columnLayout.implicitHeight
|
||||
radius: Appearance.rounding.large
|
||||
color: Appearance.colors.colLayer0
|
||||
border.width: 1
|
||||
border.color: Appearance.m3colors.m3outlineVariant
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
anchors.centerIn: parent
|
||||
spacing: 0
|
||||
|
||||
// clip: true
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: searchWidgetContent.width
|
||||
height: searchWidgetContent.width
|
||||
radius: searchWidgetContent.radius
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: searchBar
|
||||
spacing: 5
|
||||
MaterialSymbol {
|
||||
id: searchIcon
|
||||
Layout.leftMargin: 15
|
||||
iconSize: Appearance.font.pixelSize.huge
|
||||
color: Appearance.m3colors.m3onSurface
|
||||
text: root.searchingText.startsWith(Config.options.search.prefix.clipboard) ? 'content_paste_search' : 'search'
|
||||
}
|
||||
TextField { // Search box
|
||||
id: searchInput
|
||||
|
||||
focus: GlobalStates.overviewOpen
|
||||
Layout.rightMargin: 15
|
||||
padding: 15
|
||||
renderType: Text.NativeRendering
|
||||
font {
|
||||
family: Appearance?.font.family.main ?? "sans-serif"
|
||||
pixelSize: Appearance?.font.pixelSize.small ?? 15
|
||||
hintingPreference: Font.PreferFullHinting
|
||||
}
|
||||
color: activeFocus ? Appearance.m3colors.m3onSurface : Appearance.m3colors.m3onSurfaceVariant
|
||||
selectedTextColor: Appearance.m3colors.m3onSecondaryContainer
|
||||
selectionColor: Appearance.colors.colSecondaryContainer
|
||||
placeholderText: Translation.tr("Search, calculate or run")
|
||||
placeholderTextColor: Appearance.m3colors.m3outline
|
||||
implicitWidth: root.searchingText == "" ? Appearance.sizes.searchWidthCollapsed : Appearance.sizes.searchWidth
|
||||
|
||||
Behavior on implicitWidth {
|
||||
id: searchWidthBehavior
|
||||
enabled: false
|
||||
NumberAnimation {
|
||||
duration: 300
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
}
|
||||
|
||||
onTextChanged: root.searchingText = text
|
||||
|
||||
onAccepted: {
|
||||
if (appResults.count > 0) {
|
||||
// Get the first visible delegate and trigger its click
|
||||
let firstItem = appResults.itemAtIndex(0);
|
||||
if (firstItem && firstItem.clicked) {
|
||||
firstItem.clicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
background: null
|
||||
|
||||
cursorDelegate: Rectangle {
|
||||
width: 1
|
||||
color: searchInput.activeFocus ? Appearance.colors.colPrimary : "transparent"
|
||||
radius: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
// Separator
|
||||
visible: root.showResults
|
||||
Layout.fillWidth: true
|
||||
height: 1
|
||||
color: Appearance.colors.colOutlineVariant
|
||||
}
|
||||
|
||||
ListView { // App results
|
||||
id: appResults
|
||||
visible: root.showResults
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: Math.min(600, appResults.contentHeight + topMargin + bottomMargin)
|
||||
clip: true
|
||||
topMargin: 10
|
||||
bottomMargin: 10
|
||||
spacing: 2
|
||||
KeyNavigation.up: searchBar
|
||||
highlightMoveDuration: 100
|
||||
|
||||
onFocusChanged: {
|
||||
if (focus)
|
||||
appResults.currentIndex = 1;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
function onSearchingTextChanged() {
|
||||
if (appResults.count > 0)
|
||||
appResults.currentIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
model: ScriptModel {
|
||||
id: model
|
||||
values: {
|
||||
// Search results are handled here
|
||||
////////////////// Skip? //////////////////
|
||||
if (root.searchingText == "")
|
||||
return [];
|
||||
|
||||
///////////// Special cases ///////////////
|
||||
if (root.searchingText.startsWith(Config.options.search.prefix.clipboard)) {
|
||||
// Clipboard
|
||||
const searchString = root.searchingText.slice(Config.options.search.prefix.clipboard.length);
|
||||
return Cliphist.fuzzyQuery(searchString).map(entry => {
|
||||
return {
|
||||
cliphistRawString: entry,
|
||||
name: entry.replace(/^\s*\S+\s+/, ""),
|
||||
clickActionName: "",
|
||||
type: `#${entry.match(/^\s*(\S+)/)?.[1] || ""}`,
|
||||
execute: () => {
|
||||
Cliphist.copy(entry)
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
name: "Delete",
|
||||
icon: "delete",
|
||||
execute: () => {
|
||||
Cliphist.deleteEntry(entry);
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
}).filter(Boolean);
|
||||
}
|
||||
if (root.searchingText.startsWith(Config.options.search.prefix.emojis)) {
|
||||
// Clipboard
|
||||
const searchString = root.searchingText.slice(Config.options.search.prefix.emojis.length);
|
||||
return Emojis.fuzzyQuery(searchString).map(entry => {
|
||||
return {
|
||||
cliphistRawString: entry,
|
||||
bigText: entry.match(/^\s*(\S+)/)?.[1] || "",
|
||||
name: entry.replace(/^\s*\S+\s+/, ""),
|
||||
clickActionName: "",
|
||||
type: "Emoji",
|
||||
execute: () => {
|
||||
Quickshell.clipboardText = entry.match(/^\s*(\S+)/)?.[1];
|
||||
}
|
||||
};
|
||||
}).filter(Boolean);
|
||||
}
|
||||
|
||||
////////////////// Init ///////////////////
|
||||
nonAppResultsTimer.restart();
|
||||
const mathResultObject = {
|
||||
name: root.mathResult,
|
||||
clickActionName: Translation.tr("Copy"),
|
||||
type: Translation.tr("Math result"),
|
||||
fontType: "monospace",
|
||||
materialSymbol: 'calculate',
|
||||
execute: () => {
|
||||
Quickshell.clipboardText = root.mathResult;
|
||||
}
|
||||
};
|
||||
const commandResultObject = {
|
||||
name: searchingText.replace("file://", ""),
|
||||
clickActionName: Translation.tr("Run"),
|
||||
type: Translation.tr("Run command"),
|
||||
fontType: "monospace",
|
||||
materialSymbol: 'terminal',
|
||||
execute: () => {
|
||||
const cleanedCommand = root.searchingText.replace("file://", "");
|
||||
Quickshell.execDetached(["bash", "-c", searchingText.startsWith('sudo') ? `${Config.options.apps.terminal} fish -C '${cleanedCommand}'` : cleanedCommand]);
|
||||
}
|
||||
};
|
||||
const launcherActionObjects = root.searchActions.map(action => {
|
||||
const actionString = `${Config.options.search.prefix.action}${action.action}`;
|
||||
if (actionString.startsWith(root.searchingText) || root.searchingText.startsWith(actionString)) {
|
||||
return {
|
||||
name: root.searchingText.startsWith(actionString) ? root.searchingText : actionString,
|
||||
clickActionName: Translation.tr("Run"),
|
||||
type: Translation.tr("Action"),
|
||||
materialSymbol: 'settings_suggest',
|
||||
execute: () => {
|
||||
action.execute(root.searchingText.split(" ").slice(1).join(" "));
|
||||
}
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}).filter(Boolean);
|
||||
|
||||
let result = [];
|
||||
|
||||
//////////////// Apps //////////////////
|
||||
result = result.concat(AppSearch.fuzzyQuery(root.searchingText).map(entry => {
|
||||
entry.clickActionName = Translation.tr("Launch");
|
||||
entry.type = Translation.tr("App");
|
||||
return entry;
|
||||
}));
|
||||
|
||||
////////// Launcher actions ////////////
|
||||
result = result.concat(launcherActionObjects);
|
||||
|
||||
/////////// Math result & command //////////
|
||||
const startsWithNumber = /^\d/.test(root.searchingText);
|
||||
if (startsWithNumber) {
|
||||
result.push(mathResultObject);
|
||||
result.push(commandResultObject);
|
||||
} else {
|
||||
result.push(commandResultObject);
|
||||
result.push(mathResultObject);
|
||||
}
|
||||
|
||||
///////////////// Web search ////////////////
|
||||
result.push({
|
||||
name: root.searchingText,
|
||||
clickActionName: Translation.tr("Search"),
|
||||
type: Translation.tr("Search the web"),
|
||||
materialSymbol: 'travel_explore',
|
||||
execute: () => {
|
||||
let url = Config.options.search.engineBaseUrl + root.searchingText;
|
||||
for (let site of Config.options.search.excludedSites) {
|
||||
url += ` -site:${site}`;
|
||||
}
|
||||
Qt.openUrlExternally(url);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
onModelChanged: root.focusFirstItemIfNeeded()
|
||||
|
||||
delegate: SearchItem {
|
||||
// The selectable item for each search result
|
||||
required property var modelData
|
||||
anchors.left: parent?.left
|
||||
anchors.right: parent?.right
|
||||
entry: modelData
|
||||
query: root.searchingText.startsWith(Config.options.search.prefix.clipboard) ? root.searchingText.slice(Config.options.search.prefix.clipboard.length) : root.searchingText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Scope {
|
||||
id: screenCorners
|
||||
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
||||
|
||||
component CornerPanelWindow: PanelWindow {
|
||||
id: cornerPanelWindow
|
||||
visible: (Config.options.appearance.fakeScreenRounding === 1 || (Config.options.appearance.fakeScreenRounding === 2 && !activeWindow?.fullscreen))
|
||||
property var corner
|
||||
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
mask: Region {
|
||||
item: null
|
||||
}
|
||||
WlrLayershell.namespace: "quickshell:screenCorners"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
color: "transparent"
|
||||
|
||||
anchors {
|
||||
top: cornerPanelWindow.corner === RoundCorner.CornerEnum.TopLeft || cornerPanelWindow.corner === RoundCorner.CornerEnum.TopRight
|
||||
left: cornerPanelWindow.corner === RoundCorner.CornerEnum.TopLeft || cornerPanelWindow.corner === RoundCorner.CornerEnum.BottomLeft
|
||||
bottom: cornerPanelWindow.corner === RoundCorner.CornerEnum.BottomLeft || cornerPanelWindow.corner === RoundCorner.CornerEnum.BottomRight
|
||||
right: cornerPanelWindow.corner === RoundCorner.CornerEnum.TopRight || cornerPanelWindow.corner === RoundCorner.CornerEnum.BottomRight
|
||||
}
|
||||
|
||||
implicitWidth: cornerWidget.implicitWidth
|
||||
implicitHeight: cornerWidget.implicitHeight
|
||||
RoundCorner {
|
||||
id: cornerWidget
|
||||
size: Appearance.rounding.screenRounding
|
||||
corner: cornerPanelWindow.corner
|
||||
}
|
||||
}
|
||||
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
Scope {
|
||||
required property var modelData
|
||||
CornerPanelWindow {
|
||||
screen: modelData
|
||||
corner: RoundCorner.CornerEnum.TopLeft
|
||||
}
|
||||
CornerPanelWindow {
|
||||
screen: modelData
|
||||
corner: RoundCorner.CornerEnum.TopRight
|
||||
}
|
||||
CornerPanelWindow {
|
||||
screen: modelData
|
||||
corner: RoundCorner.CornerEnum.BottomLeft
|
||||
}
|
||||
CornerPanelWindow {
|
||||
screen: modelData
|
||||
corner: RoundCorner.CornerEnum.BottomRight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import QtQuick
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
|
||||
ContentPage {
|
||||
forceWidth: true
|
||||
|
||||
ContentSection {
|
||||
title: Translation.tr("Color generation")
|
||||
|
||||
ConfigRow {
|
||||
uniform: true
|
||||
ConfigSwitch {
|
||||
text: Translation.tr("Shell & utilities")
|
||||
checked: Config.options.appearance.wallpaperTheming.enableAppsAndShell
|
||||
onCheckedChanged: {
|
||||
Config.options.appearance.wallpaperTheming.enableAppsAndShell = checked;
|
||||
}
|
||||
}
|
||||
ConfigSwitch {
|
||||
text: Translation.tr("Qt apps")
|
||||
checked: Config.options.appearance.wallpaperTheming.enableQtApps
|
||||
onCheckedChanged: {
|
||||
Config.options.appearance.wallpaperTheming.enableQtApps = checked;
|
||||
}
|
||||
StyledToolTip {
|
||||
content: Translation.tr("Shell & utilities theming must also be enabled")
|
||||
}
|
||||
}
|
||||
ConfigSwitch {
|
||||
text: Translation.tr("Terminal")
|
||||
checked: Config.options.appearance.wallpaperTheming.enableTerminal
|
||||
onCheckedChanged: {
|
||||
Config.options.appearance.wallpaperTheming.enableTerminal = checked;
|
||||
}
|
||||
StyledToolTip {
|
||||
content: Translation.tr("Shell & utilities theming must also be enabled")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,374 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
|
||||
ContentPage {
|
||||
forceWidth: true
|
||||
ContentSection {
|
||||
title: Translation.tr("Policies")
|
||||
|
||||
ConfigRow {
|
||||
ColumnLayout {
|
||||
// Weeb policy
|
||||
ContentSubsectionLabel {
|
||||
text: Translation.tr("Weeb")
|
||||
}
|
||||
ConfigSelectionArray {
|
||||
currentValue: Config.options.policies.weeb
|
||||
configOptionName: "policies.weeb"
|
||||
onSelected: newValue => {
|
||||
Config.options.policies.weeb = newValue;
|
||||
}
|
||||
options: [
|
||||
{
|
||||
displayName: Translation.tr("No"),
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
displayName: Translation.tr("Yes"),
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
displayName: Translation.tr("Closet"),
|
||||
value: 2
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
// AI policy
|
||||
ContentSubsectionLabel {
|
||||
text: Translation.tr("AI")
|
||||
}
|
||||
ConfigSelectionArray {
|
||||
currentValue: Config.options.policies.ai
|
||||
configOptionName: "policies.ai"
|
||||
onSelected: newValue => {
|
||||
Config.options.policies.ai = newValue;
|
||||
}
|
||||
options: [
|
||||
{
|
||||
displayName: Translation.tr("No"),
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
displayName: Translation.tr("Yes"),
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
displayName: Translation.tr("Local only"),
|
||||
value: 2
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentSection {
|
||||
title: Translation.tr("Bar")
|
||||
|
||||
ConfigSelectionArray {
|
||||
currentValue: Config.options.bar.cornerStyle
|
||||
configOptionName: "bar.cornerStyle"
|
||||
onSelected: newValue => {
|
||||
Config.options.bar.cornerStyle = newValue;
|
||||
}
|
||||
options: [
|
||||
{
|
||||
displayName: Translation.tr("Hug"),
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
displayName: Translation.tr("Float"),
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
displayName: Translation.tr("Plain rectangle"),
|
||||
value: 2
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
ContentSubsection {
|
||||
title: Translation.tr("Appearance")
|
||||
ConfigRow {
|
||||
uniform: true
|
||||
ConfigSwitch {
|
||||
text: Translation.tr('Borderless')
|
||||
checked: Config.options.bar.borderless
|
||||
onCheckedChanged: {
|
||||
Config.options.bar.borderless = checked;
|
||||
}
|
||||
}
|
||||
ConfigSwitch {
|
||||
text: Translation.tr('Show background')
|
||||
checked: Config.options.bar.showBackground
|
||||
onCheckedChanged: {
|
||||
Config.options.bar.showBackground = checked;
|
||||
}
|
||||
StyledToolTip {
|
||||
content: Translation.tr("Note: turning off can hurt readability")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentSubsection {
|
||||
title: Translation.tr("Buttons")
|
||||
ConfigRow {
|
||||
uniform: true
|
||||
ConfigSwitch {
|
||||
text: Translation.tr("Screen snip")
|
||||
checked: Config.options.bar.utilButtons.showScreenSnip
|
||||
onCheckedChanged: {
|
||||
Config.options.bar.utilButtons.showScreenSnip = checked;
|
||||
}
|
||||
}
|
||||
ConfigSwitch {
|
||||
text: Translation.tr("Color picker")
|
||||
checked: Config.options.bar.utilButtons.showColorPicker
|
||||
onCheckedChanged: {
|
||||
Config.options.bar.utilButtons.showColorPicker = checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
ConfigRow {
|
||||
uniform: true
|
||||
ConfigSwitch {
|
||||
text: Translation.tr("Mic toggle")
|
||||
checked: Config.options.bar.utilButtons.showMicToggle
|
||||
onCheckedChanged: {
|
||||
Config.options.bar.utilButtons.showMicToggle = checked;
|
||||
}
|
||||
}
|
||||
ConfigSwitch {
|
||||
text: Translation.tr("Keyboard toggle")
|
||||
checked: Config.options.bar.utilButtons.showKeyboardToggle
|
||||
onCheckedChanged: {
|
||||
Config.options.bar.utilButtons.showKeyboardToggle = checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
ConfigRow {
|
||||
uniform: true
|
||||
ConfigSwitch {
|
||||
text: Translation.tr("Dark/Light toggle")
|
||||
checked: Config.options.bar.utilButtons.showDarkModeToggle
|
||||
onCheckedChanged: {
|
||||
Config.options.bar.utilButtons.showDarkModeToggle = checked;
|
||||
}
|
||||
}
|
||||
ConfigSwitch {
|
||||
opacity: 0
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentSubsection {
|
||||
title: Translation.tr("Workspaces")
|
||||
tooltip: Translation.tr("Tip: Hide icons and always show numbers for\nthe classic illogical-impulse experience")
|
||||
|
||||
ConfigRow {
|
||||
uniform: true
|
||||
ConfigSwitch {
|
||||
text: Translation.tr('Show app icons')
|
||||
checked: Config.options.bar.workspaces.showAppIcons
|
||||
onCheckedChanged: {
|
||||
Config.options.bar.workspaces.showAppIcons = checked;
|
||||
}
|
||||
}
|
||||
ConfigSwitch {
|
||||
text: Translation.tr('Always show numbers')
|
||||
checked: Config.options.bar.workspaces.alwaysShowNumbers
|
||||
onCheckedChanged: {
|
||||
Config.options.bar.workspaces.alwaysShowNumbers = checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
ConfigSpinBox {
|
||||
text: Translation.tr("Workspaces shown")
|
||||
value: Config.options.bar.workspaces.shown
|
||||
from: 1
|
||||
to: 30
|
||||
stepSize: 1
|
||||
onValueChanged: {
|
||||
Config.options.bar.workspaces.shown = value;
|
||||
}
|
||||
}
|
||||
ConfigSpinBox {
|
||||
text: Translation.tr("Number show delay when pressing Super (ms)")
|
||||
value: Config.options.bar.workspaces.showNumberDelay
|
||||
from: 0
|
||||
to: 1000
|
||||
stepSize: 50
|
||||
onValueChanged: {
|
||||
Config.options.bar.workspaces.showNumberDelay = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentSubsection {
|
||||
title: Translation.tr("Weather")
|
||||
ConfigSwitch {
|
||||
text: Translation.tr("Enable")
|
||||
checked: Config.options.bar.weather.enable
|
||||
onCheckedChanged: {
|
||||
Config.options.bar.weather.enable = checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentSection {
|
||||
title: Translation.tr("Battery")
|
||||
|
||||
ConfigRow {
|
||||
uniform: true
|
||||
ConfigSpinBox {
|
||||
text: Translation.tr("Low warning")
|
||||
value: Config.options.battery.low
|
||||
from: 0
|
||||
to: 100
|
||||
stepSize: 5
|
||||
onValueChanged: {
|
||||
Config.options.battery.low = value;
|
||||
}
|
||||
}
|
||||
ConfigSpinBox {
|
||||
text: Translation.tr("Critical warning")
|
||||
value: Config.options.battery.critical
|
||||
from: 0
|
||||
to: 100
|
||||
stepSize: 5
|
||||
onValueChanged: {
|
||||
Config.options.battery.critical = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
ConfigRow {
|
||||
uniform: true
|
||||
ConfigSwitch {
|
||||
text: Translation.tr("Automatic suspend")
|
||||
checked: Config.options.battery.automaticSuspend
|
||||
onCheckedChanged: {
|
||||
Config.options.battery.automaticSuspend = checked;
|
||||
}
|
||||
StyledToolTip {
|
||||
content: Translation.tr("Automatically suspends the system when battery is low")
|
||||
}
|
||||
}
|
||||
ConfigSpinBox {
|
||||
text: Translation.tr("Suspend at")
|
||||
value: Config.options.battery.suspend
|
||||
from: 0
|
||||
to: 100
|
||||
stepSize: 5
|
||||
onValueChanged: {
|
||||
Config.options.battery.suspend = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentSection {
|
||||
title: Translation.tr("Dock")
|
||||
|
||||
ConfigSwitch {
|
||||
text: Translation.tr("Enable")
|
||||
checked: Config.options.dock.enable
|
||||
onCheckedChanged: {
|
||||
Config.options.dock.enable = checked;
|
||||
}
|
||||
}
|
||||
|
||||
ConfigRow {
|
||||
uniform: true
|
||||
ConfigSwitch {
|
||||
text: Translation.tr("Hover to reveal")
|
||||
checked: Config.options.dock.hoverToReveal
|
||||
onCheckedChanged: {
|
||||
Config.options.dock.hoverToReveal = checked;
|
||||
}
|
||||
}
|
||||
ConfigSwitch {
|
||||
text: Translation.tr("Pinned on startup")
|
||||
checked: Config.options.dock.pinnedOnStartup
|
||||
onCheckedChanged: {
|
||||
Config.options.dock.pinnedOnStartup = checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentSection {
|
||||
title: Translation.tr("On-screen display")
|
||||
ConfigSpinBox {
|
||||
text: Translation.tr("Timeout (ms)")
|
||||
value: Config.options.osd.timeout
|
||||
from: 100
|
||||
to: 3000
|
||||
stepSize: 100
|
||||
onValueChanged: {
|
||||
Config.options.osd.timeout = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentSection {
|
||||
title: Translation.tr("Overview")
|
||||
ConfigSpinBox {
|
||||
text: Translation.tr("Scale (%)")
|
||||
value: Config.options.overview.scale * 100
|
||||
from: 1
|
||||
to: 100
|
||||
stepSize: 1
|
||||
onValueChanged: {
|
||||
Config.options.overview.scale = value / 100;
|
||||
}
|
||||
}
|
||||
ConfigRow {
|
||||
uniform: true
|
||||
ConfigSpinBox {
|
||||
text: Translation.tr("Rows")
|
||||
value: Config.options.overview.rows
|
||||
from: 1
|
||||
to: 20
|
||||
stepSize: 1
|
||||
onValueChanged: {
|
||||
Config.options.overview.rows = value;
|
||||
}
|
||||
}
|
||||
ConfigSpinBox {
|
||||
text: Translation.tr("Columns")
|
||||
value: Config.options.overview.columns
|
||||
from: 1
|
||||
to: 20
|
||||
stepSize: 1
|
||||
onValueChanged: {
|
||||
Config.options.overview.columns = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentSection {
|
||||
title: Translation.tr("Screenshot tool")
|
||||
|
||||
ConfigSwitch {
|
||||
text: Translation.tr('Show regions of potential interest')
|
||||
checked: Config.options.screenshotTool.showContentRegions
|
||||
onCheckedChanged: {
|
||||
Config.options.screenshotTool.showContentRegions = checked;
|
||||
}
|
||||
StyledToolTip {
|
||||
content: Translation.tr("Such regions could be images or parts of the screen that have some containment.\nMight not always be accurate.\nThis is done with an image processing algorithm run locally and no AI is used.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,233 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
|
||||
ContentPage {
|
||||
forceWidth: true
|
||||
|
||||
ContentSection {
|
||||
title: Translation.tr("Audio")
|
||||
|
||||
ConfigSwitch {
|
||||
text: Translation.tr("Earbang protection")
|
||||
checked: Config.options.audio.protection.enable
|
||||
onCheckedChanged: {
|
||||
Config.options.audio.protection.enable = checked;
|
||||
}
|
||||
StyledToolTip {
|
||||
content: Translation.tr("Prevents abrupt increments and restricts volume limit")
|
||||
}
|
||||
}
|
||||
ConfigRow {
|
||||
// uniform: true
|
||||
ConfigSpinBox {
|
||||
text: Translation.tr("Max allowed increase")
|
||||
value: Config.options.audio.protection.maxAllowedIncrease
|
||||
from: 0
|
||||
to: 100
|
||||
stepSize: 2
|
||||
onValueChanged: {
|
||||
Config.options.audio.protection.maxAllowedIncrease = value;
|
||||
}
|
||||
}
|
||||
ConfigSpinBox {
|
||||
text: Translation.tr("Volume limit")
|
||||
value: Config.options.audio.protection.maxAllowed
|
||||
from: 0
|
||||
to: 100
|
||||
stepSize: 2
|
||||
onValueChanged: {
|
||||
Config.options.audio.protection.maxAllowed = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ContentSection {
|
||||
title: Translation.tr("AI")
|
||||
MaterialTextField {
|
||||
Layout.fillWidth: true
|
||||
placeholderText: Translation.tr("System prompt")
|
||||
text: Config.options.ai.systemPrompt
|
||||
wrapMode: TextEdit.Wrap
|
||||
onTextChanged: {
|
||||
Qt.callLater(() => {
|
||||
Config.options.ai.systemPrompt = text;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentSection {
|
||||
title: Translation.tr("Battery")
|
||||
|
||||
ConfigRow {
|
||||
uniform: true
|
||||
ConfigSpinBox {
|
||||
text: Translation.tr("Low warning")
|
||||
value: Config.options.battery.low
|
||||
from: 0
|
||||
to: 100
|
||||
stepSize: 5
|
||||
onValueChanged: {
|
||||
Config.options.battery.low = value;
|
||||
}
|
||||
}
|
||||
ConfigSpinBox {
|
||||
text: Translation.tr("Critical warning")
|
||||
value: Config.options.battery.critical
|
||||
from: 0
|
||||
to: 100
|
||||
stepSize: 5
|
||||
onValueChanged: {
|
||||
Config.options.battery.critical = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
ConfigRow {
|
||||
uniform: true
|
||||
ConfigSwitch {
|
||||
text: Translation.tr("Automatic suspend")
|
||||
checked: Config.options.battery.automaticSuspend
|
||||
onCheckedChanged: {
|
||||
Config.options.battery.automaticSuspend = checked;
|
||||
}
|
||||
StyledToolTip {
|
||||
content: Translation.tr("Automatically suspends the system when battery is low")
|
||||
}
|
||||
}
|
||||
ConfigSpinBox {
|
||||
text: Translation.tr("Suspend at")
|
||||
value: Config.options.battery.suspend
|
||||
from: 0
|
||||
to: 100
|
||||
stepSize: 5
|
||||
onValueChanged: {
|
||||
Config.options.battery.suspend = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentSection {
|
||||
title: Translation.tr("Networking")
|
||||
MaterialTextField {
|
||||
Layout.fillWidth: true
|
||||
placeholderText: Translation.tr("User agent (for services that require it)")
|
||||
text: Config.options.networking.userAgent
|
||||
wrapMode: TextEdit.Wrap
|
||||
onTextChanged: {
|
||||
Config.options.networking.userAgent = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentSection {
|
||||
title: Translation.tr("Resources")
|
||||
ConfigSpinBox {
|
||||
text: Translation.tr("Polling interval (ms)")
|
||||
value: Config.options.resources.updateInterval
|
||||
from: 100
|
||||
to: 10000
|
||||
stepSize: 100
|
||||
onValueChanged: {
|
||||
Config.options.resources.updateInterval = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentSection {
|
||||
title: Translation.tr("Search")
|
||||
|
||||
ConfigSwitch {
|
||||
text: Translation.tr("Use Levenshtein distance-based algorithm instead of fuzzy")
|
||||
checked: Config.options.search.sloppy
|
||||
onCheckedChanged: {
|
||||
Config.options.search.sloppy = checked;
|
||||
}
|
||||
StyledToolTip {
|
||||
content: Translation.tr("Could be better if you make a ton of typos,\nbut results can be weird and might not work with acronyms\n(e.g. \"GIMP\" might not give you the paint program)")
|
||||
}
|
||||
}
|
||||
|
||||
ContentSubsection {
|
||||
title: Translation.tr("Prefixes")
|
||||
ConfigRow {
|
||||
uniform: true
|
||||
|
||||
MaterialTextField {
|
||||
Layout.fillWidth: true
|
||||
placeholderText: Translation.tr("Action")
|
||||
text: Config.options.search.prefix.action
|
||||
wrapMode: TextEdit.Wrap
|
||||
onTextChanged: {
|
||||
Config.options.search.prefix.action = text;
|
||||
}
|
||||
}
|
||||
MaterialTextField {
|
||||
Layout.fillWidth: true
|
||||
placeholderText: Translation.tr("Clipboard")
|
||||
text: Config.options.search.prefix.clipboard
|
||||
wrapMode: TextEdit.Wrap
|
||||
onTextChanged: {
|
||||
Config.options.search.prefix.clipboard = text;
|
||||
}
|
||||
}
|
||||
MaterialTextField {
|
||||
Layout.fillWidth: true
|
||||
placeholderText: Translation.tr("Emojis")
|
||||
text: Config.options.search.prefix.emojis
|
||||
wrapMode: TextEdit.Wrap
|
||||
onTextChanged: {
|
||||
Config.options.search.prefix.emojis = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ContentSubsection {
|
||||
title: Translation.tr("Web search")
|
||||
MaterialTextField {
|
||||
Layout.fillWidth: true
|
||||
placeholderText: Translation.tr("Base URL")
|
||||
text: Config.options.search.engineBaseUrl
|
||||
wrapMode: TextEdit.Wrap
|
||||
onTextChanged: {
|
||||
Config.options.search.engineBaseUrl = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentSection {
|
||||
title: Translation.tr("Time")
|
||||
|
||||
ContentSubsection {
|
||||
title: Translation.tr("Format")
|
||||
tooltip: ""
|
||||
|
||||
ConfigSelectionArray {
|
||||
currentValue: Config.options.time.format
|
||||
configOptionName: "time.format"
|
||||
onSelected: newValue => {
|
||||
Config.options.time.format = newValue;
|
||||
}
|
||||
options: [
|
||||
{
|
||||
displayName: Translation.tr("24h"),
|
||||
value: "hh:mm"
|
||||
},
|
||||
{
|
||||
displayName: Translation.tr("12h am/pm"),
|
||||
value: "h:mm ap"
|
||||
},
|
||||
{
|
||||
displayName: Translation.tr("12h AM/PM"),
|
||||
value: "h:mm AP"
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,245 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
|
||||
ContentPage {
|
||||
baseWidth: lightDarkButtonGroup.implicitWidth
|
||||
forceWidth: true
|
||||
|
||||
Process {
|
||||
id: konachanWallProc
|
||||
property string status: ""
|
||||
command: ["bash", "-c", FileUtils.trimFileProtocol(`${Directories.scriptPath}/colors/random_konachan_wall.sh`)]
|
||||
stdout: SplitParser {
|
||||
onRead: data => {
|
||||
console.log(`Konachan wall proc output: ${data}`);
|
||||
konachanWallProc.status = data.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentSection {
|
||||
title: Translation.tr("Colors & Wallpaper")
|
||||
|
||||
// Light/Dark mode preference
|
||||
ButtonGroup {
|
||||
id: lightDarkButtonGroup
|
||||
Layout.fillWidth: true
|
||||
LightDarkPreferenceButton {
|
||||
dark: false
|
||||
}
|
||||
LightDarkPreferenceButton {
|
||||
dark: true
|
||||
}
|
||||
}
|
||||
|
||||
// Material palette selection
|
||||
ContentSubsection {
|
||||
title: Translation.tr("Material palette")
|
||||
ConfigSelectionArray {
|
||||
currentValue: Config.options.appearance.palette.type
|
||||
configOptionName: "appearance.palette.type"
|
||||
onSelected: (newValue) => {
|
||||
Config.options.appearance.palette.type = newValue;
|
||||
Quickshell.execDetached(["bash", "-c", `${Directories.wallpaperSwitchScriptPath} --noswitch`])
|
||||
}
|
||||
options: [
|
||||
{"value": "auto", "displayName": Translation.tr("Auto")},
|
||||
{"value": "scheme-content", "displayName": Translation.tr("Content")},
|
||||
{"value": "scheme-expressive", "displayName": Translation.tr("Expressive")},
|
||||
{"value": "scheme-fidelity", "displayName": Translation.tr("Fidelity")},
|
||||
{"value": "scheme-fruit-salad", "displayName": Translation.tr("Fruit Salad")},
|
||||
{"value": "scheme-monochrome", "displayName": Translation.tr("Monochrome")},
|
||||
{"value": "scheme-neutral", "displayName": Translation.tr("Neutral")},
|
||||
{"value": "scheme-rainbow", "displayName": Translation.tr("Rainbow")},
|
||||
{"value": "scheme-tonal-spot", "displayName": Translation.tr("Tonal Spot")}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Wallpaper selection
|
||||
ContentSubsection {
|
||||
title: Translation.tr("Wallpaper")
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
RippleButtonWithIcon {
|
||||
id: rndWallBtn
|
||||
buttonRadius: Appearance.rounding.small
|
||||
materialIcon: "wallpaper"
|
||||
mainText: konachanWallProc.running ? Translation.tr("Be patient...") : Translation.tr("Random: Konachan")
|
||||
onClicked: {
|
||||
console.log(konachanWallProc.command.join(" "))
|
||||
konachanWallProc.running = true;
|
||||
}
|
||||
StyledToolTip {
|
||||
content: Translation.tr("Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers")
|
||||
}
|
||||
}
|
||||
RippleButtonWithIcon {
|
||||
materialIcon: "wallpaper"
|
||||
StyledToolTip {
|
||||
content: Translation.tr("Pick wallpaper image on your system")
|
||||
}
|
||||
onClicked: {
|
||||
Quickshell.execDetached(`${Directories.wallpaperSwitchScriptPath}`)
|
||||
}
|
||||
mainContentComponent: Component {
|
||||
RowLayout {
|
||||
spacing: 10
|
||||
StyledText {
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
text: Translation.tr("Choose file")
|
||||
color: Appearance.colors.colOnSecondaryContainer
|
||||
}
|
||||
RowLayout {
|
||||
spacing: 3
|
||||
KeyboardKey {
|
||||
key: "Ctrl"
|
||||
}
|
||||
KeyboardKey {
|
||||
key: ""
|
||||
}
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: "+"
|
||||
}
|
||||
KeyboardKey {
|
||||
key: "T"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.topMargin: 5
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: Translation.tr("Alternatively use /dark, /light, /img in the launcher")
|
||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
||||
color: Appearance.colors.colSubtext
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ContentSection {
|
||||
title: Translation.tr("Decorations & Effects")
|
||||
|
||||
ContentSubsection {
|
||||
title: Translation.tr("Transparency")
|
||||
|
||||
ConfigRow {
|
||||
ConfigSwitch {
|
||||
text: Translation.tr("Enable")
|
||||
checked: Config.options.appearance.transparency
|
||||
onCheckedChanged: {
|
||||
Config.options.appearance.transparency = checked;
|
||||
}
|
||||
StyledToolTip {
|
||||
content: Translation.tr("Might look ass. Unsupported.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentSubsection {
|
||||
title: Translation.tr("Fake screen rounding")
|
||||
|
||||
ButtonGroup {
|
||||
id: fakeScreenRoundingButtonGroup
|
||||
property int selectedPolicy: Config.options.appearance.fakeScreenRounding
|
||||
spacing: 2
|
||||
SelectionGroupButton {
|
||||
property int value: 0
|
||||
leftmost: true
|
||||
buttonText: Translation.tr("No")
|
||||
toggled: (fakeScreenRoundingButtonGroup.selectedPolicy === value)
|
||||
onClicked: {
|
||||
Config.options.appearance.fakeScreenRounding = value;
|
||||
}
|
||||
}
|
||||
SelectionGroupButton {
|
||||
property int value: 1
|
||||
buttonText: Translation.tr("Yes")
|
||||
toggled: (fakeScreenRoundingButtonGroup.selectedPolicy === value)
|
||||
onClicked: {
|
||||
Config.options.appearance.fakeScreenRounding = value;
|
||||
}
|
||||
}
|
||||
SelectionGroupButton {
|
||||
property int value: 2
|
||||
rightmost: true
|
||||
buttonText: Translation.tr("When not fullscreen")
|
||||
toggled: (fakeScreenRoundingButtonGroup.selectedPolicy === value)
|
||||
onClicked: {
|
||||
Config.options.appearance.fakeScreenRounding = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentSubsection {
|
||||
title: Translation.tr("Shell windows")
|
||||
|
||||
ConfigRow {
|
||||
uniform: true
|
||||
ConfigSwitch {
|
||||
text: Translation.tr("Title bar")
|
||||
checked: Config.options.windows.showTitlebar
|
||||
onCheckedChanged: {
|
||||
Config.options.windows.showTitlebar = checked;
|
||||
}
|
||||
}
|
||||
ConfigSwitch {
|
||||
text: Translation.tr("Center title")
|
||||
checked: Config.options.windows.centerTitle
|
||||
onCheckedChanged: {
|
||||
Config.options.windows.centerTitle = checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentSubsection {
|
||||
title: Translation.tr("Wallpaper parallax")
|
||||
|
||||
ConfigRow {
|
||||
uniform: true
|
||||
ConfigSwitch {
|
||||
text: Translation.tr("Depends on workspace")
|
||||
checked: Config.options.background.parallax.enableWorkspace
|
||||
onCheckedChanged: {
|
||||
Config.options.background.parallax.enableWorkspace = checked;
|
||||
}
|
||||
}
|
||||
ConfigSwitch {
|
||||
text: Translation.tr("Depends on sidebars")
|
||||
checked: Config.options.background.parallax.enableSidebar
|
||||
onCheckedChanged: {
|
||||
Config.options.background.parallax.enableSidebar = checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
ConfigSpinBox {
|
||||
text: Translation.tr("Preferred wallpaper zoom (%)")
|
||||
value: Config.options.background.parallax.workspaceZoom * 100
|
||||
from: 100
|
||||
to: 150
|
||||
stepSize: 1
|
||||
onValueChanged: {
|
||||
console.log(value/100)
|
||||
Config.options.background.parallax.workspaceZoom = value / 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,620 +0,0 @@
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import "./aiChat/"
|
||||
import "root:/modules/common/functions/fuzzysort.js" as Fuzzy
|
||||
import qs.modules.common.functions
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property var inputField: messageInputField
|
||||
property string commandPrefix: "/"
|
||||
|
||||
property var suggestionQuery: ""
|
||||
property var suggestionList: []
|
||||
|
||||
onFocusChanged: (focus) => {
|
||||
if (focus) {
|
||||
root.inputField.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: (event) => {
|
||||
messageInputField.forceActiveFocus()
|
||||
if (event.modifiers === Qt.NoModifier) {
|
||||
if (event.key === Qt.Key_PageUp) {
|
||||
messageListView.contentY = Math.max(0, messageListView.contentY - messageListView.height / 2)
|
||||
event.accepted = true
|
||||
} else if (event.key === Qt.Key_PageDown) {
|
||||
messageListView.contentY = Math.min(messageListView.contentHeight - messageListView.height / 2, messageListView.contentY + messageListView.height / 2)
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property var allCommands: [
|
||||
{
|
||||
name: "model",
|
||||
description: Translation.tr("Choose model"),
|
||||
execute: (args) => {
|
||||
Ai.setModel(args[0]);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "prompt",
|
||||
description: Translation.tr("Set the system prompt for the model."),
|
||||
execute: (args) => {
|
||||
if (args.length === 0 || args[0] === "get") {
|
||||
Ai.printPrompt();
|
||||
return;
|
||||
}
|
||||
Ai.loadPrompt(args.join(" ").trim());
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "key",
|
||||
description: Translation.tr("Set API key"),
|
||||
execute: (args) => {
|
||||
if (args[0] == "get") {
|
||||
Ai.printApiKey()
|
||||
} else {
|
||||
Ai.setApiKey(args[0]);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "save",
|
||||
description: Translation.tr("Save chat"),
|
||||
execute: (args) => {
|
||||
const joinedArgs = args.join(" ")
|
||||
if (joinedArgs.trim().length == 0) {
|
||||
Ai.addMessage(`Usage: ${root.commandPrefix}save CHAT_NAME`, Ai.interfaceRole);
|
||||
return;
|
||||
}
|
||||
Ai.saveChat(joinedArgs)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "load",
|
||||
description: Translation.tr("Load chat"),
|
||||
execute: (args) => {
|
||||
const joinedArgs = args.join(" ")
|
||||
if (joinedArgs.trim().length == 0) {
|
||||
Ai.addMessage(`Usage: ${root.commandPrefix}load CHAT_NAME`, Ai.interfaceRole);
|
||||
return;
|
||||
}
|
||||
Ai.loadChat(joinedArgs)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "clear",
|
||||
description: Translation.tr("Clear chat history"),
|
||||
execute: () => {
|
||||
Ai.clearMessages();
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "temp",
|
||||
description: Translation.tr("Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5."),
|
||||
execute: (args) => {
|
||||
// console.log(args)
|
||||
if (args.length == 0 || args[0] == "get") {
|
||||
Ai.printTemperature()
|
||||
} else {
|
||||
const temp = parseFloat(args[0]);
|
||||
Ai.setTemperature(temp);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "test",
|
||||
description: Translation.tr("Markdown test"),
|
||||
execute: () => {
|
||||
Ai.addMessage(`
|
||||
<think>
|
||||
A longer think block to test revealing animation
|
||||
OwO wem ipsum dowo sit amet, consekituwet awipiscing ewit, sed do eiuwsmod tempow inwididunt ut wabowe et dowo mawa. Ut enim ad minim weniam, quis nostwud exeucitation uwuwamcow bowowis nisi ut awiquip ex ea commowo consequat. Duuis aute iwuwe dowo in wepwependewit in wowuptate velit esse ciwwum dowo eu fugiat nuwa pawiatuw. Excepteuw sint occaecat cupidatat non pwowoident, sunt in cuwpa qui officia desewunt mowit anim id est wabowum. Meouw! >w<
|
||||
Mowe uwu wem ipsum!
|
||||
</think>
|
||||
## ✏️ Markdown test
|
||||
### Formatting
|
||||
|
||||
- *Italic*, \`Monospace\`, **Bold**, [Link](https://example.com)
|
||||
- Arch lincox icon <img src="${Quickshell.configPath("assets/icons/arch-symbolic.svg")}" height="${Appearance.font.pixelSize.small}"/>
|
||||
|
||||
### Table
|
||||
|
||||
Quickshell vs AGS/Astal
|
||||
|
||||
| | Quickshell | AGS/Astal |
|
||||
|--------------------------|------------------|-------------------|
|
||||
| UI Toolkit | Qt | Gtk3/Gtk4 |
|
||||
| Language | QML | Js/Ts/Lua |
|
||||
| Reactivity | Implied | Needs declaration |
|
||||
| Widget placement | Mildly difficult | More intuitive |
|
||||
| Bluetooth & Wifi support | ❌ | ✅ |
|
||||
| No-delay keybinds | ✅ | ❌ |
|
||||
| Development | New APIs | New syntax |
|
||||
|
||||
### Code block
|
||||
|
||||
Just a hello world...
|
||||
|
||||
\`\`\`cpp
|
||||
#include <bits/stdc++.h>
|
||||
// This is intentionally very long to test scrolling
|
||||
const std::string GREETING = \"UwU\";
|
||||
int main(int argc, char* argv[]) {
|
||||
std::cout << GREETING;
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
### LaTeX
|
||||
|
||||
|
||||
Inline w/ dollar signs: $\\frac{1}{2} = \\frac{2}{4}$
|
||||
|
||||
Inline w/ double dollar signs: $$\\int_0^\\infty e^{-x^2} dx = \\frac{\\sqrt{\\pi}}{2}$$
|
||||
|
||||
Inline w/ backslash and square brackets \\[\\int_0^\\infty \\frac{1}{x^2} dx = \\infty\\]
|
||||
|
||||
Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
|
||||
`,
|
||||
Ai.interfaceRole);
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
function handleInput(inputText) {
|
||||
if (inputText.startsWith(root.commandPrefix)) {
|
||||
// Handle special commands
|
||||
const command = inputText.split(" ")[0].substring(1);
|
||||
const args = inputText.split(" ").slice(1);
|
||||
const commandObj = root.allCommands.find(cmd => cmd.name === `${command}`);
|
||||
if (commandObj) {
|
||||
commandObj.execute(args);
|
||||
} else {
|
||||
Ai.addMessage(Translation.tr("Unknown command: ") + command, Ai.interfaceRole);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Ai.sendUserMessage(inputText);
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
anchors.fill: parent
|
||||
|
||||
Item { // Messages
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
StyledListView { // Message list
|
||||
id: messageListView
|
||||
anchors.fill: parent
|
||||
spacing: 10
|
||||
popin: false
|
||||
|
||||
property int lastResponseLength: 0
|
||||
|
||||
clip: true
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: swipeView.width
|
||||
height: swipeView.height
|
||||
radius: Appearance.rounding.small
|
||||
}
|
||||
}
|
||||
|
||||
add: null // Prevent function calls from being janky
|
||||
|
||||
Behavior on contentY {
|
||||
NumberAnimation {
|
||||
id: scrollAnim
|
||||
duration: Appearance.animation.scroll.duration
|
||||
easing.type: Appearance.animation.scroll.type
|
||||
easing.bezierCurve: Appearance.animation.scroll.bezierCurve
|
||||
}
|
||||
}
|
||||
|
||||
model: ScriptModel {
|
||||
values: Ai.messageIDs.filter(id => {
|
||||
const message = Ai.messageByID[id];
|
||||
return message?.visibleToUser ?? true;
|
||||
})
|
||||
}
|
||||
delegate: AiMessage {
|
||||
required property var modelData
|
||||
required property int index
|
||||
messageIndex: index
|
||||
messageData: {
|
||||
Ai.messageByID[modelData]
|
||||
}
|
||||
messageInputField: root.inputField
|
||||
}
|
||||
}
|
||||
|
||||
Item { // Placeholder when list is empty
|
||||
opacity: Ai.messageIDs.length === 0 ? 1 : 0
|
||||
visible: opacity > 0
|
||||
anchors.fill: parent
|
||||
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: 5
|
||||
|
||||
MaterialSymbol {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
iconSize: 60
|
||||
color: Appearance.m3colors.m3outline
|
||||
text: "neurology"
|
||||
}
|
||||
StyledText {
|
||||
id: widgetNameText
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
font.pixelSize: Appearance.font.pixelSize.larger
|
||||
font.family: Appearance.font.family.title
|
||||
color: Appearance.m3colors.m3outline
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: Translation.tr("Large language models")
|
||||
}
|
||||
StyledText {
|
||||
id: widgetDescriptionText
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: Appearance.m3colors.m3outline
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
wrapMode: Text.Wrap
|
||||
text: Translation.tr("Type /key to get started with online models\nCtrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DescriptionBox {
|
||||
text: root.suggestionList[suggestions.selectedIndex]?.description ?? ""
|
||||
showArrows: root.suggestionList.length > 1
|
||||
}
|
||||
|
||||
FlowButtonGroup { // Suggestions
|
||||
id: suggestions
|
||||
visible: root.suggestionList.length > 0 && messageInputField.text.length > 0
|
||||
property int selectedIndex: 0
|
||||
Layout.fillWidth: true
|
||||
spacing: 5
|
||||
|
||||
Repeater {
|
||||
id: suggestionRepeater
|
||||
model: {
|
||||
suggestions.selectedIndex = 0
|
||||
return root.suggestionList.slice(0, 10)
|
||||
}
|
||||
delegate: ApiCommandButton {
|
||||
id: commandButton
|
||||
colBackground: suggestions.selectedIndex === index ? Appearance.colors.colSecondaryContainerHover : Appearance.colors.colSecondaryContainer
|
||||
bounce: false
|
||||
contentItem: StyledText {
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: Appearance.m3colors.m3onSurface
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: modelData.displayName ?? modelData.name
|
||||
}
|
||||
|
||||
onHoveredChanged: {
|
||||
if (commandButton.hovered) {
|
||||
suggestions.selectedIndex = index;
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
suggestions.acceptSuggestion(modelData.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function acceptSuggestion(word) {
|
||||
const words = messageInputField.text.trim().split(/\s+/);
|
||||
if (words.length > 0) {
|
||||
words[words.length - 1] = word;
|
||||
} else {
|
||||
words.push(word);
|
||||
}
|
||||
const updatedText = words.join(" ") + " ";
|
||||
messageInputField.text = updatedText;
|
||||
messageInputField.cursorPosition = messageInputField.text.length;
|
||||
messageInputField.forceActiveFocus();
|
||||
}
|
||||
|
||||
function acceptSelectedWord() {
|
||||
if (suggestions.selectedIndex >= 0 && suggestions.selectedIndex < suggestionRepeater.count) {
|
||||
const word = root.suggestionList[suggestions.selectedIndex].name;
|
||||
suggestions.acceptSuggestion(word);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // Input area
|
||||
id: inputWrapper
|
||||
property real columnSpacing: 5
|
||||
Layout.fillWidth: true
|
||||
radius: Appearance.rounding.small
|
||||
color: Appearance.colors.colLayer1
|
||||
implicitWidth: messageInputField.implicitWidth
|
||||
implicitHeight: Math.max(inputFieldRowLayout.implicitHeight + inputFieldRowLayout.anchors.topMargin
|
||||
+ commandButtonsRow.implicitHeight + commandButtonsRow.anchors.bottomMargin + columnSpacing, 45)
|
||||
clip: true
|
||||
border.color: Appearance.colors.colOutlineVariant
|
||||
border.width: 1
|
||||
|
||||
Behavior on implicitHeight {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
RowLayout { // Input field and send button
|
||||
id: inputFieldRowLayout
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 5
|
||||
spacing: 0
|
||||
|
||||
StyledTextArea { // The actual TextArea
|
||||
id: messageInputField
|
||||
wrapMode: TextArea.Wrap
|
||||
Layout.fillWidth: true
|
||||
padding: 10
|
||||
color: activeFocus ? Appearance.m3colors.m3onSurface : Appearance.m3colors.m3onSurfaceVariant
|
||||
placeholderText: Translation.tr('Message the model... "%1" for commands').arg(root.commandPrefix)
|
||||
|
||||
background: null
|
||||
|
||||
onTextChanged: { // Handle suggestions
|
||||
if (messageInputField.text.length === 0) {
|
||||
root.suggestionQuery = ""
|
||||
root.suggestionList = []
|
||||
return
|
||||
} else if (messageInputField.text.startsWith(`${root.commandPrefix}model`)) {
|
||||
root.suggestionQuery = messageInputField.text.split(" ")[1] ?? ""
|
||||
const modelResults = Fuzzy.go(root.suggestionQuery, Ai.modelList.map(model => {
|
||||
return {
|
||||
name: Fuzzy.prepare(model),
|
||||
obj: model,
|
||||
}
|
||||
}), {
|
||||
all: true,
|
||||
key: "name"
|
||||
})
|
||||
root.suggestionList = modelResults.map(model => {
|
||||
return {
|
||||
name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "model ") : ""}${model.target}`,
|
||||
displayName: `${Ai.models[model.target].name}`,
|
||||
description: `${Ai.models[model.target].description}`,
|
||||
}
|
||||
})
|
||||
} else if (messageInputField.text.startsWith(`${root.commandPrefix}prompt`)) {
|
||||
root.suggestionQuery = messageInputField.text.split(" ")[1] ?? ""
|
||||
const promptFileResults = Fuzzy.go(root.suggestionQuery, Ai.promptFiles.map(file => {
|
||||
return {
|
||||
name: Fuzzy.prepare(file),
|
||||
obj: file,
|
||||
}
|
||||
}), {
|
||||
all: true,
|
||||
key: "name"
|
||||
})
|
||||
root.suggestionList = promptFileResults.map(file => {
|
||||
return {
|
||||
name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "prompt ") : ""}${file.target}`,
|
||||
displayName: `${FileUtils.trimFileExt(FileUtils.fileNameForPath(file.target))}`,
|
||||
description: Translation.tr("Load prompt from %1").arg(file.target),
|
||||
}
|
||||
})
|
||||
} else if (messageInputField.text.startsWith(`${root.commandPrefix}save`)) {
|
||||
root.suggestionQuery = messageInputField.text.split(" ")[1] ?? ""
|
||||
const promptFileResults = Fuzzy.go(root.suggestionQuery, Ai.savedChats.map(file => {
|
||||
return {
|
||||
name: Fuzzy.prepare(file),
|
||||
obj: file,
|
||||
}
|
||||
}), {
|
||||
all: true,
|
||||
key: "name"
|
||||
})
|
||||
root.suggestionList = promptFileResults.map(file => {
|
||||
const chatName = FileUtils.trimFileExt(FileUtils.fileNameForPath(file.target)).trim()
|
||||
return {
|
||||
name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "save ") : ""}${chatName}`,
|
||||
displayName: `${chatName}`,
|
||||
description: Translation.tr("Save chat to %1").arg(chatName),
|
||||
}
|
||||
})
|
||||
} else if (messageInputField.text.startsWith(`${root.commandPrefix}load`)) {
|
||||
root.suggestionQuery = messageInputField.text.split(" ")[1] ?? ""
|
||||
const promptFileResults = Fuzzy.go(root.suggestionQuery, Ai.savedChats.map(file => {
|
||||
return {
|
||||
name: Fuzzy.prepare(file),
|
||||
obj: file,
|
||||
}
|
||||
}), {
|
||||
all: true,
|
||||
key: "name"
|
||||
})
|
||||
root.suggestionList = promptFileResults.map(file => {
|
||||
const chatName = FileUtils.trimFileExt(FileUtils.fileNameForPath(file.target)).trim()
|
||||
return {
|
||||
name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "load ") : ""}${chatName}`,
|
||||
displayName: `${chatName}`,
|
||||
description: Translation.tr(`Load chat from %1`).arg(file.target),
|
||||
}
|
||||
})
|
||||
} else if(messageInputField.text.startsWith(root.commandPrefix)) {
|
||||
root.suggestionQuery = messageInputField.text
|
||||
root.suggestionList = root.allCommands.filter(cmd => cmd.name.startsWith(messageInputField.text.substring(1))).map(cmd => {
|
||||
return {
|
||||
name: `${root.commandPrefix}${cmd.name}`,
|
||||
description: `${cmd.description}`,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function accept() {
|
||||
root.handleInput(text)
|
||||
text = ""
|
||||
}
|
||||
|
||||
Keys.onPressed: (event) => {
|
||||
if (event.key === Qt.Key_Tab) {
|
||||
suggestions.acceptSelectedWord();
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Up && suggestions.visible) {
|
||||
suggestions.selectedIndex = Math.max(0, suggestions.selectedIndex - 1);
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Down && suggestions.visible) {
|
||||
suggestions.selectedIndex = Math.min(root.suggestionList.length - 1, suggestions.selectedIndex + 1);
|
||||
event.accepted = true;
|
||||
} else if ((event.key === Qt.Key_Enter || event.key === Qt.Key_Return)) {
|
||||
if (event.modifiers & Qt.ShiftModifier) {
|
||||
// Insert newline
|
||||
messageInputField.insert(messageInputField.cursorPosition, "\n")
|
||||
event.accepted = true
|
||||
} else { // Accept text
|
||||
const inputText = messageInputField.text
|
||||
messageInputField.clear()
|
||||
root.handleInput(inputText)
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RippleButton { // Send button
|
||||
id: sendButton
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.rightMargin: 5
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
buttonRadius: Appearance.rounding.small
|
||||
enabled: messageInputField.text.length > 0
|
||||
toggled: enabled
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: sendButton.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
onClicked: {
|
||||
const inputText = messageInputField.text
|
||||
root.handleInput(inputText)
|
||||
messageInputField.clear()
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
// fill: sendButton.enabled ? 1 : 0
|
||||
color: sendButton.enabled ? Appearance.m3colors.m3onPrimary : Appearance.colors.colOnLayer2Disabled
|
||||
text: "send"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout { // Controls
|
||||
id: commandButtonsRow
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 5
|
||||
anchors.leftMargin: 5
|
||||
anchors.rightMargin: 5
|
||||
spacing: 5
|
||||
|
||||
property var commandsShown: [
|
||||
{
|
||||
name: "model",
|
||||
sendDirectly: false,
|
||||
},
|
||||
{
|
||||
name: "clear",
|
||||
sendDirectly: true,
|
||||
},
|
||||
]
|
||||
|
||||
Item {
|
||||
implicitHeight: providerRowLayout.implicitHeight + 5 * 2
|
||||
implicitWidth: providerRowLayout.implicitWidth + 10 * 2
|
||||
|
||||
RowLayout {
|
||||
id: providerRowLayout
|
||||
anchors.centerIn: parent
|
||||
|
||||
MaterialSymbol {
|
||||
text: "api"
|
||||
iconSize: Appearance.font.pixelSize.large
|
||||
}
|
||||
StyledText {
|
||||
id: providerName
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: Appearance.m3colors.m3onSurface
|
||||
elide: Text.ElideRight
|
||||
text: Ai.getModel().name
|
||||
}
|
||||
}
|
||||
StyledToolTip {
|
||||
id: toolTip
|
||||
extraVisibleCondition: false
|
||||
alternativeVisibleCondition: mouseArea.containsMouse // Show tooltip when hovered
|
||||
content: Translation.tr("Current model: %1\nSet it with %2model MODEL")
|
||||
.arg(Ai.getModel().name)
|
||||
.arg(root.commandPrefix)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
ButtonGroup {
|
||||
padding: 0
|
||||
|
||||
Repeater { // Command buttons
|
||||
model: commandButtonsRow.commandsShown
|
||||
delegate: ApiCommandButton {
|
||||
property string commandRepresentation: `${root.commandPrefix}${modelData.name}`
|
||||
buttonText: commandRepresentation
|
||||
onClicked: {
|
||||
if(modelData.sendDirectly) {
|
||||
root.handleInput(commandRepresentation)
|
||||
} else {
|
||||
messageInputField.text = commandRepresentation + " "
|
||||
messageInputField.cursorPosition = messageInputField.text.length
|
||||
messageInputField.forceActiveFocus()
|
||||
}
|
||||
if (modelData.name === "clear") {
|
||||
messageInputField.text = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
Item {
|
||||
id: root
|
||||
required property var scopeRoot
|
||||
anchors.fill: parent
|
||||
property var tabButtonList: [
|
||||
...(Config.options.policies.ai !== 0 ? [{"icon": "neurology", "name": Translation.tr("Intelligence")}] : []),
|
||||
{"icon": "translate", "name": Translation.tr("Translator")},
|
||||
...(Config.options.policies.weeb === 1 ? [{"icon": "bookmark_heart", "name": Translation.tr("Anime")}] : [])
|
||||
]
|
||||
property int selectedTab: 0
|
||||
|
||||
function focusActiveItem() {
|
||||
swipeView.currentItem.forceActiveFocus()
|
||||
}
|
||||
|
||||
Keys.onPressed: (event) => {
|
||||
if (event.modifiers === Qt.ControlModifier) {
|
||||
if (event.key === Qt.Key_PageDown) {
|
||||
root.selectedTab = Math.min(root.selectedTab + 1, root.tabButtonList.length - 1)
|
||||
event.accepted = true;
|
||||
}
|
||||
else if (event.key === Qt.Key_PageUp) {
|
||||
root.selectedTab = Math.max(root.selectedTab - 1, 0)
|
||||
event.accepted = true;
|
||||
}
|
||||
else if (event.key === Qt.Key_Tab) {
|
||||
root.selectedTab = (root.selectedTab + 1) % root.tabButtonList.length;
|
||||
event.accepted = true;
|
||||
}
|
||||
else if (event.key === Qt.Key_Backtab) {
|
||||
root.selectedTab = (root.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length;
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: sidebarPadding
|
||||
|
||||
spacing: sidebarPadding
|
||||
|
||||
PrimaryTabBar { // Tab strip
|
||||
id: tabBar
|
||||
tabButtonList: root.tabButtonList
|
||||
externalTrackedTab: root.selectedTab
|
||||
function onCurrentIndexChanged(currentIndex) {
|
||||
root.selectedTab = currentIndex
|
||||
}
|
||||
}
|
||||
|
||||
SwipeView { // Content pages
|
||||
id: swipeView
|
||||
Layout.topMargin: 5
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
spacing: 10
|
||||
|
||||
currentIndex: tabBar.externalTrackedTab
|
||||
onCurrentIndexChanged: {
|
||||
tabBar.enableIndicatorAnimation = true
|
||||
root.selectedTab = currentIndex
|
||||
}
|
||||
|
||||
clip: true
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: swipeView.width
|
||||
height: swipeView.height
|
||||
radius: Appearance.rounding.small
|
||||
}
|
||||
}
|
||||
|
||||
contentChildren: [
|
||||
...(Config.options.policies.ai !== 0 ? [aiChat.createObject()] : []),
|
||||
translator.createObject(),
|
||||
...(Config.options.policies.weeb === 0 ? [] : [anime.createObject()])
|
||||
]
|
||||
}
|
||||
|
||||
Component {
|
||||
id: aiChat
|
||||
AiChat {}
|
||||
}
|
||||
Component {
|
||||
id: translator
|
||||
Translator {}
|
||||
}
|
||||
Component {
|
||||
id: anime
|
||||
Anime {}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,287 +0,0 @@
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Io
|
||||
import Quickshell
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
property int messageIndex
|
||||
property var messageData
|
||||
property var messageInputField
|
||||
|
||||
property real messagePadding: 7
|
||||
property real contentSpacing: 3
|
||||
|
||||
property bool enableMouseSelection: false
|
||||
property bool renderMarkdown: true
|
||||
property bool editing: false
|
||||
|
||||
property list<var> messageBlocks: StringUtils.splitMarkdownBlocks(root.messageData?.content)
|
||||
|
||||
anchors.left: parent?.left
|
||||
anchors.right: parent?.right
|
||||
implicitHeight: columnLayout.implicitHeight + root.messagePadding * 2
|
||||
|
||||
radius: Appearance.rounding.normal
|
||||
color: Appearance.colors.colLayer1
|
||||
|
||||
function saveMessage() {
|
||||
if (!root.editing) return;
|
||||
// Get all Loader children (each represents a segment)
|
||||
const segments = messageContentColumnLayout.children
|
||||
.map(child => child.segment)
|
||||
.filter(segment => (segment));
|
||||
|
||||
// Reconstruct markdown
|
||||
const newContent = segments.map(segment => {
|
||||
if (segment.type === "code") {
|
||||
const lang = segment.lang ? segment.lang : "";
|
||||
// Remove trailing newlines
|
||||
const code = segment.content.replace(/\n+$/, "");
|
||||
return "```" + lang + "\n" + code + "\n```";
|
||||
} else {
|
||||
return segment.content;
|
||||
}
|
||||
}).join("");
|
||||
|
||||
root.editing = false
|
||||
root.messageData.content = newContent;
|
||||
}
|
||||
|
||||
Keys.onPressed: (event) => {
|
||||
if ( // Prevent de-select
|
||||
event.key === Qt.Key_Control ||
|
||||
event.key == Qt.Key_Shift ||
|
||||
event.key == Qt.Key_Alt ||
|
||||
event.key == Qt.Key_Meta
|
||||
) {
|
||||
event.accepted = true
|
||||
}
|
||||
// Ctrl + S to save
|
||||
if ((event.key === Qt.Key_S) && event.modifiers == Qt.ControlModifier) {
|
||||
root.saveMessage();
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout { // Main layout of the whole thing
|
||||
id: columnLayout
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.margins: messagePadding
|
||||
spacing: root.contentSpacing
|
||||
|
||||
RowLayout { // Header
|
||||
spacing: 15
|
||||
Layout.fillWidth: true
|
||||
|
||||
Rectangle { // Name
|
||||
id: nameWrapper
|
||||
color: Appearance.colors.colSecondaryContainer
|
||||
// color: "transparent"
|
||||
radius: Appearance.rounding.small
|
||||
implicitHeight: Math.max(nameRowLayout.implicitHeight + 5 * 2, 30)
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
RowLayout {
|
||||
id: nameRowLayout
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
spacing: 7
|
||||
|
||||
Item {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.fillHeight: true
|
||||
implicitWidth: messageData?.role == 'assistant' ? modelIcon.width : roleIcon.implicitWidth
|
||||
implicitHeight: messageData?.role == 'assistant' ? modelIcon.height : roleIcon.implicitHeight
|
||||
|
||||
CustomIcon {
|
||||
id: modelIcon
|
||||
anchors.centerIn: parent
|
||||
visible: messageData?.role == 'assistant' && Ai.models[messageData?.model].icon
|
||||
width: Appearance.font.pixelSize.large
|
||||
height: Appearance.font.pixelSize.large
|
||||
source: messageData?.role == 'assistant' ? Ai.models[messageData?.model].icon :
|
||||
messageData?.role == 'user' ? 'linux-symbolic' : 'desktop-symbolic'
|
||||
|
||||
colorize: true
|
||||
color: Appearance.m3colors.m3onSecondaryContainer
|
||||
}
|
||||
|
||||
MaterialSymbol {
|
||||
id: roleIcon
|
||||
anchors.centerIn: parent
|
||||
visible: !modelIcon.visible
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
color: Appearance.m3colors.m3onSecondaryContainer
|
||||
text: messageData?.role == 'user' ? 'person' :
|
||||
messageData?.role == 'interface' ? 'settings' :
|
||||
messageData?.role == 'assistant' ? 'neurology' :
|
||||
'computer'
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: providerName
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
font.pixelSize: Appearance.font.pixelSize.normal
|
||||
color: Appearance.m3colors.m3onSecondaryContainer
|
||||
text: messageData?.role == 'assistant' ? Ai.models[messageData?.model].name :
|
||||
(messageData?.role == 'user' && SystemInfo.username) ? SystemInfo.username :
|
||||
Translation.tr("Interface")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button { // Not visible to model
|
||||
id: modelVisibilityIndicator
|
||||
visible: messageData?.role == 'interface'
|
||||
implicitWidth: 16
|
||||
implicitHeight: 30
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
background: Item
|
||||
|
||||
MaterialSymbol {
|
||||
id: notVisibleToModelText
|
||||
anchors.centerIn: parent
|
||||
iconSize: Appearance.font.pixelSize.small
|
||||
color: Appearance.colors.colSubtext
|
||||
text: "visibility_off"
|
||||
}
|
||||
StyledToolTip {
|
||||
content: Translation.tr("Not visible to model")
|
||||
}
|
||||
}
|
||||
|
||||
ButtonGroup {
|
||||
spacing: 5
|
||||
|
||||
AiMessageControlButton {
|
||||
id: copyButton
|
||||
buttonIcon: activated ? "inventory" : "content_copy"
|
||||
|
||||
onClicked: {
|
||||
Quickshell.clipboardText = root.messageData?.content
|
||||
copyButton.activated = true
|
||||
copyIconTimer.restart()
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: copyIconTimer
|
||||
interval: 1500
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
copyButton.activated = false
|
||||
}
|
||||
}
|
||||
|
||||
StyledToolTip {
|
||||
content: Translation.tr("Copy")
|
||||
}
|
||||
}
|
||||
AiMessageControlButton {
|
||||
id: editButton
|
||||
activated: root.editing
|
||||
enabled: root.messageData?.done ?? false
|
||||
buttonIcon: "edit"
|
||||
onClicked: {
|
||||
root.editing = !root.editing
|
||||
if (!root.editing) { // Save changes
|
||||
root.saveMessage()
|
||||
}
|
||||
}
|
||||
StyledToolTip {
|
||||
content: root.editing ? Translation.tr("Save") : Translation.tr("Edit")
|
||||
}
|
||||
}
|
||||
AiMessageControlButton {
|
||||
id: toggleMarkdownButton
|
||||
activated: !root.renderMarkdown
|
||||
buttonIcon: "code"
|
||||
onClicked: {
|
||||
root.renderMarkdown = !root.renderMarkdown
|
||||
}
|
||||
StyledToolTip {
|
||||
content: Translation.tr("View Markdown source")
|
||||
}
|
||||
}
|
||||
AiMessageControlButton {
|
||||
id: deleteButton
|
||||
buttonIcon: "close"
|
||||
onClicked: {
|
||||
Ai.removeMessage(root.messageIndex)
|
||||
}
|
||||
StyledToolTip {
|
||||
content: Translation.tr("Delete")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout { // Message content
|
||||
id: messageContentColumnLayout
|
||||
|
||||
spacing: 0
|
||||
Repeater {
|
||||
model: root.messageBlocks.length
|
||||
delegate: Loader {
|
||||
required property int index
|
||||
property var thisBlock: root.messageBlocks[index]
|
||||
Layout.fillWidth: true
|
||||
// property var segment: thisBlock
|
||||
property var segmentContent: thisBlock.content
|
||||
property var segmentLang: thisBlock.lang
|
||||
property var messageData: root.messageData
|
||||
property var editing: root.editing
|
||||
property var renderMarkdown: root.renderMarkdown
|
||||
property var enableMouseSelection: root.enableMouseSelection
|
||||
property bool thinking: root.messageData?.thinking ?? true
|
||||
property bool done: root.messageData?.done ?? false
|
||||
property bool completed: thisBlock.completed ?? false
|
||||
|
||||
source: thisBlock.type === "code" ? "MessageCodeBlock.qml" :
|
||||
thisBlock.type === "think" ? "MessageThinkBlock.qml" :
|
||||
"MessageTextBlock.qml"
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Flow { // Annotations
|
||||
id: annotationFlowLayout
|
||||
visible: root.messageData?.annotationSources?.length > 0
|
||||
spacing: 5
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: root.messageData?.annotationSources || []
|
||||
}
|
||||
delegate: AnnotationSourceButton {
|
||||
id: annotationButton
|
||||
displayText: modelData.text
|
||||
url: modelData.url
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Hyprland
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
// These are needed on the parent loader
|
||||
property bool editing: parent?.editing ?? false
|
||||
property bool renderMarkdown: parent?.renderMarkdown ?? true
|
||||
property bool enableMouseSelection: parent?.enableMouseSelection ?? false
|
||||
property string segmentContent: parent?.segmentContent ?? ({})
|
||||
property var messageData: parent?.messageData ?? {}
|
||||
property bool done: parent?.done ?? true
|
||||
property list<string> renderedLatexHashes: []
|
||||
|
||||
property string renderedSegmentContent: ""
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
Timer {
|
||||
id: renderTimer
|
||||
interval: 1000
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
renderLatex()
|
||||
for (const hash of renderedLatexHashes) {
|
||||
handleRenderedLatex(hash, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderLatex() {
|
||||
// Regex for $...$, $$...$$, \[...\]
|
||||
// Note: This is a simple approach and may need refinement for edge cases
|
||||
let regex = /(\$\$([\s\S]+?)\$\$)|(\$([^\$]+?)\$)|(\\\[((?:.|\n)+?)\\\])|(\\\(([\s\S]+?)\\\))/g;
|
||||
let match;
|
||||
while ((match = regex.exec(segmentContent)) !== null) {
|
||||
let expression = match[1] || match[2] || match[3] || match[4] || match[5] || match[6] || match[7] || match[8];
|
||||
if (expression) {
|
||||
Qt.callLater(() => {
|
||||
const [renderHash, isNew] = LatexRenderer.requestRender(expression.trim());
|
||||
if (!renderedLatexHashes.includes(renderHash)) {
|
||||
renderedLatexHashes.push(renderHash);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleRenderedLatex(hash, force = false) {
|
||||
if (renderedLatexHashes.includes(hash) || force) {
|
||||
const imagePath = LatexRenderer.renderedImagePaths[hash];
|
||||
const markdownImage = ``;
|
||||
|
||||
const expression = LatexRenderer.processedExpressions[hash];
|
||||
renderedSegmentContent = renderedSegmentContent.replace(expression, markdownImage);
|
||||
}
|
||||
}
|
||||
|
||||
onDoneChanged: {
|
||||
renderTimer.restart();
|
||||
}
|
||||
onEditingChanged: {
|
||||
if (!editing) {
|
||||
renderLatex()
|
||||
} else {
|
||||
// console.log("Editing mode enabled", segmentContent)
|
||||
textArea.text = segmentContent
|
||||
}
|
||||
}
|
||||
|
||||
onSegmentContentChanged: {
|
||||
// console.log("Segment content changed: " + segmentContent);
|
||||
renderedSegmentContent = segmentContent;
|
||||
if (!root.editing && segmentContent) {
|
||||
root.renderLatex();
|
||||
}
|
||||
}
|
||||
|
||||
onRenderedSegmentContentChanged: {
|
||||
// console.log("Rendered segment content changed: " + renderedSegmentContent);
|
||||
if (renderedSegmentContent) {
|
||||
textArea.text = renderedSegmentContent;
|
||||
}
|
||||
}
|
||||
|
||||
// When something finishes rendering
|
||||
// 1. Check if the hash is in the list
|
||||
// 2. If it is, replace the expression with the image path
|
||||
Connections {
|
||||
target: LatexRenderer
|
||||
function onRenderFinished(hash, imagePath) {
|
||||
const expression = LatexRenderer.processedExpressions[hash];
|
||||
// console.log("Render finished: " + hash + " " + expression);
|
||||
handleRenderedLatex(hash);
|
||||
}
|
||||
}
|
||||
|
||||
TextArea {
|
||||
id: textArea
|
||||
|
||||
Layout.fillWidth: true
|
||||
readOnly: !editing
|
||||
selectByMouse: enableMouseSelection || editing
|
||||
renderType: Text.NativeRendering
|
||||
font.family: Appearance.font.family.reading
|
||||
font.hintingPreference: Font.PreferNoHinting // Prevent weird bold text
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
selectedTextColor: Appearance.m3colors.m3onSecondaryContainer
|
||||
selectionColor: Appearance.colors.colSecondaryContainer
|
||||
wrapMode: TextEdit.Wrap
|
||||
color: messageData.thinking ? Appearance.colors.colSubtext : Appearance.colors.colOnLayer1
|
||||
textFormat: renderMarkdown ? TextEdit.MarkdownText : TextEdit.PlainText
|
||||
text: Translation.tr("Waiting for response...")
|
||||
|
||||
onTextChanged: {
|
||||
if (!root.editing) return
|
||||
segmentContent = text
|
||||
}
|
||||
|
||||
onLinkActivated: (link) => {
|
||||
Qt.openUrlExternally(link)
|
||||
Hyprland.dispatch("global quickshell:sidebarLeftClose")
|
||||
}
|
||||
|
||||
MouseArea { // Pointing hand for links
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton // Only for hover
|
||||
hoverEnabled: true
|
||||
cursorShape: parent.hoveredLink !== "" ? Qt.PointingHandCursor :
|
||||
(enableMouseSelection || editing) ? Qt.IBeamCursor : Qt.ArrowCursor
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import "./notifications"
|
||||
import "./volumeMixer"
|
||||
import qs
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
radius: Appearance.rounding.normal
|
||||
color: Appearance.colors.colLayer1
|
||||
|
||||
property int selectedTab: 0
|
||||
property var tabButtonList: [{"icon": "notifications", "name": Translation.tr("Notifications")}, {"icon": "volume_up", "name": Translation.tr("Volume mixer")}]
|
||||
|
||||
Keys.onPressed: (event) => {
|
||||
if (event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) {
|
||||
if (event.key === Qt.Key_PageDown) {
|
||||
root.selectedTab = Math.min(root.selectedTab + 1, root.tabButtonList.length - 1)
|
||||
} else if (event.key === Qt.Key_PageUp) {
|
||||
root.selectedTab = Math.max(root.selectedTab - 1, 0)
|
||||
}
|
||||
event.accepted = true;
|
||||
}
|
||||
if (event.modifiers === Qt.ControlModifier) {
|
||||
if (event.key === Qt.Key_Tab) {
|
||||
root.selectedTab = (root.selectedTab + 1) % root.tabButtonList.length
|
||||
} else if (event.key === Qt.Key_Backtab) {
|
||||
root.selectedTab = (root.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length
|
||||
}
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.margins: 5
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
PrimaryTabBar {
|
||||
id: tabBar
|
||||
tabButtonList: root.tabButtonList
|
||||
externalTrackedTab: root.selectedTab
|
||||
|
||||
function onCurrentIndexChanged(currentIndex) {
|
||||
root.selectedTab = currentIndex
|
||||
}
|
||||
}
|
||||
|
||||
SwipeView {
|
||||
id: swipeView
|
||||
Layout.topMargin: 5
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
spacing: 10
|
||||
currentIndex: root.selectedTab
|
||||
onCurrentIndexChanged: {
|
||||
tabBar.enableIndicatorAnimation = true
|
||||
root.selectedTab = currentIndex
|
||||
}
|
||||
|
||||
clip: true
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: swipeView.width
|
||||
height: swipeView.height
|
||||
radius: Appearance.rounding.small
|
||||
}
|
||||
}
|
||||
|
||||
NotificationList {}
|
||||
VolumeMixer {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,250 +0,0 @@
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import "./quickToggles/"
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell.Io
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
property int sidebarWidth: Appearance.sizes.sidebarWidth
|
||||
property int sidebarPadding: 12
|
||||
property string settingsQmlPath: Quickshell.configPath("settings.qml")
|
||||
|
||||
PanelWindow {
|
||||
id: sidebarRoot
|
||||
visible: GlobalStates.sidebarRightOpen
|
||||
|
||||
function hide() {
|
||||
GlobalStates.sidebarRightOpen = false
|
||||
}
|
||||
|
||||
exclusiveZone: 0
|
||||
implicitWidth: sidebarWidth
|
||||
WlrLayershell.namespace: "quickshell:sidebarRight"
|
||||
// Hyprland 0.49: Focus is always exclusive and setting this breaks mouse focus grab
|
||||
// WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||
color: "transparent"
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
right: true
|
||||
bottom: true
|
||||
}
|
||||
|
||||
HyprlandFocusGrab {
|
||||
id: grab
|
||||
windows: [ sidebarRoot ]
|
||||
active: GlobalStates.sidebarRightOpen
|
||||
onCleared: () => {
|
||||
if (!active) sidebarRoot.hide()
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: sidebarContentLoader
|
||||
active: GlobalStates.sidebarRightOpen
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
left: parent.left
|
||||
topMargin: Appearance.sizes.hyprlandGapsOut
|
||||
rightMargin: Appearance.sizes.hyprlandGapsOut
|
||||
bottomMargin: Appearance.sizes.hyprlandGapsOut
|
||||
leftMargin: Appearance.sizes.elevationMargin
|
||||
}
|
||||
width: sidebarWidth - Appearance.sizes.hyprlandGapsOut - Appearance.sizes.elevationMargin
|
||||
height: parent.height - Appearance.sizes.hyprlandGapsOut * 2
|
||||
|
||||
focus: GlobalStates.sidebarRightOpen
|
||||
Keys.onPressed: (event) => {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
sidebarRoot.hide();
|
||||
}
|
||||
}
|
||||
|
||||
sourceComponent: Item {
|
||||
implicitHeight: sidebarRightBackground.implicitHeight
|
||||
implicitWidth: sidebarRightBackground.implicitWidth
|
||||
|
||||
StyledRectangularShadow {
|
||||
target: sidebarRightBackground
|
||||
}
|
||||
Rectangle {
|
||||
id: sidebarRightBackground
|
||||
|
||||
anchors.fill: parent
|
||||
implicitHeight: parent.height - Appearance.sizes.hyprlandGapsOut * 2
|
||||
implicitWidth: sidebarWidth - Appearance.sizes.hyprlandGapsOut * 2
|
||||
color: Appearance.colors.colLayer0
|
||||
border.width: 1
|
||||
border.color: Appearance.m3colors.m3outlineVariant
|
||||
radius: Appearance.rounding.screenRounding - Appearance.sizes.hyprlandGapsOut + 1
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: sidebarPadding
|
||||
spacing: sidebarPadding
|
||||
|
||||
RowLayout {
|
||||
Layout.fillHeight: false
|
||||
spacing: 10
|
||||
Layout.margins: 10
|
||||
Layout.topMargin: 5
|
||||
Layout.bottomMargin: 0
|
||||
|
||||
Item {
|
||||
implicitWidth: distroIcon.width
|
||||
implicitHeight: distroIcon.height
|
||||
CustomIcon {
|
||||
id: distroIcon
|
||||
width: 25
|
||||
height: 25
|
||||
source: SystemInfo.distroIcon
|
||||
}
|
||||
ColorOverlay {
|
||||
anchors.fill: distroIcon
|
||||
source: distroIcon
|
||||
color: Appearance.colors.colOnLayer0
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
font.pixelSize: Appearance.font.pixelSize.normal
|
||||
color: Appearance.colors.colOnLayer0
|
||||
text: Translation.tr("Uptime: %1").arg(DateTime.uptime)
|
||||
textFormat: Text.MarkdownText
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
ButtonGroup {
|
||||
QuickToggleButton {
|
||||
toggled: false
|
||||
buttonIcon: "restart_alt"
|
||||
onClicked: {
|
||||
Hyprland.dispatch("reload")
|
||||
Quickshell.reload(true)
|
||||
}
|
||||
StyledToolTip {
|
||||
content: Translation.tr("Reload Hyprland & Quickshell")
|
||||
}
|
||||
}
|
||||
QuickToggleButton {
|
||||
toggled: false
|
||||
buttonIcon: "settings"
|
||||
onClicked: {
|
||||
Hyprland.dispatch("global quickshell:sidebarRightClose")
|
||||
Quickshell.execDetached(["qs", "-p", root.settingsQmlPath])
|
||||
}
|
||||
StyledToolTip {
|
||||
content: Translation.tr("Settings")
|
||||
}
|
||||
}
|
||||
QuickToggleButton {
|
||||
toggled: false
|
||||
buttonIcon: "power_settings_new"
|
||||
onClicked: {
|
||||
Hyprland.dispatch("global quickshell:sessionOpen")
|
||||
}
|
||||
StyledToolTip {
|
||||
content: Translation.tr("Session")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ButtonGroup {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: 5
|
||||
padding: 5
|
||||
color: Appearance.colors.colLayer1
|
||||
|
||||
NetworkToggle {}
|
||||
BluetoothToggle {}
|
||||
NightLight {}
|
||||
GameMode {}
|
||||
IdleInhibitor {}
|
||||
EasyEffectsToggle {}
|
||||
CloudflareWarp {}
|
||||
}
|
||||
|
||||
// Center widget group
|
||||
CenterWidgetGroup {
|
||||
focus: sidebarRoot.visible
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
BottomWidgetGroup {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillHeight: false
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: implicitHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "sidebarRight"
|
||||
|
||||
function toggle(): void {
|
||||
GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen;
|
||||
if(GlobalStates.sidebarRightOpen) Notifications.timeoutAll();
|
||||
}
|
||||
|
||||
function close(): void {
|
||||
GlobalStates.sidebarRightOpen = false;
|
||||
}
|
||||
|
||||
function open(): void {
|
||||
GlobalStates.sidebarRightOpen = true;
|
||||
Notifications.timeoutAll();
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "sidebarRightToggle"
|
||||
description: "Toggles right sidebar on press"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen;
|
||||
if(GlobalStates.sidebarRightOpen) Notifications.timeoutAll();
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "sidebarRightOpen"
|
||||
description: "Opens right sidebar on press"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.sidebarRightOpen = true;
|
||||
Notifications.timeoutAll();
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "sidebarRightClose"
|
||||
description: "Closes right sidebar on press"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.sidebarRightOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import qs
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
NotificationListView { // Scrollable window
|
||||
id: listview
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: statusRow.top
|
||||
anchors.bottomMargin: 5
|
||||
|
||||
clip: true
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: listview.width
|
||||
height: listview.height
|
||||
radius: Appearance.rounding.normal
|
||||
}
|
||||
}
|
||||
|
||||
popup: false
|
||||
}
|
||||
|
||||
// Placeholder when list is empty
|
||||
Item {
|
||||
anchors.fill: listview
|
||||
|
||||
visible: opacity > 0
|
||||
opacity: (Notifications.list.length === 0) ? 1 : 0
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.menuDecel.duration
|
||||
easing.type: Appearance.animation.menuDecel.type
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: 5
|
||||
|
||||
MaterialSymbol {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
iconSize: 55
|
||||
color: Appearance.m3colors.m3outline
|
||||
text: "notifications_active"
|
||||
}
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
font.pixelSize: Appearance.font.pixelSize.normal
|
||||
color: Appearance.m3colors.m3outline
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: Translation.tr("No notifications")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: statusRow
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: Math.max(
|
||||
controls.implicitHeight,
|
||||
statusText.implicitHeight
|
||||
)
|
||||
|
||||
StyledText {
|
||||
id: statusText
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 10
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: Translation.tr("%1 notifications").arg(Notifications.list.length)
|
||||
|
||||
opacity: Notifications.list.length > 0 ? 1 : 0
|
||||
visible: opacity > 0
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
ButtonGroup {
|
||||
id: controls
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.rightMargin: 5
|
||||
|
||||
NotificationStatusButton {
|
||||
buttonIcon: "notifications_paused"
|
||||
buttonText: Translation.tr("Silent")
|
||||
toggled: Notifications.silent
|
||||
onClicked: () => {
|
||||
Notifications.silent = !Notifications.silent;
|
||||
}
|
||||
}
|
||||
NotificationStatusButton {
|
||||
buttonIcon: "clear_all"
|
||||
buttonText: Translation.tr("Clear")
|
||||
onClicked: () => {
|
||||
Notifications.discardAllNotifications()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Hyprland
|
||||
|
||||
QuickToggleButton {
|
||||
toggled: Bluetooth.bluetoothEnabled
|
||||
buttonIcon: Bluetooth.bluetoothConnected ? "bluetooth_connected" : Bluetooth.bluetoothEnabled ? "bluetooth" : "bluetooth_disabled"
|
||||
onClicked: {
|
||||
toggleBluetooth.running = true
|
||||
}
|
||||
altAction: () => {
|
||||
Quickshell.execDetached(["bash", "-c", `${Config.options.apps.bluetooth}`])
|
||||
Hyprland.dispatch("global quickshell:sidebarRightClose")
|
||||
}
|
||||
Process {
|
||||
id: toggleBluetooth
|
||||
command: ["bash", "-c", `bluetoothctl power ${Bluetooth.bluetoothEnabled ? "off" : "on"}`]
|
||||
onRunningChanged: {
|
||||
if(!running) {
|
||||
Bluetooth.update()
|
||||
}
|
||||
}
|
||||
}
|
||||
StyledToolTip {
|
||||
content: Translation.tr("%1 | Right-click to configure").arg(
|
||||
(Bluetooth.bluetoothEnabled && Bluetooth.bluetoothDeviceName.length > 0) ?
|
||||
Bluetooth.bluetoothDeviceName : Translation.tr("Bluetooth"))
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import qs.modules.common.widgets
|
||||
import qs
|
||||
import Quickshell.Io
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
|
||||
QuickToggleButton {
|
||||
id: root
|
||||
toggled: false
|
||||
visible: false
|
||||
buttonIcon: "instant_mix"
|
||||
|
||||
onClicked: {
|
||||
if (toggled) {
|
||||
root.toggled = false
|
||||
Quickshell.execDetached(["pkill", "easyeffects"])
|
||||
} else {
|
||||
root.toggled = true
|
||||
Quickshell.execDetached(["easyeffects", "--gapplication-service"])
|
||||
}
|
||||
}
|
||||
|
||||
altAction: () => {
|
||||
Quickshell.execDetached(["easyeffects"])
|
||||
Hyprland.dispatch("global quickshell:sidebarRightClose")
|
||||
}
|
||||
|
||||
Process {
|
||||
id: fetchAvailability
|
||||
running: true
|
||||
command: ["bash", "-c", "command -v easyeffects"]
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
root.visible = exitCode === 0
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: fetchActiveState
|
||||
running: true
|
||||
command: ["pidof", "easyeffects"]
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
root.toggled = exitCode === 0
|
||||
}
|
||||
}
|
||||
|
||||
StyledToolTip {
|
||||
content: Translation.tr("EasyEffects | Right-click to configure")
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs
|
||||
import Quickshell.Io
|
||||
import Quickshell
|
||||
|
||||
QuickToggleButton {
|
||||
id: root
|
||||
toggled: false
|
||||
buttonIcon: "coffee"
|
||||
onClicked: {
|
||||
if (toggled) {
|
||||
root.toggled = false
|
||||
Quickshell.execDetached(["pkill", "wayland-idle"]) // pkill doesn't accept too long names
|
||||
} else {
|
||||
root.toggled = true
|
||||
Quickshell.execDetached([`${Directories.scriptPath}/wayland-idle-inhibitor.py`])
|
||||
}
|
||||
}
|
||||
Process {
|
||||
id: fetchActiveState
|
||||
running: true
|
||||
command: ["pidof", "wayland-idle-inhibitor.py"]
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
root.toggled = exitCode === 0
|
||||
}
|
||||
}
|
||||
StyledToolTip {
|
||||
content: Translation.tr("Keep system awake")
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import "../"
|
||||
import qs
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Hyprland
|
||||
|
||||
QuickToggleButton {
|
||||
toggled: Network.networkName.length > 0 && Network.networkName != "lo"
|
||||
buttonIcon: Network.materialSymbol
|
||||
onClicked: {
|
||||
toggleNetwork.running = true
|
||||
}
|
||||
altAction: () => {
|
||||
Quickshell.execDetached(["bash", "-c", `${Network.ethernet ? Config.options.apps.networkEthernet : Config.options.apps.network}`])
|
||||
Hyprland.dispatch("global quickshell:sidebarRightClose")
|
||||
}
|
||||
Process {
|
||||
id: toggleNetwork
|
||||
command: ["bash", "-c", "nmcli radio wifi | grep -q enabled && nmcli radio wifi off || nmcli radio wifi on"]
|
||||
onRunningChanged: {
|
||||
if(!running) {
|
||||
Network.update()
|
||||
}
|
||||
}
|
||||
}
|
||||
StyledToolTip {
|
||||
content: Translation.tr("%1 | Right-click to configure").arg(Network.networkName)
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs
|
||||
import Quickshell.Io
|
||||
|
||||
QuickToggleButton {
|
||||
id: nightLightButton
|
||||
property bool enabled: false
|
||||
toggled: enabled
|
||||
buttonIcon: "nightlight"
|
||||
onClicked: {
|
||||
nightLightButton.enabled = !nightLightButton.enabled
|
||||
if (enabled) {
|
||||
nightLightOn.startDetached()
|
||||
}
|
||||
else {
|
||||
nightLightOff.startDetached()
|
||||
}
|
||||
}
|
||||
Process {
|
||||
id: nightLightOn
|
||||
command: ["gammastep"]
|
||||
}
|
||||
Process {
|
||||
id: nightLightOff
|
||||
command: ["pkill", "gammastep"]
|
||||
}
|
||||
Process {
|
||||
id: updateNightLightState
|
||||
running: true
|
||||
command: ["pidof", "gammastep"]
|
||||
stdout: SplitParser {
|
||||
onRead: (data) => { // if not empty then set toggled to true
|
||||
nightLightButton.enabled = data.length > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
StyledToolTip {
|
||||
content: Translation.tr("Night Light")
|
||||
}
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
|
||||
Item {
|
||||
id: root
|
||||
required property var taskList;
|
||||
property string emptyPlaceholderIcon
|
||||
property string emptyPlaceholderText
|
||||
property int todoListItemSpacing: 5
|
||||
property int todoListItemPadding: 8
|
||||
property int listBottomPadding: 80
|
||||
|
||||
Flickable {
|
||||
id: flickable
|
||||
anchors.fill: parent
|
||||
contentHeight: columnLayout.height
|
||||
|
||||
clip: true
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: flickable.width
|
||||
height: flickable.height
|
||||
radius: Appearance.rounding.small
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
width: parent.width
|
||||
spacing: 0
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: taskList
|
||||
}
|
||||
delegate: Item {
|
||||
id: todoItem
|
||||
property bool pendingDoneToggle: false
|
||||
property bool pendingDelete: false
|
||||
property bool enableHeightAnimation: false
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: todoItemRectangle.implicitHeight + todoListItemSpacing
|
||||
height: implicitHeight
|
||||
clip: true
|
||||
|
||||
Behavior on implicitHeight {
|
||||
enabled: enableHeightAnimation
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMoveFast.duration
|
||||
easing.type: Appearance.animation.elementMoveFast.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
||||
}
|
||||
}
|
||||
|
||||
function startAction() {
|
||||
enableHeightAnimation = true
|
||||
todoItem.implicitHeight = 0
|
||||
actionTimer.start()
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: actionTimer
|
||||
interval: Appearance.animation.elementMoveFast.duration
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
if (todoItem.pendingDelete) {
|
||||
Todo.deleteItem(modelData.originalIndex)
|
||||
} else if (todoItem.pendingDoneToggle) {
|
||||
if (!modelData.done) Todo.markDone(modelData.originalIndex)
|
||||
else Todo.markUnfinished(modelData.originalIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: todoItemRectangle
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
implicitHeight: todoContentRowLayout.implicitHeight
|
||||
color: Appearance.colors.colLayer2
|
||||
radius: Appearance.rounding.small
|
||||
ColumnLayout {
|
||||
id: todoContentRowLayout
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true // Needed for wrapping
|
||||
Layout.leftMargin: 10
|
||||
Layout.rightMargin: 10
|
||||
Layout.topMargin: todoListItemPadding
|
||||
id: todoContentText
|
||||
text: modelData.content
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
RowLayout {
|
||||
Layout.leftMargin: 10
|
||||
Layout.rightMargin: 10
|
||||
Layout.bottomMargin: todoListItemPadding
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
TodoItemActionButton {
|
||||
Layout.fillWidth: false
|
||||
onClicked: {
|
||||
todoItem.pendingDoneToggle = true
|
||||
todoItem.startAction()
|
||||
}
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: modelData.done ? "remove_done" : "check"
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
color: Appearance.colors.colOnLayer1
|
||||
}
|
||||
}
|
||||
TodoItemActionButton {
|
||||
Layout.fillWidth: false
|
||||
onClicked: {
|
||||
todoItem.pendingDelete = true
|
||||
todoItem.startAction()
|
||||
}
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: "delete_forever"
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
color: Appearance.colors.colOnLayer1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// Bottom padding
|
||||
Item {
|
||||
implicitHeight: listBottomPadding
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { // Placeholder when list is empty
|
||||
visible: opacity > 0
|
||||
opacity: taskList.length === 0 ? 1 : 0
|
||||
anchors.fill: parent
|
||||
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: 5
|
||||
|
||||
MaterialSymbol {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
iconSize: 55
|
||||
color: Appearance.m3colors.m3outline
|
||||
text: emptyPlaceholderIcon
|
||||
}
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
font.pixelSize: Appearance.font.pixelSize.normal
|
||||
color: Appearance.m3colors.m3outline
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: emptyPlaceholderText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,282 +0,0 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import qs
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Services.Pipewire
|
||||
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property bool showDeviceSelector: false
|
||||
property bool deviceSelectorInput
|
||||
property int dialogMargins: 16
|
||||
property PwNode selectedDevice
|
||||
readonly property list<PwNode> appPwNodes: Pipewire.nodes.values.filter((node) => {
|
||||
// return node.type == "21" // Alternative, not as clean
|
||||
return node.isSink && node.isStream
|
||||
})
|
||||
|
||||
function showDeviceSelectorDialog(input: bool) {
|
||||
root.selectedDevice = null
|
||||
root.showDeviceSelector = true
|
||||
root.deviceSelectorInput = input
|
||||
}
|
||||
|
||||
Keys.onPressed: (event) => {
|
||||
// Close dialog on pressing Esc if open
|
||||
if (event.key === Qt.Key_Escape && root.showDeviceSelector) {
|
||||
root.showDeviceSelector = false
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
ListView {
|
||||
id: listView
|
||||
model: root.appPwNodes
|
||||
clip: true
|
||||
anchors {
|
||||
fill: parent
|
||||
topMargin: 10
|
||||
bottomMargin: 10
|
||||
}
|
||||
spacing: 6
|
||||
|
||||
delegate: VolumeMixerEntry {
|
||||
// Layout.fillWidth: true
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
leftMargin: 10
|
||||
rightMargin: 10
|
||||
}
|
||||
required property var modelData
|
||||
node: modelData
|
||||
}
|
||||
}
|
||||
|
||||
// Placeholder when list is empty
|
||||
Item {
|
||||
anchors.fill: listView
|
||||
|
||||
visible: opacity > 0
|
||||
opacity: (root.appPwNodes.length === 0) ? 1 : 0
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.menuDecel.duration
|
||||
easing.type: Appearance.animation.menuDecel.type
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: 5
|
||||
|
||||
MaterialSymbol {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
iconSize: 55
|
||||
color: Appearance.m3colors.m3outline
|
||||
text: "brand_awareness"
|
||||
}
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
font.pixelSize: Appearance.font.pixelSize.normal
|
||||
color: Appearance.m3colors.m3outline
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: Translation.tr("No audio source")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Separator
|
||||
Rectangle {
|
||||
color: Appearance.m3colors.m3outlineVariant
|
||||
implicitHeight: 1
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
|
||||
// Device selector
|
||||
ButtonGroup {
|
||||
id: deviceSelectorRowLayout
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: false
|
||||
AudioDeviceSelectorButton {
|
||||
Layout.fillWidth: true
|
||||
input: false
|
||||
onClicked: root.showDeviceSelectorDialog(input)
|
||||
}
|
||||
AudioDeviceSelectorButton {
|
||||
Layout.fillWidth: true
|
||||
input: true
|
||||
onClicked: root.showDeviceSelectorDialog(input)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Device selector dialog
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
z: 9999
|
||||
|
||||
visible: opacity > 0
|
||||
opacity: root.showDeviceSelector ? 1 : 0
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMoveFast.duration
|
||||
easing.type: Appearance.animation.elementMoveFast.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // Scrim
|
||||
id: scrimOverlay
|
||||
anchors.fill: parent
|
||||
radius: Appearance.rounding.small
|
||||
color: Appearance.colors.colScrim
|
||||
MouseArea {
|
||||
hoverEnabled: true
|
||||
anchors.fill: parent
|
||||
preventStealing: true
|
||||
propagateComposedEvents: false
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // The dialog
|
||||
id: dialog
|
||||
color: Appearance.colors.colSurfaceContainerHigh
|
||||
radius: Appearance.rounding.normal
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.margins: 30
|
||||
implicitHeight: dialogColumnLayout.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: dialogColumnLayout
|
||||
anchors.fill: parent
|
||||
spacing: 16
|
||||
|
||||
StyledText {
|
||||
id: dialogTitle
|
||||
Layout.topMargin: dialogMargins
|
||||
Layout.leftMargin: dialogMargins
|
||||
Layout.rightMargin: dialogMargins
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
color: Appearance.m3colors.m3onSurface
|
||||
font.pixelSize: Appearance.font.pixelSize.larger
|
||||
text: root.deviceSelectorInput ? Translation.tr("Select input device") : Translation.tr("Select output device")
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: Appearance.m3colors.m3outline
|
||||
implicitHeight: 1
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: dialogMargins
|
||||
Layout.rightMargin: dialogMargins
|
||||
}
|
||||
|
||||
Flickable {
|
||||
id: dialogFlickable
|
||||
Layout.fillWidth: true
|
||||
clip: true
|
||||
implicitHeight: Math.min(scrimOverlay.height - dialogMargins * 8 - dialogTitle.height - dialogButtonsRowLayout.height, devicesColumnLayout.implicitHeight)
|
||||
|
||||
contentHeight: devicesColumnLayout.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: devicesColumnLayout
|
||||
anchors.fill: parent
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: Pipewire.nodes.values.filter(node => {
|
||||
return !node.isStream && node.isSink !== root.deviceSelectorInput && node.audio
|
||||
})
|
||||
}
|
||||
|
||||
// This could and should be refractored, but all data becomes null when passed wtf
|
||||
delegate: StyledRadioButton {
|
||||
id: radioButton
|
||||
required property var modelData
|
||||
Layout.leftMargin: root.dialogMargins
|
||||
Layout.rightMargin: root.dialogMargins
|
||||
Layout.fillWidth: true
|
||||
|
||||
description: modelData.description
|
||||
checked: modelData.id === Pipewire.defaultAudioSink?.id
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
function onShowDeviceSelectorChanged() {
|
||||
if(!root.showDeviceSelector) return;
|
||||
radioButton.checked = (modelData.id === Pipewire.defaultAudioSink?.id)
|
||||
}
|
||||
}
|
||||
|
||||
onCheckedChanged: {
|
||||
if (checked) {
|
||||
root.selectedDevice = modelData
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
implicitHeight: dialogMargins
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: Appearance.m3colors.m3outline
|
||||
implicitHeight: 1
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: dialogMargins
|
||||
Layout.rightMargin: dialogMargins
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: dialogButtonsRowLayout
|
||||
Layout.bottomMargin: dialogMargins
|
||||
Layout.leftMargin: dialogMargins
|
||||
Layout.rightMargin: dialogMargins
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
DialogButton {
|
||||
buttonText: Translation.tr("Cancel")
|
||||
onClicked: {
|
||||
root.showDeviceSelector = false
|
||||
}
|
||||
}
|
||||
DialogButton {
|
||||
buttonText: Translation.tr("OK")
|
||||
onClicked: {
|
||||
root.showDeviceSelector = false
|
||||
if (root.selectedDevice) {
|
||||
if (root.deviceSelectorInput) {
|
||||
Pipewire.preferredDefaultAudioSource = root.selectedDevice
|
||||
} else {
|
||||
Pipewire.preferredDefaultAudioSink = root.selectedDevice
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Services.Pipewire
|
||||
|
||||
Item {
|
||||
id: root
|
||||
required property PwNode node
|
||||
PwObjectTracker {
|
||||
objects: [node]
|
||||
}
|
||||
|
||||
implicitHeight: rowLayout.implicitHeight
|
||||
|
||||
RowLayout {
|
||||
id: rowLayout
|
||||
anchors.fill: parent
|
||||
spacing: 6
|
||||
|
||||
Image {
|
||||
property real size: slider.height * 0.9
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||
visible: source != ""
|
||||
sourceSize.width: size
|
||||
sourceSize.height: size
|
||||
source: {
|
||||
let icon;
|
||||
icon = AppSearch.guessIcon(root.node.properties["application.icon-name"]);
|
||||
if (AppSearch.iconExists(icon))
|
||||
return Quickshell.iconPath(icon, "image-missing");
|
||||
icon = AppSearch.guessIcon(root.node.properties["node.name"]);
|
||||
return Quickshell.iconPath(icon, "image-missing");
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: -4
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: Appearance.colors.colSubtext
|
||||
elide: Text.ElideRight
|
||||
text: {
|
||||
// application.name -> description -> name
|
||||
const app = root.node.properties["application.name"] ?? (root.node.description != "" ? root.node.description : root.node.name);
|
||||
const media = root.node.properties["media.name"];
|
||||
return media != undefined ? `${app} • ${media}` : app;
|
||||
}
|
||||
}
|
||||
|
||||
StyledSlider {
|
||||
id: slider
|
||||
value: root.node.audio.volume
|
||||
onValueChanged: root.node.audio.volume = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,552 +0,0 @@
|
||||
//@ pragma UseQApplication
|
||||
//@ pragma Env QS_NO_RELOAD_POPUP=1
|
||||
//@ pragma Env QT_QUICK_CONTROLS_STYLE=Basic
|
||||
|
||||
// Adjust this to make it smaller or larger
|
||||
//@ pragma Env QT_SCALE_FACTOR=1
|
||||
|
||||
pragma ComponentBehavior: "Bound"
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
ShellRoot {
|
||||
id: root
|
||||
property string screenshotDir: Directories.screenshotTemp
|
||||
property color overlayColor: "#77111111"
|
||||
property color genericContentColor: Qt.alpha(root.overlayColor, 0.9)
|
||||
property color genericContentForeground: "#ddffffff"
|
||||
property color selectionBorderColor: "#ddf1f1f1"
|
||||
property color selectionFillColor: "#33ffffff"
|
||||
property color windowBorderColor: "#dda0c0da"
|
||||
property color windowFillColor: "#22a0c0da"
|
||||
property color imageBorderColor: "#ddf1d1ff"
|
||||
property color imageFillColor: "#33f1d1ff"
|
||||
property color onBorderColor: "#ff000000"
|
||||
property real standardRounding: 4
|
||||
readonly property var windows: HyprlandData.windowList
|
||||
readonly property var layers: HyprlandData.layers
|
||||
readonly property real falsePositivePreventionRatio: 0.5
|
||||
|
||||
// Force initialization of some singletons
|
||||
Component.onCompleted: {
|
||||
MaterialThemeLoader.reapplyTheme();
|
||||
}
|
||||
|
||||
component TargetRegion: Rectangle {
|
||||
id: regionRect
|
||||
property bool showIcon: false
|
||||
property bool targeted: false
|
||||
property color borderColor
|
||||
property color fillColor: "transparent"
|
||||
property string text: ""
|
||||
property real textPadding: 10
|
||||
z: 2
|
||||
color: fillColor
|
||||
border.color: borderColor
|
||||
border.width: targeted ? 3 : 1
|
||||
radius: root.standardRounding
|
||||
|
||||
Rectangle {
|
||||
id: regionLabelBackground
|
||||
property real verticalPadding: 5
|
||||
property real horizontalPadding: 10
|
||||
radius: 10
|
||||
color: root.genericContentColor
|
||||
border.width: 1
|
||||
border.color: Appearance.m3colors.m3outlineVariant
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
topMargin: regionRect.textPadding
|
||||
leftMargin: regionRect.textPadding
|
||||
}
|
||||
implicitWidth: regionInfoRow.implicitWidth + horizontalPadding * 2
|
||||
implicitHeight: regionInfoRow.implicitHeight + verticalPadding * 2
|
||||
RowLayout {
|
||||
id: regionInfoRow
|
||||
anchors.centerIn: parent
|
||||
spacing: 8
|
||||
|
||||
Loader {
|
||||
id: regionIconLoader
|
||||
active: regionRect.showIcon
|
||||
visible: active
|
||||
sourceComponent: IconImage {
|
||||
implicitSize: Appearance.font.pixelSize.larger
|
||||
source: Quickshell.iconPath(AppSearch.guessIcon(regionRect.text), "image-missing")
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: regionText
|
||||
text: regionRect.text
|
||||
color: root.genericContentForeground
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
PanelWindow {
|
||||
id: panelWindow
|
||||
required property var modelData
|
||||
readonly property HyprlandMonitor hyprlandMonitor: Hyprland.monitorFor(modelData)
|
||||
readonly property real monitorScale: hyprlandMonitor.scale
|
||||
readonly property real monitorOffsetX: hyprlandMonitor.x
|
||||
readonly property real monitorOffsetY: hyprlandMonitor.y
|
||||
property int activeWorkspaceId: hyprlandMonitor.activeWorkspace?.id ?? 0
|
||||
property string screenshotPath: `${root.screenshotDir}/image-${modelData.name}`
|
||||
property real dragStartX: 0
|
||||
property real dragStartY: 0
|
||||
property real draggingX: 0
|
||||
property real draggingY: 0
|
||||
property real dragDiffX: 0
|
||||
property real dragDiffY: 0
|
||||
property bool draggedAway: (dragDiffX !== 0 || dragDiffY !== 0)
|
||||
property bool dragging: false
|
||||
property var mouseButton: null
|
||||
property var imageRegions: []
|
||||
readonly property list<var> windowRegions: filterWindowRegionsByLayers(
|
||||
root.windows.filter(w => w.workspace.id === panelWindow.activeWorkspaceId),
|
||||
panelWindow.layerRegions
|
||||
).map(window => {
|
||||
return {
|
||||
at: [window.at[0] - panelWindow.monitorOffsetX, window.at[1] - panelWindow.monitorOffsetY],
|
||||
size: [window.size[0], window.size[1]],
|
||||
class: window.class,
|
||||
title: window.title,
|
||||
}
|
||||
})
|
||||
readonly property list<var> layerRegions: {
|
||||
const layersOfThisMonitor = root.layers[panelWindow.hyprlandMonitor.name]
|
||||
const topLayers = layersOfThisMonitor.levels["2"]
|
||||
const nonBarTopLayers = topLayers
|
||||
.filter(layer => !(layer.namespace.includes(":bar") || layer.namespace.includes(":dock")))
|
||||
.map(layer => {
|
||||
return {
|
||||
at: [layer.x, layer.y],
|
||||
size: [layer.w, layer.h],
|
||||
namespace: layer.namespace,
|
||||
}
|
||||
})
|
||||
const offsetAdjustedLayers = nonBarTopLayers.map(layer => {
|
||||
return {
|
||||
at: [layer.at[0] - panelWindow.monitorOffsetX, layer.at[1] - panelWindow.monitorOffsetY],
|
||||
size: layer.size,
|
||||
namespace: layer.namespace,
|
||||
}
|
||||
});
|
||||
return offsetAdjustedLayers;
|
||||
}
|
||||
|
||||
property real targetedRegionX: -1
|
||||
property real targetedRegionY: -1
|
||||
property real targetedRegionWidth: 0
|
||||
property real targetedRegionHeight: 0
|
||||
|
||||
function intersectionOverUnion(regionA, regionB) {
|
||||
// region: { at: [x, y], size: [w, h] }
|
||||
const ax1 = regionA.at[0], ay1 = regionA.at[1];
|
||||
const ax2 = ax1 + regionA.size[0], ay2 = ay1 + regionA.size[1];
|
||||
const bx1 = regionB.at[0], by1 = regionB.at[1];
|
||||
const bx2 = bx1 + regionB.size[0], by2 = by1 + regionB.size[1];
|
||||
|
||||
const interX1 = Math.max(ax1, bx1);
|
||||
const interY1 = Math.max(ay1, by1);
|
||||
const interX2 = Math.min(ax2, bx2);
|
||||
const interY2 = Math.min(ay2, by2);
|
||||
|
||||
const interArea = Math.max(0, interX2 - interX1) * Math.max(0, interY2 - interY1);
|
||||
const areaA = (ax2 - ax1) * (ay2 - ay1);
|
||||
const areaB = (bx2 - bx1) * (by2 - by1);
|
||||
const unionArea = areaA + areaB - interArea;
|
||||
|
||||
return unionArea > 0 ? interArea / unionArea : 0;
|
||||
}
|
||||
|
||||
function filterOverlappingImageRegions(regions) {
|
||||
let keep = [];
|
||||
let removed = new Set();
|
||||
for (let i = 0; i < regions.length; ++i) {
|
||||
if (removed.has(i)) continue;
|
||||
let regionA = regions[i];
|
||||
for (let j = i + 1; j < regions.length; ++j) {
|
||||
if (removed.has(j)) continue;
|
||||
let regionB = regions[j];
|
||||
if (intersectionOverUnion(regionA, regionB) > 0) {
|
||||
// Compare areas
|
||||
let areaA = regionA.size[0] * regionA.size[1];
|
||||
let areaB = regionB.size[0] * regionB.size[1];
|
||||
if (areaA <= areaB) {
|
||||
removed.add(j);
|
||||
} else {
|
||||
removed.add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < regions.length; ++i) {
|
||||
if (!removed.has(i)) keep.push(regions[i]);
|
||||
}
|
||||
return keep;
|
||||
}
|
||||
|
||||
function filterWindowRegionsByLayers(windowRegions, layerRegions) {
|
||||
return windowRegions.filter(windowRegion => {
|
||||
for (let i = 0; i < layerRegions.length; ++i) {
|
||||
if (intersectionOverUnion(windowRegion, layerRegions[i]) > 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function filterImageRegions(regions, windowRegions, threshold = 0.1) {
|
||||
// Remove image regions that overlap too much with any window region
|
||||
let filtered = regions.filter(region => {
|
||||
for (let i = 0; i < windowRegions.length; ++i) {
|
||||
if (intersectionOverUnion(region, windowRegions[i]) > threshold)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
// Remove overlapping image regions, keep only the smaller one
|
||||
return filterOverlappingImageRegions(filtered);
|
||||
}
|
||||
|
||||
function updateTargetedRegion(x, y) {
|
||||
// Image regions
|
||||
const clickedRegion = panelWindow.imageRegions.find(region => {
|
||||
return region.at[0] <= x && x <= region.at[0] + region.size[0] && region.at[1] <= y && y <= region.at[1] + region.size[1];
|
||||
});
|
||||
if (clickedRegion) {
|
||||
panelWindow.targetedRegionX = clickedRegion.at[0];
|
||||
panelWindow.targetedRegionY = clickedRegion.at[1];
|
||||
panelWindow.targetedRegionWidth = clickedRegion.size[0];
|
||||
panelWindow.targetedRegionHeight = clickedRegion.size[1];
|
||||
return;
|
||||
}
|
||||
|
||||
// Layer regions
|
||||
const clickedLayer = panelWindow.layerRegions.find(region => {
|
||||
return region.at[0] <= x && x <= region.at[0] + region.size[0] && region.at[1] <= y && y <= region.at[1] + region.size[1];
|
||||
});
|
||||
if (clickedLayer) {
|
||||
panelWindow.targetedRegionX = clickedLayer.at[0];
|
||||
panelWindow.targetedRegionY = clickedLayer.at[1];
|
||||
panelWindow.targetedRegionWidth = clickedLayer.size[0];
|
||||
panelWindow.targetedRegionHeight = clickedLayer.size[1];
|
||||
return;
|
||||
}
|
||||
|
||||
// Window regions
|
||||
const clickedWindow = panelWindow.windowRegions.find(region => {
|
||||
return region.at[0] <= x && x <= region.at[0] + region.size[0] && region.at[1] <= y && y <= region.at[1] + region.size[1];
|
||||
});
|
||||
if (clickedWindow) {
|
||||
panelWindow.targetedRegionX = clickedWindow.at[0];
|
||||
panelWindow.targetedRegionY = clickedWindow.at[1];
|
||||
panelWindow.targetedRegionWidth = clickedWindow.size[0];
|
||||
panelWindow.targetedRegionHeight = clickedWindow.size[1];
|
||||
return;
|
||||
}
|
||||
|
||||
panelWindow.targetedRegionX = -1;
|
||||
panelWindow.targetedRegionY = -1;
|
||||
panelWindow.targetedRegionWidth = 0;
|
||||
panelWindow.targetedRegionHeight = 0;
|
||||
}
|
||||
|
||||
property real regionWidth: Math.abs(draggingX - dragStartX)
|
||||
property real regionHeight: Math.abs(draggingY - dragStartY)
|
||||
property real regionX: Math.min(dragStartX, draggingX)
|
||||
property real regionY: Math.min(dragStartY, draggingY)
|
||||
|
||||
visible: false
|
||||
screen: modelData
|
||||
WlrLayershell.namespace: "quickshell:screenshot"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
anchors {
|
||||
left: true
|
||||
right: true
|
||||
top: true
|
||||
bottom: true
|
||||
}
|
||||
|
||||
Process {
|
||||
id: screenshotProcess
|
||||
running: true
|
||||
command: ["bash", "-c", `mkdir -p '${StringUtils.shellSingleQuoteEscape(root.screenshotDir)}' && grim -o '${StringUtils.shellSingleQuoteEscape(modelData.name)}' '${StringUtils.shellSingleQuoteEscape(panelWindow.screenshotPath)}'`]
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
panelWindow.visible = true;
|
||||
imageDetectionProcess.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: imageDetectionProcess
|
||||
command: ["bash", "-c", `${Directories.scriptPath}/images/find_regions.py `
|
||||
+ `--hyprctl `
|
||||
+ `--image '${StringUtils.shellSingleQuoteEscape(panelWindow.screenshotPath)}' `
|
||||
+ `--max-width ${Math.round(panelWindow.screen.width * root.falsePositivePreventionRatio)} `
|
||||
+ `--max-height ${Math.round(panelWindow.screen.height * root.falsePositivePreventionRatio)} `]
|
||||
stdout: StdioCollector {
|
||||
id: imageDimensionCollector
|
||||
onStreamFinished: {
|
||||
imageRegions = filterImageRegions(
|
||||
JSON.parse(imageDimensionCollector.text),
|
||||
panelWindow.windowRegions
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: snipProc
|
||||
function snip() {
|
||||
if (panelWindow.regionWidth <= 0 || panelWindow.regionHeight <= 0) {
|
||||
console.warn("Invalid region size, skipping snip.");
|
||||
Qt.quit();
|
||||
}
|
||||
snipProc.startDetached();
|
||||
Qt.quit();
|
||||
}
|
||||
command: ["bash", "-c",
|
||||
`magick ${StringUtils.shellSingleQuoteEscape(panelWindow.screenshotPath)} `
|
||||
+ `-crop ${panelWindow.regionWidth * panelWindow.monitorScale}x${panelWindow.regionHeight * panelWindow.monitorScale}+${panelWindow.regionX * panelWindow.monitorScale}+${panelWindow.regionY * panelWindow.monitorScale} - `
|
||||
+ `| ${panelWindow.mouseButton === Qt.LeftButton ? "wl-copy" : "swappy -f -"}`]
|
||||
}
|
||||
|
||||
ScreencopyView {
|
||||
anchors.fill: parent
|
||||
live: false
|
||||
captureSource: modelData
|
||||
|
||||
focus: panelWindow.visible
|
||||
Keys.onPressed: (event) => { // Esc to close
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
Qt.quit();
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.CrossCursor
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
hoverEnabled: true
|
||||
|
||||
// Controls
|
||||
onPressed: mouse => {
|
||||
panelWindow.dragStartX = mouse.x;
|
||||
panelWindow.dragStartY = mouse.y;
|
||||
panelWindow.draggingX = mouse.x;
|
||||
panelWindow.draggingY = mouse.y;
|
||||
panelWindow.dragging = true;
|
||||
panelWindow.mouseButton = mouse.button;
|
||||
}
|
||||
onReleased: mouse => {
|
||||
// Detect if it was a click
|
||||
|
||||
// Image regions
|
||||
if (panelWindow.draggingX === panelWindow.dragStartX && panelWindow.draggingY === panelWindow.dragStartY) {
|
||||
if (panelWindow.targetedRegionX >= 0 && panelWindow.targetedRegionY >= 0) {
|
||||
panelWindow.regionX = panelWindow.targetedRegionX;
|
||||
panelWindow.regionY = panelWindow.targetedRegionY;
|
||||
panelWindow.regionWidth = panelWindow.targetedRegionWidth;
|
||||
panelWindow.regionHeight = panelWindow.targetedRegionHeight;
|
||||
}
|
||||
}
|
||||
snipProc.snip();
|
||||
}
|
||||
onPositionChanged: mouse => {
|
||||
if (panelWindow.dragging) {
|
||||
panelWindow.draggingX = mouse.x;
|
||||
panelWindow.draggingY = mouse.y;
|
||||
panelWindow.dragDiffX = mouse.x - panelWindow.dragStartX;
|
||||
panelWindow.dragDiffY = mouse.y - panelWindow.dragStartY;
|
||||
}
|
||||
panelWindow.updateTargetedRegion(mouse.x, mouse.y);
|
||||
}
|
||||
|
||||
// Overlay to darken screen
|
||||
Rectangle { // Base
|
||||
id: overlayRect
|
||||
z: 0
|
||||
anchors.fill: parent
|
||||
color: root.overlayColor
|
||||
layer.enabled: true
|
||||
}
|
||||
Rectangle {
|
||||
// TODO: Make this mask the base instead of just overlaying a border
|
||||
z: 1
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
leftMargin: panelWindow.regionX
|
||||
topMargin: panelWindow.regionY
|
||||
}
|
||||
width: panelWindow.regionWidth
|
||||
height: panelWindow.regionHeight
|
||||
color: "transparent"
|
||||
border.color: root.selectionBorderColor
|
||||
border.width: 2
|
||||
radius: root.standardRounding
|
||||
}
|
||||
|
||||
// Instructions
|
||||
Rectangle {
|
||||
anchors {
|
||||
top: parent.top
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
topMargin: (Appearance.sizes.barHeight - implicitHeight) / 2
|
||||
}
|
||||
|
||||
opacity: panelWindow.dragging ? 0 : 1
|
||||
visible: opacity > 0
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
color: root.genericContentColor
|
||||
radius: 10
|
||||
border.width: 1
|
||||
border.color: Appearance.m3colors.m3outlineVariant
|
||||
implicitWidth: instructionsRow.implicitWidth + 10 * 2
|
||||
implicitHeight: instructionsRow.implicitHeight + 5 * 2
|
||||
|
||||
RowLayout {
|
||||
id: instructionsRow
|
||||
anchors.centerIn: parent
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
implicitWidth: screenshotRegionIcon.implicitWidth
|
||||
MaterialSymbol {
|
||||
id: screenshotRegionIcon
|
||||
anchors.centerIn: parent
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
text: "screenshot_region"
|
||||
color: root.genericContentForeground
|
||||
}
|
||||
}
|
||||
StyledText {
|
||||
text: Translation.tr("Drag or click a region • LMB: Copy • RMB: Edit")
|
||||
color: root.genericContentForeground
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Window regions
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: panelWindow.windowRegions
|
||||
}
|
||||
delegate: TargetRegion {
|
||||
z: 2
|
||||
required property var modelData
|
||||
showIcon: true
|
||||
targeted: !panelWindow.draggedAway &&
|
||||
(panelWindow.targetedRegionX === modelData.at[0]
|
||||
&& panelWindow.targetedRegionY === modelData.at[1]
|
||||
&& panelWindow.targetedRegionWidth === modelData.size[0]
|
||||
&& panelWindow.targetedRegionHeight === modelData.size[1])
|
||||
|
||||
opacity: panelWindow.draggedAway ? 0 : 1
|
||||
visible: opacity > 0
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
x: modelData.at[0]
|
||||
y: modelData.at[1]
|
||||
width: modelData.size[0]
|
||||
height: modelData.size[1]
|
||||
borderColor: root.windowBorderColor
|
||||
fillColor: targeted ? root.windowFillColor : "transparent"
|
||||
border.width: targeted ? 4 : 2
|
||||
text: `${modelData.class}`
|
||||
radius: Appearance.rounding.windowRounding
|
||||
}
|
||||
}
|
||||
|
||||
// Layer regions
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: panelWindow.layerRegions
|
||||
}
|
||||
delegate: TargetRegion {
|
||||
z: 3
|
||||
required property var modelData
|
||||
targeted: !panelWindow.draggedAway &&
|
||||
(panelWindow.targetedRegionX === modelData.at[0]
|
||||
&& panelWindow.targetedRegionY === modelData.at[1]
|
||||
&& panelWindow.targetedRegionWidth === modelData.size[0]
|
||||
&& panelWindow.targetedRegionHeight === modelData.size[1])
|
||||
|
||||
opacity: panelWindow.draggedAway ? 0 : 1
|
||||
visible: opacity > 0
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
x: modelData.at[0]
|
||||
y: modelData.at[1]
|
||||
width: modelData.size[0]
|
||||
height: modelData.size[1]
|
||||
borderColor: root.windowBorderColor
|
||||
fillColor: targeted ? root.windowFillColor : "transparent"
|
||||
border.width: targeted ? 4 : 2
|
||||
text: `${modelData.namespace}`
|
||||
radius: Appearance.rounding.windowRounding
|
||||
}
|
||||
}
|
||||
|
||||
// Image regions
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: Config.options.screenshotTool.showContentRegions ? panelWindow.imageRegions : []
|
||||
}
|
||||
delegate: TargetRegion {
|
||||
z: 4
|
||||
required property var modelData
|
||||
targeted: !panelWindow.draggedAway &&
|
||||
(panelWindow.targetedRegionX === modelData.at[0]
|
||||
&& panelWindow.targetedRegionY === modelData.at[1]
|
||||
&& panelWindow.targetedRegionWidth === modelData.size[0]
|
||||
&& panelWindow.targetedRegionHeight === modelData.size[1])
|
||||
|
||||
opacity: panelWindow.draggedAway ? 0 : 1
|
||||
visible: opacity > 0
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
x: modelData.at[0]
|
||||
y: modelData.at[1]
|
||||
width: modelData.size[0]
|
||||
height: modelData.size[1]
|
||||
borderColor: root.imageBorderColor
|
||||
fillColor: targeted ? root.imageFillColor : "transparent"
|
||||
border.width: targeted ? 4 : 2
|
||||
text: "Content region"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
]4;0;#$term0 #\]1;0;#$term0 #\]4;1;#$term1 #\]4;2;#$term2 #\]4;3;#$term3 #\]4;4;#$term4 #\]4;5;#$term5 #\]4;6;#$term6 #\]4;7;#$term7 #\]4;8;#$term8 #\]4;9;#$term9 #\]4;10;#$term10 #\]4;11;#$term11 #\]4;12;#$term12 #\]4;13;#$term13 #\]4;14;#$term14 #\]4;15;#$term15 #\]10;#$term7 #\]11;[100]#$term0 #\]12;#$term7 #\]13;#$term7 #\]17;#$term7 #\]19;#$term0 #\]4;232;#$term7 #\]4;256;#$term7 #\]708;[100]#$term0 #\]11;#$term0 #\
|
||||
@@ -1,222 +0,0 @@
|
||||
#!/usr/bin/env -S\_/bin/sh\_-c\_"source\_\$(eval\_echo\_\$ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate&&exec\_python\_-E\_"\$0"\_"\$@""
|
||||
import argparse
|
||||
import re
|
||||
import os
|
||||
from os.path import expandvars as os_expandvars
|
||||
from typing import Dict, List
|
||||
|
||||
TITLE_REGEX = "#+!"
|
||||
HIDE_COMMENT = "[hidden]"
|
||||
MOD_SEPARATORS = ['+', ' ']
|
||||
COMMENT_BIND_PATTERN = "#/#"
|
||||
|
||||
parser = argparse.ArgumentParser(description='Hyprland keybind reader')
|
||||
parser.add_argument('--path', type=str, default="$HOME/.config/hypr/hyprland.conf", help='path to keybind file (sourcing isn\'t supported)')
|
||||
args = parser.parse_args()
|
||||
content_lines = []
|
||||
reading_line = 0
|
||||
|
||||
# Little Parser made for hyprland keybindings conf file
|
||||
Variables: Dict[str, str] = {}
|
||||
|
||||
|
||||
class KeyBinding(dict):
|
||||
def __init__(self, mods, key, dispatcher, params, comment) -> None:
|
||||
self["mods"] = mods
|
||||
self["key"] = key
|
||||
self["dispatcher"] = dispatcher
|
||||
self["params"] = params
|
||||
self["comment"] = comment
|
||||
|
||||
class Section(dict):
|
||||
def __init__(self, children, keybinds, name) -> None:
|
||||
self["children"] = children
|
||||
self["keybinds"] = keybinds
|
||||
self["name"] = name
|
||||
|
||||
|
||||
def read_content(path: str) -> str:
|
||||
if (not os.access(os.path.expanduser(os.path.expandvars(path)), os.R_OK)):
|
||||
return ("error")
|
||||
with open(os.path.expanduser(os.path.expandvars(path)), "r") as file:
|
||||
return file.read()
|
||||
|
||||
|
||||
def autogenerate_comment(dispatcher: str, params: str = "") -> str:
|
||||
match dispatcher:
|
||||
|
||||
case "resizewindow":
|
||||
return "Resize window"
|
||||
|
||||
case "movewindow":
|
||||
if(params == ""):
|
||||
return "Move window"
|
||||
else:
|
||||
return "Window: move in {} direction".format({
|
||||
"l": "left",
|
||||
"r": "right",
|
||||
"u": "up",
|
||||
"d": "down",
|
||||
}.get(params, "null"))
|
||||
|
||||
case "pin":
|
||||
return "Window: pin (show on all workspaces)"
|
||||
|
||||
case "splitratio":
|
||||
return "Window split ratio {}".format(params)
|
||||
|
||||
case "togglefloating":
|
||||
return "Float/unfloat window"
|
||||
|
||||
case "resizeactive":
|
||||
return "Resize window by {}".format(params)
|
||||
|
||||
case "killactive":
|
||||
return "Close window"
|
||||
|
||||
case "fullscreen":
|
||||
return "Toggle {}".format(
|
||||
{
|
||||
"0": "fullscreen",
|
||||
"1": "maximization",
|
||||
"2": "fullscreen on Hyprland's side",
|
||||
}.get(params, "null")
|
||||
)
|
||||
|
||||
case "fakefullscreen":
|
||||
return "Toggle fake fullscreen"
|
||||
|
||||
case "workspace":
|
||||
if params == "+1":
|
||||
return "Workspace: focus right"
|
||||
elif params == "-1":
|
||||
return "Workspace: focus left"
|
||||
return "Focus workspace {}".format(params)
|
||||
|
||||
case "movefocus":
|
||||
return "Window: move focus {}".format(
|
||||
{
|
||||
"l": "left",
|
||||
"r": "right",
|
||||
"u": "up",
|
||||
"d": "down",
|
||||
}.get(params, "null")
|
||||
)
|
||||
|
||||
case "swapwindow":
|
||||
return "Window: swap in {} direction".format(
|
||||
{
|
||||
"l": "left",
|
||||
"r": "right",
|
||||
"u": "up",
|
||||
"d": "down",
|
||||
}.get(params, "null")
|
||||
)
|
||||
|
||||
case "movetoworkspace":
|
||||
if params == "+1":
|
||||
return "Window: move to right workspace (non-silent)"
|
||||
elif params == "-1":
|
||||
return "Window: move to left workspace (non-silent)"
|
||||
return "Window: move to workspace {} (non-silent)".format(params)
|
||||
|
||||
case "movetoworkspacesilent":
|
||||
if params == "+1":
|
||||
return "Window: move to right workspace"
|
||||
elif params == "-1":
|
||||
return "Window: move to right workspace"
|
||||
return "Window: move to workspace {}".format(params)
|
||||
|
||||
case "togglespecialworkspace":
|
||||
return "Workspace: toggle special"
|
||||
|
||||
case "exec":
|
||||
return "Execute: {}".format(params)
|
||||
|
||||
case _:
|
||||
return ""
|
||||
|
||||
def get_keybind_at_line(line_number, line_start = 0):
|
||||
global content_lines
|
||||
line = content_lines[line_number]
|
||||
_, keys = line.split("=", 1)
|
||||
keys, *comment = keys.split("#", 1)
|
||||
|
||||
mods, key, dispatcher, *params = list(map(str.strip, keys.split(",", 4)))
|
||||
params = "".join(map(str.strip, params))
|
||||
|
||||
# Remove empty spaces
|
||||
comment = list(map(str.strip, comment))
|
||||
# Add comment if it exists, else generate it
|
||||
if comment:
|
||||
comment = comment[0]
|
||||
if comment.startswith("[hidden]"):
|
||||
return None
|
||||
else:
|
||||
comment = autogenerate_comment(dispatcher, params)
|
||||
|
||||
if mods:
|
||||
modstring = mods + MOD_SEPARATORS[0] # Add separator at end to ensure last mod is read
|
||||
mods = []
|
||||
p = 0
|
||||
for index, char in enumerate(modstring):
|
||||
if(char in MOD_SEPARATORS):
|
||||
if(index - p > 1):
|
||||
mods.append(modstring[p:index])
|
||||
p = index+1
|
||||
else:
|
||||
mods = []
|
||||
|
||||
return KeyBinding(mods, key, dispatcher, params, comment)
|
||||
|
||||
def get_binds_recursive(current_content, scope):
|
||||
global content_lines
|
||||
global reading_line
|
||||
# print("get_binds_recursive({0}, {1}) [@L{2}]".format(current_content, scope, reading_line + 1))
|
||||
while reading_line < len(content_lines): # TODO: Adjust condition
|
||||
line = content_lines[reading_line]
|
||||
heading_search_result = re.search(TITLE_REGEX, line)
|
||||
# print("Read line {0}: {1}\tisHeading: {2}".format(reading_line + 1, content_lines[reading_line], "[{0}, {1}, {2}]".format(heading_search_result.start(), heading_search_result.start() == 0, ((heading_search_result != None) and (heading_search_result.start() == 0))) if heading_search_result != None else "No"))
|
||||
if ((heading_search_result != None) and (heading_search_result.start() == 0)): # Found title
|
||||
# Determine scope
|
||||
heading_scope = line.find('!')
|
||||
# Lower? Return
|
||||
if(heading_scope <= scope):
|
||||
reading_line -= 1
|
||||
return current_content
|
||||
|
||||
section_name = line[(heading_scope+1):].strip()
|
||||
# print("[[ Found h{0} at line {1} ]] {2}".format(heading_scope, reading_line+1, content_lines[reading_line]))
|
||||
reading_line += 1
|
||||
current_content["children"].append(get_binds_recursive(Section([], [], section_name), heading_scope))
|
||||
|
||||
elif line.startswith(COMMENT_BIND_PATTERN):
|
||||
keybind = get_keybind_at_line(reading_line, line_start=len(COMMENT_BIND_PATTERN))
|
||||
if(keybind != None):
|
||||
current_content["keybinds"].append(keybind)
|
||||
|
||||
elif line == "" or not line.lstrip().startswith("bind"): # Comment, ignore
|
||||
pass
|
||||
|
||||
else: # Normal keybind
|
||||
keybind = get_keybind_at_line(reading_line)
|
||||
if(keybind != None):
|
||||
current_content["keybinds"].append(keybind)
|
||||
|
||||
reading_line += 1
|
||||
|
||||
return current_content;
|
||||
|
||||
def parse_keys(path: str) -> Dict[str, List[KeyBinding]]:
|
||||
global content_lines
|
||||
content_lines = read_content(path).splitlines()
|
||||
if content_lines[0] == "error":
|
||||
return "error"
|
||||
return get_binds_recursive(Section([], [], ""), 0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import json
|
||||
|
||||
ParsedKeys = parse_keys(args.path)
|
||||
print(json.dumps(ParsedKeys))
|
||||
@@ -1,79 +0,0 @@
|
||||
import re
|
||||
import os
|
||||
|
||||
def read_scss(file_path):
|
||||
"""Reads an SCSS file and returns a dictionary of color variables."""
|
||||
colors = {}
|
||||
with open(file_path, 'r') as file:
|
||||
for line in file:
|
||||
match = re.match(r'\$(\w+):\s*(#[0-9A-Fa-f]{6});', line.strip())
|
||||
if match:
|
||||
variable_name, color = match.groups()
|
||||
colors[variable_name] = color
|
||||
return colors
|
||||
|
||||
def update_svg_colors(svg_path, old_to_new_colors, output_path):
|
||||
"""
|
||||
Updates the colors in an SVG file based on the provided color map.
|
||||
|
||||
:param svg_path: Path to the SVG file.
|
||||
:param old_to_new_colors: Dictionary mapping old colors to new colors.
|
||||
:param output_path: Path to save the updated SVG file.
|
||||
"""
|
||||
# Read the SVG content
|
||||
with open(svg_path, 'r') as file:
|
||||
svg_content = file.read()
|
||||
|
||||
# Replace old colors with new colors
|
||||
for old_color, new_color in old_to_new_colors.items():
|
||||
svg_content = re.sub(old_color, new_color, svg_content, flags=re.IGNORECASE)
|
||||
|
||||
# Write the updated SVG content to the output file
|
||||
with open(output_path, 'w') as file:
|
||||
file.write(svg_content)
|
||||
|
||||
print(f"SVG colors have been updated and saved to {output_path}!")
|
||||
|
||||
def main():
|
||||
xdg_config_home = os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config"))
|
||||
xdg_state_home = os.environ.get("XDG_STATE_HOME", os.path.expanduser("~/.local/state"))
|
||||
|
||||
scss_file = os.path.join(xdg_state_home, "quickshell", "user", "generated", "material_colors.scss")
|
||||
svg_path = os.path.join(xdg_config_home, "Kvantum", "Colloid", "Colloid.svg")
|
||||
output_path = os.path.join(xdg_config_home, "Kvantum", "MaterialAdw", "MaterialAdw.svg")
|
||||
|
||||
# Read colors from the SCSS file
|
||||
color_data = read_scss(scss_file)
|
||||
|
||||
# Specify the old colors and map them to new colors from the SCSS file
|
||||
old_to_new_colors = {
|
||||
#'#cccccc': color_data['surfaceDim'], # Map old SVG color to new SCSS color
|
||||
#'#666666': color_data['surfaceDim'],
|
||||
'#3c84f7': color_data['primary'],
|
||||
#'#5a5a5a': color_data['neutral_paletteKeyColor'],
|
||||
'#000000': color_data['shadow'],
|
||||
'#f04a50': color_data['error'],
|
||||
'#4285f4': color_data['primaryFixedDim'],
|
||||
'#f2f2f2': color_data['background'],
|
||||
#'#dfdfdf': color_data['surfaceContainerLow'],
|
||||
'#ffffff': color_data['background'],
|
||||
'#1e1e1e': color_data['onPrimaryFixed'],
|
||||
#'#b6b6b6': color_data['surfaceContainer'],
|
||||
'#333': color_data['inverseSurface'],
|
||||
'#212121': color_data['onSecondaryFixed'],
|
||||
'#5b9bf8': color_data['secondaryContainer'],
|
||||
'#26272a': color_data['term7'],
|
||||
#'#b3b3b3': color_data['surfaceBright'],
|
||||
#'#b74aff': color_data['tertiary'],
|
||||
#'#989898': color_data['surfaceContainerHighest'],
|
||||
#'#c1c1c1': color_data['surfaceContainerHigh'],
|
||||
'#444444': color_data['onBackground'],
|
||||
'#333333': color_data['onPrimaryFixed'],
|
||||
}
|
||||
|
||||
# Update the SVG colors
|
||||
update_svg_colors(svg_path, old_to_new_colors, output_path)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
import re
|
||||
import os
|
||||
|
||||
def read_scss(file_path):
|
||||
"""Reads an SCSS file and returns a dictionary of color variables."""
|
||||
colors = {}
|
||||
with open(file_path, 'r') as file:
|
||||
for line in file:
|
||||
match = re.match(r'\$(\w+):\s*(#[0-9A-Fa-f]{6});', line.strip())
|
||||
if match:
|
||||
variable_name, color = match.groups()
|
||||
colors[variable_name] = color
|
||||
return colors
|
||||
|
||||
def update_svg_colors(svg_path, old_to_new_colors, output_path):
|
||||
"""
|
||||
Updates the colors in an SVG file based on the provided color map.
|
||||
|
||||
:param svg_path: Path to the SVG file.
|
||||
:param old_to_new_colors: Dictionary mapping old colors to new colors.
|
||||
:param output_path: Path to save the updated SVG file.
|
||||
"""
|
||||
# Read the SVG content
|
||||
with open(svg_path, 'r') as file:
|
||||
svg_content = file.read()
|
||||
|
||||
# Replace old colors with new colors
|
||||
for old_color, new_color in old_to_new_colors.items():
|
||||
svg_content = re.sub(old_color, new_color, svg_content, flags=re.IGNORECASE)
|
||||
|
||||
# Write the updated SVG content to the output file
|
||||
with open(output_path, 'w') as file:
|
||||
file.write(svg_content)
|
||||
|
||||
print(f"SVG colors have been updated and saved to {output_path}!")
|
||||
|
||||
def main():
|
||||
xdg_config_home = os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config"))
|
||||
xdg_state_home = os.environ.get("XDG_STATE_HOME", os.path.expanduser("~/.local/state"))
|
||||
|
||||
scss_file = os.path.join(xdg_state_home, "quickshell", "user", "generated", "material_colors.scss")
|
||||
svg_path = os.path.join(xdg_config_home, "Kvantum", "Colloid", "ColloidDark.svg")
|
||||
output_path = os.path.join(xdg_config_home, "Kvantum", "MaterialAdw", "MaterialAdw.svg")
|
||||
|
||||
# Read colors from the SCSS file
|
||||
color_data = read_scss(scss_file)
|
||||
|
||||
# Specify the old colors and map them to new colors from the SCSS file
|
||||
old_to_new_colors = {
|
||||
#'#525252': color_data['surfaceDim'], # Map old SVG color to new SCSS color
|
||||
#'#666666': color_data['surfaceDim'],
|
||||
'#31363b': color_data['background'],
|
||||
#'#eff0f1': color_data['neutral_paletteKeyColor'],
|
||||
'#000000': color_data['shadow'],
|
||||
'#5b9bf8': color_data['primary'],
|
||||
'#93cee9': color_data['onSecondaryContainer'],
|
||||
'#3daee9': color_data['secondary'],
|
||||
#'#fff': color_data['term10'],
|
||||
#'#5a5a5a': color_data['surfaceVariant'],
|
||||
#'#acb1bc': color_data['onPrimaryFixed'],
|
||||
'#ffffff': color_data['term11'],
|
||||
'#5a616e': color_data['surfaceVariant'],
|
||||
'#f04a50': color_data['error'],
|
||||
'#4285f4': color_data['secondary'],
|
||||
'#242424': color_data['background'],
|
||||
'#2c2c2c': color_data['background'],
|
||||
#'#dfdfdf': color_data['onSurfaceVariant'],
|
||||
#'#646464': color_data['surfaceContainerHighest'],
|
||||
#'#989898': color_data['surfaceContainerHigh'],
|
||||
#'#c1c1c1': color_data['primaryFixedDim'],
|
||||
'#1e1e1e': color_data['background'],
|
||||
'#3c3c3c': color_data['background'],
|
||||
'#26272a': color_data['surfaceBright'],
|
||||
'#000000': color_data['shadow'],
|
||||
'#b74aff': color_data['tertiary'],
|
||||
#'#b6b6b6': color_data['onSurfaceVariant'],
|
||||
'#1a1a1a': color_data['background'],
|
||||
'#333': color_data['term0'],
|
||||
'#212121': color_data['background'],
|
||||
}
|
||||
|
||||
# Update the SVG colors
|
||||
update_svg_colors(svg_path, old_to_new_colors, output_path)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
import re
|
||||
import os
|
||||
|
||||
def get_colors_from_scss(scss_file):
|
||||
colors = {}
|
||||
with open(scss_file, 'r') as file:
|
||||
for line in file:
|
||||
match = re.match(r'\$(\w+):\s*(#[0-9A-Fa-f]{6});', line)
|
||||
if match:
|
||||
colors[match.group(1)] = match.group(2)
|
||||
return colors
|
||||
|
||||
def update_config_colors(config_file, colors, mappings):
|
||||
with open(config_file, 'r') as file:
|
||||
config_content = file.read()
|
||||
|
||||
for key, variable in mappings.items():
|
||||
if variable in colors:
|
||||
color = colors[variable]
|
||||
pattern = rf'({key}=)#?\w+\b'
|
||||
new_line = f'\\1{color}'
|
||||
if re.search(pattern, config_content):
|
||||
config_content = re.sub(pattern, new_line, config_content)
|
||||
else:
|
||||
config_content += f"\n{key}={color}"
|
||||
|
||||
with open(config_file, 'w') as file:
|
||||
file.write(config_content)
|
||||
|
||||
if __name__ == "__main__":
|
||||
xdg_config_home = os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config"))
|
||||
xdg_state_home = os.environ.get("XDG_STATE_HOME", os.path.expanduser("~/.local/state"))
|
||||
|
||||
config_file = os.path.join(xdg_config_home, "Kvantum", "MaterialAdw", "MaterialAdw.kvconfig")
|
||||
scss_file = os.path.join(xdg_state_home, "quickshell", "user", "generated", "material_colors.scss")
|
||||
|
||||
# Define your mappings here
|
||||
mappings = {
|
||||
'window.color': 'background',
|
||||
'base.color': 'background',
|
||||
'alt.base.color': 'background',
|
||||
'button.color': 'surfaceContainer',
|
||||
'light.color': 'surfaceContainerLow',
|
||||
'mid.light.color': 'surfaceContainer',
|
||||
'dark.color': 'surfaceContainerHighest',
|
||||
'mid.color': 'surfaceContainerHigh',
|
||||
'highlight.color': 'primary',
|
||||
'inactive.highlight.color': 'primary',
|
||||
'text.color': 'onBackground',
|
||||
'window.text.color': 'onBackground',
|
||||
'button.text.color': 'onBackground',
|
||||
'disabled.text.color': 'onBackground',
|
||||
'tooltip.text.color': 'onBackground',
|
||||
'highlight.text.color': 'onSurface',
|
||||
'link.color': 'tertiary',
|
||||
'link.visited.color': 'tertiaryFixed',
|
||||
'progress.indicator.text.color': 'onBackground',
|
||||
'text.normal.color': 'onBackground',
|
||||
'text.focus.color': 'onBackground',
|
||||
'text.press.color': 'onsecondarycontainer',
|
||||
'text.toggle.color': 'onsecondarycontainer',
|
||||
'text.disabled.color': 'surfaceDim',
|
||||
|
||||
|
||||
# Add more mappings as needed
|
||||
}
|
||||
|
||||
colors = get_colors_from_scss(scss_file)
|
||||
update_config_colors(config_file, colors, mappings)
|
||||
print("Config colors updated successfully!")
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
QUICKSHELL_CONFIG_NAME="ii"
|
||||
XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
|
||||
XDG_CACHE_HOME="${XDG_CACHE_HOME:-$HOME/.cache}"
|
||||
XDG_STATE_HOME="${XDG_STATE_HOME:-$HOME/.local/state}"
|
||||
CONFIG_DIR="$XDG_CONFIG_HOME/quickshell/$QUICKSHELL_CONFIG_NAME"
|
||||
CACHE_DIR="$XDG_CACHE_HOME/quickshell"
|
||||
STATE_DIR="$XDG_STATE_HOME/quickshell"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
get_light_dark() {
|
||||
current_mode=$(gsettings get org.gnome.desktop.interface color-scheme 2>/dev/null | tr -d "'")
|
||||
if [[ "$current_mode" == "prefer-dark" ]]; then
|
||||
echo "dark"
|
||||
else
|
||||
echo "light"
|
||||
fi
|
||||
}
|
||||
|
||||
apply_qt() {
|
||||
# Check if the theme exists
|
||||
FOLDER_PATH="$XDG_CONFIG_HOME/Kvantum/Colloid/"
|
||||
|
||||
if [ ! -d "$FOLDER_PATH" ]; then
|
||||
# Send a notification
|
||||
notify-send "Colloid-kde theme required" " The folder '$FOLDER_PATH' does not exist."
|
||||
exit 1 # Exit the function if the folder does not exist
|
||||
fi
|
||||
|
||||
lightdark=$(get_light_dark)
|
||||
if [ "$lightdark" = "light" ]; then
|
||||
# apply ligght colors
|
||||
cp "$XDG_CONFIG_HOME/Kvantum/Colloid/Colloid.kvconfig" "$XDG_CONFIG_HOME/Kvantum/MaterialAdw/MaterialAdw.kvconfig"
|
||||
python "$CONFIG_DIR/scripts/kvantum/adwsvg.py"
|
||||
|
||||
else
|
||||
#apply dark colors
|
||||
cp "$XDG_CONFIG_HOME/Kvantum/Colloid/ColloidDark.kvconfig" "$XDG_CONFIG_HOME/Kvantum/MaterialAdw/MaterialAdw.kvconfig"
|
||||
python "$CONFIG_DIR/scripts/kvantum/adwsvgDark.py"
|
||||
fi
|
||||
}
|
||||
|
||||
apply_qt
|
||||
@@ -1,86 +0,0 @@
|
||||
#!/usr/bin/env -S\_/bin/sh\_-xc\_"source\_\$(eval\_echo\_\$ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate&&exec\_python\_-E\_"\$0"\_"\$@""
|
||||
|
||||
# From https://github.com/stwa/wayland-idle-inhibitor
|
||||
# License: WTFPL Version 2
|
||||
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from signal import SIGINT, SIGTERM, signal
|
||||
from threading import Event
|
||||
import setproctitle
|
||||
|
||||
from pywayland.client.display import Display
|
||||
from pywayland.protocol.idle_inhibit_unstable_v1.zwp_idle_inhibit_manager_v1 import (
|
||||
ZwpIdleInhibitManagerV1,
|
||||
)
|
||||
from pywayland.protocol.wayland.wl_compositor import WlCompositor
|
||||
from pywayland.protocol.wayland.wl_registry import WlRegistryProxy
|
||||
from pywayland.protocol.wayland.wl_surface import WlSurface
|
||||
|
||||
|
||||
@dataclass
|
||||
class GlobalRegistry:
|
||||
surface: WlSurface | None = None
|
||||
inhibit_manager: ZwpIdleInhibitManagerV1 | None = None
|
||||
|
||||
|
||||
def handle_registry_global(
|
||||
wl_registry: WlRegistryProxy, id_num: int, iface_name: str, version: int
|
||||
) -> None:
|
||||
global_registry: GlobalRegistry = wl_registry.user_data or GlobalRegistry()
|
||||
|
||||
if iface_name == "wl_compositor":
|
||||
compositor = wl_registry.bind(id_num, WlCompositor, version)
|
||||
global_registry.surface = compositor.create_surface() # type: ignore
|
||||
elif iface_name == "zwp_idle_inhibit_manager_v1":
|
||||
global_registry.inhibit_manager = wl_registry.bind(
|
||||
id_num, ZwpIdleInhibitManagerV1, version
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
done = Event()
|
||||
signal(SIGINT, lambda _, __: done.set())
|
||||
signal(SIGTERM, lambda _, __: done.set())
|
||||
|
||||
global_registry = GlobalRegistry()
|
||||
|
||||
display = Display()
|
||||
display.connect()
|
||||
|
||||
registry = display.get_registry() # type: ignore
|
||||
registry.user_data = global_registry
|
||||
registry.dispatcher["global"] = handle_registry_global
|
||||
|
||||
def shutdown() -> None:
|
||||
display.dispatch()
|
||||
display.roundtrip()
|
||||
display.disconnect()
|
||||
|
||||
display.dispatch()
|
||||
display.roundtrip()
|
||||
|
||||
if global_registry.surface is None or global_registry.inhibit_manager is None:
|
||||
print("Wayland seems not to support idle_inhibit_unstable_v1 protocol.")
|
||||
shutdown()
|
||||
sys.exit(1)
|
||||
|
||||
inhibitor = global_registry.inhibit_manager.create_inhibitor( # type: ignore
|
||||
global_registry.surface
|
||||
)
|
||||
|
||||
display.dispatch()
|
||||
display.roundtrip()
|
||||
|
||||
print("Inhibiting idle...")
|
||||
done.wait()
|
||||
print("Shutting down...")
|
||||
|
||||
inhibitor.destroy()
|
||||
|
||||
shutdown()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
setproctitle.setproctitle("wayland-idle-inhibitor.py")
|
||||
main()
|
||||
@@ -1,54 +0,0 @@
|
||||
import qs.modules.common
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Services.Pipewire
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
/**
|
||||
* A nice wrapper for default Pipewire audio sink and source.
|
||||
*/
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property bool ready: Pipewire.defaultAudioSink?.ready ?? false
|
||||
property PwNode sink: Pipewire.defaultAudioSink
|
||||
property PwNode source: Pipewire.defaultAudioSource
|
||||
|
||||
signal sinkProtectionTriggered(string reason);
|
||||
|
||||
PwObjectTracker {
|
||||
objects: [sink, source]
|
||||
}
|
||||
|
||||
Connections { // Protection against sudden volume changes
|
||||
target: sink?.audio ?? null
|
||||
property bool lastReady: false
|
||||
property real lastVolume: 0
|
||||
function onVolumeChanged() {
|
||||
if (!Config.options.audio.protection.enable) return;
|
||||
if (!lastReady) {
|
||||
lastVolume = sink.audio.volume;
|
||||
lastReady = true;
|
||||
return;
|
||||
}
|
||||
const newVolume = sink.audio.volume;
|
||||
const maxAllowedIncrease = Config.options.audio.protection.maxAllowedIncrease / 100;
|
||||
const maxAllowed = Config.options.audio.protection.maxAllowed / 100;
|
||||
|
||||
if (newVolume - lastVolume > maxAllowedIncrease) {
|
||||
sink.audio.volume = lastVolume;
|
||||
root.sinkProtectionTriggered("Illegal increment");
|
||||
} else if (newVolume > maxAllowed) {
|
||||
root.sinkProtectionTriggered("Exceeded max allowed");
|
||||
sink.audio.volume = Math.min(lastVolume, maxAllowed);
|
||||
}
|
||||
if (sink.ready && (isNaN(sink.audio.volume) || sink.audio.volume === undefined || sink.audio.volume === null)) {
|
||||
sink.audio.volume = 0;
|
||||
}
|
||||
lastVolume = sink.audio.volume;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
pragma Singleton
|
||||
|
||||
import qs
|
||||
import qs.modules.common
|
||||
import Quickshell
|
||||
import Quickshell.Services.UPower
|
||||
|
||||
Singleton {
|
||||
property bool available: UPower.displayDevice.isLaptopBattery
|
||||
property var chargeState: UPower.displayDevice.state
|
||||
property bool isCharging: chargeState == UPowerDeviceState.Charging
|
||||
property bool isPluggedIn: isCharging || chargeState == UPowerDeviceState.PendingCharge
|
||||
property real percentage: UPower.displayDevice.percentage
|
||||
readonly property bool allowAutomaticSuspend: Config.options.battery.automaticSuspend
|
||||
|
||||
property bool isLow: percentage <= Config.options.battery.low / 100
|
||||
property bool isCritical: percentage <= Config.options.battery.critical / 100
|
||||
property bool isSuspending: percentage <= Config.options.battery.suspend / 100
|
||||
|
||||
property bool isLowAndNotCharging: isLow && !isCharging
|
||||
property bool isCriticalAndNotCharging: isCritical && !isCharging
|
||||
property bool isSuspendingAndNotCharging: allowAutomaticSuspend && isSuspending && !isCharging
|
||||
|
||||
onIsLowAndNotChargingChanged: {
|
||||
if (available && isLowAndNotCharging) Quickshell.execDetached([
|
||||
"notify-send",
|
||||
Translation.tr("Low battery"),
|
||||
Translation.tr("Consider plugging in your device"),
|
||||
"-u", "critical",
|
||||
"-a", "Shell"
|
||||
])
|
||||
}
|
||||
|
||||
onIsCriticalAndNotChargingChanged: {
|
||||
if (available && isCriticalAndNotCharging) Quickshell.execDetached([
|
||||
"notify-send",
|
||||
Translation.tr("Critically low battery"),
|
||||
Translation.tr("Please charge!\nAutomatic suspend triggers at %1").arg(Config.options.battery.suspend),
|
||||
"-u", "critical",
|
||||
"-a", "Shell"
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
onIsSuspendingAndNotChargingChanged: {
|
||||
if (available && isSuspendingAndNotCharging) {
|
||||
Quickshell.execDetached(["bash", "-c", `systemctl suspend || loginctl suspend`]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import Quickshell;
|
||||
import Quickshell.Io;
|
||||
import QtQuick;
|
||||
|
||||
/**
|
||||
* Basic polled Bluetooth state.
|
||||
*/
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property int updateInterval: 1000
|
||||
property string bluetoothDeviceName: ""
|
||||
property string bluetoothDeviceAddress: ""
|
||||
property bool bluetoothEnabled: false
|
||||
property bool bluetoothConnected: false
|
||||
|
||||
function update() {
|
||||
updateBluetoothDevice.running = true
|
||||
updateBluetoothStatus.running = true
|
||||
updateBluetoothEnabled.running = true
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 10
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
update()
|
||||
interval = root.updateInterval
|
||||
}
|
||||
}
|
||||
|
||||
// Check if Bluetooth is enabled (controller powered on)
|
||||
Process {
|
||||
id: updateBluetoothEnabled
|
||||
command: ["sh", "-c", "bluetoothctl show | grep -q 'Powered: yes' && echo 1 || echo 0"]
|
||||
running: true
|
||||
stdout: SplitParser {
|
||||
onRead: data => {
|
||||
root.bluetoothEnabled = (parseInt(data) === 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the name and address of the first connected Bluetooth device
|
||||
Process {
|
||||
id: updateBluetoothDevice
|
||||
command: ["sh", "-c", "bluetoothctl info | awk -F': ' '/Name: /{name=$2} /Device /{addr=$2} END{print name \":\" addr}'"]
|
||||
running: true
|
||||
stdout: SplitParser {
|
||||
onRead: data => {
|
||||
let parts = data.split(":")
|
||||
root.bluetoothDeviceName = parts[0] || ""
|
||||
root.bluetoothDeviceAddress = parts[1] || ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if any device is connected
|
||||
Process {
|
||||
id: updateBluetoothStatus
|
||||
command: ["sh", "-c", "bluetoothctl info | grep -q 'Connected: yes' && echo 1 || echo 0"]
|
||||
running: true
|
||||
stdout: SplitParser {
|
||||
onRead: data => {
|
||||
root.bluetoothConnected = (parseInt(data) === 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user