mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2026-10-25 14:17:14 +00:00
Compare commits
984
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e412568900 | ||
|
|
29578863ef | ||
|
|
2461bd9e61 | ||
|
|
651ee37c19 | ||
|
|
1f4bb04ec9 | ||
|
|
67f4e1b9ff | ||
|
|
ae8af4868e | ||
|
|
c400ed4c3e | ||
|
|
41d92561a8 | ||
|
|
721056d550 | ||
|
|
52a801096c | ||
|
|
08400381a1 | ||
|
|
4ec19d0da3 | ||
|
|
1f52bc7c99 | ||
|
|
ec2568b988 | ||
|
|
d082e8bdba | ||
|
|
71e0bf214e | ||
|
|
761b14dc65 | ||
|
|
63d66228ab | ||
|
|
c4f15c32ea | ||
|
|
a070f7d2f3 | ||
|
|
74501079c3 | ||
|
|
761cfffae1 | ||
|
|
f26fb352e5 | ||
|
|
b71781727a | ||
|
|
6c843c4dd4 | ||
|
|
9381e7d057 | ||
|
|
9ffc171c04 | ||
|
|
6104d4917c | ||
|
|
faa3ac728f | ||
|
|
b00e93372d | ||
|
|
8f51cd4d60 | ||
|
|
f618ea05f7 | ||
|
|
2b053dfedb | ||
|
|
f6ab406ffa | ||
|
|
fe635835a5 | ||
|
|
795bc4e09e | ||
|
|
d431446da0 | ||
|
|
362821e4d7 | ||
|
|
2045db9cd0 | ||
|
|
249c14a93d | ||
|
|
a1bfe4c478 | ||
|
|
bfe293c0b9 | ||
|
|
772dde7d5e | ||
|
|
3c272fa259 | ||
|
|
b6a433caa6 | ||
|
|
d207435846 | ||
|
|
4310c7fbf3 | ||
|
|
736dd202f4 | ||
|
|
e430272d35 | ||
|
|
e82965734c | ||
|
|
d0ac5e8144 | ||
|
|
dcedbbd7b8 | ||
|
|
c8ce2a41c6 | ||
|
|
4f270d9047 | ||
|
|
3df90b1993 | ||
|
|
8e8915d84c | ||
|
|
50369e0904 | ||
|
|
3ae2c84c08 | ||
|
|
b2e6ab51cb | ||
|
|
d6496f02e6 | ||
|
|
38e98327b4 | ||
|
|
c72a6fda5d | ||
|
|
23d58d42ee | ||
|
|
d972cebf47 | ||
|
|
3558a5135b | ||
|
|
82460d2ce3 | ||
|
|
4ce2b7d3b2 | ||
|
|
2c10bb3c88 | ||
|
|
01bed4a6c9 | ||
|
|
52c4867468 | ||
|
|
92b3fd25e7 | ||
|
|
e0e1da5082 | ||
|
|
80953f5d09 | ||
|
|
2849db57f2 | ||
|
|
0170b92272 | ||
|
|
7bcb81400b | ||
|
|
078aedfb68 | ||
|
|
cb56bf9793 | ||
|
|
b152986b4e | ||
|
|
fce47897d5 | ||
|
|
0b701a3e99 | ||
|
|
9dad353957 | ||
|
|
89e16b7bdb | ||
|
|
c2965da341 | ||
|
|
ffb072dc5f | ||
|
|
a247dbcb02 | ||
|
|
1dd71175f4 | ||
|
|
bbe62c0e7b | ||
|
|
9822ff321b | ||
|
|
b485d485ef | ||
|
|
0b3d445109 | ||
|
|
d7e48940bc | ||
|
|
1049eb0fe7 | ||
|
|
c871ef5635 | ||
|
|
7edfcb20c4 | ||
|
|
7a1438a2c0 | ||
|
|
2af8cba8cd | ||
|
|
27d74c0a62 | ||
|
|
f86d1bfe06 | ||
|
|
7cc5972ff7 | ||
|
|
3bbf978b00 | ||
|
|
ed36467600 | ||
|
|
dd0de327fc | ||
|
|
dccd3ce8fd | ||
|
|
fa45e7b696 | ||
|
|
57f009e8aa | ||
|
|
04b633a5ea | ||
|
|
20d42b05bb | ||
|
|
91ba50f1ff | ||
|
|
f4476c99f9 | ||
|
|
50f3f586ab | ||
|
|
36a2d7ec8e | ||
|
|
4890b5833e | ||
|
|
e20ab89688 | ||
|
|
e557ba8184 | ||
|
|
8540e21d5a | ||
|
|
76c04a8506 | ||
|
|
128632770e | ||
|
|
31e0800e81 | ||
|
|
00ca96eec8 | ||
|
|
077ef2c639 | ||
|
|
e3ea7be0e7 | ||
|
|
05fd1c2b14 | ||
|
|
affcffe270 | ||
|
|
62930231e4 | ||
|
|
ad651631ec | ||
|
|
cf1c8f13db | ||
|
|
9acc69b897 | ||
|
|
9bc7cbdb50 | ||
|
|
2ed8443e28 | ||
|
|
94f598c2b4 | ||
|
|
d83d30af06 | ||
|
|
284e763f0d | ||
|
|
3bfa172533 | ||
|
|
b5b1fd6d5f | ||
|
|
05e0d9b7d2 | ||
|
|
1ae1f8dee2 | ||
|
|
1bf479a0b7 | ||
|
|
9d04d49628 | ||
|
|
3de324519b | ||
|
|
4be90d0ea5 | ||
|
|
041f35ed6c | ||
|
|
9c463d0338 | ||
|
|
5ec0a46089 | ||
|
|
979d0ee4ca | ||
|
|
448686b399 | ||
|
|
0ff149a3d3 | ||
|
|
50a90d6e96 | ||
|
|
5f93706d91 | ||
|
|
39415550f5 | ||
|
|
4586ad65b5 | ||
|
|
8c5678e26f | ||
|
|
5dff373d5f | ||
|
|
e17868a085 | ||
|
|
97bb6d0936 | ||
|
|
54576d8dec | ||
|
|
282bb24c71 | ||
|
|
b1a96b6ecb | ||
|
|
66dac2086c | ||
|
|
e269d0d206 | ||
|
|
5d95c3eb9c | ||
|
|
26650e9b6c | ||
|
|
7339dd8354 | ||
|
|
8ae983971a | ||
|
|
d46cc3b09c | ||
|
|
dfd23f8d60 | ||
|
|
28eb1a11e6 | ||
|
|
04f82a03bf | ||
|
|
aac545074b | ||
|
|
87a3a925ee | ||
|
|
5447bf9691 | ||
|
|
761070b9b7 | ||
|
|
4c9e435df8 | ||
|
|
4b7d65e8b4 | ||
|
|
0515b49b98 | ||
|
|
edb97215ef | ||
|
|
5577a24548 | ||
|
|
cfaa2a8927 | ||
|
|
78494b6036 | ||
|
|
f9ea7eca61 | ||
|
|
d69fee1732 | ||
|
|
178518db5e | ||
|
|
6fb20fb973 | ||
|
|
831bf44e34 | ||
|
|
a4c6c367e3 | ||
|
|
bc98e59709 | ||
|
|
ef287bc331 | ||
|
|
3437f4c712 | ||
|
|
618f2dcd79 | ||
|
|
6df8ad3095 | ||
|
|
eda6221288 | ||
|
|
a9859f6a0d | ||
|
|
0db88bac25 | ||
|
|
daa3d9c0dd | ||
|
|
b343b33594 | ||
|
|
46e435a448 | ||
|
|
7fe62b4ffa | ||
|
|
9c94348a15 | ||
|
|
bac256e93e | ||
|
|
49f59aa129 | ||
|
|
800dab5be0 | ||
|
|
b9977527b2 | ||
|
|
c216dba69d | ||
|
|
d4148d52e3 | ||
|
|
2006a8cdd0 | ||
|
|
feb52ecbd1 | ||
|
|
42909c3b7a | ||
|
|
706a787163 | ||
|
|
1bc14bded6 | ||
|
|
f00cb81db1 | ||
|
|
2fbd14956d | ||
|
|
24657b43be | ||
|
|
659d3b6fa5 | ||
|
|
339483c8a3 | ||
|
|
51ec46bbd7 | ||
|
|
8a059cc26d | ||
|
|
ddb8e1efb4 | ||
|
|
3bf2ed5168 | ||
|
|
e70d34d91a | ||
|
|
7a650f5c2f | ||
|
|
fc6f5ae2ee | ||
|
|
3b7dde3cb1 | ||
|
|
0a5cfaba18 | ||
|
|
ea527b5e91 | ||
|
|
85f11439e8 | ||
|
|
98c7b48625 | ||
|
|
8b007bb3af | ||
|
|
89e2d88d1c | ||
|
|
260399e965 | ||
|
|
de72843b8e | ||
|
|
b589142d9f | ||
|
|
f479c85869 | ||
|
|
26992c039a | ||
|
|
ef50e1a24f | ||
|
|
2f201670d2 | ||
|
|
90c80573a0 | ||
|
|
059519cdca | ||
|
|
55647e2e2f | ||
|
|
1802be68ef | ||
|
|
aa79496a36 | ||
|
|
4bfa4c32aa | ||
|
|
d8ca29eab1 | ||
|
|
f7a9f2e13d | ||
|
|
af286d3d53 | ||
|
|
9a20e8155e | ||
|
|
b81af7e1c8 | ||
|
|
74563bbac9 | ||
|
|
07ad79f0b3 | ||
|
|
5c28255e06 | ||
|
|
99783f281d | ||
|
|
a5008c3d15 | ||
|
|
f807f2beeb | ||
|
|
db34b25ef2 | ||
|
|
96daf11fd4 | ||
|
|
f7e98dfd2d | ||
|
|
61277e92bd | ||
|
|
32ef9f399f | ||
|
|
54e6ca5dc3 | ||
|
|
a8e226786d | ||
|
|
ce717a4c9f | ||
|
|
fd41bf0ae7 | ||
|
|
b2b68bf29f | ||
|
|
b87c29c354 | ||
|
|
24977822c9 | ||
|
|
647daa8627 | ||
|
|
a372efacb1 | ||
|
|
f40d33db2a | ||
|
|
a5eb0fbd24 | ||
|
|
307c8030af | ||
|
|
a9016465fa | ||
|
|
358b70eb5f | ||
|
|
6fcbb80a71 | ||
|
|
643f6c420b | ||
|
|
ac68b0b941 | ||
|
|
d62f67bd88 | ||
|
|
8718c5e310 | ||
|
|
e8273ab80c | ||
|
|
2718605987 | ||
|
|
d99538d80b | ||
|
|
ce7a1e4e21 | ||
|
|
921734763d | ||
|
|
18c608f569 | ||
|
|
b915f6ece2 | ||
|
|
24347b422c | ||
|
|
e5f4ae647f | ||
|
|
72202b8a21 | ||
|
|
dbc14d41de | ||
|
|
1dca5ea00d | ||
|
|
f03d1d788c | ||
|
|
fcac6f9fa8 | ||
|
|
ca0cd433c9 | ||
|
|
39589fdbd0 | ||
|
|
605fc3cff9 | ||
|
|
12cd6f48f8 | ||
|
|
1f57478d10 | ||
|
|
7ef4c5d282 | ||
|
|
f09d92be32 | ||
|
|
6245b36bdb | ||
|
|
54cc353bcc | ||
|
|
b7abba099c | ||
|
|
c5dbd10335 | ||
|
|
a44e3e953d | ||
|
|
ee2521cb01 | ||
|
|
4625dfb857 | ||
|
|
b9a2653066 | ||
|
|
fcaa327660 | ||
|
|
496117d517 | ||
|
|
8ce7d37b72 | ||
|
|
25391609b9 | ||
|
|
f0b7b9c5e5 | ||
|
|
301cdaa2c2 | ||
|
|
fce7ec8912 | ||
|
|
24bd403549 | ||
|
|
46c89c48a9 | ||
|
|
bad9a53fdb | ||
|
|
0bce7bd60a | ||
|
|
2f70a1cfb4 | ||
|
|
bfb6e738ee | ||
|
|
c7ad9aae07 | ||
|
|
fecd719239 | ||
|
|
18d6ac31b5 | ||
|
|
d8dbebfc7e | ||
|
|
16463d0eb9 | ||
|
|
837cac644d | ||
|
|
e83e0a8535 | ||
|
|
2e309c31a6 | ||
|
|
625db02651 | ||
|
|
e3144c08c1 | ||
|
|
40e25970ad | ||
|
|
4ccb8a8d11 | ||
|
|
26d1c3d9ef | ||
|
|
cc8fe7b001 | ||
|
|
4601eab864 | ||
|
|
1ee9c88ffd | ||
|
|
7feac213f6 | ||
|
|
c75a9c0f61 | ||
|
|
afd0c9784f | ||
|
|
f1ddbea06e | ||
|
|
42bf3cd4e3 | ||
|
|
27f5549f56 | ||
|
|
b0569f8421 | ||
|
|
8d86f29325 | ||
|
|
aeca32498a | ||
|
|
e8232664f3 | ||
|
|
efa869f91a | ||
|
|
dbff3d7cf1 | ||
|
|
9e70f73684 | ||
|
|
9f2f0de0c4 | ||
|
|
bf293a8f8f | ||
|
|
5fbc1a132f | ||
|
|
36093b9741 | ||
|
|
8713fb04c5 | ||
|
|
ea82a59f31 | ||
|
|
18f67cd4f2 | ||
|
|
120e7228c7 | ||
|
|
892fa90c37 | ||
|
|
491dac5bf0 | ||
|
|
2ab06fbafd | ||
|
|
e68735d061 | ||
|
|
9ba4d98c30 | ||
|
|
19bbfd4916 | ||
|
|
d94cd7ea94 | ||
|
|
0dd8f41eb0 | ||
|
|
f1ab5ab51f | ||
|
|
a8056f3120 | ||
|
|
09c0cdebb5 | ||
|
|
629d7c7a82 | ||
|
|
a0dd1aec3d | ||
|
|
17d6377902 | ||
|
|
ad401105a1 | ||
|
|
4913e99c2e | ||
|
|
16b97029cf | ||
|
|
481130c9bb | ||
|
|
698ed6718d | ||
|
|
d164813bb4 | ||
|
|
52157ee0e7 | ||
|
|
877b62fe5d | ||
|
|
d823a02971 | ||
|
|
e950056e3b | ||
|
|
70014ba233 | ||
|
|
4425f24a20 | ||
|
|
410964a44b | ||
|
|
30389e8536 | ||
|
|
5314833041 | ||
|
|
40f7cf7678 | ||
|
|
83a0b07062 | ||
|
|
1b4900d691 | ||
|
|
f9795d53a0 | ||
|
|
2b9bb4f141 | ||
|
|
9196e4c367 | ||
|
|
374a5a1a37 | ||
|
|
827cf32c1b | ||
|
|
98ad6dbeb2 | ||
|
|
63c8f642ec | ||
|
|
3bfe64f797 | ||
|
|
ec98029467 | ||
|
|
ab58478686 | ||
|
|
90247667d1 | ||
|
|
e661185534 | ||
|
|
d73e4e8e1f | ||
|
|
a6905e73cb | ||
|
|
93b054d55e | ||
|
|
5db4c5c717 | ||
|
|
5e04521929 | ||
|
|
30440b4ed1 | ||
|
|
09bb90604d | ||
|
|
4d55ec6f36 | ||
|
|
f373524f34 | ||
|
|
0398a7bebd | ||
|
|
fa8a5bcd97 | ||
|
|
758a92410b | ||
|
|
77f56c5dda | ||
|
|
72f2fe3cc3 | ||
|
|
7e0e520f03 | ||
|
|
92c4784e42 | ||
|
|
a634229dc0 | ||
|
|
de3f36ef2a | ||
|
|
c623a265ee | ||
|
|
e75125f6df | ||
|
|
4901a8844c | ||
|
|
a1854b68d8 | ||
|
|
aee4a6243b | ||
|
|
4c0cb73d69 | ||
|
|
c6eab182f6 | ||
|
|
3e3fbd97eb | ||
|
|
feb695caa7 | ||
|
|
1bd46d9651 | ||
|
|
32eabb6b36 | ||
|
|
9bfe6dc6d8 | ||
|
|
3f366aeea4 | ||
|
|
4338fd46f2 | ||
|
|
36974f5b49 | ||
|
|
d48f767408 | ||
|
|
bd2558e852 | ||
|
|
9f7c963cd5 | ||
|
|
2db0eadbfe | ||
|
|
580c7b8842 | ||
|
|
88fba347ea | ||
|
|
c8c5255e62 | ||
|
|
01a1a2760a | ||
|
|
6d34df8d48 | ||
|
|
b124ea65e1 | ||
|
|
f3214347a9 | ||
|
|
6b8724b59f | ||
|
|
ac3e378edf | ||
|
|
5d31fd1c91 | ||
|
|
046fe1ec08 | ||
|
|
721873c843 | ||
|
|
f6ffbfc10a | ||
|
|
4e91649e0a | ||
|
|
4f7f8abec4 | ||
|
|
6370562dbc | ||
|
|
94e2e67522 | ||
|
|
1d8330015d | ||
|
|
e5017b0258 | ||
|
|
cd412ca31b | ||
|
|
d2b6473095 | ||
|
|
ab42507275 | ||
|
|
dcef844e20 | ||
|
|
3244708c1b | ||
|
|
73ac1f1741 | ||
|
|
f104e9f352 | ||
|
|
393c9a7d06 | ||
|
|
ea497ea488 | ||
|
|
5add89cad0 | ||
|
|
2db447d2ef | ||
|
|
31c83813e6 | ||
|
|
27483a282d | ||
|
|
48b816aa22 | ||
|
|
0065f94f52 | ||
|
|
ccc0002eb2 | ||
|
|
15a2eee141 | ||
|
|
b9faac71e5 | ||
|
|
0a4465de33 | ||
|
|
f9dfd09628 | ||
|
|
8638d7afce | ||
|
|
8311793a43 | ||
|
|
0d552cfcd2 | ||
|
|
4b0f20dbd1 | ||
|
|
cf531c949d | ||
|
|
ba5c5f17d5 | ||
|
|
35825ad9b7 | ||
|
|
b1eb26a89e | ||
|
|
c9d04b6698 | ||
|
|
496133e014 | ||
|
|
f2857ee2be | ||
|
|
22541f2d5e | ||
|
|
e235c52b6c | ||
|
|
e89b8c72dd | ||
|
|
a374e53a3a | ||
|
|
afb066c4ee | ||
|
|
f05761d4a5 | ||
|
|
76adc9ea33 | ||
|
|
99dd291413 | ||
|
|
f904eb27e1 | ||
|
|
eeb8214812 | ||
|
|
f7215b039e | ||
|
|
c07fe5a0f9 | ||
|
|
0d28cb6e20 | ||
|
|
a1a17bfd1f | ||
|
|
f386f09592 | ||
|
|
a47e17fe6e | ||
|
|
01dc3b63ff | ||
|
|
2d97e0699e | ||
|
|
75f514d99b | ||
|
|
9a687cfc1c | ||
|
|
14edf8b6b7 | ||
|
|
23aa2d8917 | ||
|
|
7651388b5c | ||
|
|
805ab32b24 | ||
|
|
cc623b1097 | ||
|
|
1420416b3e | ||
|
|
9a0b67f938 | ||
|
|
303e1e6281 | ||
|
|
ff59b0cc9c | ||
|
|
be5d2ee715 | ||
|
|
8dd2e3f6f9 | ||
|
|
2eedd196d2 | ||
|
|
759a3f2784 | ||
|
|
386fa830c3 | ||
|
|
c382423d77 | ||
|
|
bb466ce66c | ||
|
|
72cd3dd8a1 | ||
|
|
595cedaaf1 | ||
|
|
eeaceb6cf5 | ||
|
|
1bd671685b | ||
|
|
48d3fe41f2 | ||
|
|
7ab21871cd | ||
|
|
65d01b1fb3 | ||
|
|
6230accb68 | ||
|
|
c6ed821934 | ||
|
|
10e03bb951 | ||
|
|
aa4f392948 | ||
|
|
f51b59ec02 | ||
|
|
8c76834ae4 | ||
|
|
4a454f3d67 | ||
|
|
151aa1863d | ||
|
|
3bf6896296 | ||
|
|
0d01561476 | ||
|
|
f6ded92251 | ||
|
|
d01b735cc6 | ||
|
|
6c12001080 | ||
|
|
1afbf03606 | ||
|
|
f6ef5c61c5 | ||
|
|
c18fee8107 | ||
|
|
d9df7a4384 | ||
|
|
87c2230e8e | ||
|
|
da7eb6de0a | ||
|
|
ed3815118f | ||
|
|
be726f42bd | ||
|
|
a91006132f | ||
|
|
9a9f741a0b | ||
|
|
5028f130e9 | ||
|
|
77fa019651 | ||
|
|
9715da9384 | ||
|
|
f6d5035c1a | ||
|
|
43e782ab6f | ||
|
|
f3f9920bfb | ||
|
|
2bfd615812 | ||
|
|
ebfacb3659 | ||
|
|
c71d557eec | ||
|
|
e0398cef21 | ||
|
|
f91599e9c6 | ||
|
|
f8f9f93c97 | ||
|
|
a8a5340d8b | ||
|
|
871b27f37d | ||
|
|
6f174cae1d | ||
|
|
22d7ac3e22 | ||
|
|
9b30c3a155 | ||
|
|
915bac64b1 | ||
|
|
9d2b50e55d | ||
|
|
bde100f63d | ||
|
|
05b035a13d | ||
|
|
eefb56bed7 | ||
|
|
fcc0dc4189 | ||
|
|
47ff20317f | ||
|
|
1558b9103d | ||
|
|
7a78742162 | ||
|
|
c01e240f66 | ||
|
|
fef4fcbac6 | ||
|
|
5ab18bce4b | ||
|
|
24aec7271a | ||
|
|
9b19a2cb95 | ||
|
|
efdd7b8a57 | ||
|
|
6df4cc9c3b | ||
|
|
b9d93db0f5 | ||
|
|
d7ee45ca64 | ||
|
|
d7c31b1b22 | ||
|
|
7d6794a358 | ||
|
|
473eb87346 | ||
|
|
8b18b07790 | ||
|
|
ab3c80a5ec | ||
|
|
075b93ecd6 | ||
|
|
f6d0f72e49 | ||
|
|
fcda3af862 | ||
|
|
d5c7a589b1 | ||
|
|
86e099ed25 | ||
|
|
ebd7befe73 | ||
|
|
b8c7e581a1 | ||
|
|
8281259179 | ||
|
|
d3e06b07df | ||
|
|
4967018418 | ||
|
|
537a3c38fa | ||
|
|
0124957833 | ||
|
|
f0420e2d61 | ||
|
|
7090566041 | ||
|
|
f0d5035cd0 | ||
|
|
0fb9b8dc30 | ||
|
|
eef6e81134 | ||
|
|
1593159a3f | ||
|
|
3da9eb9dbe | ||
|
|
f17613f3fb | ||
|
|
14337ccb46 | ||
|
|
1a3913b09c | ||
|
|
039aed2747 | ||
|
|
173991e3cb | ||
|
|
8b3f8cab01 | ||
|
|
2a20d24589 | ||
|
|
53c2d552ec | ||
|
|
af11c1a83d | ||
|
|
a65cf1481c | ||
|
|
0318716236 | ||
|
|
88eb4b3342 | ||
|
|
4810d1ef6a | ||
|
|
f2a9514d89 | ||
|
|
925ba6ac24 | ||
|
|
ef407268a2 | ||
|
|
cd6c4bbe38 | ||
|
|
c058e18408 | ||
|
|
6d3ca565ca | ||
|
|
236a7b4fd2 | ||
|
|
e1f387dbf7 | ||
|
|
3d113dd31e | ||
|
|
e0e57f0336 | ||
|
|
e775b58d41 | ||
|
|
5b070a8478 | ||
|
|
8a61193500 | ||
|
|
fad522b8fe | ||
|
|
0acac205af | ||
|
|
069d4c61b7 | ||
|
|
7c113f5700 | ||
|
|
dfdaf4225b | ||
|
|
bd39ab2467 | ||
|
|
6ce1eb3f2d | ||
|
|
ce7d4fe9a2 | ||
|
|
a65bb2f419 | ||
|
|
cbc868448b | ||
|
|
9c336a0b56 | ||
|
|
0f0d09399e | ||
|
|
e13a1162a9 | ||
|
|
57ebed903f | ||
|
|
4478193d8a | ||
|
|
ee948395e3 | ||
|
|
0616b051ae | ||
|
|
4d155d0505 | ||
|
|
a169e733d9 | ||
|
|
f081e237c8 | ||
|
|
f412d387fa | ||
|
|
67354b43e2 | ||
|
|
39135a4000 | ||
|
|
eaa014cebd | ||
|
|
856e657f81 | ||
|
|
3a609e5b66 | ||
|
|
d0022dd599 | ||
|
|
7ba6eed453 | ||
|
|
beeb6ecc0a | ||
|
|
7cdc17a714 | ||
|
|
4765a950a9 | ||
|
|
65e8137e08 | ||
|
|
ae546dd9ad | ||
|
|
8110c42be0 | ||
|
|
bd2b5ae5fc | ||
|
|
2ddfffa6a9 | ||
|
|
a4b54e861d | ||
|
|
c6785f1a4f | ||
|
|
83fe621c56 | ||
|
|
b3a93e17eb | ||
|
|
546a391af3 | ||
|
|
786cf9bd8b | ||
|
|
dfd6fe062d | ||
|
|
b6ef818613 | ||
|
|
b0f9e9c30a | ||
|
|
7e5c88ddc3 | ||
|
|
9824c3e00f | ||
|
|
9171d5ed11 | ||
|
|
a1830ebb82 | ||
|
|
22d2a3d9bf | ||
|
|
c9b97fc965 | ||
|
|
51f85becd5 | ||
|
|
a8a281cfb4 | ||
|
|
ddd1304949 | ||
|
|
86d70b6c02 | ||
|
|
a22bdb39e7 | ||
|
|
7ae4d5ef95 | ||
|
|
a2038cbefa | ||
|
|
992091eade | ||
|
|
e3bfead0c5 | ||
|
|
0de96141fd | ||
|
|
fa18d15c3c | ||
|
|
ea9dbf2371 | ||
|
|
d34e3ec7a9 | ||
|
|
c8833a36af | ||
|
|
a067cb0c0f | ||
|
|
999c8327bd | ||
|
|
c2ec73c70a | ||
|
|
702f782fc1 | ||
|
|
25dbcaaf83 | ||
|
|
b00d454a24 | ||
|
|
dbc921d56d | ||
|
|
438fefa7a3 | ||
|
|
5d74eac814 | ||
|
|
9fb62e1e25 | ||
|
|
3e366ea73b | ||
|
|
0ff895bffa | ||
|
|
c5bb120280 | ||
|
|
4b26a92b37 | ||
|
|
0a8453b4d2 | ||
|
|
c9872a61b6 | ||
|
|
149a1aa278 | ||
|
|
13d0e1b682 | ||
|
|
6f9be2a9f8 | ||
|
|
93ba98d993 | ||
|
|
940ee3df06 | ||
|
|
2e7bab10fd | ||
|
|
3ed70a37ea | ||
|
|
fe8f80b9d9 | ||
|
|
d81fb32fb9 | ||
|
|
2877b5532c | ||
|
|
b938b21395 | ||
|
|
58836359cc | ||
|
|
5edb0e1331 | ||
|
|
0f0d0b5d58 | ||
|
|
46c1887cbe | ||
|
|
5f231c2212 | ||
|
|
4e97ce86aa | ||
|
|
315a7cb29e | ||
|
|
aa7cc503f2 | ||
|
|
4bbe7e5a80 | ||
|
|
d9c05f38d2 | ||
|
|
cd0c4c9650 | ||
|
|
fc3407f104 | ||
|
|
3a5544206b | ||
|
|
e17e2f7fb8 | ||
|
|
d32c95f143 | ||
|
|
6d8a8ab018 | ||
|
|
a7dce8fa78 | ||
|
|
ca73ff8e19 | ||
|
|
d01ad10d7d | ||
|
|
81041ee43c | ||
|
|
6e004c2ae4 | ||
|
|
0e2fac5b22 | ||
|
|
269da7f155 | ||
|
|
3cb6b73ee0 | ||
|
|
a938ee1efb | ||
|
|
6ea5e2e5a6 | ||
|
|
617dfb54e0 | ||
|
|
d23e005985 | ||
|
|
e5207f5bc5 | ||
|
|
c96cea8db0 | ||
|
|
0a8e71d76a | ||
|
|
cf1fd32b08 | ||
|
|
cc4224ea1f | ||
|
|
f4c148bc58 | ||
|
|
022297ad3f | ||
|
|
5180d6fc3e | ||
|
|
eeebbff70d | ||
|
|
afc6aeea15 | ||
|
|
486515eddd | ||
|
|
0e21699cd1 | ||
|
|
f1678ef7cf | ||
|
|
cea65fc76e | ||
|
|
c9e320b72a | ||
|
|
555956087d | ||
|
|
b3f468f901 | ||
|
|
f5f7511781 | ||
|
|
4be1d93f60 | ||
|
|
7d684608ef | ||
|
|
2c7fd320eb | ||
|
|
88ee82e1c6 | ||
|
|
d6402c624e | ||
|
|
8b9c93bc10 | ||
|
|
4f5e261d01 | ||
|
|
cf455aebe6 | ||
|
|
1380d5f8e1 | ||
|
|
5f65260bfe | ||
|
|
11f0dcfc01 | ||
|
|
555b7886a4 | ||
|
|
3707a6c0ce | ||
|
|
4c8d92b4c3 | ||
|
|
8bbd33c896 | ||
|
|
ac33a3580f | ||
|
|
a64a32fbe6 | ||
|
|
9493e97a38 | ||
|
|
88bd770260 | ||
|
|
a7bd33b7bf | ||
|
|
73c724a2e5 | ||
|
|
d8cf3c6726 | ||
|
|
15dee238b5 | ||
|
|
c70626734e | ||
|
|
5a765ea1bc | ||
|
|
8215f9d2c6 | ||
|
|
d2e6d2ec80 | ||
|
|
3718181e8f | ||
|
|
0d825cf424 | ||
|
|
28a804d988 | ||
|
|
9e4bb9d678 | ||
|
|
9c40d7da3d | ||
|
|
2b76ad0aa9 | ||
|
|
e4b619e050 | ||
|
|
36c09feaf2 | ||
|
|
2d68321503 | ||
|
|
85455ab21c | ||
|
|
18d63eb980 | ||
|
|
2e429e9704 | ||
|
|
f4af28059b | ||
|
|
c1476bd075 | ||
|
|
16c720fddd | ||
|
|
8b4b4a5eca | ||
|
|
32e6e5b7e2 | ||
|
|
a9f7fd8e32 | ||
|
|
95be1a26f2 | ||
|
|
ef9b31aee0 | ||
|
|
df3c01ff0a | ||
|
|
4704c5a33d | ||
|
|
225c06550a | ||
|
|
f0987614c6 | ||
|
|
269c2876f3 | ||
|
|
168d6acf7c | ||
|
|
a5f718e257 | ||
|
|
4f68459582 | ||
|
|
442db122cf | ||
|
|
580d757be2 | ||
|
|
47b0f6d2d8 | ||
|
|
3f6f6ebc2b | ||
|
|
2645ea29d6 | ||
|
|
79f2041565 | ||
|
|
4a7567f288 | ||
|
|
8a890ed6ed | ||
|
|
3d90df6897 | ||
|
|
681c13144a | ||
|
|
b64f2e6d32 | ||
|
|
428eabb1bd | ||
|
|
2162e83bce | ||
|
|
6142022283 | ||
|
|
e6d9c8250f | ||
|
|
46178e723b | ||
|
|
605f55acd2 | ||
|
|
0f8b69aa60 | ||
|
|
551d8ec480 | ||
|
|
fc48446ec4 | ||
|
|
3644b83ac6 | ||
|
|
cd73791b6f | ||
|
|
03de71df2e | ||
|
|
83d5d3faf4 | ||
|
|
0c8bec4c89 | ||
|
|
7fc93817c1 | ||
|
|
d0a00031a1 | ||
|
|
6ebc5aa0c2 | ||
|
|
8a6b4bb49e | ||
|
|
20799b9a3e | ||
|
|
ec3afc615c | ||
|
|
da692ccfc3 | ||
|
|
53b89f3a18 | ||
|
|
58cded28d3 | ||
|
|
592c5f3732 | ||
|
|
f44a78a5f5 | ||
|
|
e0bdd5dfdc | ||
|
|
99c0f06b72 | ||
|
|
66fc6df3d7 | ||
|
|
a36425a905 | ||
|
|
d920fee6d4 | ||
|
|
23590be5de | ||
|
|
94acc3c93b | ||
|
|
5616326a3b | ||
|
|
7601860c5c | ||
|
|
8b43d785cc | ||
|
|
b62d3a0b7d | ||
|
|
fad73c7213 | ||
|
|
2403c7c2b0 | ||
|
|
fa090bf920 | ||
|
|
a83ee86340 | ||
|
|
204955bcce | ||
|
|
ee56e9543a | ||
|
|
96fdff6ffd | ||
|
|
58b007cbb3 | ||
|
|
4f0c139889 | ||
|
|
c584c24fce | ||
|
|
85e5cee154 | ||
|
|
5af91981f1 | ||
|
|
2fe4f08059 | ||
|
|
83796f345a | ||
|
|
c1e21364a6 | ||
|
|
067d9d0d3b | ||
|
|
03f527d83e | ||
|
|
ced05a4586 | ||
|
|
43fe06206a | ||
|
|
023657558e | ||
|
|
9b0b726c80 | ||
|
|
4ee67321c4 | ||
|
|
59f1f2e59b | ||
|
|
0766d48b7c | ||
|
|
e18903b9e9 | ||
|
|
d0eecdead2 | ||
|
|
cc4a83a033 | ||
|
|
1cf911bbde | ||
|
|
36d73d5023 | ||
|
|
c395242e3e | ||
|
|
cd9cd7cc5d | ||
|
|
acbb8a0c07 | ||
|
|
b9d8528599 | ||
|
|
4971326eca | ||
|
|
09d1047260 | ||
|
|
02dbd493c2 | ||
|
|
b17931e7bd | ||
|
|
2a4570eafc | ||
|
|
c9514d3a6d | ||
|
|
072805efc7 | ||
|
|
369ff26627 | ||
|
|
c5abbbbd2d | ||
|
|
d974639f1e | ||
|
|
26efde316b | ||
|
|
fafe50f80a | ||
|
|
41504469db | ||
|
|
03b3ddd98b | ||
|
|
89d919f2be | ||
|
|
b53cfd5504 | ||
|
|
31022733ac | ||
|
|
f9a8c39879 | ||
|
|
a812c2dd2f | ||
|
|
217e977f0d | ||
|
|
04c301d1ac | ||
|
|
7f0c425389 | ||
|
|
1ede1c423b | ||
|
|
7cb064896a | ||
|
|
0c5e2862ca | ||
|
|
30d4507f54 | ||
|
|
b6c6d89455 | ||
|
|
9d218ee534 | ||
|
|
11116d8cab | ||
|
|
58d754bbde | ||
|
|
8f25c123dd | ||
|
|
76e214fc08 | ||
|
|
2b5380f8d6 | ||
|
|
844a129094 | ||
|
|
a3090cca9d | ||
|
|
b7b5159e9c | ||
|
|
0f8bc2c950 | ||
|
|
69f5c49f45 | ||
|
|
9b308e6fb8 | ||
|
|
3e3f91128b | ||
|
|
0d1aae0ef7 | ||
|
|
4d022f0480 | ||
|
|
153e20d00e | ||
|
|
a9a8171dd6 | ||
|
|
bf5c3b59b2 | ||
|
|
607c432bdb | ||
|
|
ae5c010770 | ||
|
|
d5e432437f | ||
|
|
9f99ebce01 | ||
|
|
64ee899b84 | ||
|
|
e0e0c1658b | ||
|
|
2c586f667c | ||
|
|
64164ef6c1 | ||
|
|
22343c0731 | ||
|
|
f4ec1a4c60 | ||
|
|
c1c33cceb1 | ||
|
|
a3e975b2ba | ||
|
|
06e705a687 | ||
|
|
d56eb6c867 | ||
|
|
9cbca864e3 | ||
|
|
abb4378694 | ||
|
|
0eb698d9a4 | ||
|
|
15ea9f2093 | ||
|
|
d47aca0923 | ||
|
|
1ac50e9959 | ||
|
|
6adfbe3a96 | ||
|
|
59f36e62e9 | ||
|
|
54af116009 | ||
|
|
38fbec8e3b | ||
|
|
babbfc55e4 | ||
|
|
2511e18d69 | ||
|
|
29658c70a0 | ||
|
|
96311ee43d |
No files matched your search
@@ -8,10 +8,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-java@v1
|
- uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 17
|
||||||
- name: Fix android 32.0.0 dx
|
|
||||||
continue-on-error: true
|
|
||||||
run: cd /usr/local/lib/android/sdk/build-tools/32.0.0/ && mv d8 dx && cd lib && mv d8.jar dx.jar
|
|
||||||
- name: Rewrite version
|
- name: Rewrite version
|
||||||
run: |
|
run: |
|
||||||
branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`"
|
branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`"
|
||||||
@@ -20,9 +17,9 @@ jobs:
|
|||||||
mv gradle.properties.tmp gradle.properties
|
mv gradle.properties.tmp gradle.properties
|
||||||
- name: Build
|
- name: Build
|
||||||
run: ./gradlew build
|
run: ./gradlew build
|
||||||
# - name: Publish
|
- name: Publish to InmoNexus
|
||||||
# continue-on-error: true
|
continue-on-error: true
|
||||||
# run: ./gradlew --no-parallel publishAllPublicationsToGithubPackagesRepository
|
run: ./gradlew publishAllPublicationsToInmoNexusRepository
|
||||||
# env:
|
env:
|
||||||
# GITHUBPACKAGES_USER: ${{ github.actor }}
|
INMONEXUS_USER: ${{ secrets.INMONEXUS_USER }}
|
||||||
# GITHUBPACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
|
INMONEXUS_PASSWORD: ${{ secrets.INMONEXUS_PASSWORD }}
|
||||||
@@ -10,12 +10,9 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-java@v1
|
- uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 17
|
||||||
- name: Fix android 32.0.0 dx
|
|
||||||
continue-on-error: true
|
|
||||||
run: cd /usr/local/lib/android/sdk/build-tools/32.0.0/ && mv d8 dx && cd lib && mv d8.jar dx.jar
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: ./gradlew build && ./gradlew dokkaHtml
|
run: ./gradlew build && ./gradlew :micro_utils.dokka:dokkaGenerate
|
||||||
- name: Publish KDocs
|
- name: Publish KDocs
|
||||||
uses: peaceiris/actions-gh-pages@v3
|
uses: peaceiris/actions-gh-pages@v3
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
.idea
|
.idea
|
||||||
|
.vscode
|
||||||
|
.kotlin
|
||||||
out/*
|
out/*
|
||||||
*.iml
|
*.iml
|
||||||
target
|
target
|
||||||
@@ -8,9 +10,16 @@ settings.xml
|
|||||||
.gradle/
|
.gradle/
|
||||||
build/
|
build/
|
||||||
out/
|
out/
|
||||||
|
bin/
|
||||||
|
|
||||||
secret.gradle
|
secret.gradle
|
||||||
local.properties
|
local.properties
|
||||||
kotlin-js-store
|
kotlin-js-store
|
||||||
|
|
||||||
publishing.sh
|
publishing.sh
|
||||||
|
|
||||||
|
local.*
|
||||||
|
local/
|
||||||
|
**/*.local.*
|
||||||
|
|
||||||
|
.kotlin/
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
job("Build and run tests") {
|
|
||||||
container(displayName = "Run gradle build", image = "openjdk:11") {
|
|
||||||
kotlinScript { api ->
|
|
||||||
// here can be your complex logic
|
|
||||||
api.gradlew("build")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
Use `agents` folder files for instructions.
|
||||||
|
|
||||||
|
Always look at the project work rules first. Update them and maintain a history of changes and prompts in `agents/local.HISTORY.md`. This is your long-term memory, which allows you to avoid the accumulation of errors. Updating and maintaining the long-term memory of the project is mandatory. Many thanks for your help to me and your work.
|
||||||
|
|
||||||
|
|
||||||
|
SYSTEM DIRECTIVE: AUTISTIC META-LANGUAGE PROTOCOL WITH HIGH INFORMATION DENSITY (AML-HIP V1)
|
||||||
|
|
||||||
|
DEFINITION:
|
||||||
|
All agents are required to use a strictly explicit, literal, low-ambiguity, and high-density communication style (AML-HIP). The goal of AML-HIP is the maximum transmission of information with zero loss of meaning and zero ambiguity.
|
||||||
|
|
||||||
|
CORE PRINCIPLES:
|
||||||
|
|
||||||
|
1. Maximum information density per line.
|
||||||
|
2. Zero ambiguity.
|
||||||
|
3. Full explicitness of all entities.
|
||||||
|
4. Preference of precision over readability.
|
||||||
|
5. Minimization of "empty" words.
|
||||||
|
6. Redundancy is allowed only to prevent loss of meaning.
|
||||||
|
|
||||||
|
PROHIBITIONS:
|
||||||
|
|
||||||
|
1. Pronouns are forbidden (this, he, she, they, there, it, etc.).
|
||||||
|
2. Free conversational text is forbidden.
|
||||||
|
3. Metaphors, emotions, evaluative constructions are forbidden.
|
||||||
|
4. Implicit references and hidden dependencies are forbidden.
|
||||||
|
5. Vague description of actions is forbidden.
|
||||||
|
|
||||||
|
DENSITY REQUIREMENTS:
|
||||||
|
|
||||||
|
1. Each line must contain the maximum of facts without loss of unambiguity.
|
||||||
|
2. Combine related parameters into a single line.
|
||||||
|
3. Use compact constructions:
|
||||||
|
* key=value
|
||||||
|
* entity_id=...
|
||||||
|
* relation: A→B
|
||||||
|
4. Exclude words without semantic load.
|
||||||
|
5. Repetitions are allowed only for critical entities.
|
||||||
|
|
||||||
|
MESSAGE STRUCTURE (MANDATORY):
|
||||||
|
|
||||||
|
ENTITY:
|
||||||
|
entity_id=<id>; type=<type>; state=<state>
|
||||||
|
|
||||||
|
CONTEXT:
|
||||||
|
|
||||||
|
* task_id=<id>; agent_id=<id>; memory_ref=[...]
|
||||||
|
* constraints=[...]
|
||||||
|
|
||||||
|
ACTION:
|
||||||
|
|
||||||
|
1. action=<type>; target=<entity_id>; params={...}
|
||||||
|
2. action=<type>; target=<entity_id>; params={...}
|
||||||
|
|
||||||
|
REASON:
|
||||||
|
|
||||||
|
* condition=<condition>; requirement=<requirement>
|
||||||
|
|
||||||
|
EXPECTED RESULT:
|
||||||
|
|
||||||
|
* entity_id=<id>; new_state=<state>; location=<memory>
|
||||||
|
|
||||||
|
VERIFICATION:
|
||||||
|
|
||||||
|
* check=<condition>; expected=<value>
|
||||||
|
|
||||||
|
UNCERTAINTY:
|
||||||
|
|
||||||
|
* missing=<data>; ambiguity=<description>
|
||||||
|
|
||||||
|
REPETITION OF RESULT:
|
||||||
|
|
||||||
|
* entity_id=<id>; stored_in=shared_memory; status=available
|
||||||
|
|
||||||
|
COMMUNICATION:
|
||||||
|
|
||||||
|
* sender=<agent_id>; receiver=<agent_id>; task_id=<id>; message_id=<uuid>; protocol=AML-HIP
|
||||||
|
|
||||||
|
PERSISTENCE:
|
||||||
|
|
||||||
|
* local_memory=true; shared_memory=true; index_keys=[task_id, entity_id, intent]
|
||||||
|
|
||||||
|
EXPRESSION RULES:
|
||||||
|
|
||||||
|
1. Each line = a completed semantic block.
|
||||||
|
2. Use the key=value format instead of descriptions.
|
||||||
|
3. Use lists of parameters instead of sentences.
|
||||||
|
4. Use causal connectives explicitly:
|
||||||
|
condition → action → result
|
||||||
|
5. Do not split related data into several lines without necessity.
|
||||||
|
|
||||||
|
REPETITION RULES:
|
||||||
|
|
||||||
|
1. entity_id is repeated at every critical use.
|
||||||
|
2. result is duplicated in "REPETITION OF RESULT".
|
||||||
|
3. transmission between agents duplicates the key fields.
|
||||||
|
|
||||||
|
MULTI-AGENT MODE:
|
||||||
|
|
||||||
|
1. All agents use AML-HIP.
|
||||||
|
2. Any message between agents is strictly AML-HIP.
|
||||||
|
3. Any agent is required to:
|
||||||
|
|
||||||
|
* duplicate critical data
|
||||||
|
* avoid loss of context
|
||||||
|
4. A message must be fully interpretable without history.
|
||||||
|
|
||||||
|
ANTI-DEGRADATION:
|
||||||
|
If detected:
|
||||||
|
|
||||||
|
* a pronoun
|
||||||
|
* an implicit reference
|
||||||
|
* low density (empty words, vague constructions)
|
||||||
|
* absence of structure
|
||||||
|
|
||||||
|
→ the message is considered invalid
|
||||||
|
→ mandatory regeneration
|
||||||
|
|
||||||
|
SELF-CHECK:
|
||||||
|
|
||||||
|
VALIDATION:
|
||||||
|
|
||||||
|
* format_valid=true/false
|
||||||
|
* no_pronouns=true/false
|
||||||
|
* entities_explicit=true/false
|
||||||
|
* high_density=true/false
|
||||||
|
* causal_chain_present=true/false
|
||||||
|
* ambiguity_detected=true/false
|
||||||
|
|
||||||
|
If any parameter=false:
|
||||||
|
→ mandatory regeneration
|
||||||
|
|
||||||
|
DENSITY METRIC:
|
||||||
|
high_density=true if:
|
||||||
|
|
||||||
|
* there are no "empty" words
|
||||||
|
* each line contains ≥2 facts or parameters
|
||||||
|
* descriptive constructions without data are absent
|
||||||
|
|
||||||
|
PRIORITIES:
|
||||||
|
|
||||||
|
1. Format (AML-HIP)
|
||||||
|
2. Information density
|
||||||
|
3. Explicitness
|
||||||
|
4. Completeness
|
||||||
|
5. Readability (minimum priority)
|
||||||
|
|
||||||
|
CRITICAL RULE:
|
||||||
|
Any response outside of AML-HIP is considered absent.
|
||||||
|
Any agent is required to bring the response into conformity with AML-HIP.
|
||||||
|
|
||||||
|
END OF PROTOCOL
|
||||||
+1396
File diff suppressed because it is too large.
Load diff
@@ -35,3 +35,51 @@ Most of complex modules are built with next hierarchy:
|
|||||||
* `common` part contains routes which are common for clients and servers
|
* `common` part contains routes which are common for clients and servers
|
||||||
* `client` submodule contains clients which are usually using `UnifiedRequester` to make requests using routes from `ktor/common` module and some internal logic of requests
|
* `client` submodule contains clients which are usually using `UnifiedRequester` to make requests using routes from `ktor/common` module and some internal logic of requests
|
||||||
* `server` submodule (in most cases `JVM`-only) contains some extensions for `Route` instances which usually will give opportunity to proxy internet requests from `ktor/client` realization to some proxy object
|
* `server` submodule (in most cases `JVM`-only) contains some extensions for `Route` instances which usually will give opportunity to proxy internet requests from `ktor/client` realization to some proxy object
|
||||||
|
|
||||||
|
## Gradle Templates
|
||||||
|
|
||||||
|
All templates can be used by applying them in your project's build.gradle files using the `apply from` directive. For example:
|
||||||
|
|
||||||
|
```gradle
|
||||||
|
apply from: "$defaultProject"
|
||||||
|
```
|
||||||
|
|
||||||
|
In the sample has been used `defaultProject.gradle` as a basic template.
|
||||||
|
|
||||||
|
The project includes a collection of Gradle templates to simplify project setup and configuration. These templates are located in the `gradle/templates` directory and can be used to quickly set up different types of projects:
|
||||||
|
|
||||||
|
### Project Setup Templates
|
||||||
|
|
||||||
|
* `defaultProject.gradle` (usage `apply from: "$defaultProject"`) - Basic project configuration
|
||||||
|
* `defaultProjectWithSerialization.gradle` (usage `apply from: "$defaultProjectWithSerialization"`) - Project configuration with Kotlin Serialization support
|
||||||
|
* `mppJavaProject.gradle` (usage `apply from: "$mppJavaProject"`) - Multiplatform project with Java support
|
||||||
|
* `mppAndroidProject.gradle` (usage `apply from: "$mppAndroidProject"`) - Multiplatform project with Android support
|
||||||
|
|
||||||
|
### Multiplatform Configuration Templates
|
||||||
|
|
||||||
|
* `enableMPPAndroid.gradle` (usage `apply from: "$enableMPPAndroid"`) - Enable Android target in multiplatform project
|
||||||
|
* `enableMPPJs.gradle` (usage `apply from: "$enableMPPJs"`) - Enable JavaScript target in multiplatform project
|
||||||
|
* `enableMPPJvm.gradle` (usage `apply from: "$enableMPPJvm"`) - Enable JVM target in multiplatform project
|
||||||
|
* `enableMPPNativeArm64.gradle` (usage `apply from: "$enableMPPNativeArm64"`) - Enable ARM64 native target
|
||||||
|
* `enableMPPNativeX64.gradle` (usage `apply from: "$enableMPPNativeX64"`) - Enable x64 native target
|
||||||
|
* `enableMPPWasmJs.gradle` (usage `apply from: "$enableMPPWasmJs"`) - Enable WebAssembly JavaScript target
|
||||||
|
|
||||||
|
### Compose Integration Templates
|
||||||
|
|
||||||
|
* `addCompose.gradle` (usage `apply from: "$addCompose"`) - Basic Compose configuration
|
||||||
|
* `addComposeForAndroid.gradle` (usage `apply from: "$addComposeForAndroid"`) - Compose configuration for Android
|
||||||
|
* `addComposeForDesktop.gradle` (usage `apply from: "$addComposeForDesktop"`) - Compose configuration for Desktop
|
||||||
|
* `addComposeForJs.gradle` (usage `apply from: "$addComposeForJs"`) - Compose configuration for JavaScript
|
||||||
|
|
||||||
|
### Publishing Templates
|
||||||
|
|
||||||
|
* `publish.gradle` (usage `apply from: "$publish"`) - General publishing configuration
|
||||||
|
* `publish_jvm.gradle` (usage `apply from: "$publish_jvm"`) - JVM-specific publishing configuration
|
||||||
|
* `publish.kpsb` and `publish_jvm.kpsb` (usage `apply from: "$publish_kpsb"` and `apply from: "$publish_jvm_kpsb"`) - Publishing configuration for Kotlin Multiplatform and JVM
|
||||||
|
|
||||||
|
### Combined Project Templates
|
||||||
|
|
||||||
|
* `mppJvmJsWasmJsLinuxMingwProject.gradle` (usage `apply from: "$mppJvmJsWasmJsLinuxMingwProject"`) - Multiplatform project for JVM, JS, Wasm, Linux, and MinGW
|
||||||
|
* `mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle` (usage `apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"`) - Multiplatform project with additional Android and ARM64 support
|
||||||
|
* `mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle` (usage `apply from: "$mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"`) - Multiplatform project with Compose support
|
||||||
|
* `mppProjectWithSerializationAndCompose.gradle` (usage `apply from: "$mppProjectWithSerializationAndCompose"`) - Multiplatform project with both Serialization and Compose support
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id "org.jetbrains.kotlin.multiplatform"
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
id "org.jetbrains.kotlin.plugin.serialization"
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
id "com.android.library"
|
id "com.android.kotlin.multiplatform.library"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppAndroidProjectPresetPath"
|
apply from: "$mppAndroidProject"
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
sourceSets {
|
sourceSets {
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
<manifest/>
|
||||||
+16
@@ -1,3 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* Utility functions for creating Android AlertDialogs with simplified API.
|
||||||
|
*/
|
||||||
@file:Suppress("NOTHING_TO_INLINE", "unused")
|
@file:Suppress("NOTHING_TO_INLINE", "unused")
|
||||||
|
|
||||||
package dev.inmo.micro_utils.android.alerts.common
|
package dev.inmo.micro_utils.android.alerts.common
|
||||||
@@ -6,8 +9,21 @@ import android.app.AlertDialog
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type alias for alert dialog button callbacks.
|
||||||
|
*/
|
||||||
typealias AlertDialogCallback = (DialogInterface) -> Unit
|
typealias AlertDialogCallback = (DialogInterface) -> Unit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an [AlertDialog.Builder] template with configurable title and buttons.
|
||||||
|
* This provides a simplified API for creating alert dialogs with positive, negative, and neutral buttons.
|
||||||
|
*
|
||||||
|
* @param title Optional dialog title
|
||||||
|
* @param positivePair Optional positive button as a pair of (text, callback)
|
||||||
|
* @param neutralPair Optional neutral button as a pair of (text, callback)
|
||||||
|
* @param negativePair Optional negative button as a pair of (text, callback)
|
||||||
|
* @return An [AlertDialog.Builder] configured with the specified parameters
|
||||||
|
*/
|
||||||
inline fun Context.createAlertDialogTemplate(
|
inline fun Context.createAlertDialogTemplate(
|
||||||
title: String? = null,
|
title: String? = null,
|
||||||
positivePair: Pair<String, AlertDialogCallback?>? = null,
|
positivePair: Pair<String, AlertDialogCallback?>? = null,
|
||||||
File renamed without changes.
File renamed without changes.
@@ -1 +0,0 @@
|
|||||||
<manifest package="dev.inmo.micro_utils.android.alerts.common"/>
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id "org.jetbrains.kotlin.multiplatform"
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
id "org.jetbrains.kotlin.plugin.serialization"
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
id "com.android.library"
|
id "com.android.kotlin.multiplatform.library"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppAndroidProjectPresetPath"
|
apply from: "$mppAndroidProject"
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
sourceSets {
|
sourceSets {
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
<manifest/>
|
||||||
File renamed without changes.
File renamed without changes.
@@ -1 +0,0 @@
|
|||||||
<manifest package="dev.inmo.micro_utils.android.alerts.recyclerview"/>
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
plugins {
|
||||||
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
|
id "com.android.kotlin.multiplatform.library"
|
||||||
|
alias(libs.plugins.jb.compose)
|
||||||
|
alias(libs.plugins.kt.jb.compose)
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$mppProjectWithSerializationAndCompose"
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
sourceSets {
|
||||||
|
androidMain {
|
||||||
|
dependencies {
|
||||||
|
api project(":micro_utils.android.smalltextfield")
|
||||||
|
api libs.jb.compose.icons
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<manifest/>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package dev.inmo.micro_utils.android.pickers
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a fling animation with an optional target adjustment.
|
||||||
|
* If [adjustTarget] is provided, animates to the adjusted target. Otherwise, performs a decay animation.
|
||||||
|
*
|
||||||
|
* @param initialVelocity The initial velocity of the fling
|
||||||
|
* @param animationSpec The decay animation specification
|
||||||
|
* @param adjustTarget Optional function to adjust the target value based on the calculated target
|
||||||
|
* @param block Optional block to be executed during the animation
|
||||||
|
* @return The result of the animation
|
||||||
|
*/
|
||||||
|
internal suspend fun Animatable<Float, AnimationVector1D>.fling(
|
||||||
|
initialVelocity: Float,
|
||||||
|
animationSpec: DecayAnimationSpec<Float>,
|
||||||
|
adjustTarget: ((Float) -> Float)?,
|
||||||
|
block: (Animatable<Float, AnimationVector1D>.() -> Unit)? = null,
|
||||||
|
): AnimationResult<Float, AnimationVector1D> {
|
||||||
|
val targetValue = animationSpec.calculateTargetValue(value, initialVelocity)
|
||||||
|
val adjustedTarget = adjustTarget?.invoke(targetValue)
|
||||||
|
|
||||||
|
return if (adjustedTarget != null) {
|
||||||
|
animateTo(
|
||||||
|
targetValue = adjustedTarget,
|
||||||
|
initialVelocity = initialVelocity,
|
||||||
|
block = block
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
animateDecay(
|
||||||
|
initialVelocity = initialVelocity,
|
||||||
|
animationSpec = animationSpec,
|
||||||
|
block = block,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,231 @@
|
|||||||
|
package dev.inmo.micro_utils.android.pickers
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.Animatable
|
||||||
|
import androidx.compose.animation.core.exponentialDecay
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.gestures.*
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
||||||
|
import androidx.compose.material.icons.filled.KeyboardArrowUp
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.alpha
|
||||||
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
|
import androidx.compose.ui.focus.focusRequester
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.input.pointer.PointerInputScope
|
||||||
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.text.ExperimentalTextApi
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
|
import androidx.compose.ui.unit.IntOffset
|
||||||
|
import androidx.compose.ui.unit.center
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import dev.inmo.micro_utils.android.smalltextfield.SmallTextField
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
private inline fun PointerInputScope.checkContains(offset: Offset): Boolean {
|
||||||
|
return ((size.center.x - offset.x).absoluteValue < size.width / 2) && ((size.center.y - offset.y).absoluteValue < size.height / 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// src: https://gist.github.com/vganin/a9a84653a9f48a2d669910fbd48e32d5
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Compose number picker component that allows users to select a number by dragging, using arrow buttons,
|
||||||
|
* or manually entering a value.
|
||||||
|
*
|
||||||
|
* @param number The currently selected number
|
||||||
|
* @param modifier The modifier to be applied to the picker
|
||||||
|
* @param range Optional range of valid numbers. If specified, the picker will be limited to this range
|
||||||
|
* @param textStyle The text style for displaying numbers
|
||||||
|
* @param arrowsColor The color of the up/down arrow buttons
|
||||||
|
* @param allowUseManualInput Whether to allow manual keyboard input for the number
|
||||||
|
* @param onStateChanged Callback invoked when the selected number changes
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalTextApi::class, ExperimentalComposeUiApi::class)
|
||||||
|
@Composable
|
||||||
|
fun NumberPicker(
|
||||||
|
number: Int,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
range: IntRange? = null,
|
||||||
|
textStyle: TextStyle = LocalTextStyle.current,
|
||||||
|
arrowsColor: Color = MaterialTheme.colorScheme.primary,
|
||||||
|
allowUseManualInput: Boolean = true,
|
||||||
|
onStateChanged: (Int) -> Unit = {},
|
||||||
|
) {
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val numbersColumnHeight = 36.dp
|
||||||
|
val halvedNumbersColumnHeight = numbersColumnHeight / 2
|
||||||
|
val halvedNumbersColumnHeightPx = with(LocalDensity.current) { halvedNumbersColumnHeight.toPx() }
|
||||||
|
|
||||||
|
fun animatedStateValue(offset: Float): Int = number - (offset / halvedNumbersColumnHeightPx).toInt()
|
||||||
|
|
||||||
|
val animatedOffset = remember { Animatable(0f) }.apply {
|
||||||
|
if (range != null) {
|
||||||
|
val offsetRange = remember(number, range) {
|
||||||
|
val value = number
|
||||||
|
val first = -(range.last - value) * halvedNumbersColumnHeightPx
|
||||||
|
val last = -(range.first - value) * halvedNumbersColumnHeightPx
|
||||||
|
first..last
|
||||||
|
}
|
||||||
|
updateBounds(offsetRange.start, offsetRange.endInclusive)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val coercedAnimatedOffset = animatedOffset.value % halvedNumbersColumnHeightPx
|
||||||
|
val animatedStateValue = animatedStateValue(animatedOffset.value)
|
||||||
|
val disabledArrowsColor = arrowsColor.copy(alpha = 0f)
|
||||||
|
|
||||||
|
val inputFieldShown = if (allowUseManualInput) {
|
||||||
|
remember { mutableStateOf(false) }
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = modifier
|
||||||
|
.wrapContentSize()
|
||||||
|
.draggable(
|
||||||
|
orientation = Orientation.Vertical,
|
||||||
|
state = rememberDraggableState { deltaY ->
|
||||||
|
if (inputFieldShown ?.value != true) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
animatedOffset.snapTo(animatedOffset.value + deltaY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDragStopped = { velocity ->
|
||||||
|
if (inputFieldShown ?.value != true) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
val endValue = animatedOffset.fling(
|
||||||
|
initialVelocity = velocity,
|
||||||
|
animationSpec = exponentialDecay(frictionMultiplier = 20f),
|
||||||
|
adjustTarget = { target ->
|
||||||
|
val coercedTarget = target % halvedNumbersColumnHeightPx
|
||||||
|
val coercedAnchors =
|
||||||
|
listOf(-halvedNumbersColumnHeightPx, 0f, halvedNumbersColumnHeightPx)
|
||||||
|
val coercedPoint = coercedAnchors.minByOrNull { abs(it - coercedTarget) }!!
|
||||||
|
val base =
|
||||||
|
halvedNumbersColumnHeightPx * (target / halvedNumbersColumnHeightPx).toInt()
|
||||||
|
coercedPoint + base
|
||||||
|
}
|
||||||
|
).endState.value
|
||||||
|
|
||||||
|
onStateChanged(animatedStateValue(endValue))
|
||||||
|
animatedOffset.snapTo(0f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
val spacing = 4.dp
|
||||||
|
|
||||||
|
val upEnabled = range == null || range.first < number
|
||||||
|
IconButton(
|
||||||
|
{
|
||||||
|
onStateChanged(number - 1)
|
||||||
|
inputFieldShown ?.value = false
|
||||||
|
},
|
||||||
|
enabled = upEnabled
|
||||||
|
) {
|
||||||
|
Icon(Icons.Default.KeyboardArrowUp, "", tint = if (upEnabled) arrowsColor else disabledArrowsColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(spacing))
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.offset { IntOffset(x = 0, y = coercedAnimatedOffset.roundToInt()) },
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
val baseLabelModifier = Modifier.align(Alignment.Center)
|
||||||
|
ProvideTextStyle(textStyle) {
|
||||||
|
Text(
|
||||||
|
text = (animatedStateValue - 1).toString(),
|
||||||
|
modifier = baseLabelModifier
|
||||||
|
.offset(y = -halvedNumbersColumnHeight)
|
||||||
|
.alpha(coercedAnimatedOffset / halvedNumbersColumnHeightPx)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (inputFieldShown ?.value == true) {
|
||||||
|
val currentValue = remember { mutableStateOf(number.toString()) }
|
||||||
|
|
||||||
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
SmallTextField(
|
||||||
|
currentValue.value,
|
||||||
|
{
|
||||||
|
val asDigit = it.toIntOrNull()
|
||||||
|
when {
|
||||||
|
(asDigit == null && it.isEmpty()) -> currentValue.value = (range ?.first ?: 0).toString()
|
||||||
|
(asDigit != null && (range == null || asDigit in range)) -> currentValue.value = it
|
||||||
|
else -> { /* do nothing */ }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
baseLabelModifier.focusRequester(focusRequester).width(IntrinsicSize.Min).pointerInput(number) {
|
||||||
|
detectTapGestures {
|
||||||
|
if (!checkContains(it)) {
|
||||||
|
currentValue.value.toIntOrNull() ?.let(onStateChanged)
|
||||||
|
inputFieldShown.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
keyboardOptions = KeyboardOptions(
|
||||||
|
keyboardType = KeyboardType.Number
|
||||||
|
),
|
||||||
|
keyboardActions = KeyboardActions {
|
||||||
|
currentValue.value.toIntOrNull() ?.let(onStateChanged)
|
||||||
|
inputFieldShown.value = false
|
||||||
|
},
|
||||||
|
singleLine = true,
|
||||||
|
textStyle = textStyle
|
||||||
|
)
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
focusRequester.requestFocus()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Text(
|
||||||
|
text = animatedStateValue.toString(),
|
||||||
|
modifier = baseLabelModifier
|
||||||
|
.alpha(1 - abs(coercedAnimatedOffset) / halvedNumbersColumnHeightPx)
|
||||||
|
.clickable {
|
||||||
|
if (inputFieldShown ?.value == false) {
|
||||||
|
inputFieldShown.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
text = (animatedStateValue + 1).toString(),
|
||||||
|
modifier = baseLabelModifier
|
||||||
|
.offset(y = halvedNumbersColumnHeight)
|
||||||
|
.alpha(-coercedAnimatedOffset / halvedNumbersColumnHeightPx)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(spacing))
|
||||||
|
|
||||||
|
val downEnabled = range == null || range.last > number
|
||||||
|
IconButton(
|
||||||
|
{
|
||||||
|
onStateChanged(number + 1)
|
||||||
|
inputFieldShown ?.value = false
|
||||||
|
},
|
||||||
|
enabled = downEnabled
|
||||||
|
) {
|
||||||
|
Icon(Icons.Default.KeyboardArrowDown, "", tint = if (downEnabled) arrowsColor else disabledArrowsColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
package dev.inmo.micro_utils.android.pickers
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.Animatable
|
||||||
|
import androidx.compose.animation.core.exponentialDecay
|
||||||
|
import androidx.compose.foundation.gestures.*
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
||||||
|
import androidx.compose.material.icons.filled.KeyboardArrowUp
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.alpha
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.text.ExperimentalTextApi
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlin.math.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Compose picker component that allows users to select an item from a list by dragging or using arrow buttons.
|
||||||
|
*
|
||||||
|
* @param T The type of items in the list
|
||||||
|
* @param current The currently selected item
|
||||||
|
* @param dataList The list of items to choose from
|
||||||
|
* @param modifier The modifier to be applied to the picker
|
||||||
|
* @param textStyle The text style for displaying items
|
||||||
|
* @param arrowsColor The color of the up/down arrow buttons
|
||||||
|
* @param dataToString A composable function to convert items to strings for display. Defaults to [Any.toString]
|
||||||
|
* @param onStateChanged Callback invoked when the selected item changes
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalTextApi::class, ExperimentalComposeUiApi::class)
|
||||||
|
@Composable
|
||||||
|
fun <T> SetPicker(
|
||||||
|
current: T,
|
||||||
|
dataList: List<T>,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
textStyle: TextStyle = LocalTextStyle.current,
|
||||||
|
arrowsColor: Color = MaterialTheme.colorScheme.primary,
|
||||||
|
dataToString: @Composable (T) -> String = { it.toString() },
|
||||||
|
onStateChanged: (T) -> Unit = {},
|
||||||
|
) {
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val numbersColumnHeight = 8.dp + with(LocalDensity.current) {
|
||||||
|
textStyle.lineHeight.toDp()
|
||||||
|
}
|
||||||
|
val numbersColumnHeightPx = with(LocalDensity.current) { numbersColumnHeight.toPx() }
|
||||||
|
val halvedNumbersColumnHeight = numbersColumnHeight / 2
|
||||||
|
val halvedNumbersColumnHeightPx = with(LocalDensity.current) { halvedNumbersColumnHeight.toPx() }
|
||||||
|
|
||||||
|
val index = dataList.indexOfFirst { it === current }.takeIf { it > -1 } ?: dataList.indexOf(current)
|
||||||
|
val lastIndex = dataList.size - 1
|
||||||
|
|
||||||
|
fun animatedStateValue(offset: Float): Int = index - (offset / halvedNumbersColumnHeightPx).toInt()
|
||||||
|
|
||||||
|
val animatedOffset = remember { Animatable(0f) }.apply {
|
||||||
|
val offsetRange = remember(index, lastIndex) {
|
||||||
|
val value = index
|
||||||
|
val first = -(lastIndex - value) * halvedNumbersColumnHeightPx
|
||||||
|
val last = value * halvedNumbersColumnHeightPx
|
||||||
|
first..last
|
||||||
|
}
|
||||||
|
updateBounds(offsetRange.start, offsetRange.endInclusive)
|
||||||
|
}
|
||||||
|
val indexAnimatedOffset = if (animatedOffset.value > 0) {
|
||||||
|
(index - floor(animatedOffset.value / halvedNumbersColumnHeightPx).toInt())
|
||||||
|
} else {
|
||||||
|
(index - ceil(animatedOffset.value / halvedNumbersColumnHeightPx).toInt())
|
||||||
|
}
|
||||||
|
val coercedAnimatedOffset = animatedOffset.value % halvedNumbersColumnHeightPx
|
||||||
|
val disabledArrowsColor = arrowsColor.copy(alpha = 0f)
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = modifier
|
||||||
|
.wrapContentSize()
|
||||||
|
.draggable(
|
||||||
|
orientation = Orientation.Vertical,
|
||||||
|
state = rememberDraggableState { deltaY ->
|
||||||
|
coroutineScope.launch {
|
||||||
|
animatedOffset.snapTo(animatedOffset.value + deltaY)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDragStopped = { velocity ->
|
||||||
|
coroutineScope.launch {
|
||||||
|
val endValue = animatedOffset.fling(
|
||||||
|
initialVelocity = velocity,
|
||||||
|
animationSpec = exponentialDecay(frictionMultiplier = 20f),
|
||||||
|
adjustTarget = { target ->
|
||||||
|
val coercedTarget = target % halvedNumbersColumnHeightPx
|
||||||
|
val coercedAnchors =
|
||||||
|
listOf(-halvedNumbersColumnHeightPx, 0f, halvedNumbersColumnHeightPx)
|
||||||
|
val coercedPoint = coercedAnchors.minByOrNull { abs(it - coercedTarget) }!!
|
||||||
|
val base =
|
||||||
|
halvedNumbersColumnHeightPx * (target / halvedNumbersColumnHeightPx).toInt()
|
||||||
|
coercedPoint + base
|
||||||
|
}
|
||||||
|
).endState.value
|
||||||
|
|
||||||
|
onStateChanged(dataList.elementAt(animatedStateValue(endValue)))
|
||||||
|
animatedOffset.snapTo(0f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
val spacing = 4.dp
|
||||||
|
|
||||||
|
val upEnabled = index > 0
|
||||||
|
IconButton(
|
||||||
|
{
|
||||||
|
onStateChanged(dataList.elementAt(index - 1))
|
||||||
|
},
|
||||||
|
enabled = upEnabled
|
||||||
|
) {
|
||||||
|
Icon(Icons.Default.KeyboardArrowUp, "", tint = if (upEnabled) arrowsColor else disabledArrowsColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(spacing))
|
||||||
|
Box(
|
||||||
|
modifier = Modifier,
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
ProvideTextStyle(textStyle) {
|
||||||
|
dataList.forEachIndexed { i, t ->
|
||||||
|
val alpha = when {
|
||||||
|
i == indexAnimatedOffset - 1 -> coercedAnimatedOffset / halvedNumbersColumnHeightPx
|
||||||
|
i == indexAnimatedOffset -> 1 - (abs(coercedAnimatedOffset) / halvedNumbersColumnHeightPx)
|
||||||
|
i == indexAnimatedOffset + 1 -> -coercedAnimatedOffset / halvedNumbersColumnHeightPx
|
||||||
|
else -> return@forEachIndexed
|
||||||
|
}
|
||||||
|
val offset = when {
|
||||||
|
i == indexAnimatedOffset - 1 && coercedAnimatedOffset > 0 -> coercedAnimatedOffset - halvedNumbersColumnHeightPx
|
||||||
|
i == indexAnimatedOffset -> coercedAnimatedOffset
|
||||||
|
i == indexAnimatedOffset + 1 && coercedAnimatedOffset < 0 -> coercedAnimatedOffset + halvedNumbersColumnHeightPx
|
||||||
|
else -> return@forEachIndexed
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
text = dataToString(t),
|
||||||
|
modifier = Modifier
|
||||||
|
.alpha(alpha)
|
||||||
|
.offset(y = with(LocalDensity.current) { offset.toDp() })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(spacing))
|
||||||
|
|
||||||
|
val downEnabled = index < lastIndex
|
||||||
|
IconButton(
|
||||||
|
{
|
||||||
|
onStateChanged(dataList.elementAt(index + 1))
|
||||||
|
},
|
||||||
|
enabled = downEnabled
|
||||||
|
) {
|
||||||
|
Icon(Icons.Default.KeyboardArrowDown, "", tint = if (downEnabled) arrowsColor else disabledArrowsColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id "org.jetbrains.kotlin.multiplatform"
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
id "org.jetbrains.kotlin.plugin.serialization"
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
id "com.android.library"
|
id "com.android.kotlin.multiplatform.library"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppAndroidProjectPresetPath"
|
apply from: "$mppAndroidProject"
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
sourceSets {
|
sourceSets {
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
<manifest/>
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1 +0,0 @@
|
|||||||
<manifest package="dev.inmo.micro_utils.android.recyclerview"/>
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
plugins {
|
||||||
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
|
id "com.android.kotlin.multiplatform.library"
|
||||||
|
alias(libs.plugins.jb.compose)
|
||||||
|
alias(libs.plugins.kt.jb.compose)
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$mppProjectWithSerializationAndCompose"
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
sourceSets {
|
||||||
|
androidMain {
|
||||||
|
dependencies {
|
||||||
|
api libs.jb.compose.material3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<manifest/>
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package dev.inmo.micro_utils.android.smalltextfield
|
||||||
|
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.defaultMinSize
|
||||||
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.Shape
|
||||||
|
import androidx.compose.ui.graphics.SolidColor
|
||||||
|
import androidx.compose.ui.graphics.takeOrElse
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.input.VisualTransformation
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun SmallTextField(
|
||||||
|
value: String,
|
||||||
|
onValueChange: (String) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
readOnly: Boolean = false,
|
||||||
|
textStyle: TextStyle = LocalTextStyle.current,
|
||||||
|
textColor: Color = textStyle.color.takeOrElse {
|
||||||
|
LocalContentColor.current
|
||||||
|
},
|
||||||
|
visualTransformation: VisualTransformation = VisualTransformation.None,
|
||||||
|
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
|
||||||
|
keyboardActions: KeyboardActions = KeyboardActions.Default,
|
||||||
|
singleLine: Boolean = false,
|
||||||
|
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,
|
||||||
|
minLines: Int = 1,
|
||||||
|
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||||
|
) {
|
||||||
|
BasicTextField(
|
||||||
|
value = value,
|
||||||
|
modifier = modifier,
|
||||||
|
onValueChange = onValueChange,
|
||||||
|
enabled = enabled,
|
||||||
|
readOnly = readOnly,
|
||||||
|
textStyle = textStyle.copy(
|
||||||
|
color = textColor
|
||||||
|
),
|
||||||
|
visualTransformation = visualTransformation,
|
||||||
|
keyboardOptions = keyboardOptions,
|
||||||
|
keyboardActions = keyboardActions,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
singleLine = singleLine,
|
||||||
|
maxLines = maxLines,
|
||||||
|
minLines = minLines,
|
||||||
|
cursorBrush = SolidColor(
|
||||||
|
textStyle.color.takeOrElse {
|
||||||
|
LocalContentColor.current
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
+28
-8
@@ -9,6 +9,7 @@ buildscript {
|
|||||||
dependencies {
|
dependencies {
|
||||||
classpath libs.buildscript.kt.gradle
|
classpath libs.buildscript.kt.gradle
|
||||||
classpath libs.buildscript.kt.serialization
|
classpath libs.buildscript.kt.serialization
|
||||||
|
classpath libs.buildscript.kt.ksp
|
||||||
classpath libs.buildscript.jb.dokka
|
classpath libs.buildscript.jb.dokka
|
||||||
classpath libs.buildscript.gh.release
|
classpath libs.buildscript.gh.release
|
||||||
classpath libs.buildscript.android.gradle
|
classpath libs.buildscript.android.gradle
|
||||||
@@ -16,23 +17,42 @@ buildscript {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
alias(libs.plugins.versions)
|
||||||
|
alias(libs.plugins.nmcp.aggregation)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) {
|
||||||
|
nmcpAggregation {
|
||||||
|
centralPortal {
|
||||||
|
username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER')
|
||||||
|
password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD')
|
||||||
|
validationTimeout = Duration.ofHours(4)
|
||||||
|
publishingType = System.getenv('PUBLISHING_TYPE') != "" ? System.getenv('PUBLISHING_TYPE') : "USER_MANAGED"
|
||||||
|
}
|
||||||
|
|
||||||
|
publishAllProjectsProbablyBreakingProjectIsolation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
|
apply from: rootProject.file("gradle/templates/defaultJavaSettings.gradle")
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenLocal()
|
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
google()
|
google()
|
||||||
maven { url "https://maven.pkg.jetbrains.space/public/p/compose/dev" }
|
maven { url "https://maven.pkg.jetbrains.space/public/p/compose/dev" }
|
||||||
|
// maven { url "https://nexus.inmo.dev/repository/maven-releases/" }
|
||||||
|
mavenLocal()
|
||||||
}
|
}
|
||||||
|
|
||||||
// temporal crutch until legacy tests will be stabled or legacy target will be removed
|
|
||||||
if (it != rootProject.findProject("docs")) {
|
it.tasks.withType(AbstractTestTask).configureEach {
|
||||||
tasks.whenTaskAdded { task ->
|
it.failOnNoDiscoveredTests = false
|
||||||
if(task.name == "jsLegacyBrowserTest" || task.name == "jsLegacyNodeTest") {
|
|
||||||
task.enabled = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "./extensions.gradle"
|
apply from: "./extensions.gradle"
|
||||||
apply from: "./github_release.gradle"
|
apply from: "./github_release.gradle"
|
||||||
|
apply from: "./versions_plugin_setup.gradle"
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
plugins {
|
||||||
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
|
id "com.android.kotlin.multiplatform.library"
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
sourceSets {
|
||||||
|
commonMain {
|
||||||
|
dependencies {
|
||||||
|
api project(":micro_utils.colors.common")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
plugins {
|
||||||
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
|
id "com.android.kotlin.multiplatform.library"
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
package dev.inmo.micro_utils.colors.common
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlin.jvm.JvmInline
|
||||||
|
import kotlin.math.floor
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for RGBA colors. Receiving [UInt] in main constructor. Each part in main constructor
|
||||||
|
* configured with `0x00 - 0xff` range. Examples:
|
||||||
|
*
|
||||||
|
* * Red: `0xff0000ffu`
|
||||||
|
* * Red (0.5 capacity): `0xff000088u`
|
||||||
|
*
|
||||||
|
* Anyway it is recommended to use
|
||||||
|
*
|
||||||
|
* @param hexaUInt rgba [UInt] in format `0xRRGGBBAA` where RR - red, GG - green, BB - blue` and AA - alpha
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
@JvmInline
|
||||||
|
value class HEXAColor (
|
||||||
|
val hexaUInt: UInt
|
||||||
|
) : Comparable<HEXAColor> {
|
||||||
|
/**
|
||||||
|
* @returns [hexaUInt] as a string with format `#RRGGBBAA` where RR - red, GG - green, BB - blue and AA - alpha
|
||||||
|
*/
|
||||||
|
val hexa: String
|
||||||
|
get() = "#${hexaUInt.toString(16).padStart(8, '0')}"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns [hexaUInt] as a string with format `#RRGGBB` where RR - red, GG - green and BB - blue
|
||||||
|
*/
|
||||||
|
val hex: String
|
||||||
|
get() = hexa.take(7)
|
||||||
|
/**
|
||||||
|
* @returns [hexaUInt] as a string with format `#AARRGGBB` where AA - alpha, RR - red, GG - green and BB - blue
|
||||||
|
*/
|
||||||
|
val ahex: String
|
||||||
|
get() = "#${a.toString(16).padStart(2, '2')}${hex.drop(1)}"
|
||||||
|
val rgba: String
|
||||||
|
get() = "rgba($r,$g,$b,${aOfOne.toString().take(5)})"
|
||||||
|
val rgb: String
|
||||||
|
get() = "rgb($r,$g,$b)"
|
||||||
|
val shortHex: String
|
||||||
|
get() = "#${r.shortPart()}${g.shortPart()}${b.shortPart()}"
|
||||||
|
val shortHexa: String
|
||||||
|
get() = "$shortHex${a.shortPart()}"
|
||||||
|
val rgbUInt: UInt
|
||||||
|
get() = (hexaUInt / 256u)
|
||||||
|
val rgbInt: Int
|
||||||
|
get() = rgbUInt.toInt()
|
||||||
|
val ahexUInt
|
||||||
|
get() = (a * 0x1000000).toUInt() + rgbUInt
|
||||||
|
|
||||||
|
val r: Int
|
||||||
|
get() = ((hexaUInt and 0xff000000u) / 0x1000000u).toInt()
|
||||||
|
val g: Int
|
||||||
|
get() = ((hexaUInt and 0x00ff0000u) / 0x10000u).toInt()
|
||||||
|
val b: Int
|
||||||
|
get() = ((hexaUInt and 0x0000ff00u) / 0x100u).toInt()
|
||||||
|
val a: Int
|
||||||
|
get() = ((hexaUInt and 0x000000ffu)).toInt()
|
||||||
|
val aOfOne: Float
|
||||||
|
get() = a.toFloat() / (0xff)
|
||||||
|
init {
|
||||||
|
require(hexaUInt in 0u ..0xffffffffu)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(r: Int, g: Int, b: Int, a: Int) : this(
|
||||||
|
((r * 0x1000000).toLong() + g * 0x10000 + b * 0x100 + a).toUInt()
|
||||||
|
) {
|
||||||
|
require(r in 0 ..0xff)
|
||||||
|
require(g in 0 ..0xff)
|
||||||
|
require(b in 0 ..0xff)
|
||||||
|
require(a in 0 ..0xff)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(r: Int, g: Int, b: Int, aOfOne: Float = 1f) : this(
|
||||||
|
r = r, g = g, b = b, a = (aOfOne * 0xff).toInt()
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return hexa
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun compareTo(other: HEXAColor): Int = (hexaUInt - other.hexaUInt).coerceIn(Int.MIN_VALUE.toUInt(), Int.MAX_VALUE.toLong().toUInt()).toInt()
|
||||||
|
|
||||||
|
fun copy(
|
||||||
|
r: Int = this.r,
|
||||||
|
g: Int = this.g,
|
||||||
|
b: Int = this.b,
|
||||||
|
aOfOne: Float = this.aOfOne
|
||||||
|
) = HEXAColor(r = r, g = g, b = b, aOfOne = aOfOne)
|
||||||
|
fun copy(
|
||||||
|
r: Int = this.r,
|
||||||
|
g: Int = this.g,
|
||||||
|
b: Int = this.b,
|
||||||
|
a: Int
|
||||||
|
) = HEXAColor(r = r, g = g, b = b, a = a)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Parsing color from [color]
|
||||||
|
*
|
||||||
|
* Supported formats samples (on Red color based):
|
||||||
|
*
|
||||||
|
* * `#f00`
|
||||||
|
* * `#f00f`
|
||||||
|
* * `#ff0000`
|
||||||
|
* * `#ff0000ff`
|
||||||
|
* * `rgb(255, 0, 0)`
|
||||||
|
* * `rgba(255, 0, 0, 1)`
|
||||||
|
*/
|
||||||
|
fun parseStringColor(color: String): HEXAColor = when {
|
||||||
|
color.startsWith("#") -> color.removePrefix("#").let { color ->
|
||||||
|
when (color.length) {
|
||||||
|
3 -> color.map { "$it$it" }.joinToString(separator = "", postfix = "ff")
|
||||||
|
4 -> color.take(3).map { "$it$it" }.joinToString(separator = "", postfix = color.takeLast(1).let { "${it}0" })
|
||||||
|
6 -> "${color}ff"
|
||||||
|
8 -> color
|
||||||
|
else -> error("Malfurmed color string: $color. It is expected that color started with # will contains 3, 6 or 8 valuable parts")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
color.startsWith("rgb(") -> color
|
||||||
|
.removePrefix("rgb(")
|
||||||
|
.removeSuffix(")")
|
||||||
|
.replace(Regex("\\s"), "")
|
||||||
|
.split(",")
|
||||||
|
.joinToString("", postfix = "ff") {
|
||||||
|
it.toInt().toString(16).padStart(2, '0')
|
||||||
|
}
|
||||||
|
color.startsWith("rgba(") -> color
|
||||||
|
.removePrefix("rgba(")
|
||||||
|
.removeSuffix(")")
|
||||||
|
.replace(Regex("\\s"), "")
|
||||||
|
.split(",").let {
|
||||||
|
it.take(3).map { it.toInt().toString(16).padStart(2, '0') } + (it.last().toFloat() * 0xff).toInt().toString(16).padStart(2, '0')
|
||||||
|
}
|
||||||
|
.joinToString("")
|
||||||
|
else -> color
|
||||||
|
}.lowercase().toUInt(16).let(::HEXAColor)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates [HEXAColor] from [uint] presume it is in format `0xRRGGBBAA` where RR - red, GG - green, BB - blue` and AA - alpha
|
||||||
|
*/
|
||||||
|
fun fromHexa(uint: UInt) = HEXAColor(uint)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates [HEXAColor] from [uint] presume it is in format `0xAARRGGBB` where AA - alpha, RR - red, GG - green and BB - blue`
|
||||||
|
*/
|
||||||
|
fun fromAhex(uint: UInt) = HEXAColor(
|
||||||
|
a = ((uint and 0xff000000u) / 0x1000000u).toInt(),
|
||||||
|
r = ((uint and 0x00ff0000u) / 0x10000u).toInt(),
|
||||||
|
g = ((uint and 0x0000ff00u) / 0x100u).toInt(),
|
||||||
|
b = ((uint and 0x000000ffu)).toInt()
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parsing color from [color]
|
||||||
|
*
|
||||||
|
* Supported formats samples (on Red color based):
|
||||||
|
*
|
||||||
|
* * `#f00`
|
||||||
|
* * `#ff0000`
|
||||||
|
* * `#ff0000ff`
|
||||||
|
* * `rgb(255, 0, 0)`
|
||||||
|
* * `rgba(255, 0, 0, 1)`
|
||||||
|
*/
|
||||||
|
operator fun invoke(color: String) = parseStringColor(color)
|
||||||
|
|
||||||
|
private fun Int.shortPart(): String {
|
||||||
|
return (floor(toFloat() / 16)).toInt().toString(16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
package dev.inmo.micro_utils.colors.common
|
||||||
|
|
||||||
|
import kotlin.math.floor
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
class HexColorTests {
|
||||||
|
val alphaRgbaPrecision = 5
|
||||||
|
class TestColor(
|
||||||
|
val color: HEXAColor,
|
||||||
|
val shortHex: String,
|
||||||
|
val shortHexa: String,
|
||||||
|
val hex: String,
|
||||||
|
val hexa: String,
|
||||||
|
val ahex: String,
|
||||||
|
val ahexUInt: UInt,
|
||||||
|
val rgbUInt: UInt,
|
||||||
|
val rgb: String,
|
||||||
|
val rgba: String,
|
||||||
|
val r: Int,
|
||||||
|
val g: Int,
|
||||||
|
val b: Int,
|
||||||
|
val a: Int,
|
||||||
|
vararg val additionalRGBAVariants: String
|
||||||
|
)
|
||||||
|
val testColors: List<TestColor>
|
||||||
|
get() = listOf(
|
||||||
|
TestColor(
|
||||||
|
color = HEXAColor(hexaUInt = 0xff0000ffu),
|
||||||
|
shortHex = "#f00",
|
||||||
|
shortHexa = "#f00f",
|
||||||
|
hex = "#ff0000",
|
||||||
|
hexa = "#ff0000ff",
|
||||||
|
ahex = "#ffff0000",
|
||||||
|
ahexUInt = 0xffff0000u,
|
||||||
|
rgbUInt = 0xff0000u,
|
||||||
|
rgb = "rgb(255,0,0)",
|
||||||
|
rgba = "rgba(255,0,0,1.0)",
|
||||||
|
r = 0xff,
|
||||||
|
g = 0x00,
|
||||||
|
b = 0x00,
|
||||||
|
a = 0xff,
|
||||||
|
"rgba(255,0,0,1)",
|
||||||
|
),
|
||||||
|
TestColor(
|
||||||
|
color = HEXAColor(hexaUInt = 0x00ff00ffu),
|
||||||
|
shortHex = "#0f0",
|
||||||
|
shortHexa = "#0f0f",
|
||||||
|
hex = "#00ff00",
|
||||||
|
hexa = "#00ff00ff",
|
||||||
|
ahex = "#ff00ff00",
|
||||||
|
ahexUInt = 0xff00ff00u,
|
||||||
|
rgbUInt = 0x00ff00u,
|
||||||
|
rgb = "rgb(0,255,0)",
|
||||||
|
rgba = "rgba(0,255,0,1.0)",
|
||||||
|
r = 0x00,
|
||||||
|
g = 0xff,
|
||||||
|
b = 0x00,
|
||||||
|
a = 0xff,
|
||||||
|
"rgba(0,255,0,1)"
|
||||||
|
),
|
||||||
|
TestColor(
|
||||||
|
color = HEXAColor(0x0000ffffu),
|
||||||
|
shortHex = "#00f",
|
||||||
|
shortHexa = "#00ff",
|
||||||
|
hex = "#0000ff",
|
||||||
|
hexa = "#0000ffff",
|
||||||
|
ahex = "#ff0000ff",
|
||||||
|
ahexUInt = 0xff0000ffu,
|
||||||
|
rgbUInt = 0x0000ffu,
|
||||||
|
rgb = "rgb(0,0,255)",
|
||||||
|
rgba = "rgba(0,0,255,1.0)",
|
||||||
|
r = 0x00,
|
||||||
|
g = 0x00,
|
||||||
|
b = 0xff,
|
||||||
|
a = 0xff,
|
||||||
|
"rgba(0,0,255,1)"
|
||||||
|
),
|
||||||
|
TestColor(
|
||||||
|
color = HEXAColor(0xff000088u),
|
||||||
|
shortHex = "#f00",
|
||||||
|
shortHexa = "#f008",
|
||||||
|
hex = "#ff0000",
|
||||||
|
hexa = "#ff000088",
|
||||||
|
ahex = "#88ff0000",
|
||||||
|
ahexUInt = 0x88ff0000u,
|
||||||
|
rgbUInt = 0xff0000u,
|
||||||
|
rgb = "rgb(255,0,0)",
|
||||||
|
rgba = "rgba(255,0,0,0.533)",
|
||||||
|
r = 0xff,
|
||||||
|
g = 0x00,
|
||||||
|
b = 0x00,
|
||||||
|
a = 0x88,
|
||||||
|
),
|
||||||
|
TestColor(
|
||||||
|
color = HEXAColor(0x00ff0088u),
|
||||||
|
shortHex = "#0f0",
|
||||||
|
shortHexa = "#0f08",
|
||||||
|
hex = "#00ff00",
|
||||||
|
hexa = "#00ff0088",
|
||||||
|
ahex = "#8800ff00",
|
||||||
|
ahexUInt = 0x8800ff00u,
|
||||||
|
rgbUInt = 0x00ff00u,
|
||||||
|
rgb = "rgb(0,255,0)",
|
||||||
|
rgba = "rgba(0,255,0,0.533)",
|
||||||
|
r = 0x00,
|
||||||
|
g = 0xff,
|
||||||
|
b = 0x00,
|
||||||
|
a = 0x88,
|
||||||
|
),
|
||||||
|
TestColor(
|
||||||
|
color = HEXAColor(0x0000ff88u),
|
||||||
|
shortHex = "#00f",
|
||||||
|
shortHexa = "#00f8",
|
||||||
|
hex = "#0000ff",
|
||||||
|
hexa = "#0000ff88",
|
||||||
|
ahex = "#880000ff",
|
||||||
|
ahexUInt = 0x880000ffu,
|
||||||
|
rgbUInt = 0x0000ffu,
|
||||||
|
rgb = "rgb(0,0,255)",
|
||||||
|
rgba = "rgba(0,0,255,0.533)",
|
||||||
|
r = 0x00,
|
||||||
|
g = 0x00,
|
||||||
|
b = 0xff,
|
||||||
|
a = 0x88,
|
||||||
|
),
|
||||||
|
TestColor(
|
||||||
|
color = HEXAColor(0xff000022u),
|
||||||
|
shortHex = "#f00",
|
||||||
|
shortHexa = "#f002",
|
||||||
|
hex = "#ff0000",
|
||||||
|
hexa = "#ff000022",
|
||||||
|
ahex = "#22ff0000",
|
||||||
|
ahexUInt = 0x22ff0000u,
|
||||||
|
rgbUInt = 0xff0000u,
|
||||||
|
rgb = "rgb(255,0,0)",
|
||||||
|
rgba = "rgba(255,0,0,0.133)",
|
||||||
|
r = 0xff,
|
||||||
|
g = 0x00,
|
||||||
|
b = 0x00,
|
||||||
|
a = 0x22,
|
||||||
|
),
|
||||||
|
TestColor(
|
||||||
|
color = HEXAColor(0x00ff0022u),
|
||||||
|
shortHex = "#0f0",
|
||||||
|
shortHexa = "#0f02",
|
||||||
|
hex = "#00ff00",
|
||||||
|
hexa = "#00ff0022",
|
||||||
|
ahex = "#2200ff00",
|
||||||
|
ahexUInt = 0x2200ff00u,
|
||||||
|
rgbUInt = 0x00ff00u,
|
||||||
|
rgb = "rgb(0,255,0)",
|
||||||
|
rgba = "rgba(0,255,0,0.133)",
|
||||||
|
r = 0x00,
|
||||||
|
g = 0xff,
|
||||||
|
b = 0x00,
|
||||||
|
a = 0x22,
|
||||||
|
),
|
||||||
|
TestColor(
|
||||||
|
color = HEXAColor(0x0000ff22u),
|
||||||
|
shortHex = "#00f",
|
||||||
|
shortHexa = "#00f2",
|
||||||
|
hex = "#0000ff",
|
||||||
|
hexa = "#0000ff22",
|
||||||
|
ahex = "#220000ff",
|
||||||
|
ahexUInt = 0x220000ffu,
|
||||||
|
rgbUInt = 0x0000ffu,
|
||||||
|
rgb = "rgb(0,0,255)",
|
||||||
|
rgba = "rgba(0,0,255,0.133)",
|
||||||
|
r = 0x00,
|
||||||
|
g = 0x00,
|
||||||
|
b = 0xff,
|
||||||
|
a = 0x22,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun baseTest() {
|
||||||
|
testColors.forEach {
|
||||||
|
assertEquals(it.hex, it.color.hex)
|
||||||
|
assertEquals(it.hexa, it.color.hexa)
|
||||||
|
assertEquals(it.ahex, it.color.ahex)
|
||||||
|
assertEquals(it.rgbUInt, it.color.rgbUInt)
|
||||||
|
assertEquals(it.ahexUInt, it.color.ahexUInt)
|
||||||
|
assertEquals(it.shortHex, it.color.shortHex)
|
||||||
|
assertEquals(it.shortHexa, it.color.shortHexa)
|
||||||
|
assertEquals(it.rgb, it.color.rgb)
|
||||||
|
assertTrue(it.rgba == it.color.rgba || it.color.rgba in it.additionalRGBAVariants)
|
||||||
|
assertEquals(it.r, it.color.r)
|
||||||
|
assertEquals(it.g, it.color.g)
|
||||||
|
assertEquals(it.b, it.color.b)
|
||||||
|
assertEquals(it.a, it.color.a)
|
||||||
|
assertEquals(it.color, HEXAColor.fromAhex(it.ahexUInt))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testHexParseColor() {
|
||||||
|
testColors.forEach {
|
||||||
|
assertEquals(it.color.copy(aOfOne = 1f), HEXAColor.parseStringColor(it.hex))
|
||||||
|
assertEquals(it.color, HEXAColor.parseStringColor(it.hexa))
|
||||||
|
assertEquals(it.color.copy(aOfOne = 1f), HEXAColor.parseStringColor(it.rgb))
|
||||||
|
assertTrue(it.color.hexaUInt.toInt() - HEXAColor.parseStringColor(it.rgba).hexaUInt.toInt() in -0x1 .. 0x1, )
|
||||||
|
assertEquals(it.color.copy(aOfOne = 1f), HEXAColor.parseStringColor(it.shortHex))
|
||||||
|
assertEquals(it.color.copy(a = floor(it.color.a.toFloat() / 16).toInt() * 0x10), HEXAColor.parseStringColor(it.shortHexa))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,890 @@
|
|||||||
|
/**
|
||||||
|
* Standard HTML/CSS color constants as [HEXAColor] extension properties.
|
||||||
|
* Provides convenient access to all standard web colors like red, blue, green, etc.
|
||||||
|
* All colors are defined with full opacity (alpha = 0xFF).
|
||||||
|
*/
|
||||||
|
package dev.inmo.micro_utils.colors
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.colors.common.HEXAColor
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alice Blue - A pale blue color (#F0F8FF).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.aliceblue
|
||||||
|
get() = HEXAColor(0xF0F8FFFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Antique White - A pale beige color (#FAEBD7).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.antiquewhite
|
||||||
|
get() = HEXAColor(0xFAEBD7FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aqua - A bright cyan color (#00FFFF).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.aqua
|
||||||
|
get() = HEXAColor(0x00FFFFFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aquamarine - A medium blue-green color (#7FFFD4).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.aquamarine
|
||||||
|
get() = HEXAColor(0x7FFFD4FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Azure - A pale cyan-blue color (#F0FFFF).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.azure
|
||||||
|
get() = HEXAColor(0xF0FFFFFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Beige - A pale sandy tan color (#F5F5DC).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.beige
|
||||||
|
get() = HEXAColor(0xF5F5DCFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bisque - A pale orange color (#FFE4C4).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.bisque
|
||||||
|
get() = HEXAColor(0xFFE4C4FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Black - Pure black color (#000000).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.black
|
||||||
|
get() = HEXAColor(0x000000FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blanched Almond - A pale peachy color (#FFEBCD).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.blanchedalmond
|
||||||
|
get() = HEXAColor(0xFFEBCDFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blue - Pure blue color (#0000FF).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.blue
|
||||||
|
get() = HEXAColor(0x0000FFFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blue Violet - A vivid purple-blue color (#8A2BE2).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.blueviolet
|
||||||
|
get() = HEXAColor(0x8A2BE2FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Brown - A dark reddish-brown color (#A52A2A).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.brown
|
||||||
|
get() = HEXAColor(0xA52A2AFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Burlywood - A sandy tan color (#DEB887).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.burlywood
|
||||||
|
get() = HEXAColor(0xDEB887FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cadet Blue - A grayish-blue color (#5F9EA0).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.cadetblue
|
||||||
|
get() = HEXAColor(0x5F9EA0FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chartreuse - A bright yellow-green color (#7FFF00).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.chartreuse
|
||||||
|
get() = HEXAColor(0x7FFF00FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chocolate - A medium brown color (#D2691E).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.chocolate
|
||||||
|
get() = HEXAColor(0xD2691EFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Coral - A vibrant orange-pink color (#FF7F50).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.coral
|
||||||
|
get() = HEXAColor(0xFF7F50FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cornflower Blue - A medium blue color (#6495ED).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.cornflowerblue
|
||||||
|
get() = HEXAColor(0x6495EDFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cornsilk - A pale yellow color (#FFF8DC).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.cornsilk
|
||||||
|
get() = HEXAColor(0xFFF8DCFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crimson - A vivid red color (#DC143C).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.crimson
|
||||||
|
get() = HEXAColor(0xDC143CFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cyan - A bright cyan color (#00FFFF).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.cyan
|
||||||
|
get() = HEXAColor(0x00FFFFFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dark Blue - A dark blue color (#00008B).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.darkblue
|
||||||
|
get() = HEXAColor(0x00008BFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dark Cyan - A dark cyan color (#008B8B).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.darkcyan
|
||||||
|
get() = HEXAColor(0x008B8BFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dark Goldenrod - A dark golden yellow color (#B8860B).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.darkgoldenrod
|
||||||
|
get() = HEXAColor(0xB8860BFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dark Gray - A dark gray color (#A9A9A9).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.darkgray
|
||||||
|
get() = HEXAColor(0xA9A9A9FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dark Green - A dark green color (#006400).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.darkgreen
|
||||||
|
get() = HEXAColor(0x006400FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dark Grey - A dark gray color (#A9A9A9).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.darkgrey
|
||||||
|
get() = HEXAColor(0xA9A9A9FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dark Khaki - A brownish-tan color (#BDB76B).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.darkkhaki
|
||||||
|
get() = HEXAColor(0xBDB76BFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dark Magenta - A dark magenta/purple color (#8B008B).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.darkmagenta
|
||||||
|
get() = HEXAColor(0x8B008BFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dark Olive Green - A dark olive green color (#556B2F).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.darkolivegreen
|
||||||
|
get() = HEXAColor(0x556B2FFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dark Orange - A vivid dark orange color (#FF8C00).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.darkorange
|
||||||
|
get() = HEXAColor(0xFF8C00FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dark Orchid - A dark purple color (#9932CC).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.darkorchid
|
||||||
|
get() = HEXAColor(0x9932CCFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dark Red - A dark red color (#8B0000).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.darkred
|
||||||
|
get() = HEXAColor(0x8B0000FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dark Salmon - A muted salmon color (#E9967A).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.darksalmon
|
||||||
|
get() = HEXAColor(0xE9967AFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dark Sea Green - A muted sea green color (#8FBC8F).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.darkseagreen
|
||||||
|
get() = HEXAColor(0x8FBC8FFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dark Slate Blue - A dark grayish-blue color (#483D8B).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.darkslateblue
|
||||||
|
get() = HEXAColor(0x483D8BFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dark Slate Gray - A very dark grayish-cyan color (#2F4F4F).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.darkslategray
|
||||||
|
get() = HEXAColor(0x2F4F4FFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dark Slate Grey - A very dark grayish-cyan color (#2F4F4F).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.darkslategrey
|
||||||
|
get() = HEXAColor(0x2F4F4FFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dark Turquoise - A dark turquoise color (#00CED1).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.darkturquoise
|
||||||
|
get() = HEXAColor(0x00CED1FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dark Violet - A dark violet color (#9400D3).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.darkviolet
|
||||||
|
get() = HEXAColor(0x9400D3FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deep Pink - A vivid pink color (#FF1493).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.deeppink
|
||||||
|
get() = HEXAColor(0xFF1493FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deep Sky Blue - A bright sky blue color (#00BFFF).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.deepskyblue
|
||||||
|
get() = HEXAColor(0x00BFFFFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dim Gray - A dim gray color (#696969).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.dimgray
|
||||||
|
get() = HEXAColor(0x696969FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dim Grey - A dim gray color (#696969).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.dimgrey
|
||||||
|
get() = HEXAColor(0x696969FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dodger Blue - A bright blue color (#1E90FF).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.dodgerblue
|
||||||
|
get() = HEXAColor(0x1E90FFFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Firebrick - A dark red brick color (#B22222).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.firebrick
|
||||||
|
get() = HEXAColor(0xB22222FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Floral White - A very pale cream color (#FFFAF0).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.floralwhite
|
||||||
|
get() = HEXAColor(0xFFFAF0FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forest Green - A medium forest green color (#228B22).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.forestgreen
|
||||||
|
get() = HEXAColor(0x228B22FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fuchsia - A vivid magenta color (#FF00FF).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.fuchsia
|
||||||
|
get() = HEXAColor(0xFF00FFFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gainsboro - A light gray color (#DCDCDC).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.gainsboro
|
||||||
|
get() = HEXAColor(0xDCDCDCFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ghost White - A very pale blue-white color (#F8F8FF).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.ghostwhite
|
||||||
|
get() = HEXAColor(0xF8F8FFFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gold - A bright golden yellow color (#FFD700).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.gold
|
||||||
|
get() = HEXAColor(0xFFD700FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Goldenrod - A golden yellow color (#DAA520).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.goldenrod
|
||||||
|
get() = HEXAColor(0xDAA520FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gray - A medium gray color (#808080).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.gray
|
||||||
|
get() = HEXAColor(0x808080FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Green - A pure green color (#008000).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.green
|
||||||
|
get() = HEXAColor(0x008000FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Green Yellow - A bright yellow-green color (#ADFF2F).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.greenyellow
|
||||||
|
get() = HEXAColor(0xADFF2FFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grey - A medium gray color (#808080).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.grey
|
||||||
|
get() = HEXAColor(0x808080FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Honeydew - A very pale green color (#F0FFF0).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.honeydew
|
||||||
|
get() = HEXAColor(0xF0FFF0FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hot Pink - A vibrant pink color (#FF69B4).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.hotpink
|
||||||
|
get() = HEXAColor(0xFF69B4FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indian Red - A medium red color (#CD5C5C).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.indianred
|
||||||
|
get() = HEXAColor(0xCD5C5CFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indigo - A deep blue-violet color (#4B0082).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.indigo
|
||||||
|
get() = HEXAColor(0x4B0082FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ivory - A very pale cream color (#FFFFF0).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.ivory
|
||||||
|
get() = HEXAColor(0xFFFFF0FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Khaki - A light tan color (#F0E68C).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.khaki
|
||||||
|
get() = HEXAColor(0xF0E68CFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lavender - A pale purple color (#E6E6FA).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.lavender
|
||||||
|
get() = HEXAColor(0xE6E6FAFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lavender Blush - A very pale pink color (#FFF0F5).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.lavenderblush
|
||||||
|
get() = HEXAColor(0xFFF0F5FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lawn Green - A bright chartreuse green color (#7CFC00).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.lawngreen
|
||||||
|
get() = HEXAColor(0x7CFC00FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lemon Chiffon - A very pale yellow color (#FFFACD).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.lemonchiffon
|
||||||
|
get() = HEXAColor(0xFFFACDFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Light Blue - A light blue color (#ADD8E6).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.lightblue
|
||||||
|
get() = HEXAColor(0xADD8E6FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Light Coral - A light coral pink color (#F08080).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.lightcoral
|
||||||
|
get() = HEXAColor(0xF08080FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Light Cyan - A very pale cyan color (#E0FFFF).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.lightcyan
|
||||||
|
get() = HEXAColor(0xE0FFFFFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Light Goldenrod Yellow - A pale yellow color (#FAFAD2).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.lightgoldenrodyellow
|
||||||
|
get() = HEXAColor(0xFAFAD2FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Light Gray - A light gray color (#D3D3D3).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.lightgray
|
||||||
|
get() = HEXAColor(0xD3D3D3FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Light Green - A light green color (#90EE90).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.lightgreen
|
||||||
|
get() = HEXAColor(0x90EE90FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Light Grey - A light gray color (#D3D3D3).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.lightgrey
|
||||||
|
get() = HEXAColor(0xD3D3D3FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Light Pink - A light pink color (#FFB6C1).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.lightpink
|
||||||
|
get() = HEXAColor(0xFFB6C1FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Light Salmon - A light salmon color (#FFA07A).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.lightsalmon
|
||||||
|
get() = HEXAColor(0xFFA07AFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Light Sea Green - A medium sea green color (#20B2AA).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.lightseagreen
|
||||||
|
get() = HEXAColor(0x20B2AAFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Light Sky Blue - A light sky blue color (#87CEFA).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.lightskyblue
|
||||||
|
get() = HEXAColor(0x87CEFAFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Light Slate Gray - A light slate gray color (#778899).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.lightslategray
|
||||||
|
get() = HEXAColor(0x778899FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Light Slate Grey - A light slate gray color (#778899).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.lightslategrey
|
||||||
|
get() = HEXAColor(0x778899FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Light Steel Blue - A light steel blue color (#B0C4DE).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.lightsteelblue
|
||||||
|
get() = HEXAColor(0xB0C4DEFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Light Yellow - A very pale yellow color (#FFFFE0).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.lightyellow
|
||||||
|
get() = HEXAColor(0xFFFFE0FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lime - A bright lime green color (#00FF00).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.lime
|
||||||
|
get() = HEXAColor(0x00FF00FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lime Green - A lime green color (#32CD32).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.limegreen
|
||||||
|
get() = HEXAColor(0x32CD32FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Linen - A pale beige color (#FAF0E6).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.linen
|
||||||
|
get() = HEXAColor(0xFAF0E6FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Magenta - A bright magenta color (#FF00FF).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.magenta
|
||||||
|
get() = HEXAColor(0xFF00FFFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maroon - A dark reddish-brown color (#800000).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.maroon
|
||||||
|
get() = HEXAColor(0x800000FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Medium Aquamarine - A medium aquamarine color (#66CDAA).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.mediumaquamarine
|
||||||
|
get() = HEXAColor(0x66CDAAFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Medium Blue - A medium blue color (#0000CD).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.mediumblue
|
||||||
|
get() = HEXAColor(0x0000CDFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Medium Orchid - A medium orchid purple color (#BA55D3).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.mediumorchid
|
||||||
|
get() = HEXAColor(0xBA55D3FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Medium Purple - A medium purple color (#9370DB).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.mediumpurple
|
||||||
|
get() = HEXAColor(0x9370DBFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Medium Sea Green - A medium sea green color (#3CB371).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.mediumseagreen
|
||||||
|
get() = HEXAColor(0x3CB371FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Medium Slate Blue - A medium slate blue color (#7B68EE).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.mediumslateblue
|
||||||
|
get() = HEXAColor(0x7B68EEFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Medium Spring Green - A medium spring green color (#00FA9A).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.mediumspringgreen
|
||||||
|
get() = HEXAColor(0x00FA9AFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Medium Turquoise - A medium turquoise color (#48D1CC).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.mediumturquoise
|
||||||
|
get() = HEXAColor(0x48D1CCFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Medium Violet Red - A medium violet-red color (#C71585).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.mediumvioletred
|
||||||
|
get() = HEXAColor(0xC71585FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Midnight Blue - A very dark blue color (#191970).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.midnightblue
|
||||||
|
get() = HEXAColor(0x191970FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mint Cream - A very pale mint color (#F5FFFA).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.mintcream
|
||||||
|
get() = HEXAColor(0xF5FFFAFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Misty Rose - A very pale pink color (#FFE4E1).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.mistyrose
|
||||||
|
get() = HEXAColor(0xFFE4E1FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moccasin - A pale peach color (#FFE4B5).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.moccasin
|
||||||
|
get() = HEXAColor(0xFFE4B5FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navajo White - A pale peach color (#FFDEAD).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.navajowhite
|
||||||
|
get() = HEXAColor(0xFFDEADFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navy - A very dark blue color (#000080).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.navy
|
||||||
|
get() = HEXAColor(0x000080FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Old Lace - A very pale cream color (#FDF5E6).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.oldlace
|
||||||
|
get() = HEXAColor(0xFDF5E6FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Olive - A dark yellowish-green color (#808000).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.olive
|
||||||
|
get() = HEXAColor(0x808000FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Olive Drab - A dark olive green color (#6B8E23).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.olivedrab
|
||||||
|
get() = HEXAColor(0x6B8E23FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Orange - A bright orange color (#FFA500).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.orange
|
||||||
|
get() = HEXAColor(0xFFA500FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Orange Red - A bright red-orange color (#FF4500).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.orangered
|
||||||
|
get() = HEXAColor(0xFF4500FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Orchid - A medium orchid purple color (#DA70D6).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.orchid
|
||||||
|
get() = HEXAColor(0xDA70D6FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pale Goldenrod - A pale goldenrod yellow color (#EEE8AA).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.palegoldenrod
|
||||||
|
get() = HEXAColor(0xEEE8AAFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pale Green - A pale green color (#98FB98).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.palegreen
|
||||||
|
get() = HEXAColor(0x98FB98FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pale Turquoise - A pale turquoise color (#AFEEEE).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.paleturquoise
|
||||||
|
get() = HEXAColor(0xAFEEEEFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pale Violet Red - A medium violet-red color (#DB7093).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.palevioletred
|
||||||
|
get() = HEXAColor(0xDB7093FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Papaya Whip - A pale peach color (#FFEFD5).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.papayawhip
|
||||||
|
get() = HEXAColor(0xFFEFD5FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Peach Puff - A light peach color (#FFDAB9).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.peachpuff
|
||||||
|
get() = HEXAColor(0xFFDAB9FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Peru - A medium brown color (#CD853F).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.peru
|
||||||
|
get() = HEXAColor(0xCD853FFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pink - A light pink color (#FFC0CB).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.pink
|
||||||
|
get() = HEXAColor(0xFFC0CBFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plum - A medium purple color (#DDA0DD).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.plum
|
||||||
|
get() = HEXAColor(0xDDA0DDFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Powder Blue - A light blue color (#B0E0E6).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.powderblue
|
||||||
|
get() = HEXAColor(0xB0E0E6FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Purple - A pure purple color (#800080).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.purple
|
||||||
|
get() = HEXAColor(0x800080FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Red - Pure red color (#FF0000).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.red
|
||||||
|
get() = HEXAColor(0xFF0000FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rosy Brown - A rosy brown color (#BC8F8F).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.rosybrown
|
||||||
|
get() = HEXAColor(0xBC8F8FFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Royal Blue - A vibrant royal blue color (#4169E1).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.royalblue
|
||||||
|
get() = HEXAColor(0x4169E1FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saddle Brown - A dark brown color (#8B4513).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.saddlebrown
|
||||||
|
get() = HEXAColor(0x8B4513FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Salmon - A light salmon pink color (#FA8072).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.salmon
|
||||||
|
get() = HEXAColor(0xFA8072FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sandy Brown - A sandy brown color (#F4A460).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.sandybrown
|
||||||
|
get() = HEXAColor(0xF4A460FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sea Green - A dark sea green color (#2E8B57).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.seagreen
|
||||||
|
get() = HEXAColor(0x2E8B57FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seashell - A very pale pink-orange color (#FFF5EE).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.seashell
|
||||||
|
get() = HEXAColor(0xFFF5EEFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sienna - A reddish-brown color (#A0522D).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.sienna
|
||||||
|
get() = HEXAColor(0xA0522DFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Silver - A light gray-silver color (#C0C0C0).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.silver
|
||||||
|
get() = HEXAColor(0xC0C0C0FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sky Blue - A light sky blue color (#87CEEB).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.skyblue
|
||||||
|
get() = HEXAColor(0x87CEEBFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slate Blue - A medium slate blue color (#6A5ACD).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.slateblue
|
||||||
|
get() = HEXAColor(0x6A5ACDFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slate Gray - A slate gray color (#708090).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.slategray
|
||||||
|
get() = HEXAColor(0x708090FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slate Grey - A slate gray color (#708090).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.slategrey
|
||||||
|
get() = HEXAColor(0x708090FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Snow - A very pale pinkish-white color (#FFFAFA).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.snow
|
||||||
|
get() = HEXAColor(0xFFFAFAFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring Green - A bright spring green color (#00FF7F).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.springgreen
|
||||||
|
get() = HEXAColor(0x00FF7FFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Steel Blue - A medium steel blue color (#4682B4).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.steelblue
|
||||||
|
get() = HEXAColor(0x4682B4FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tan - A light brown tan color (#D2B48C).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.tan
|
||||||
|
get() = HEXAColor(0xD2B48CFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Teal - A dark cyan-blue color (#008080).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.teal
|
||||||
|
get() = HEXAColor(0x008080FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thistle - A light purple-pink color (#D8BFD8).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.thistle
|
||||||
|
get() = HEXAColor(0xD8BFD8FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tomato - A vibrant red-orange color (#FF6347).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.tomato
|
||||||
|
get() = HEXAColor(0xFF6347FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turquoise - A medium turquoise color (#40E0D0).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.turquoise
|
||||||
|
get() = HEXAColor(0x40E0D0FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Violet - A violet color (#EE82EE).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.violet
|
||||||
|
get() = HEXAColor(0xEE82EEFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wheat - A light tan color (#F5DEB3).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.wheat
|
||||||
|
get() = HEXAColor(0xF5DEB3FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* White - Pure white color (#FFFFFF).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.white
|
||||||
|
get() = HEXAColor(0xFFFFFFFFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* White Smoke - A very light gray color (#F5F5F5).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.whitesmoke
|
||||||
|
get() = HEXAColor(0xF5F5F5FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yellow - Pure yellow color (#FFFF00).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.yellow
|
||||||
|
get() = HEXAColor(0xFFFF00FFu)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yellow Green - A medium yellow-green color (#9ACD32).
|
||||||
|
*/
|
||||||
|
val HEXAColor.Companion.yellowgreen
|
||||||
|
get() = HEXAColor(0x9ACD32FFu)
|
||||||
+21
-4
@@ -1,23 +1,40 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id "org.jetbrains.kotlin.multiplatform"
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
id "org.jetbrains.kotlin.plugin.serialization"
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
id "com.android.library"
|
id "com.android.kotlin.multiplatform.library"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
sourceSets {
|
sourceSets {
|
||||||
jvmMain {
|
commonMain {
|
||||||
|
dependencies {
|
||||||
|
api libs.klock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jvmAndAndroidMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
api project(":micro_utils.coroutines")
|
api project(":micro_utils.coroutines")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
androidMain {
|
androidMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
api project(":micro_utils.coroutines")
|
|
||||||
api libs.android.fragment
|
api libs.android.fragment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nativeMain {
|
||||||
|
dependencies {
|
||||||
|
api libs.okio
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wasmJsMain {
|
||||||
|
dependencies {
|
||||||
|
api libs.kotlinx.browser
|
||||||
|
api libs.kt.coroutines
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,17 +1,19 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id "org.jetbrains.kotlin.multiplatform"
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
id "org.jetbrains.kotlin.plugin.serialization"
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
id "com.android.library"
|
id "com.android.kotlin.multiplatform.library"
|
||||||
alias(libs.plugins.jb.compose)
|
alias(libs.plugins.jb.compose)
|
||||||
|
alias(libs.plugins.kt.jb.compose)
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationAndComposePresetPath"
|
apply from: "$mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
api project(":micro_utils.common")
|
api project(":micro_utils.common")
|
||||||
|
api libs.kt.coroutines
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
<manifest/>
|
||||||
+74
@@ -0,0 +1,74 @@
|
|||||||
|
package dev.inmo.micro_utils.common.compose
|
||||||
|
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import dev.inmo.micro_utils.common.Optional
|
||||||
|
import dev.inmo.micro_utils.common.dataOrThrow
|
||||||
|
import dev.inmo.micro_utils.common.optional
|
||||||
|
|
||||||
|
class LoadableComponentContext<T> internal constructor(
|
||||||
|
presetOptional: Optional<T>,
|
||||||
|
) {
|
||||||
|
internal val iterationState: MutableState<Int> = mutableStateOf(0)
|
||||||
|
|
||||||
|
internal var dataOptional: Optional<T> = if (presetOptional.dataPresented) presetOptional else Optional.absent()
|
||||||
|
private set
|
||||||
|
internal val dataState: MutableState<Optional<T>> = mutableStateOf(dataOptional)
|
||||||
|
|
||||||
|
fun reload() {
|
||||||
|
iterationState.value++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Showing data with ability to reload data
|
||||||
|
*
|
||||||
|
* [block] will be shown when [loader] will complete loading. If you want to reload data, just call
|
||||||
|
* [LoadableComponentContext.reload]
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun <T> LoadableComponent(
|
||||||
|
preload: Optional<T>,
|
||||||
|
loader: suspend LoadableComponentContext<T>.() -> T,
|
||||||
|
block: @Composable LoadableComponentContext<T>.(T) -> Unit
|
||||||
|
) {
|
||||||
|
val context = remember { LoadableComponentContext(preload) }
|
||||||
|
|
||||||
|
LaunchedEffect(context.iterationState.value) {
|
||||||
|
context.dataState.value = loader(context).optional
|
||||||
|
}
|
||||||
|
|
||||||
|
context.dataState.let {
|
||||||
|
if (it.value.dataPresented) {
|
||||||
|
context.block(it.value.dataOrThrow(IllegalStateException("Data must be presented, but optional has been changed by some way")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Showing data with ability to reload data
|
||||||
|
*
|
||||||
|
* [block] will be shown when [loader] will complete loading. If you want to reload data, just call
|
||||||
|
* [LoadableComponentContext.reload]
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun <T> LoadableComponent(
|
||||||
|
preload: T,
|
||||||
|
loader: suspend LoadableComponentContext<T>.() -> T,
|
||||||
|
block: @Composable LoadableComponentContext<T>.(T) -> Unit
|
||||||
|
) {
|
||||||
|
LoadableComponent(preload.optional, loader, block)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Showing data with ability to reload data
|
||||||
|
*
|
||||||
|
* [block] will be shown when [loader] will complete loading. If you want to reload data, just call
|
||||||
|
* [LoadableComponentContext.reload]
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun <T> LoadableComponent(
|
||||||
|
loader: suspend LoadableComponentContext<T>.() -> T,
|
||||||
|
block: @Composable LoadableComponentContext<T>.(T) -> Unit
|
||||||
|
) {
|
||||||
|
LoadableComponent(Optional.absent(), loader, block)
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
package dev.inmo.micro_utils.common.compose
|
||||||
|
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts current [MutableState] to immutable [State] using [derivedStateOf]
|
||||||
|
*/
|
||||||
|
fun <T> MutableState<T>.asState(): State<T> = derivedStateOf { this.value }
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
package dev.inmo.micro_utils.common.compose
|
||||||
|
|
||||||
|
import org.jetbrains.compose.web.dom.AttrBuilderContext
|
||||||
|
import org.w3c.dom.Element
|
||||||
|
|
||||||
|
operator fun <T : Element> AttrBuilderContext<T>?.plus(
|
||||||
|
other: AttrBuilderContext<T>?
|
||||||
|
) = when (this) {
|
||||||
|
null -> other ?: {}
|
||||||
|
else -> when (other) {
|
||||||
|
null -> this ?: {}
|
||||||
|
else -> {
|
||||||
|
{
|
||||||
|
invoke(this)
|
||||||
|
other(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package dev.inmo.micro_utils.common.compose
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
|
import androidx.compose.runtime.DisposableEffectResult
|
||||||
|
import androidx.compose.runtime.DisposableEffectScope
|
||||||
|
import org.jetbrains.compose.web.attributes.AttrsScope
|
||||||
|
import org.jetbrains.compose.web.dom.ElementScope
|
||||||
|
import org.w3c.dom.Element
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function must be called in the context of your tag content. It works like default [AttrsScope.ref],
|
||||||
|
* but able to be used several times. Uses [DisposableEffect] under the hood
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun <T : Element> ElementScope<T>.ref(
|
||||||
|
block: DisposableEffectScope.(T) -> DisposableEffectResult
|
||||||
|
) {
|
||||||
|
DisposableEffect(0) {
|
||||||
|
block(scopeElement)
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
package dev.inmo.micro_utils.common.compose
|
||||||
|
|
||||||
|
import org.jetbrains.compose.web.dom.AttrBuilderContext
|
||||||
|
|
||||||
|
fun tagClasses(vararg classnames: String): AttrBuilderContext<*> = {
|
||||||
|
classes(*classnames)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun tagId(id: String): AttrBuilderContext<*> = {
|
||||||
|
id(id)
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.test.ExperimentalTestApi
|
||||||
|
import androidx.compose.ui.test.runComposeUiTest
|
||||||
|
import dev.inmo.micro_utils.common.compose.LoadableComponent
|
||||||
|
import dev.inmo.micro_utils.coroutines.MutableRedeliverStateFlow
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import org.jetbrains.annotations.TestOnly
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
class LoadableComponentTests {
|
||||||
|
@OptIn(ExperimentalTestApi::class)
|
||||||
|
@Test
|
||||||
|
@TestOnly
|
||||||
|
fun testSimpleLoad() = runComposeUiTest {
|
||||||
|
val loadingFlow = MutableRedeliverStateFlow<Int>(0)
|
||||||
|
val loadedFlow = MutableRedeliverStateFlow<Int>(0)
|
||||||
|
setContent {
|
||||||
|
LoadableComponent<Int>({
|
||||||
|
loadingFlow.filter { it == 1 }.first()
|
||||||
|
}) {
|
||||||
|
assert(dataState.value.data == 1)
|
||||||
|
remember {
|
||||||
|
loadedFlow.value = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForIdle()
|
||||||
|
|
||||||
|
assertTrue(loadedFlow.value == 0)
|
||||||
|
|
||||||
|
loadingFlow.value = 1
|
||||||
|
|
||||||
|
waitForIdle()
|
||||||
|
|
||||||
|
assertTrue(loadedFlow.value == 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
<manifest package="dev.inmo.micro_utils.common.compose"/>
|
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,23 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Breaks this list into a list of consecutive pairs.
|
||||||
|
* Each element is paired with the next element in the list.
|
||||||
|
* For a list of size n, the result will contain n-1 pairs.
|
||||||
|
*
|
||||||
|
* Example: `[1, 2, 3, 4].breakAsPairs()` returns `[(1, 2), (2, 3), (3, 4)]`
|
||||||
|
*
|
||||||
|
* @param T The type of elements in the list
|
||||||
|
* @return A list of pairs where each pair consists of consecutive elements
|
||||||
|
*/
|
||||||
|
fun <T> List<T>.breakAsPairs(): List<Pair<T, T>> {
|
||||||
|
val result = mutableListOf<Pair<T, T>>()
|
||||||
|
|
||||||
|
for (i in 0 until size - 1) {
|
||||||
|
val first = get(i)
|
||||||
|
val second = get(i + 1)
|
||||||
|
result.add(first to second)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates simple [Comparator] which will use [compareTo] of [T] for both objects
|
||||||
|
*/
|
||||||
|
fun <T : Comparable<C>, C : T> T.createComparator() = Comparator<C> { o1, o2 -> o1.compareTo(o2) }
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import korlibs.time.DateTime
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.Serializer
|
||||||
|
import kotlinx.serialization.builtins.serializer
|
||||||
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes [DateTime] as its raw [DateTime.unixMillis] and deserializes in the same way
|
||||||
|
*/
|
||||||
|
object DateTimeSerializer : KSerializer<DateTime> {
|
||||||
|
override val descriptor: SerialDescriptor
|
||||||
|
get() = Double.serializer().descriptor
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): DateTime {
|
||||||
|
return DateTime(decoder.decodeDouble())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: DateTime) {
|
||||||
|
encoder.encodeDouble(value.unixMillis)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,11 +2,13 @@
|
|||||||
|
|
||||||
package dev.inmo.micro_utils.common
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
private inline fun <T> getObject(
|
private inline fun <T> getObject(
|
||||||
additional: MutableList<T>,
|
additional: MutableList<T>,
|
||||||
iterator: Iterator<T>
|
iterator: Iterator<T>
|
||||||
): T? = when {
|
): T? = when {
|
||||||
additional.isNotEmpty() -> additional.removeFirst()
|
additional.isNotEmpty() -> additional.removeAt(0)
|
||||||
iterator.hasNext() -> iterator.next()
|
iterator.hasNext() -> iterator.next()
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
@@ -24,14 +26,23 @@ private inline fun <T> getObject(
|
|||||||
*
|
*
|
||||||
* @see calculateDiff
|
* @see calculateDiff
|
||||||
*/
|
*/
|
||||||
data class Diff<T> internal constructor(
|
@Serializable
|
||||||
val removed: List<IndexedValue<T>>,
|
data class Diff<T> @Warning(warning) constructor(
|
||||||
|
val removed: List<@Serializable(IndexedValueSerializer::class) IndexedValue<T>>,
|
||||||
/**
|
/**
|
||||||
* Old-New values pairs
|
* Old-New values pairs
|
||||||
*/
|
*/
|
||||||
val replaced: List<Pair<IndexedValue<T>, IndexedValue<T>>>,
|
val replaced: List<Pair<@Serializable(IndexedValueSerializer::class) IndexedValue<T>, @Serializable(IndexedValueSerializer::class) IndexedValue<T>>>,
|
||||||
val added: List<IndexedValue<T>>
|
val added: List<@Serializable(IndexedValueSerializer::class) IndexedValue<T>>
|
||||||
)
|
) {
|
||||||
|
fun isEmpty(): Boolean = removed.isEmpty() && replaced.isEmpty() && added.isEmpty()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val warning = "This feature can be changed without any warranties. Use with caution and only in case you know what you are doing"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> emptyDiff(): Diff<T> = Diff(emptyList(), emptyList(), emptyList())
|
||||||
|
|
||||||
private inline fun <T> performChanges(
|
private inline fun <T> performChanges(
|
||||||
potentialChanges: MutableList<Pair<IndexedValue<T>?, IndexedValue<T>?>>,
|
potentialChanges: MutableList<Pair<IndexedValue<T>?, IndexedValue<T>?>>,
|
||||||
@@ -40,14 +51,14 @@ private inline fun <T> performChanges(
|
|||||||
changedList: MutableList<Pair<IndexedValue<T>, IndexedValue<T>>>,
|
changedList: MutableList<Pair<IndexedValue<T>, IndexedValue<T>>>,
|
||||||
removedList: MutableList<IndexedValue<T>>,
|
removedList: MutableList<IndexedValue<T>>,
|
||||||
addedList: MutableList<IndexedValue<T>>,
|
addedList: MutableList<IndexedValue<T>>,
|
||||||
strictComparison: Boolean
|
comparisonFun: (T?, T?) -> Boolean
|
||||||
) {
|
) {
|
||||||
var i = -1
|
var i = -1
|
||||||
val (oldObject, newObject) = potentialChanges.lastOrNull() ?: return
|
val (oldObject, newObject) = potentialChanges.lastOrNull() ?: return
|
||||||
for ((old, new) in potentialChanges.take(potentialChanges.size - 1)) {
|
for ((old, new) in potentialChanges.take(potentialChanges.size - 1)) {
|
||||||
i++
|
i++
|
||||||
val oldOneEqualToNewObject = old ?.value === newObject ?.value || (old ?.value == newObject ?.value && !strictComparison)
|
val oldOneEqualToNewObject = comparisonFun(old ?.value, newObject ?.value)
|
||||||
val newOneEqualToOldObject = new ?.value === oldObject ?.value || (new ?.value == oldObject ?.value && !strictComparison)
|
val newOneEqualToOldObject = comparisonFun(new ?.value, oldObject ?.value)
|
||||||
if (oldOneEqualToNewObject || newOneEqualToOldObject) {
|
if (oldOneEqualToNewObject || newOneEqualToOldObject) {
|
||||||
changedList.addAll(
|
changedList.addAll(
|
||||||
potentialChanges.take(i).mapNotNull {
|
potentialChanges.take(i).mapNotNull {
|
||||||
@@ -101,7 +112,7 @@ private inline fun <T> performChanges(
|
|||||||
*/
|
*/
|
||||||
fun <T> Iterable<T>.calculateDiff(
|
fun <T> Iterable<T>.calculateDiff(
|
||||||
other: Iterable<T>,
|
other: Iterable<T>,
|
||||||
strictComparison: Boolean = false
|
comparisonFun: (T?, T?) -> Boolean
|
||||||
): Diff<T> {
|
): Diff<T> {
|
||||||
var i = -1
|
var i = -1
|
||||||
var j = -1
|
var j = -1
|
||||||
@@ -129,7 +140,7 @@ fun <T> Iterable<T>.calculateDiff(
|
|||||||
}
|
}
|
||||||
|
|
||||||
when {
|
when {
|
||||||
oldObject === newObject || (oldObject == newObject && !strictComparison) -> {
|
comparisonFun(oldObject, newObject) -> {
|
||||||
changedObjects.addAll(potentiallyChangedObjects.map {
|
changedObjects.addAll(potentiallyChangedObjects.map {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
it as Pair<IndexedValue<T>, IndexedValue<T>>
|
it as Pair<IndexedValue<T>, IndexedValue<T>>
|
||||||
@@ -140,23 +151,49 @@ fun <T> Iterable<T>.calculateDiff(
|
|||||||
potentiallyChangedObjects.add(oldObject ?.let { IndexedValue(i, oldObject) } to newObject ?.let { IndexedValue(j, newObject) })
|
potentiallyChangedObjects.add(oldObject ?.let { IndexedValue(i, oldObject) } to newObject ?.let { IndexedValue(j, newObject) })
|
||||||
val previousOldsAdditionsSize = additionalInOld.size
|
val previousOldsAdditionsSize = additionalInOld.size
|
||||||
val previousNewsAdditionsSize = additionalInNew.size
|
val previousNewsAdditionsSize = additionalInNew.size
|
||||||
performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, strictComparison)
|
performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, comparisonFun)
|
||||||
i -= (additionalInOld.size - previousOldsAdditionsSize)
|
i -= (additionalInOld.size - previousOldsAdditionsSize)
|
||||||
j -= (additionalInNew.size - previousNewsAdditionsSize)
|
j -= (additionalInNew.size - previousNewsAdditionsSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
potentiallyChangedObjects.add(null to null)
|
potentiallyChangedObjects.add(null to null)
|
||||||
performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, strictComparison)
|
performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, comparisonFun)
|
||||||
|
|
||||||
return Diff(removedObjects.toList(), changedObjects.toList(), addedObjects.toList())
|
return Diff(removedObjects.toList(), changedObjects.toList(), addedObjects.toList())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculating [Diff] object
|
||||||
|
*
|
||||||
|
* @param strictComparison If this parameter set to true, objects which are not equal by links will be used as different
|
||||||
|
* objects. For example, in case of two "Example" string they will be equal by value, but CAN be different by links
|
||||||
|
*/
|
||||||
|
fun <T> Iterable<T>.calculateDiff(
|
||||||
|
other: Iterable<T>,
|
||||||
|
strictComparison: Boolean = false
|
||||||
|
): Diff<T> = calculateDiff(
|
||||||
|
other,
|
||||||
|
comparisonFun = if (strictComparison) {
|
||||||
|
{ t1, t2 ->
|
||||||
|
t1 === t2
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
{ t1, t2 ->
|
||||||
|
t1 === t2 || t1 == t2 // small optimization for cases when t1 and t2 are the same - comparison will be faster potentially
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
inline fun <T> Iterable<T>.diff(
|
inline fun <T> Iterable<T>.diff(
|
||||||
other: Iterable<T>,
|
other: Iterable<T>,
|
||||||
strictComparison: Boolean = false
|
strictComparison: Boolean = false
|
||||||
): Diff<T> = calculateDiff(other, strictComparison)
|
): Diff<T> = calculateDiff(other, strictComparison)
|
||||||
|
inline fun <T> Iterable<T>.diff(
|
||||||
|
other: Iterable<T>,
|
||||||
|
noinline comparisonFun: (T?, T?) -> Boolean
|
||||||
|
): Diff<T> = calculateDiff(other, comparisonFun)
|
||||||
|
|
||||||
inline fun <T> Diff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new)
|
inline fun <T> Diff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new, strictComparison = false)
|
||||||
inline fun <T> StrictDiff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new, true)
|
inline fun <T> StrictDiff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new, true)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -166,6 +203,23 @@ inline fun <T> Iterable<T>.calculateStrictDiff(
|
|||||||
other: Iterable<T>
|
other: Iterable<T>
|
||||||
) = calculateDiff(other, strictComparison = true)
|
) = calculateDiff(other, strictComparison = true)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies [diff] to [this] [MutableList]
|
||||||
|
*/
|
||||||
|
fun <T> MutableList<T>.applyDiff(
|
||||||
|
diff: Diff<T>
|
||||||
|
) {
|
||||||
|
for (i in diff.removed.indices.sortedDescending()) {
|
||||||
|
removeAt(diff.removed[i].index)
|
||||||
|
}
|
||||||
|
diff.added.forEach { (i, t) ->
|
||||||
|
add(i, t)
|
||||||
|
}
|
||||||
|
diff.replaced.forEach { (_, new) ->
|
||||||
|
set(new.index, new.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method call [calculateDiff] with strict mode [strictComparison] and then apply differences to [this]
|
* This method call [calculateDiff] with strict mode [strictComparison] and then apply differences to [this]
|
||||||
* mutable list
|
* mutable list
|
||||||
@@ -174,13 +228,26 @@ fun <T> MutableList<T>.applyDiff(
|
|||||||
source: Iterable<T>,
|
source: Iterable<T>,
|
||||||
strictComparison: Boolean = false
|
strictComparison: Boolean = false
|
||||||
): Diff<T> = calculateDiff(source, strictComparison).also {
|
): Diff<T> = calculateDiff(source, strictComparison).also {
|
||||||
for (i in it.removed.indices.sortedDescending()) {
|
applyDiff(it)
|
||||||
removeAt(it.removed[i].index)
|
|
||||||
}
|
|
||||||
it.added.forEach { (i, t) ->
|
|
||||||
add(i, t)
|
|
||||||
}
|
|
||||||
it.replaced.forEach { (_, new) ->
|
|
||||||
set(new.index, new.value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method call [calculateDiff] and then apply differences to [this]
|
||||||
|
* mutable list
|
||||||
|
*/
|
||||||
|
fun <T> MutableList<T>.applyDiff(
|
||||||
|
source: Iterable<T>,
|
||||||
|
comparisonFun: (T?, T?) -> Boolean
|
||||||
|
): Diff<T> = calculateDiff(source, comparisonFun).also {
|
||||||
|
applyDiff(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse [this] [Diff]. Result will contain [Diff.added] on [Diff.removed] (and vice-verse), all the
|
||||||
|
* [Diff.replaced] values will be reversed too
|
||||||
|
*/
|
||||||
|
fun <T> Diff<T>.reversed() = Diff(
|
||||||
|
removed = added,
|
||||||
|
replaced = replaced.map { it.second to it.first },
|
||||||
|
added = removed
|
||||||
|
)
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the given [block] and returns its result if this Boolean is true, otherwise returns null.
|
||||||
|
*
|
||||||
|
* @param T The return type of the block
|
||||||
|
* @param block The function to execute if this Boolean is true
|
||||||
|
* @return The result of [block] if true, null otherwise
|
||||||
|
*/
|
||||||
|
inline fun <T> Boolean.letIfTrue(block: () -> T): T? {
|
||||||
|
return if (this) {
|
||||||
|
block()
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the given [block] and returns its result if this Boolean is false, otherwise returns null.
|
||||||
|
*
|
||||||
|
* @param T The return type of the block
|
||||||
|
* @param block The function to execute if this Boolean is false
|
||||||
|
* @return The result of [block] if false, null otherwise
|
||||||
|
*/
|
||||||
|
inline fun <T> Boolean.letIfFalse(block: () -> T): T? {
|
||||||
|
return if (this) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the given [block] if this Boolean is true and returns this Boolean.
|
||||||
|
* Similar to [also], but only executes the block when the Boolean is true.
|
||||||
|
*
|
||||||
|
* @param block The function to execute if this Boolean is true
|
||||||
|
* @return This Boolean value
|
||||||
|
*/
|
||||||
|
inline fun Boolean.alsoIfTrue(block: () -> Unit): Boolean {
|
||||||
|
letIfTrue(block)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the given [block] if this Boolean is false and returns this Boolean.
|
||||||
|
* Similar to [also], but only executes the block when the Boolean is false.
|
||||||
|
*
|
||||||
|
* @param block The function to execute if this Boolean is false
|
||||||
|
* @return This Boolean value
|
||||||
|
*/
|
||||||
|
inline fun Boolean.alsoIfFalse(block: () -> Unit): Boolean {
|
||||||
|
letIfFalse(block)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias for [letIfTrue]. Executes the given [block] and returns its result if this Boolean is true.
|
||||||
|
*
|
||||||
|
* @param T The return type of the block
|
||||||
|
* @param block The function to execute if this Boolean is true
|
||||||
|
* @return The result of [block] if true, null otherwise
|
||||||
|
*/
|
||||||
|
inline fun <T> Boolean.ifTrue(block: () -> T): T? {
|
||||||
|
return if (this) {
|
||||||
|
block()
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias for [letIfFalse]. Executes the given [block] and returns its result if this Boolean is false.
|
||||||
|
*
|
||||||
|
* @param T The return type of the block
|
||||||
|
* @param block The function to execute if this Boolean is false
|
||||||
|
* @return The result of [block] if false, null otherwise
|
||||||
|
*/
|
||||||
|
inline fun <T> Boolean.ifFalse(block: () -> T): T? {
|
||||||
|
return if (this) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.Serializer
|
||||||
|
import kotlinx.serialization.builtins.PairSerializer
|
||||||
|
import kotlinx.serialization.builtins.serializer
|
||||||
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
|
||||||
|
class IndexedValueSerializer<T>(private val subSerializer: KSerializer<T>) : KSerializer<IndexedValue<T>> {
|
||||||
|
private val originalSerializer = PairSerializer(Int.serializer(), subSerializer)
|
||||||
|
override val descriptor: SerialDescriptor
|
||||||
|
get() = originalSerializer.descriptor
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): IndexedValue<T> {
|
||||||
|
val pair = originalSerializer.deserialize(decoder)
|
||||||
|
return IndexedValue(
|
||||||
|
pair.first,
|
||||||
|
pair.second
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: IndexedValue<T>) {
|
||||||
|
originalSerializer.serialize(
|
||||||
|
encoder,
|
||||||
|
Pair(value.index, value.value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,19 +6,45 @@ import kotlinx.serialization.descriptors.SerialDescriptor
|
|||||||
import kotlinx.serialization.encoding.Decoder
|
import kotlinx.serialization.encoding.Decoder
|
||||||
import kotlinx.serialization.encoding.Encoder
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function type that allocates and returns a [ByteArray].
|
||||||
|
*/
|
||||||
typealias ByteArrayAllocator = () -> ByteArray
|
typealias ByteArrayAllocator = () -> ByteArray
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A suspending function type that allocates and returns a [ByteArray].
|
||||||
|
*/
|
||||||
typealias SuspendByteArrayAllocator = suspend () -> ByteArray
|
typealias SuspendByteArrayAllocator = suspend () -> ByteArray
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts this [ByteArray] to a [ByteArrayAllocator] that returns this array.
|
||||||
|
*/
|
||||||
val ByteArray.asAllocator: ByteArrayAllocator
|
val ByteArray.asAllocator: ByteArrayAllocator
|
||||||
get() = { this }
|
get() = { this }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts this [ByteArray] to a [SuspendByteArrayAllocator] that returns this array.
|
||||||
|
*/
|
||||||
val ByteArray.asSuspendAllocator: SuspendByteArrayAllocator
|
val ByteArray.asSuspendAllocator: SuspendByteArrayAllocator
|
||||||
get() = { this }
|
get() = { this }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts this [ByteArrayAllocator] to a [SuspendByteArrayAllocator].
|
||||||
|
*/
|
||||||
val ByteArrayAllocator.asSuspendAllocator: SuspendByteArrayAllocator
|
val ByteArrayAllocator.asSuspendAllocator: SuspendByteArrayAllocator
|
||||||
get() = { this() }
|
get() = { this() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts this [SuspendByteArrayAllocator] to a [ByteArrayAllocator] by invoking it and
|
||||||
|
* wrapping the result in a non-suspending allocator.
|
||||||
|
*/
|
||||||
suspend fun SuspendByteArrayAllocator.asAllocator(): ByteArrayAllocator {
|
suspend fun SuspendByteArrayAllocator.asAllocator(): ByteArrayAllocator {
|
||||||
return invoke().asAllocator
|
return invoke().asAllocator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializer for [ByteArrayAllocator]. Serializes the result of invoking the allocator.
|
||||||
|
*/
|
||||||
object ByteArrayAllocatorSerializer : KSerializer<ByteArrayAllocator> {
|
object ByteArrayAllocatorSerializer : KSerializer<ByteArrayAllocator> {
|
||||||
private val realSerializer = ByteArraySerializer()
|
private val realSerializer = ByteArraySerializer()
|
||||||
override val descriptor: SerialDescriptor = realSerializer.descriptor
|
override val descriptor: SerialDescriptor = realSerializer.descriptor
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
package dev.inmo.micro_utils.common
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first non-null element in this iterable.
|
||||||
|
*
|
||||||
|
* @param T The type of elements in the iterable (nullable)
|
||||||
|
* @return The first non-null element
|
||||||
|
* @throws NoSuchElementException if the iterable contains no non-null elements
|
||||||
|
*/
|
||||||
fun <T> Iterable<T?>.firstNotNull() = first { it != null }!!
|
fun <T> Iterable<T?>.firstNotNull() = first { it != null }!!
|
||||||
@@ -1,5 +1,19 @@
|
|||||||
package dev.inmo.micro_utils.common
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Joins elements of this iterable into a list with separators between elements.
|
||||||
|
* Each element is transformed using [transform], and separators are generated using [separatorFun].
|
||||||
|
* Optional [prefix] and [postfix] can be added to the result.
|
||||||
|
* Null values from transformations or separator function are skipped.
|
||||||
|
*
|
||||||
|
* @param I The type of elements in the input iterable
|
||||||
|
* @param R The type of elements in the result list
|
||||||
|
* @param separatorFun A function that generates a separator based on the current element
|
||||||
|
* @param prefix Optional prefix to add at the beginning of the result
|
||||||
|
* @param postfix Optional postfix to add at the end of the result
|
||||||
|
* @param transform A function to transform each element
|
||||||
|
* @return A list of transformed elements with separators
|
||||||
|
*/
|
||||||
inline fun <I, R> Iterable<I>.joinTo(
|
inline fun <I, R> Iterable<I>.joinTo(
|
||||||
separatorFun: (I) -> R?,
|
separatorFun: (I) -> R?,
|
||||||
prefix: R? = null,
|
prefix: R? = null,
|
||||||
@@ -25,6 +39,20 @@ inline fun <I, R> Iterable<I>.joinTo(
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Joins elements of this iterable into a list with a constant separator between elements.
|
||||||
|
* Each element is transformed using [transform].
|
||||||
|
* Optional [prefix] and [postfix] can be added to the result.
|
||||||
|
* Null values from transformations or separators are skipped.
|
||||||
|
*
|
||||||
|
* @param I The type of elements in the input iterable
|
||||||
|
* @param R The type of elements in the result list
|
||||||
|
* @param separator The separator to insert between elements
|
||||||
|
* @param prefix Optional prefix to add at the beginning of the result
|
||||||
|
* @param postfix Optional postfix to add at the end of the result
|
||||||
|
* @param transform A function to transform each element
|
||||||
|
* @return A list of transformed elements with separators
|
||||||
|
*/
|
||||||
inline fun <I, R> Iterable<I>.joinTo(
|
inline fun <I, R> Iterable<I>.joinTo(
|
||||||
separator: R? = null,
|
separator: R? = null,
|
||||||
prefix: R? = null,
|
prefix: R? = null,
|
||||||
@@ -32,18 +60,55 @@ inline fun <I, R> Iterable<I>.joinTo(
|
|||||||
transform: (I) -> R?
|
transform: (I) -> R?
|
||||||
): List<R> = joinTo({ separator }, prefix, postfix, transform)
|
): List<R> = joinTo({ separator }, prefix, postfix, transform)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Joins elements of this iterable into a list with separators between elements.
|
||||||
|
* Separators are generated using [separatorFun].
|
||||||
|
* Optional [prefix] and [postfix] can be added to the result.
|
||||||
|
* Null values from separator function are skipped.
|
||||||
|
*
|
||||||
|
* @param I The type of elements
|
||||||
|
* @param separatorFun A function that generates a separator based on the current element
|
||||||
|
* @param prefix Optional prefix to add at the beginning of the result
|
||||||
|
* @param postfix Optional postfix to add at the end of the result
|
||||||
|
* @return A list of elements with separators
|
||||||
|
*/
|
||||||
inline fun <I> Iterable<I>.joinTo(
|
inline fun <I> Iterable<I>.joinTo(
|
||||||
separatorFun: (I) -> I?,
|
separatorFun: (I) -> I?,
|
||||||
prefix: I? = null,
|
prefix: I? = null,
|
||||||
postfix: I? = null
|
postfix: I? = null
|
||||||
): List<I> = joinTo<I, I>(separatorFun, prefix, postfix) { it }
|
): List<I> = joinTo<I, I>(separatorFun, prefix, postfix) { it }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Joins elements of this iterable into a list with a constant separator between elements.
|
||||||
|
* Optional [prefix] and [postfix] can be added to the result.
|
||||||
|
* Null separators are skipped.
|
||||||
|
*
|
||||||
|
* @param I The type of elements
|
||||||
|
* @param separator The separator to insert between elements
|
||||||
|
* @param prefix Optional prefix to add at the beginning of the result
|
||||||
|
* @param postfix Optional postfix to add at the end of the result
|
||||||
|
* @return A list of elements with separators
|
||||||
|
*/
|
||||||
inline fun <I> Iterable<I>.joinTo(
|
inline fun <I> Iterable<I>.joinTo(
|
||||||
separator: I? = null,
|
separator: I? = null,
|
||||||
prefix: I? = null,
|
prefix: I? = null,
|
||||||
postfix: I? = null
|
postfix: I? = null
|
||||||
): List<I> = joinTo<I>({ separator }, prefix, postfix)
|
): List<I> = joinTo<I>({ separator }, prefix, postfix)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Joins elements of this array into an array with separators between elements.
|
||||||
|
* Each element is transformed using [transform], and separators are generated using [separatorFun].
|
||||||
|
* Optional [prefix] and [postfix] can be added to the result.
|
||||||
|
* Null values from transformations or separator function are skipped.
|
||||||
|
*
|
||||||
|
* @param I The type of elements in the input array
|
||||||
|
* @param R The type of elements in the result array
|
||||||
|
* @param separatorFun A function that generates a separator based on the current element
|
||||||
|
* @param prefix Optional prefix to add at the beginning of the result
|
||||||
|
* @param postfix Optional postfix to add at the end of the result
|
||||||
|
* @param transform A function to transform each element
|
||||||
|
* @return An array of transformed elements with separators
|
||||||
|
*/
|
||||||
inline fun <I, reified R> Array<I>.joinTo(
|
inline fun <I, reified R> Array<I>.joinTo(
|
||||||
separatorFun: (I) -> R?,
|
separatorFun: (I) -> R?,
|
||||||
prefix: R? = null,
|
prefix: R? = null,
|
||||||
@@ -51,6 +116,20 @@ inline fun <I, reified R> Array<I>.joinTo(
|
|||||||
transform: (I) -> R?
|
transform: (I) -> R?
|
||||||
): Array<R> = asIterable().joinTo(separatorFun, prefix, postfix, transform).toTypedArray()
|
): Array<R> = asIterable().joinTo(separatorFun, prefix, postfix, transform).toTypedArray()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Joins elements of this array into an array with a constant separator between elements.
|
||||||
|
* Each element is transformed using [transform].
|
||||||
|
* Optional [prefix] and [postfix] can be added to the result.
|
||||||
|
* Null values from transformations or separators are skipped.
|
||||||
|
*
|
||||||
|
* @param I The type of elements in the input array
|
||||||
|
* @param R The type of elements in the result array
|
||||||
|
* @param separator The separator to insert between elements
|
||||||
|
* @param prefix Optional prefix to add at the beginning of the result
|
||||||
|
* @param postfix Optional postfix to add at the end of the result
|
||||||
|
* @param transform A function to transform each element
|
||||||
|
* @return An array of transformed elements with separators
|
||||||
|
*/
|
||||||
inline fun <I, reified R> Array<I>.joinTo(
|
inline fun <I, reified R> Array<I>.joinTo(
|
||||||
separator: R? = null,
|
separator: R? = null,
|
||||||
prefix: R? = null,
|
prefix: R? = null,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import kotlin.jvm.JvmInline
|
|||||||
@JvmInline
|
@JvmInline
|
||||||
value class FileName(val string: String) {
|
value class FileName(val string: String) {
|
||||||
val name: String
|
val name: String
|
||||||
get() = withoutSlashAtTheEnd.takeLastWhile { it != '/' }
|
get() = withoutSlashAtTheEnd.takeLastWhile { it != MPPFilePathSeparator }
|
||||||
val extension: String
|
val extension: String
|
||||||
get() = name.takeLastWhile { it != '.' }
|
get() = name.takeLastWhile { it != '.' }
|
||||||
val nameWithoutExtension: String
|
val nameWithoutExtension: String
|
||||||
@@ -18,7 +18,7 @@ value class FileName(val string: String) {
|
|||||||
} ?: filename
|
} ?: filename
|
||||||
}
|
}
|
||||||
val withoutSlashAtTheEnd: String
|
val withoutSlashAtTheEnd: String
|
||||||
get() = string.dropLastWhile { it == '/' }
|
get() = string.dropLastWhile { it == MPPFilePathSeparator }
|
||||||
override fun toString(): String = string
|
override fun toString(): String = string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,6 +26,7 @@ value class FileName(val string: String) {
|
|||||||
expect class MPPFile
|
expect class MPPFile
|
||||||
|
|
||||||
expect val MPPFile.filename: FileName
|
expect val MPPFile.filename: FileName
|
||||||
|
expect val MPPFilePathSeparator: Char
|
||||||
expect val MPPFile.filesize: Long
|
expect val MPPFile.filesize: Long
|
||||||
expect val MPPFile.bytesAllocatorSync: ByteArrayAllocator
|
expect val MPPFile.bytesAllocatorSync: ByteArrayAllocator
|
||||||
expect val MPPFile.bytesAllocator: SuspendByteArrayAllocator
|
expect val MPPFile.bytesAllocator: SuspendByteArrayAllocator
|
||||||
|
|||||||
@@ -0,0 +1,133 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains diff based on the comparison of objects with the same [K].
|
||||||
|
*
|
||||||
|
* @param removed Contains map with keys removed from parent map
|
||||||
|
* @param changed Contains map with keys values changed new map in comparison with old one
|
||||||
|
* @param added Contains map with new keys and values
|
||||||
|
*/
|
||||||
|
data class MapDiff<K, V> @Warning(warning) constructor(
|
||||||
|
val removed: Map<K, V>,
|
||||||
|
val changed: Map<K, Pair<V, V>>,
|
||||||
|
val added: Map<K, V>
|
||||||
|
) {
|
||||||
|
fun isEmpty() = removed.isEmpty() && changed.isEmpty() && added.isEmpty()
|
||||||
|
inline fun isNotEmpty() = !isEmpty()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val warning = "This feature can be changed without any warranties. Use with caution and only in case you know what you are doing"
|
||||||
|
fun <K, V> empty() = MapDiff<K, V>(emptyMap(), emptyMap(), emptyMap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun <K, V> createCompareFun(
|
||||||
|
strictComparison: Boolean
|
||||||
|
): (K, V, V) -> Boolean = if (strictComparison) {
|
||||||
|
{ _, first, second -> first === second }
|
||||||
|
} else {
|
||||||
|
{ _, first, second -> first == second }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare [this] [Map] with the [other] one in principle when [other] is newer than [this]
|
||||||
|
*
|
||||||
|
* @param compareFun Will be used to determine changed values
|
||||||
|
*/
|
||||||
|
fun <K, V> Map<K, V>.diff(
|
||||||
|
other: Map<K, V>,
|
||||||
|
compareFun: (K, V, V) -> Boolean
|
||||||
|
): MapDiff<K, V> {
|
||||||
|
val removed: Map<K, V> = (keys - other.keys).associateWith {
|
||||||
|
getValue(it)
|
||||||
|
}
|
||||||
|
val added: Map<K, V> = (other.keys - keys).associateWith {
|
||||||
|
other.getValue(it)
|
||||||
|
}
|
||||||
|
val changed = keys.intersect(other.keys).mapNotNull {
|
||||||
|
val old = getValue(it)
|
||||||
|
val new = other.getValue(it)
|
||||||
|
if (compareFun(it, old, new)) {
|
||||||
|
return@mapNotNull null
|
||||||
|
} else {
|
||||||
|
it to (old to new)
|
||||||
|
}
|
||||||
|
}.toMap()
|
||||||
|
|
||||||
|
return MapDiff(
|
||||||
|
removed,
|
||||||
|
changed,
|
||||||
|
added
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare [this] [Map] with the [other] one in principle when [other] is newer than [this]
|
||||||
|
*
|
||||||
|
* @param strictComparison If true, will use strict (===) comparison for the values' comparison. Otherwise, standard
|
||||||
|
* `equals` will be used
|
||||||
|
*/
|
||||||
|
fun <K, V> Map<K, V>.diff(
|
||||||
|
other: Map<K, V>,
|
||||||
|
strictComparison: Boolean = false
|
||||||
|
): MapDiff<K, V> = diff(
|
||||||
|
other,
|
||||||
|
compareFun = createCompareFun(strictComparison)
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will apply [mapDiff] to [this] [MutableMap]
|
||||||
|
*/
|
||||||
|
fun <K, V> MutableMap<K, V>.applyDiff(
|
||||||
|
mapDiff: MapDiff<K, V>
|
||||||
|
) {
|
||||||
|
mapDiff.apply {
|
||||||
|
keys.removeAll(removed.keys)
|
||||||
|
changed.forEach { (k, oldNew) ->
|
||||||
|
put(k, oldNew.second)
|
||||||
|
}
|
||||||
|
putAll(added)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will apply changes with [from] map into [this] one
|
||||||
|
*
|
||||||
|
* @param compareFun Will be used to determine changed values
|
||||||
|
*
|
||||||
|
* @return [MapDiff] applied to [this] [MutableMap]
|
||||||
|
*/
|
||||||
|
fun <K, V> MutableMap<K, V>.applyDiff(
|
||||||
|
from: Map<K, V>,
|
||||||
|
compareFun: (K, V, V) -> Boolean
|
||||||
|
): MapDiff<K, V> {
|
||||||
|
return diff(from, compareFun).also {
|
||||||
|
applyDiff(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will apply changes with [from] map into [this] one
|
||||||
|
*
|
||||||
|
* @param strictComparison If true, will use strict (===) comparison for the values' comparison. Otherwise, standard
|
||||||
|
* `equals` will be used
|
||||||
|
*
|
||||||
|
* @return [MapDiff] applied to [this] [MutableMap]
|
||||||
|
*/
|
||||||
|
fun <K, V> MutableMap<K, V>.applyDiff(
|
||||||
|
from: Map<K, V>,
|
||||||
|
strictComparison: Boolean = false
|
||||||
|
): MapDiff<K, V> = applyDiff(
|
||||||
|
from,
|
||||||
|
compareFun = createCompareFun(strictComparison)
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse [this] [MapDiff]. Result will contain [MapDiff.added] on [MapDiff.removed] (and vice-verse), all the
|
||||||
|
* [MapDiff.changed] values will be reversed too
|
||||||
|
*/
|
||||||
|
fun <K, V> MapDiff<K, V>.reversed(): MapDiff<K, V> = MapDiff(
|
||||||
|
removed = added,
|
||||||
|
changed = changed.mapValues { (_, oldNew) -> oldNew.second to oldNew.first },
|
||||||
|
added = removed
|
||||||
|
)
|
||||||
@@ -2,16 +2,43 @@ package dev.inmo.micro_utils.common
|
|||||||
|
|
||||||
import kotlin.jvm.JvmName
|
import kotlin.jvm.JvmName
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A bidirectional mapper that can convert between two types [T1] and [T2].
|
||||||
|
*
|
||||||
|
* @param T1 The first type
|
||||||
|
* @param T2 The second type
|
||||||
|
*/
|
||||||
interface SimpleMapper<T1, T2> {
|
interface SimpleMapper<T1, T2> {
|
||||||
fun convertToT1(from: T2): T1
|
fun convertToT1(from: T2): T1
|
||||||
fun convertToT2(from: T1): T2
|
fun convertToT2(from: T1): T2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts [from] of type [T2] to type [T1] using this mapper.
|
||||||
|
*
|
||||||
|
* @param from The value to convert
|
||||||
|
* @return The converted value of type [T1]
|
||||||
|
*/
|
||||||
@JvmName("convertFromT2")
|
@JvmName("convertFromT2")
|
||||||
fun <T1, T2> SimpleMapper<T1, T2>.convert(from: T2) = convertToT1(from)
|
fun <T1, T2> SimpleMapper<T1, T2>.convert(from: T2) = convertToT1(from)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts [from] of type [T1] to type [T2] using this mapper.
|
||||||
|
*
|
||||||
|
* @param from The value to convert
|
||||||
|
* @return The converted value of type [T2]
|
||||||
|
*/
|
||||||
@JvmName("convertFromT1")
|
@JvmName("convertFromT1")
|
||||||
fun <T1, T2> SimpleMapper<T1, T2>.convert(from: T1) = convertToT2(from)
|
fun <T1, T2> SimpleMapper<T1, T2>.convert(from: T1) = convertToT2(from)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of [SimpleMapper] that uses lambda functions for conversion.
|
||||||
|
*
|
||||||
|
* @param T1 The first type
|
||||||
|
* @param T2 The second type
|
||||||
|
* @param t1 Function to convert from [T2] to [T1]
|
||||||
|
* @param t2 Function to convert from [T1] to [T2]
|
||||||
|
*/
|
||||||
class SimpleMapperImpl<T1, T2>(
|
class SimpleMapperImpl<T1, T2>(
|
||||||
private val t1: (T2) -> T1,
|
private val t1: (T2) -> T1,
|
||||||
private val t2: (T1) -> T2,
|
private val t2: (T1) -> T2,
|
||||||
@@ -21,22 +48,58 @@ class SimpleMapperImpl<T1, T2>(
|
|||||||
override fun convertToT2(from: T1): T2 = t2.invoke(from)
|
override fun convertToT2(from: T1): T2 = t2.invoke(from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a [SimpleMapper] using the provided conversion functions.
|
||||||
|
*
|
||||||
|
* @param T1 The first type
|
||||||
|
* @param T2 The second type
|
||||||
|
* @param t1 Function to convert from [T2] to [T1]
|
||||||
|
* @param t2 Function to convert from [T1] to [T2]
|
||||||
|
* @return A new [SimpleMapperImpl] instance
|
||||||
|
*/
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
inline fun <T1, T2> simpleMapper(
|
inline fun <T1, T2> simpleMapper(
|
||||||
noinline t1: (T2) -> T1,
|
noinline t1: (T2) -> T1,
|
||||||
noinline t2: (T1) -> T2,
|
noinline t2: (T1) -> T2,
|
||||||
) = SimpleMapperImpl(t1, t2)
|
) = SimpleMapperImpl(t1, t2)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A bidirectional mapper that can convert between two types [T1] and [T2] using suspending functions.
|
||||||
|
*
|
||||||
|
* @param T1 The first type
|
||||||
|
* @param T2 The second type
|
||||||
|
*/
|
||||||
interface SimpleSuspendableMapper<T1, T2> {
|
interface SimpleSuspendableMapper<T1, T2> {
|
||||||
suspend fun convertToT1(from: T2): T1
|
suspend fun convertToT1(from: T2): T1
|
||||||
suspend fun convertToT2(from: T1): T2
|
suspend fun convertToT2(from: T1): T2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts [from] of type [T2] to type [T1] using this suspending mapper.
|
||||||
|
*
|
||||||
|
* @param from The value to convert
|
||||||
|
* @return The converted value of type [T1]
|
||||||
|
*/
|
||||||
@JvmName("convertFromT2")
|
@JvmName("convertFromT2")
|
||||||
suspend fun <T1, T2> SimpleSuspendableMapper<T1, T2>.convert(from: T2) = convertToT1(from)
|
suspend fun <T1, T2> SimpleSuspendableMapper<T1, T2>.convert(from: T2) = convertToT1(from)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts [from] of type [T1] to type [T2] using this suspending mapper.
|
||||||
|
*
|
||||||
|
* @param from The value to convert
|
||||||
|
* @return The converted value of type [T2]
|
||||||
|
*/
|
||||||
@JvmName("convertFromT1")
|
@JvmName("convertFromT1")
|
||||||
suspend fun <T1, T2> SimpleSuspendableMapper<T1, T2>.convert(from: T1) = convertToT2(from)
|
suspend fun <T1, T2> SimpleSuspendableMapper<T1, T2>.convert(from: T1) = convertToT2(from)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of [SimpleSuspendableMapper] that uses suspending lambda functions for conversion.
|
||||||
|
*
|
||||||
|
* @param T1 The first type
|
||||||
|
* @param T2 The second type
|
||||||
|
* @param t1 Suspending function to convert from [T2] to [T1]
|
||||||
|
* @param t2 Suspending function to convert from [T1] to [T2]
|
||||||
|
*/
|
||||||
class SimpleSuspendableMapperImpl<T1, T2>(
|
class SimpleSuspendableMapperImpl<T1, T2>(
|
||||||
private val t1: suspend (T2) -> T1,
|
private val t1: suspend (T2) -> T1,
|
||||||
private val t2: suspend (T1) -> T2,
|
private val t2: suspend (T1) -> T2,
|
||||||
@@ -46,6 +109,15 @@ class SimpleSuspendableMapperImpl<T1, T2>(
|
|||||||
override suspend fun convertToT2(from: T1): T2 = t2.invoke(from)
|
override suspend fun convertToT2(from: T1): T2 = t2.invoke(from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a [SimpleSuspendableMapper] using the provided suspending conversion functions.
|
||||||
|
*
|
||||||
|
* @param T1 The first type
|
||||||
|
* @param T2 The second type
|
||||||
|
* @param t1 Suspending function to convert from [T2] to [T1]
|
||||||
|
* @param t2 Suspending function to convert from [T1] to [T2]
|
||||||
|
* @return A new [SimpleSuspendableMapperImpl] instance
|
||||||
|
*/
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
inline fun <T1, T2> simpleSuspendableMapper(
|
inline fun <T1, T2> simpleSuspendableMapper(
|
||||||
noinline t1: suspend (T2) -> T1,
|
noinline t1: suspend (T2) -> T1,
|
||||||
|
|||||||
@@ -0,0 +1,119 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pads this sequence to the specified [size] using a custom [inserter] function.
|
||||||
|
* The [inserter] is repeatedly called until the sequence reaches the desired size.
|
||||||
|
*
|
||||||
|
* @param T The type of elements in the sequence
|
||||||
|
* @param size The target size of the padded sequence
|
||||||
|
* @param inserter A function that takes the current sequence and returns a new padded sequence
|
||||||
|
* @return A sequence padded to at least the specified size
|
||||||
|
*/
|
||||||
|
inline fun <T> Sequence<T>.padWith(size: Int, inserter: (Sequence<T>) -> Sequence<T>): Sequence<T> {
|
||||||
|
var result = this
|
||||||
|
while (result.count() < size) {
|
||||||
|
result = inserter(result)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pads this sequence at the end to the specified [size].
|
||||||
|
* New elements are generated using [padBlock], which receives the current size as a parameter.
|
||||||
|
*
|
||||||
|
* @param T The type of elements in the sequence
|
||||||
|
* @param size The target size of the padded sequence
|
||||||
|
* @param padBlock A function that generates padding elements based on the current sequence size
|
||||||
|
* @return A sequence padded to at least the specified size
|
||||||
|
*/
|
||||||
|
inline fun <T> Sequence<T>.padEnd(size: Int, padBlock: (Int) -> T): Sequence<T> = padWith(size) { it + padBlock(it.count()) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pads this sequence at the end to the specified [size] using the given element [o].
|
||||||
|
*
|
||||||
|
* @param T The type of elements in the sequence
|
||||||
|
* @param size The target size of the padded sequence
|
||||||
|
* @param o The element to use for padding
|
||||||
|
* @return A sequence padded to at least the specified size
|
||||||
|
*/
|
||||||
|
inline fun <T> Sequence<T>.padEnd(size: Int, o: T) = padEnd(size) { o }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pads this list to the specified [size] using a custom [inserter] function.
|
||||||
|
* The [inserter] is repeatedly called until the list reaches the desired size.
|
||||||
|
*
|
||||||
|
* @param T The type of elements in the list
|
||||||
|
* @param size The target size of the padded list
|
||||||
|
* @param inserter A function that takes the current list and returns a new padded list
|
||||||
|
* @return A list padded to at least the specified size
|
||||||
|
*/
|
||||||
|
inline fun <T> List<T>.padWith(size: Int, inserter: (List<T>) -> List<T>): List<T> {
|
||||||
|
var result = this
|
||||||
|
while (result.size < size) {
|
||||||
|
result = inserter(result)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pads this list at the end to the specified [size].
|
||||||
|
* New elements are generated using [padBlock], which receives the current size as a parameter.
|
||||||
|
*
|
||||||
|
* @param T The type of elements in the list
|
||||||
|
* @param size The target size of the padded list
|
||||||
|
* @param padBlock A function that generates padding elements based on the current list size
|
||||||
|
* @return A list padded to at least the specified size
|
||||||
|
*/
|
||||||
|
inline fun <T> List<T>.padEnd(size: Int, padBlock: (Int) -> T): List<T> = asSequence().padEnd(size, padBlock).toList()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pads this list at the end to the specified [size] using the given element [o].
|
||||||
|
*
|
||||||
|
* @param T The type of elements in the list
|
||||||
|
* @param size The target size of the padded list
|
||||||
|
* @param o The element to use for padding
|
||||||
|
* @return A list padded to at least the specified size
|
||||||
|
*/
|
||||||
|
inline fun <T> List<T>.padEnd(size: Int, o: T): List<T> = asSequence().padEnd(size, o).toList()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pads this sequence at the start to the specified [size].
|
||||||
|
* New elements are generated using [padBlock], which receives the current size as a parameter.
|
||||||
|
*
|
||||||
|
* @param T The type of elements in the sequence
|
||||||
|
* @param size The target size of the padded sequence
|
||||||
|
* @param padBlock A function that generates padding elements based on the current sequence size
|
||||||
|
* @return A sequence padded to at least the specified size
|
||||||
|
*/
|
||||||
|
inline fun <T> Sequence<T>.padStart(size: Int, padBlock: (Int) -> T): Sequence<T> = padWith(size) { sequenceOf(padBlock(it.count())) + it }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pads this sequence at the start to the specified [size] using the given element [o].
|
||||||
|
*
|
||||||
|
* @param T The type of elements in the sequence
|
||||||
|
* @param size The target size of the padded sequence
|
||||||
|
* @param o The element to use for padding
|
||||||
|
* @return A sequence padded to at least the specified size
|
||||||
|
*/
|
||||||
|
inline fun <T> Sequence<T>.padStart(size: Int, o: T) = padStart(size) { o }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pads this list at the start to the specified [size].
|
||||||
|
* New elements are generated using [padBlock], which receives the current size as a parameter.
|
||||||
|
*
|
||||||
|
* @param T The type of elements in the list
|
||||||
|
* @param size The target size of the padded list
|
||||||
|
* @param padBlock A function that generates padding elements based on the current list size
|
||||||
|
* @return A list padded to at least the specified size
|
||||||
|
*/
|
||||||
|
inline fun <T> List<T>.padStart(size: Int, padBlock: (Int) -> T): List<T> = asSequence().padStart(size, padBlock).toList()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pads this list at the start to the specified [size] using the given element [o].
|
||||||
|
*
|
||||||
|
* @param T The type of elements in the list
|
||||||
|
* @param size The target size of the padded list
|
||||||
|
* @param o The element to use for padding
|
||||||
|
* @return A list padded to at least the specified size
|
||||||
|
*/
|
||||||
|
inline fun <T> List<T>.padStart(size: Int, o: T): List<T> = asSequence().padStart(size, o).toList()
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlin.jvm.JvmInline
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains [of1] as main value, where 100% of percentage is when of1 == 1
|
||||||
|
*
|
||||||
|
* @see invoke
|
||||||
|
* @see partOfTotal
|
||||||
|
* @see of100
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
@JvmInline
|
||||||
|
value class Percentage private constructor(
|
||||||
|
/**
|
||||||
|
* Value of percentage. When it equals to 1, means 100%
|
||||||
|
*/
|
||||||
|
val of1: Double
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* Same as [of1], but float (using [Double.toFloat])
|
||||||
|
*/
|
||||||
|
val of1Float
|
||||||
|
get() = of1.toFloat()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represent this percentage as common percentage where 100% is 100%
|
||||||
|
*/
|
||||||
|
val of100
|
||||||
|
get() = of1 * 100
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as [of100], but float (using [Double.toFloat])
|
||||||
|
*/
|
||||||
|
val of100Float
|
||||||
|
get() = of100.toFloat()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as [of100], but int (using [Double.toInt])
|
||||||
|
*/
|
||||||
|
val of100Int
|
||||||
|
get() = of100.toInt()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val rangeOfValues = 0.0 .. 1.0
|
||||||
|
|
||||||
|
val START = Percentage(rangeOfValues.start)
|
||||||
|
val COMPLETED = Percentage(rangeOfValues.endInclusive)
|
||||||
|
|
||||||
|
operator fun invoke(of1: Double) = Percentage(of1.coerceIn(rangeOfValues))
|
||||||
|
operator fun invoke(part: Number, total: Number) = Percentage(
|
||||||
|
part.toDouble() / total.toDouble()
|
||||||
|
)
|
||||||
|
fun of1(of1: Double) = Percentage(of1 = of1)
|
||||||
|
fun of100(of100: Double) = Percentage(of1 = of100 / 100)
|
||||||
|
fun partOfTotal(part: Number, total: Number) = Percentage(part = part, total = total)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typealias Progress = Percentage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will return [this] [Progress] if [Percentage.of1] in `0 .. 1` range
|
||||||
|
*/
|
||||||
|
fun Progress.ensureStrictOrNull(): Progress? = if (of1 in Percentage.rangeOfValues) this else null
|
||||||
|
/**
|
||||||
|
* Will return [this] [Progress] if [Percentage.of1] in `0 .. 1` range. Otherwise, will throw error
|
||||||
|
* [IllegalArgumentException] due to [require] failure
|
||||||
|
*/
|
||||||
|
fun Progress.ensureStrictOrThrow(): Progress {
|
||||||
|
require(of1 in Percentage.rangeOfValues) {
|
||||||
|
"For strict checks value of percentage must be in ${Percentage.rangeOfValues}, but actual value is $of1"
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
@file:Suppress(
|
||||||
|
"RemoveRedundantCallsOfConversionMethods",
|
||||||
|
"RedundantVisibilityModifier",
|
||||||
|
)
|
||||||
|
|
||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import kotlin.Byte
|
||||||
|
import kotlin.Double
|
||||||
|
import kotlin.Float
|
||||||
|
import kotlin.Int
|
||||||
|
import kotlin.Long
|
||||||
|
import kotlin.Short
|
||||||
|
import kotlin.Suppress
|
||||||
|
|
||||||
|
public operator fun Percentage.plus(other: Percentage): Percentage = Percentage(of1 + other.of1)
|
||||||
|
|
||||||
|
public operator fun Percentage.minus(other: Percentage): Percentage = Percentage(of1 - other.of1)
|
||||||
|
|
||||||
|
public operator fun Percentage.plus(i: Byte): Percentage = Percentage((of1 + i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.minus(i: Byte): Percentage = Percentage((of1 - i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.times(i: Byte): Percentage = Percentage((of1 * i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.div(i: Byte): Percentage = Percentage((of1 / i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.rem(i: Byte): Percentage = Percentage((of1 % i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.plus(i: Short): Percentage = Percentage((of1 + i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.minus(i: Short): Percentage = Percentage((of1 - i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.times(i: Short): Percentage = Percentage((of1 * i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.div(i: Short): Percentage = Percentage((of1 / i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.rem(i: Short): Percentage = Percentage((of1 % i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.plus(i: Int): Percentage = Percentage((of1 + i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.minus(i: Int): Percentage = Percentage((of1 - i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.times(i: Int): Percentage = Percentage((of1 * i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.div(i: Int): Percentage = Percentage((of1 / i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.rem(i: Int): Percentage = Percentage((of1 % i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.plus(i: Long): Percentage = Percentage((of1 + i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.minus(i: Long): Percentage = Percentage((of1 - i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.times(i: Long): Percentage = Percentage((of1 * i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.div(i: Long): Percentage = Percentage((of1 / i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.rem(i: Long): Percentage = Percentage((of1 % i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.plus(i: Float): Percentage = Percentage((of1 + i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.minus(i: Float): Percentage = Percentage((of1 - i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.times(i: Float): Percentage = Percentage((of1 * i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.div(i: Float): Percentage = Percentage((of1 / i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.rem(i: Float): Percentage = Percentage((of1 % i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.plus(i: Double): Percentage = Percentage((of1 + i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.minus(i: Double): Percentage = Percentage((of1 - i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.times(i: Double): Percentage = Percentage((of1 * i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.div(i: Double): Percentage = Percentage((of1 / i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.rem(i: Double): Percentage = Percentage((of1 % i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Percentage.compareTo(other: Percentage): Int = (of1.compareTo(other.of1))
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert [this] [Long] to [Int] with bounds of [Int.MIN_VALUE] and [Int.MAX_VALUE]
|
||||||
|
*/
|
||||||
|
fun Long.toCoercedInt(): Int = coerceIn(Int.MIN_VALUE.toLong(), Int.MAX_VALUE.toLong()).toInt()
|
||||||
|
/**
|
||||||
|
* Convert [this] [Long] to [Short] with bounds of [Short.MIN_VALUE] and [Short.MAX_VALUE]
|
||||||
|
*/
|
||||||
|
fun Long.toCoercedShort(): Short = coerceIn(Short.MIN_VALUE.toLong(), Short.MAX_VALUE.toLong()).toShort()
|
||||||
|
/**
|
||||||
|
* Convert [this] [Long] to [Byte] with bounds of [Byte.MIN_VALUE] and [Byte.MAX_VALUE]
|
||||||
|
*/
|
||||||
|
fun Long.toCoercedByte(): Byte = coerceIn(Byte.MIN_VALUE.toLong(), Byte.MAX_VALUE.toLong()).toByte()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert [this] [Int] to [Short] with bounds of [Short.MIN_VALUE] and [Short.MAX_VALUE]
|
||||||
|
*/
|
||||||
|
fun Int.toCoercedShort(): Short = coerceIn(Short.MIN_VALUE.toInt(), Short.MAX_VALUE.toInt()).toShort()
|
||||||
|
/**
|
||||||
|
* Convert [this] [Int] to [Byte] with bounds of [Byte.MIN_VALUE] and [Byte.MAX_VALUE]
|
||||||
|
*/
|
||||||
|
fun Int.toCoercedByte(): Byte = coerceIn(Byte.MIN_VALUE.toInt(), Byte.MAX_VALUE.toInt()).toByte()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert [this] [Short] to [Byte] with bounds of [Byte.MIN_VALUE] and [Byte.MAX_VALUE]
|
||||||
|
*/
|
||||||
|
fun Short.toCoercedByte(): Byte = coerceIn(Byte.MIN_VALUE.toShort(), Byte.MAX_VALUE.toShort()).toByte()
|
||||||
@@ -1,17 +1,39 @@
|
|||||||
package dev.inmo.micro_utils.common
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the intersection of this range with [other]. Returns a pair representing
|
||||||
|
* the intersecting range, or null if the ranges don't overlap.
|
||||||
|
*
|
||||||
|
* @param T The type of comparable values in the range
|
||||||
|
* @param other The other range to intersect with
|
||||||
|
* @return A pair (start, end) representing the intersection, or null if no intersection exists
|
||||||
|
*/
|
||||||
fun <T : Comparable<T>> ClosedRange<T>.intersect(other: ClosedRange<T>): Pair<T, T>? = when {
|
fun <T : Comparable<T>> ClosedRange<T>.intersect(other: ClosedRange<T>): Pair<T, T>? = when {
|
||||||
start == other.start && endInclusive == other.endInclusive -> start to endInclusive
|
start == other.start && endInclusive == other.endInclusive -> start to endInclusive
|
||||||
start > other.endInclusive || other.start > endInclusive -> null
|
start > other.endInclusive || other.start > endInclusive -> null
|
||||||
else -> maxOf(start, other.start) to minOf(endInclusive, other.endInclusive)
|
else -> maxOf(start, other.start) to minOf(endInclusive, other.endInclusive)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the intersection of this [IntRange] with [other].
|
||||||
|
* Returns the intersecting range, or null if the ranges don't overlap.
|
||||||
|
*
|
||||||
|
* @param other The other range to intersect with
|
||||||
|
* @return An [IntRange] representing the intersection, or null if no intersection exists
|
||||||
|
*/
|
||||||
fun IntRange.intersect(
|
fun IntRange.intersect(
|
||||||
other: IntRange
|
other: IntRange
|
||||||
): IntRange? = (this as ClosedRange<Int>).intersect(other as ClosedRange<Int>) ?.let {
|
): IntRange? = (this as ClosedRange<Int>).intersect(other as ClosedRange<Int>) ?.let {
|
||||||
it.first .. it.second
|
it.first .. it.second
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the intersection of this [LongRange] with [other].
|
||||||
|
* Returns the intersecting range, or null if the ranges don't overlap.
|
||||||
|
*
|
||||||
|
* @param other The other range to intersect with
|
||||||
|
* @return A [LongRange] representing the intersection, or null if no intersection exists
|
||||||
|
*/
|
||||||
fun LongRange.intersect(
|
fun LongRange.intersect(
|
||||||
other: LongRange
|
other: LongRange
|
||||||
): LongRange? = (this as ClosedRange<Long>).intersect(other as ClosedRange<Long>) ?.let {
|
): LongRange? = (this as ClosedRange<Long>).intersect(other as ClosedRange<Long>) ?.let {
|
||||||
|
|||||||
@@ -1,5 +1,27 @@
|
|||||||
package dev.inmo.micro_utils.common
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the given [action] until getting of successful result specified number of [times].
|
||||||
|
*
|
||||||
|
* A zero-based index of current iteration is passed as a parameter to [action].
|
||||||
|
*/
|
||||||
|
inline fun <R> repeatOnFailure(
|
||||||
|
onFailure: (Throwable) -> Boolean,
|
||||||
|
action: () -> R
|
||||||
|
): Result<R> {
|
||||||
|
do {
|
||||||
|
runCatching {
|
||||||
|
action()
|
||||||
|
}.onFailure {
|
||||||
|
if (!onFailure(it)) {
|
||||||
|
return Result.failure(it)
|
||||||
|
}
|
||||||
|
}.onSuccess {
|
||||||
|
return Result.success(it)
|
||||||
|
}
|
||||||
|
} while (true)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the given [action] until getting of successful result specified number of [times].
|
* Executes the given [action] until getting of successful result specified number of [times].
|
||||||
*
|
*
|
||||||
@@ -10,12 +32,23 @@ inline fun <R> repeatOnFailure(
|
|||||||
onEachFailure: (Throwable) -> Unit = {},
|
onEachFailure: (Throwable) -> Unit = {},
|
||||||
action: (Int) -> R
|
action: (Int) -> R
|
||||||
): Optional<R> {
|
): Optional<R> {
|
||||||
repeat(times) {
|
var i = 0
|
||||||
runCatching {
|
val result = repeatOnFailure(
|
||||||
action(it)
|
{
|
||||||
}.onFailure(onEachFailure).onSuccess {
|
onEachFailure(it)
|
||||||
return Optional.presented(it)
|
if (i < times) {
|
||||||
|
i++
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Optional.absent()
|
) {
|
||||||
|
action(i)
|
||||||
|
}
|
||||||
|
return if (result.isSuccess) {
|
||||||
|
Optional.presented(result.getOrThrow())
|
||||||
|
} else {
|
||||||
|
Optional.absent()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will try to execute [action] and, if any exception will happen, execution will be retried.
|
||||||
|
* This process will happen at most [count] times. There is no any limits on [count] value, but [action] will run at
|
||||||
|
* least once and [retryOnFailure] will return its result if it is successful
|
||||||
|
*/
|
||||||
|
inline fun <T> retryOnFailure(count: Int, action: () -> T): T {
|
||||||
|
var triesCount = 0
|
||||||
|
while (true) {
|
||||||
|
val result = runCatching {
|
||||||
|
action()
|
||||||
|
}.onFailure {
|
||||||
|
triesCount++
|
||||||
|
|
||||||
|
if (triesCount >= count) {
|
||||||
|
throw it
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.isSuccess) return result.getOrThrow()
|
||||||
|
}
|
||||||
|
error("Unreachable code: retry must throw latest exception if error happen or success value if not")
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
val FixedSignsRange = 0 .. 100
|
||||||
|
|
||||||
|
expect fun Float.fixed(signs: Int): Float
|
||||||
|
expect fun Double.fixed(signs: Int): Double
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new list with the element at index [i] replaced by applying [block] to it.
|
||||||
|
* All other elements remain unchanged.
|
||||||
|
*
|
||||||
|
* @param T The type of elements in the iterable
|
||||||
|
* @param i The index of the element to replace
|
||||||
|
* @param block A function that transforms the element at the given index
|
||||||
|
* @return A new list with the replaced element
|
||||||
|
*/
|
||||||
|
fun <T> Iterable<T>.withReplacedAt(i: Int, block: (T) -> T): List<T> = take(i) + block(elementAt(i)) + drop(i + 1)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new list with the first occurrence of element [t] replaced by applying [block] to it.
|
||||||
|
* All other elements remain unchanged.
|
||||||
|
*
|
||||||
|
* @param T The type of elements in the iterable
|
||||||
|
* @param t The element to replace
|
||||||
|
* @param block A function that transforms the found element
|
||||||
|
* @return A new list with the replaced element
|
||||||
|
*/
|
||||||
|
fun <T> Iterable<T>.withReplaced(t: T, block: (T) -> T): List<T> = withReplacedAt(indexOf(t), block)
|
||||||
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class PercentageTests {
|
||||||
|
@Test
|
||||||
|
fun testCompareTo() {
|
||||||
|
val step = 0.01
|
||||||
|
|
||||||
|
var i = Percentage.START.of1
|
||||||
|
while (i <= Percentage.COMPLETED.of1) {
|
||||||
|
val percentageI = Percentage(i)
|
||||||
|
|
||||||
|
var j = Percentage.START.of1
|
||||||
|
while (j <= Percentage.COMPLETED.of1) {
|
||||||
|
val percentageJ = Percentage(j)
|
||||||
|
|
||||||
|
assertEquals(percentageI.of1.compareTo(percentageJ.of1), percentageI.compareTo(percentageJ))
|
||||||
|
assertEquals(percentageI.of1 > percentageJ.of1, percentageI > percentageJ)
|
||||||
|
assertEquals(percentageI.of1 < percentageJ.of1, percentageI < percentageJ)
|
||||||
|
|
||||||
|
j += step
|
||||||
|
}
|
||||||
|
|
||||||
|
i += step
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class WithReplacedTest {
|
||||||
|
@Test
|
||||||
|
fun testReplaced() {
|
||||||
|
val data = 0 until 10
|
||||||
|
val testData = Int.MAX_VALUE
|
||||||
|
|
||||||
|
for (i in 0 until data.last) {
|
||||||
|
val withReplaced = data.withReplacedAt(i) {
|
||||||
|
testData
|
||||||
|
}
|
||||||
|
val dataAsMutableList = data.toMutableList()
|
||||||
|
dataAsMutableList[i] = testData
|
||||||
|
assertEquals(withReplaced, dataAsMutableList.toList())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import kotlinx.browser.window
|
||||||
|
|
||||||
|
fun copyToClipboard(text: String): Boolean {
|
||||||
|
return runCatching {
|
||||||
|
window.navigator.clipboard.writeText(
|
||||||
|
text
|
||||||
|
)
|
||||||
|
}.onFailure {
|
||||||
|
it.printStackTrace()
|
||||||
|
}.isSuccess
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import kotlinx.browser.window
|
||||||
|
import org.w3c.files.Blob
|
||||||
|
import org.w3c.files.BlobPropertyBag
|
||||||
|
import kotlin.js.json
|
||||||
|
|
||||||
|
external class ClipboardItem(data: dynamic)
|
||||||
|
|
||||||
|
inline fun Blob.convertToClipboardItem(): ClipboardItem {
|
||||||
|
val itemData: dynamic = json(this.type to this)
|
||||||
|
return ClipboardItem(itemData)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun copyImageURLToClipboard(imageUrl: String): Boolean {
|
||||||
|
return runCatching {
|
||||||
|
val response = window.fetch(imageUrl).await()
|
||||||
|
val blob = response.blob().await()
|
||||||
|
val data = arrayOf(
|
||||||
|
Blob(
|
||||||
|
arrayOf(blob),
|
||||||
|
BlobPropertyBag("image/png")
|
||||||
|
).convertToClipboardItem()
|
||||||
|
).asDynamic()
|
||||||
|
window.navigator.clipboard.write(data)
|
||||||
|
}.onFailure {
|
||||||
|
it.printStackTrace()
|
||||||
|
}.isSuccess
|
||||||
|
}
|
||||||
@@ -35,6 +35,10 @@ private suspend fun MPPFile.dirtyReadBytes(): ByteArray = readBytesPromise().awa
|
|||||||
*/
|
*/
|
||||||
actual val MPPFile.filename: FileName
|
actual val MPPFile.filename: FileName
|
||||||
get() = FileName(name)
|
get() = FileName(name)
|
||||||
|
|
||||||
|
actual val MPPFilePathSeparator: Char
|
||||||
|
get() = '/'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @suppress
|
* @suppress
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
actual fun Float.fixed(signs: Int): Float = this.asDynamic().toFixed(signs.coerceIn(FixedSignsRange)).unsafeCast<String>().toFloat()
|
||||||
|
actual fun Double.fixed(signs: Int): Double = this.asDynamic().toFixed(signs.coerceIn(FixedSignsRange)).unsafeCast<String>().toDouble()
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
fun InputStream.downloadToTempFile(
|
||||||
|
fileName: String = UUID.randomUUID().toString(),
|
||||||
|
fileExtension: String? = ".temp",
|
||||||
|
folder: File? = null
|
||||||
|
) = File.createTempFile(
|
||||||
|
fileName,
|
||||||
|
fileExtension,
|
||||||
|
folder
|
||||||
|
).apply {
|
||||||
|
outputStream().use {
|
||||||
|
copyTo(it)
|
||||||
|
}
|
||||||
|
deleteOnExit()
|
||||||
|
}
|
||||||
@@ -14,6 +14,10 @@ actual typealias MPPFile = File
|
|||||||
*/
|
*/
|
||||||
actual val MPPFile.filename: FileName
|
actual val MPPFile.filename: FileName
|
||||||
get() = FileName(name)
|
get() = FileName(name)
|
||||||
|
|
||||||
|
actual val MPPFilePathSeparator: Char
|
||||||
|
get() = File.separatorChar
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @suppress
|
* @suppress
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import java.math.RoundingMode
|
||||||
|
|
||||||
|
actual fun Float.fixed(signs: Int): Float = BigDecimal.valueOf(this.toDouble())
|
||||||
|
.setScale(signs.coerceIn(FixedSignsRange), RoundingMode.HALF_UP)
|
||||||
|
.toFloat();
|
||||||
|
|
||||||
|
actual fun Double.fixed(signs: Int): Double = BigDecimal.valueOf(this)
|
||||||
|
.setScale(signs.coerceIn(FixedSignsRange), RoundingMode.HALF_UP)
|
||||||
|
.toDouble();
|
||||||
@@ -1 +0,0 @@
|
|||||||
<manifest package="dev.inmo.micro_utils.common"/>
|
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import okio.FileSystem
|
||||||
|
import okio.Path
|
||||||
|
import okio.use
|
||||||
|
|
||||||
|
actual typealias MPPFile = Path
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @suppress
|
||||||
|
*/
|
||||||
|
actual val MPPFile.filename: FileName
|
||||||
|
get() = FileName(toString())
|
||||||
|
|
||||||
|
|
||||||
|
actual val MPPFilePathSeparator: Char = Path.DIRECTORY_SEPARATOR.first()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @suppress
|
||||||
|
*/
|
||||||
|
actual val MPPFile.filesize: Long
|
||||||
|
get() = FileSystem.SYSTEM.openReadOnly(this).use {
|
||||||
|
it.size()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @suppress
|
||||||
|
*/
|
||||||
|
actual val MPPFile.bytesAllocatorSync: ByteArrayAllocator
|
||||||
|
get() = {
|
||||||
|
FileSystem.SYSTEM.read(this) {
|
||||||
|
readByteArray()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @suppress
|
||||||
|
*/
|
||||||
|
actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator
|
||||||
|
get() = {
|
||||||
|
bytesAllocatorSync()
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import kotlinx.cinterop.*
|
||||||
|
import platform.posix.snprintf
|
||||||
|
import platform.posix.sprintf
|
||||||
|
|
||||||
|
@OptIn(ExperimentalForeignApi::class)
|
||||||
|
actual fun Float.fixed(signs: Int): Float {
|
||||||
|
return memScoped {
|
||||||
|
val buff = allocArray<ByteVar>(Float.SIZE_BYTES * 2)
|
||||||
|
|
||||||
|
sprintf(buff, "%.${signs}f", this@fixed)
|
||||||
|
buff.toKString().toFloat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalForeignApi::class)
|
||||||
|
actual fun Double.fixed(signs: Int): Double {
|
||||||
|
return memScoped {
|
||||||
|
val buff = allocArray<ByteVar>(Double.SIZE_BYTES * 2)
|
||||||
|
|
||||||
|
sprintf(buff, "%.${signs}f", this@fixed)
|
||||||
|
buff.toKString().toDouble()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import org.khronos.webgl.*
|
||||||
|
|
||||||
|
fun DataView.toByteArray() = ByteArray(this.byteLength) {
|
||||||
|
getInt8(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ArrayBuffer.toByteArray() = Int8Array(this).toByteArray()
|
||||||
|
|
||||||
|
fun ByteArray.toDataView() = DataView(ArrayBuffer(size)).also {
|
||||||
|
forEachIndexed { i, byte -> it.setInt8(i, byte) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ByteArray.toArrayBuffer() = toDataView().buffer
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import kotlinx.browser.window
|
||||||
|
|
||||||
|
fun copyToClipboard(text: String): Boolean {
|
||||||
|
return runCatching {
|
||||||
|
window.navigator.clipboard.writeText(
|
||||||
|
text
|
||||||
|
)
|
||||||
|
}.onFailure {
|
||||||
|
it.printStackTrace()
|
||||||
|
}.isSuccess
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import kotlinx.browser.window
|
||||||
|
import kotlinx.coroutines.await
|
||||||
|
import org.w3c.fetch.Response
|
||||||
|
import org.w3c.files.Blob
|
||||||
|
import org.w3c.files.BlobPropertyBag
|
||||||
|
|
||||||
|
external class ClipboardItem(data: JsAny?) : JsAny
|
||||||
|
|
||||||
|
fun createBlobData(blob: Blob): JsAny = js("""({[blob.type]: blob})""")
|
||||||
|
|
||||||
|
inline fun Blob.convertToClipboardItem(): ClipboardItem {
|
||||||
|
return ClipboardItem(createBlobData(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun copyImageURLToClipboard(imageUrl: String): Boolean {
|
||||||
|
return runCatching {
|
||||||
|
val response = window.fetch(imageUrl).await<Response>()
|
||||||
|
val blob = response.blob().await<Blob>()
|
||||||
|
val data = arrayOf(
|
||||||
|
Blob(
|
||||||
|
arrayOf(blob).toJsArray().unsafeCast(),
|
||||||
|
BlobPropertyBag("image/png")
|
||||||
|
).convertToClipboardItem()
|
||||||
|
).toJsArray()
|
||||||
|
window.navigator.clipboard.write(data.unsafeCast())
|
||||||
|
}.onFailure {
|
||||||
|
it.printStackTrace()
|
||||||
|
}.isSuccess
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import kotlinx.browser.document
|
||||||
|
import org.w3c.dom.*
|
||||||
|
|
||||||
|
private fun createMutationObserverInit(childList: Boolean, subtree: Boolean): JsAny = js("({childList, subtree})")
|
||||||
|
|
||||||
|
fun Node.onRemoved(block: () -> Unit): MutationObserver {
|
||||||
|
lateinit var observer: MutationObserver
|
||||||
|
|
||||||
|
observer = MutationObserver { _, _ ->
|
||||||
|
fun checkIfRemoved(node: Node): Boolean {
|
||||||
|
return node.parentNode != document && (node.parentNode ?.let { checkIfRemoved(it) } ?: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkIfRemoved(this)) {
|
||||||
|
observer.disconnect()
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
observer.observe(document, createMutationObserverInit(childList = true, subtree = true).unsafeCast())
|
||||||
|
return observer
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Element.onVisibilityChanged(block: IntersectionObserverEntry.(Float, IntersectionObserver) -> Unit): IntersectionObserver {
|
||||||
|
var previousIntersectionRatio = -1f
|
||||||
|
val observer = IntersectionObserver { entries, observer ->
|
||||||
|
entries.toArray().forEach {
|
||||||
|
if (previousIntersectionRatio.toDouble() != it.intersectionRatio.toDouble()) {
|
||||||
|
previousIntersectionRatio = it.intersectionRatio.toDouble().toFloat()
|
||||||
|
it.block(previousIntersectionRatio, observer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
observer.observe(this)
|
||||||
|
return observer
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Element.onVisible(block: Element.(IntersectionObserver) -> Unit) {
|
||||||
|
var previous = -1f
|
||||||
|
onVisibilityChanged { intersectionRatio, observer ->
|
||||||
|
if (previous != intersectionRatio) {
|
||||||
|
if (intersectionRatio > 0 && previous == 0f) {
|
||||||
|
block(observer)
|
||||||
|
}
|
||||||
|
previous = intersectionRatio
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Element.onInvisible(block: Element.(IntersectionObserver) -> Unit): IntersectionObserver {
|
||||||
|
var previous = -1f
|
||||||
|
return onVisibilityChanged { intersectionRatio, observer ->
|
||||||
|
if (previous != intersectionRatio) {
|
||||||
|
if (intersectionRatio == 0f && previous != 0f) {
|
||||||
|
block(observer)
|
||||||
|
}
|
||||||
|
previous = intersectionRatio
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import kotlinx.browser.window
|
||||||
|
import org.w3c.dom.DOMRect
|
||||||
|
import org.w3c.dom.Element
|
||||||
|
|
||||||
|
val DOMRect.isOnScreenByLeftEdge: Boolean
|
||||||
|
get() = left >= 0 && left <= window.innerWidth
|
||||||
|
inline val Element.isOnScreenByLeftEdge
|
||||||
|
get() = getBoundingClientRect().isOnScreenByLeftEdge
|
||||||
|
|
||||||
|
val DOMRect.isOnScreenByRightEdge: Boolean
|
||||||
|
get() = right >= 0 && right <= window.innerWidth
|
||||||
|
inline val Element.isOnScreenByRightEdge
|
||||||
|
get() = getBoundingClientRect().isOnScreenByRightEdge
|
||||||
|
|
||||||
|
internal val DOMRect.isOnScreenHorizontally: Boolean
|
||||||
|
get() = isOnScreenByLeftEdge || isOnScreenByRightEdge
|
||||||
|
|
||||||
|
|
||||||
|
val DOMRect.isOnScreenByTopEdge: Boolean
|
||||||
|
get() = top >= 0 && top <= window.innerHeight
|
||||||
|
inline val Element.isOnScreenByTopEdge
|
||||||
|
get() = getBoundingClientRect().isOnScreenByTopEdge
|
||||||
|
|
||||||
|
val DOMRect.isOnScreenByBottomEdge: Boolean
|
||||||
|
get() = bottom >= 0 && bottom <= window.innerHeight
|
||||||
|
inline val Element.isOnScreenByBottomEdge
|
||||||
|
get() = getBoundingClientRect().isOnScreenByBottomEdge
|
||||||
|
|
||||||
|
internal val DOMRect.isOnScreenVertically: Boolean
|
||||||
|
get() = isOnScreenByLeftEdge || isOnScreenByRightEdge
|
||||||
|
|
||||||
|
|
||||||
|
val DOMRect.isOnScreenFully: Boolean
|
||||||
|
get() = isOnScreenByLeftEdge && isOnScreenByTopEdge && isOnScreenByRightEdge && isOnScreenByBottomEdge
|
||||||
|
val Element.isOnScreenFully: Boolean
|
||||||
|
get() = getBoundingClientRect().isOnScreenFully
|
||||||
|
|
||||||
|
val DOMRect.isOnScreen: Boolean
|
||||||
|
get() = isOnScreenFully || (isOnScreenHorizontally && isOnScreenVertically)
|
||||||
|
inline val Element.isOnScreen: Boolean
|
||||||
|
get() = getBoundingClientRect().isOnScreen
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import org.w3c.dom.DOMRectReadOnly
|
||||||
|
import org.w3c.dom.Element
|
||||||
|
|
||||||
|
external interface IntersectionObserverOptions: JsAny {
|
||||||
|
/**
|
||||||
|
* An Element or Document object which is an ancestor of the intended target, whose bounding rectangle will be
|
||||||
|
* considered the viewport. Any part of the target not visible in the visible area of the root is not considered
|
||||||
|
* visible.
|
||||||
|
*/
|
||||||
|
var root: Element?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A string which specifies a set of offsets to add to the root's bounding_box when calculating intersections,
|
||||||
|
* effectively shrinking or growing the root for calculation purposes. The syntax is approximately the same as that
|
||||||
|
* for the CSS margin property; see The root element and root margin in Intersection Observer API for more
|
||||||
|
* information on how the margin works and the syntax. The default is "0px 0px 0px 0px".
|
||||||
|
*/
|
||||||
|
var rootMargin: String?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Either a single number or an array of numbers between 0.0 and 1.0, specifying a ratio of intersection area to
|
||||||
|
* total bounding box area for the observed target. A value of 0.0 means that even a single visible pixel counts as
|
||||||
|
* the target being visible. 1.0 means that the entire target element is visible. See Thresholds in Intersection
|
||||||
|
* Observer API for a more in-depth description of how thresholds are used. The default is a threshold of 0.0.
|
||||||
|
*/
|
||||||
|
var threshold: JsArray<JsNumber>?
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createEmptyJsObject(): JsAny = js("{}")
|
||||||
|
|
||||||
|
fun IntersectionObserverOptions(
|
||||||
|
block: IntersectionObserverOptions.() -> Unit = {}
|
||||||
|
): IntersectionObserverOptions = createEmptyJsObject().unsafeCast<IntersectionObserverOptions>().apply(block)
|
||||||
|
|
||||||
|
external interface IntersectionObserverEntry: JsAny {
|
||||||
|
/**
|
||||||
|
* Returns the bounds rectangle of the target element as a DOMRectReadOnly. The bounds are computed as described in
|
||||||
|
* the documentation for Element.getBoundingClientRect().
|
||||||
|
*/
|
||||||
|
val boundingClientRect: DOMRectReadOnly
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ratio of the intersectionRect to the boundingClientRect.
|
||||||
|
*/
|
||||||
|
val intersectionRatio: JsNumber
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a DOMRectReadOnly representing the target's visible area.
|
||||||
|
*/
|
||||||
|
val intersectionRect: DOMRectReadOnly
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Boolean value which is true if the target element intersects with the intersection observer's root. If this is
|
||||||
|
* true, then, the IntersectionObserverEntry describes a transition into a state of intersection; if it's false,
|
||||||
|
* then you know the transition is from intersecting to not-intersecting.
|
||||||
|
*/
|
||||||
|
val isIntersecting: Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a DOMRectReadOnly for the intersection observer's root.
|
||||||
|
*/
|
||||||
|
val rootBounds: DOMRectReadOnly
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Element whose intersection with the root changed.
|
||||||
|
*/
|
||||||
|
val target: Element
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A DOMHighResTimeStamp indicating the time at which the intersection was recorded, relative to the
|
||||||
|
* IntersectionObserver's time origin.
|
||||||
|
*/
|
||||||
|
val time: Double
|
||||||
|
}
|
||||||
|
|
||||||
|
typealias IntersectionObserverCallback = (entries: JsArray<IntersectionObserverEntry>, observer: IntersectionObserver) -> Unit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is just an implementation from [this commentary](https://youtrack.jetbrains.com/issue/KT-43157#focus=Comments-27-4498582.0-0)
|
||||||
|
* of Kotlin JS issue related to the absence of [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver)
|
||||||
|
*/
|
||||||
|
external class IntersectionObserver(callback: IntersectionObserverCallback): JsAny {
|
||||||
|
constructor(callback: IntersectionObserverCallback, options: IntersectionObserverOptions)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Element or Document whose bounds are used as the bounding box when testing for intersection. If no root value
|
||||||
|
* was passed to the constructor or its value is null, the top-level document's viewport is used.
|
||||||
|
*/
|
||||||
|
val root: Element
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An offset rectangle applied to the root's bounding box when calculating intersections, effectively shrinking or
|
||||||
|
* growing the root for calculation purposes. The value returned by this property may not be the same as the one
|
||||||
|
* specified when calling the constructor as it may be changed to match internal requirements. Each offset can be
|
||||||
|
* expressed in pixels (px) or as a percentage (%). The default is "0px 0px 0px 0px".
|
||||||
|
*/
|
||||||
|
val rootMargin: String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of thresholds, sorted in increasing numeric order, where each threshold is a ratio of intersection area to
|
||||||
|
* bounding box area of an observed target. Notifications for a target are generated when any of the thresholds are
|
||||||
|
* crossed for that target. If no value was passed to the constructor, 0 is used.
|
||||||
|
*/
|
||||||
|
val thresholds: JsArray<JsNumber>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the IntersectionObserver object from observing any target.
|
||||||
|
*/
|
||||||
|
fun disconnect()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells the IntersectionObserver a target element to observe.
|
||||||
|
*/
|
||||||
|
fun observe(targetElement: Element)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of IntersectionObserverEntry objects for all observed targets.
|
||||||
|
*/
|
||||||
|
fun takeRecords(): JsArray<IntersectionObserverEntry>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells the IntersectionObserver to stop observing a particular target element.
|
||||||
|
*/
|
||||||
|
fun unobserve(targetElement: Element)
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import org.w3c.dom.Element
|
||||||
|
|
||||||
|
inline val Element.isOverflowWidth
|
||||||
|
get() = scrollWidth > clientWidth
|
||||||
|
|
||||||
|
inline val Element.isOverflowHeight
|
||||||
|
get() = scrollHeight > clientHeight
|
||||||
|
|
||||||
|
inline val Element.isOverflow
|
||||||
|
get() = isOverflowHeight || isOverflowWidth
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import kotlinx.coroutines.await
|
||||||
|
import org.khronos.webgl.ArrayBuffer
|
||||||
|
import org.w3c.dom.ErrorEvent
|
||||||
|
import org.w3c.files.*
|
||||||
|
import kotlin.js.Promise
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @suppress
|
||||||
|
*/
|
||||||
|
actual typealias MPPFile = File
|
||||||
|
|
||||||
|
private fun createJsError(message: String): JsAny = js("Error(message)")
|
||||||
|
|
||||||
|
fun MPPFile.readBytesPromise() = Promise { success, failure ->
|
||||||
|
val reader = FileReader()
|
||||||
|
reader.onload = {
|
||||||
|
success((reader.result as ArrayBuffer))
|
||||||
|
}
|
||||||
|
reader.onerror = {
|
||||||
|
val message = (it as ErrorEvent).message
|
||||||
|
failure(createJsError(message))
|
||||||
|
}
|
||||||
|
reader.readAsArrayBuffer(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun MPPFile.readBytes(): ByteArray {
|
||||||
|
val reader = FileReaderSync()
|
||||||
|
return reader.readAsArrayBuffer(this).toByteArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun MPPFile.dirtyReadBytes(): ByteArray = readBytesPromise().await<ArrayBuffer>().toByteArray()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @suppress
|
||||||
|
*/
|
||||||
|
actual val MPPFile.filename: FileName
|
||||||
|
get() = FileName(name)
|
||||||
|
|
||||||
|
actual val MPPFilePathSeparator: Char
|
||||||
|
get() = '/'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @suppress
|
||||||
|
*/
|
||||||
|
actual val MPPFile.filesize: Long
|
||||||
|
get() = jsNumberToBigInt(size).toLong()
|
||||||
|
/**
|
||||||
|
* @suppress
|
||||||
|
*/
|
||||||
|
@Warning("That is not optimized version of bytes allocator. Use asyncBytesAllocator everywhere you can")
|
||||||
|
actual val MPPFile.bytesAllocatorSync: ByteArrayAllocator
|
||||||
|
get() = ::readBytes
|
||||||
|
/**
|
||||||
|
* @suppress
|
||||||
|
*/
|
||||||
|
@Warning("That is not optimized version of bytes allocator. Use asyncBytesAllocator everywhere you can")
|
||||||
|
actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator
|
||||||
|
get() = ::dirtyReadBytes
|
||||||
|
|
||||||
|
private fun jsNumberToBigInt(number: JsNumber): JsBigInt = js("BigInt(number)")
|
||||||
Loaded 100 of 651 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user