Compare commits
998 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
e3c34328ee | ||
|
0e9d34dd9d | ||
|
4597519128 | ||
|
8a7c690d21 | ||
|
07cc8f9787 | ||
|
2a09a76fb0 | ||
|
ddc5001a79 | ||
|
ae53630df3 | ||
|
6f090f3883 | ||
|
b343862f35 | ||
|
c7c9d5d974 | ||
|
10ed8358f9 | ||
|
0be7615056 | ||
|
a0955e225a | ||
|
2ae6f75938 | ||
|
420ec4d24e | ||
|
7bd9e7fa45 | ||
|
e9f6873d8a | ||
|
9dcee3a6ac | ||
|
5fb9e981b7 | ||
|
643114867a | ||
|
879c6acf57 | ||
|
cbbab3adda | ||
|
87f3c2aff0 | ||
|
32d80497cf | ||
|
93061a79a0 | ||
|
7d74ca2372 | ||
|
5d2189a6bb | ||
|
d4b92ad765 | ||
|
9a1f24ddb9 | ||
|
908f114400 | ||
|
53f35d19ed | ||
|
77fd709836 | ||
|
f3d7b6c2e1 | ||
|
94d2f562b8 | ||
|
b68d4e2ae1 | ||
|
8cfc2e1ac7 | ||
|
36bfd90672 | ||
|
dc2618d0f4 | ||
|
fa62fc4240 | ||
|
d81a70b4c6 | ||
|
34957f7282 | ||
|
98fc1110df | ||
|
06cecb946e | ||
|
86061a7680 | ||
|
4584c0b5ec | ||
|
3673ca15e2 | ||
|
b632397022 | ||
|
a19bf7d1e2 | ||
|
57ae0ef0c4 | ||
|
7f39417349 | ||
|
4625b5bb97 | ||
|
bfcf5550cf | ||
|
b672f52bf8 | ||
|
25f2dc1cc8 | ||
|
454f3e15ae | ||
|
83ca3e8a2b | ||
|
f526ad5d6b | ||
|
fecf32f3c6 | ||
|
34ad0aae0f | ||
|
f07ee800a8 | ||
|
47acd98a6b | ||
|
94b4b5c4b1 | ||
|
99a7fdb9bd | ||
|
c3bb69aaed | ||
|
0a92b9772a | ||
|
edf6f80adc | ||
|
87406c1278 | ||
|
c5d7b4d6e7 | ||
|
8c7b452b1f | ||
|
d3ce661865 | ||
|
ab2f70a258 | ||
|
12e2c9cbfb | ||
|
431d849bcd | ||
|
4319c3160b | ||
|
9299056daa | ||
|
81900cdc6d | ||
|
ca1599296c | ||
|
b4bcb46fae | ||
|
591026c65c | ||
|
4b1b3fb12b | ||
|
a089732ba2 | ||
|
f9b1f23b11 | ||
|
4a65b1cb29 | ||
|
b31f4ff67b | ||
|
938396b500 | ||
|
1006b9b2b4 | ||
|
1c27842ab3 | ||
|
0eec6d8189 | ||
|
3f611f7ca2 | ||
|
aaf145feb7 | ||
|
3a6628adea | ||
|
a213ec8467 | ||
|
85bcfc74bc | ||
|
59c6e88311 | ||
|
37c8490abc | ||
|
7f687871b7 | ||
|
da49563c35 | ||
|
b31d74e8ba | ||
|
d65fbacac6 | ||
|
58ebb7e3bc | ||
|
d025f24928 | ||
|
56a82d7cbf | ||
|
655676de7e | ||
|
44ea6d38ac | ||
|
e69bc3efaf | ||
|
e903be2564 | ||
|
b0dd62a90b | ||
|
774f4b4690 | ||
|
3388795df5 | ||
|
59b6740ce5 | ||
|
4f573e54d4 | ||
|
134d271f68 | ||
|
e1452fcda4 | ||
|
edac9e3381 | ||
|
366a602260 | ||
|
45ee641a78 | ||
|
f7370be6a5 | ||
|
2dd845f7e0 | ||
|
8ccb67cb8a | ||
|
0931854b98 | ||
|
9697badf22 | ||
|
679c29069d | ||
|
76b92815b0 | ||
|
e5ed751c1f | ||
|
b84a584995 | ||
|
6035b4ee37 | ||
|
5bd1122a2b | ||
|
cc028458bd | ||
|
b9757404e3 | ||
|
ecc9eab92e | ||
|
6649018303 | ||
|
d7032bf945 | ||
|
e5f473d9b6 | ||
|
ec56f9471c | ||
|
65531c7870 | ||
|
ca387e3261 | ||
|
4a5a4b4cde | ||
|
94d2cdf81d | ||
|
97e051235d | ||
|
c85d6462b5 | ||
|
893f795228 | ||
|
9f426e2c6d | ||
|
8b3b816005 | ||
|
41fb45cd90 | ||
|
793907383a | ||
|
76b3559dfe | ||
|
e51a8d73aa | ||
|
8e2118dc67 | ||
|
cc1e231297 | ||
|
7cf3823bea | ||
|
338c732e5f | ||
|
c646ca611b | ||
|
b79ddd453d | ||
|
5158e408d7 | ||
|
a0aec0b176 | ||
|
0bf579e4b0 | ||
|
75d41ee146 | ||
|
d161a58424 | ||
|
e76cf4097f | ||
|
0092ae1e30 | ||
|
0ed68fd993 | ||
|
69dde233e4 | ||
|
7f3fe72503 | ||
|
0b5809b07d | ||
|
ebd7d029cf | ||
|
04c1df671d | ||
|
862fc14744 | ||
|
eeff96e1db | ||
|
eea9e9122e | ||
|
537d31521b | ||
|
73989239b9 | ||
|
67a12518bd | ||
|
0cc9ed3ae9 | ||
|
72c44056b2 | ||
|
5891f28548 | ||
|
efe9189d76 | ||
|
108174e040 | ||
|
782889079f | ||
|
de4772bf1e | ||
|
30269e7dde | ||
|
828fa2e91d | ||
|
39cb8ca758 | ||
|
d60117236f | ||
|
4b30e5f6f2 | ||
|
22a6f63230 | ||
|
b7b309f1d4 | ||
|
64bf58e2ea | ||
|
5ca1b6a3e7 | ||
|
6813a4c98e | ||
|
09e3ec218e | ||
|
2122882f37 | ||
|
21b5cd056e | ||
|
97a528c5d3 | ||
|
bca6fd7f67 | ||
|
5307cb1fe0 | ||
|
21b6e3b74b | ||
|
426b99ec73 | ||
|
b5ef87f99f | ||
|
57941187dd | ||
|
10776c0f52 | ||
|
5ea7f82c2f | ||
|
5ef500e916 | ||
|
5c774ceccc | ||
|
16661287c3 | ||
|
c8015d121d | ||
|
8a88b7fb07 | ||
|
cebd738569 | ||
|
71e4f016dd | ||
|
6616969582 | ||
|
c5325049f6 | ||
|
b7c45229d0 | ||
|
8a7e0fe8ff | ||
|
cb6c0ecffe | ||
|
865733b151 | ||
|
67f6808f6d | ||
|
e0a7eb4cd1 | ||
|
316e7296e4 | ||
|
da2e81fbdb | ||
|
4372f80a6b | ||
|
93ccda449f | ||
|
9396a8b036 | ||
|
213b5bd5dd | ||
|
d29766fad5 | ||
|
930ab34755 | ||
|
25c79797d1 | ||
|
f07d350628 | ||
|
5c9f9a2aff | ||
|
69da78094c | ||
|
163fe968f2 | ||
|
d702e0bb56 | ||
|
2c66f77be7 | ||
|
15f21d1467 | ||
|
d870c1497d | ||
|
7a1d8ffa19 | ||
|
a2cc7999c3 | ||
|
8e623c55c4 | ||
|
834a06612a | ||
|
d63a96c509 | ||
|
3dc3333d99 | ||
|
c46404cbbf | ||
|
b2dd3acd08 | ||
|
da8fb3b112 | ||
|
065e139126 | ||
|
25b7b1603b | ||
|
3a6416f1b3 | ||
|
194beff585 | ||
|
3d0c9a78e7 | ||
|
77d1c8a846 | ||
|
b7eaa606bc | ||
|
b577acb584 | ||
|
9f9b0129f0 | ||
|
c1a669a9a2 | ||
|
14edc8d9fa | ||
|
41ab799c37 | ||
|
d7b79251e1 | ||
|
d427026b5b | ||
|
49f2f71e61 | ||
|
8af7a922e3 | ||
|
a263904386 | ||
|
e0f8bd5c28 | ||
|
8a78a7a3bf | ||
|
5c66a39c0c | ||
|
48f569f327 | ||
|
a137928496 | ||
|
8285bc794b | ||
|
fa51da6393 | ||
|
e708570703 | ||
|
d63d5c229b | ||
|
9566d14940 | ||
|
121219e539 | ||
|
2c84f96d46 | ||
|
a0fb433fd2 | ||
|
8eeb955746 | ||
|
fba0195977 | ||
|
d78a59dfed | ||
|
e7727a4b82 | ||
|
f513aa5f8b | ||
|
ed66251c10 | ||
|
7b425eef0e | ||
|
2a5da4b12e | ||
|
2e1bfefde6 | ||
|
cb921ac21e | ||
|
37fc796c9c | ||
|
384a80afbb | ||
|
72ea4095c9 | ||
|
eab4d059f1 | ||
|
b57f072c22 | ||
|
b1050a555a | ||
|
73d9a142a8 | ||
|
be97fb5d03 | ||
|
ad8d43667e | ||
|
c55e39a640 | ||
|
cd1b8419dc | ||
|
af780758e9 | ||
|
dea4ca177a | ||
|
a7d41b9f99 | ||
|
aabae3d500 | ||
|
499f9f9370 | ||
|
54b4540d32 | ||
|
5a1cc6497e | ||
|
03d27d222d | ||
|
694a07bfec | ||
|
84b5d8f50e | ||
|
b509d80b9f | ||
|
2941169b80 | ||
|
33edfe65cf | ||
|
6b8c6db53d | ||
|
9ea87bd5a3 | ||
|
5aefc3601e | ||
|
901690e77f | ||
|
c58781521d | ||
|
32d4dfdaf8 | ||
|
91d66c6654 | ||
|
10d3c7ec8d | ||
|
4b846fd9d7 | ||
|
6d383f0a9b | ||
|
126a5daa53 | ||
|
cc1062b423 | ||
|
2f107332fd | ||
|
cdeee6bffb | ||
|
8126ce3a40 | ||
|
41efd8b809 | ||
|
c2253f2507 | ||
|
5564609c99 | ||
|
be8c021c7f | ||
|
e3e5a2bf33 | ||
|
8cc9b6c705 | ||
|
d8cc3eed82 | ||
|
95a37306ff | ||
|
d5cbeeef99 | ||
|
d726cbc5a9 | ||
|
c51971f37c | ||
|
7c8e4b2577 | ||
|
0fb493674a | ||
|
2cafa29b92 | ||
|
7911dd2134 | ||
|
2b8276c416 | ||
|
b4224653c9 | ||
|
6b04bbcf93 | ||
|
c0620fbafb | ||
|
986491fcaa | ||
|
67362776c6 | ||
|
5fff035bff | ||
|
8a6b71e496 | ||
|
1d69e8282b | ||
|
3736e2782d | ||
|
a37e63aecc | ||
|
8471160d23 | ||
|
2f9908fd07 | ||
|
8dc5668ff6 | ||
|
df23a0ec3a | ||
|
49f2ab6677 | ||
|
5f48f0f973 | ||
|
a005dfea72 | ||
|
30a764b4e3 | ||
|
14ec802dfd | ||
|
ca18d920c1 | ||
|
229a5a1d31 | ||
|
56d395581b | ||
|
b67d763dea | ||
|
e9b4ffa5b0 | ||
|
cedc6fc539 | ||
|
6246535d5b | ||
|
a22d09af91 | ||
|
42c8bd045a | ||
|
929c12836c | ||
|
42b5fddadb | ||
|
cfbf9ffa61 | ||
|
3618573f83 | ||
|
e8f4b641b9 | ||
|
38b6523235 | ||
|
2187ca7146 | ||
|
53cec746df | ||
|
be682d69b0 | ||
|
81181b3dad | ||
|
e5683ed537 | ||
|
7ba8a2d98e | ||
|
85c63d9a62 | ||
|
a3df283fa3 | ||
|
f8ce19f33b | ||
|
9280b05b9f | ||
|
0a105f5b83 | ||
|
b882606bf8 | ||
|
5af9cf7e9b | ||
|
e1c9658d34 | ||
|
11b42442aa | ||
|
129b841c47 | ||
|
407fbba876 | ||
|
94c333f8a0 | ||
|
a04dc4cd69 | ||
|
f50c06b5fe | ||
|
c5955bbc74 | ||
|
55ce058dc5 | ||
|
1250725930 | ||
|
3a22a65549 | ||
|
fcb4b80194 | ||
|
edb65a3d14 | ||
|
6f699fa949 | ||
|
98d0113460 | ||
|
86237c09ff | ||
|
274c0cc1a6 | ||
|
5357575947 | ||
|
61d5e1bd13 | ||
|
46f6a77a6c | ||
|
7cd95632de | ||
|
f480a232a8 | ||
|
63b4fe073b | ||
|
5bd51cf27d | ||
|
07f36e2184 | ||
|
a4dec0503c | ||
|
2ff346f3fd | ||
|
c23ded9f55 | ||
|
b64ad3bbec | ||
|
7d3ea11331 | ||
|
5cec99f599 | ||
|
80255955c7 | ||
|
7e40957a5e | ||
|
e59d99e344 | ||
|
68f9c46559 | ||
|
54a8eabf64 | ||
|
97c6c46b18 | ||
|
f11c57ed5e | ||
|
734c6d6d8c | ||
|
0b6cc2019f | ||
|
706c842d56 | ||
|
40af0116fb | ||
|
a5c71dec29 | ||
|
8bccf661d4 | ||
|
3eb5f9b99b | ||
|
37fbb89a26 | ||
|
6508f7411a | ||
|
0ba4b655c0 | ||
|
2835b999f8 | ||
|
10be4b5a6e | ||
|
321232e005 | ||
|
a57e25c304 | ||
|
a849341e3a | ||
|
7d64667c90 | ||
|
7407ce97ae | ||
|
ebd5fbc1cf | ||
|
06471e66b9 | ||
|
47d47b290e | ||
|
ff12986452 | ||
|
d6388dfc97 | ||
|
ffed9ac679 | ||
|
42f7dba972 | ||
|
d1b1f4ad3f | ||
|
5116bb49d8 | ||
|
e6f2e59fe5 | ||
|
0f5d6e9831 | ||
|
e744adbe32 | ||
|
0ee4286ff2 | ||
|
f8bde01838 | ||
|
f98f3a046b | ||
|
f293b5ef60 | ||
|
b2b48e7a64 | ||
|
8123065122 | ||
|
01a3835641 | ||
|
50430263c9 | ||
|
fd48e88fcc | ||
|
c32ba2efad | ||
|
0c144bcfb5 | ||
|
ea1a12763b | ||
|
f0cb618fd7 | ||
|
2d0552b74d | ||
|
cb784482b7 | ||
|
44c387f74c | ||
|
cccc676bdd | ||
|
87f5205c8e | ||
|
de8ccf8007 | ||
|
aee4319640 | ||
|
c8345ac4b1 | ||
|
a81dd8400e | ||
|
0298b69f12 | ||
|
8a463486c9 | ||
|
63e3562bea | ||
|
28e17f4379 | ||
|
3ea780bfe1 | ||
|
49d04959d2 | ||
|
4461c28d0a | ||
|
aab3a13c35 | ||
|
04bc8c6aef | ||
|
573e15d8a7 | ||
|
9627e8e862 | ||
|
b4efcb049a | ||
|
24dddb3043 | ||
|
e7bf640b21 | ||
|
5f8ca2e66a | ||
|
076298a71b | ||
|
e577d1c1b6 | ||
|
b6c074b768 | ||
|
332fcefbe0 | ||
|
a65ffc1b08 | ||
|
e0b528125b | ||
|
2cface3adb | ||
|
daf6a01d1f | ||
|
33f46ac49f | ||
|
7ce6d404e8 | ||
|
efc6565076 | ||
|
15902b31d9 | ||
|
4d820c01f0 | ||
|
2a5157b1df | ||
|
07bc3302fc | ||
|
1741a99b93 | ||
|
9048261ccd | ||
|
9c330d3114 | ||
|
37be41c8ca | ||
|
023494813a | ||
|
8d9093c70b | ||
|
1fd5ec42c7 | ||
|
c58cb065d2 | ||
|
3d0dbf5319 | ||
|
87bc3dc4fd | ||
|
076119eda7 | ||
|
5f176df711 | ||
|
2de11e9cae | ||
|
b11baa03d1 | ||
|
c795f87c2f | ||
|
28a3d36052 | ||
|
eaf1220d36 | ||
|
5e95a23b76 | ||
|
5aee42ce1c | ||
|
a8cf08531f | ||
|
16020e849a | ||
|
8c7e12ee0a | ||
|
bf631bc323 | ||
|
c0d0ce935b | ||
|
eabf884352 | ||
|
e2e12bfb8f | ||
|
560f1e7a64 | ||
|
f35a5c96cc | ||
|
10e854f782 | ||
|
79010ba8b3 | ||
|
3dfc710c30 | ||
|
7bf6b52c56 | ||
|
130dfee8a1 | ||
|
2e0060effa | ||
|
bfd6aa9008 | ||
|
43cd8b5052 | ||
|
09d8a1b1b5 | ||
|
162ce7dacb | ||
|
405c308552 | ||
|
805c6adf44 | ||
|
760e5e0b40 | ||
|
89162d4ac1 | ||
|
fd570510ae | ||
|
ae1ca01f97 | ||
|
752f8941c0 | ||
|
a7aa573373 | ||
|
df8a020776 | ||
|
c7b29cd8ac | ||
|
fcbdb5da1c | ||
|
65a5db9166 | ||
|
a1957981ee | ||
|
9e5f1f4a4a | ||
|
55724695ce | ||
|
34af3e2a0d | ||
|
2b5b537f9e | ||
|
55e0d13615 | ||
|
23d0960389 | ||
|
efeb245066 | ||
|
53cb04cdc7 | ||
|
0fea602ec9 | ||
|
73745fe94e | ||
|
a911a13eed | ||
|
68af0e23c5 | ||
|
1f2dd7a8cf | ||
|
72bee09a8c | ||
|
0446069a3f | ||
|
b97530a327 | ||
|
94d531602b | ||
|
a0df0592cb | ||
|
3c5eadb988 | ||
|
c6327c6a74 | ||
|
e739c979ce | ||
|
e19ef66d11 | ||
|
44311dfbe1 | ||
|
efebc94ecf | ||
|
e9f581e450 | ||
|
cb26488cc1 | ||
|
8891505584 | ||
|
f4664ce701 | ||
|
e190a20be7 | ||
|
58fad0a138 | ||
|
6deef7148f | ||
|
b3b59c555c | ||
|
933d74ec81 | ||
|
0ad6656b34 | ||
|
b847a13f77 | ||
|
cabb46ccdc | ||
|
6aacd5c1b9 | ||
|
17bbf1a6a5 | ||
|
ddcc69dd55 | ||
|
e38140adb8 | ||
|
8b7aa4f429 | ||
|
f3fcfdbd7f | ||
|
63fca01cfc | ||
|
aa0a8be796 | ||
|
f751d58ec5 | ||
|
f3d8673913 | ||
|
4e2f271038 | ||
|
5665f77a99 | ||
|
0c08892570 | ||
|
d349ce4983 | ||
|
20b5ded557 | ||
|
c3242b1080 | ||
|
c4b2420bd8 | ||
|
1fdcd73be2 | ||
|
9859e4c800 | ||
|
c96609ff8a | ||
|
473c5d53bf | ||
|
78cd7b057d | ||
|
dd5b1c9335 | ||
|
0b044491a1 | ||
|
0fed338255 | ||
|
9ddbf96e2a | ||
|
4650c2d22c | ||
|
bf7cb9024e | ||
|
4a8625b5fe | ||
|
113c97d5f2 | ||
|
b4118978df | ||
|
f535b931a8 | ||
|
25e7ee4cb7 | ||
|
f94357ebe8 | ||
|
4f4085ada4 | ||
|
857cf0d75c | ||
|
52d6b66329 | ||
|
478dab3ecd | ||
|
8e38f554e3 | ||
|
cae850afd2 | ||
|
3400931fb3 | ||
|
a58b71f15a | ||
|
7f99225953 | ||
|
662b590a0b | ||
|
7e2f334d41 | ||
|
098a03ef81 | ||
|
488ed24fe9 | ||
|
71d492ea6f | ||
|
f1d2fce121 | ||
|
bd248f9df7 | ||
|
e9d9ae44da | ||
|
2a86f44038 | ||
|
5795a59547 | ||
|
e112fa3d96 | ||
|
92fc80f90a | ||
|
16ade07284 | ||
|
63404476b5 | ||
|
5fe52ce014 | ||
|
b0356547ef | ||
|
553881fcc7 | ||
|
74a2a1fe26 | ||
|
f19552dbb6 | ||
|
7cbdedfabd | ||
|
1a144e4b58 | ||
|
1f123518dc | ||
|
3e303b7910 | ||
|
091b55fc8a | ||
|
18f863d70a | ||
|
ba47397def | ||
|
4ec71c2541 | ||
|
0f509a20be | ||
|
39c7d8b1c7 | ||
|
8e6edd2793 | ||
|
467e40b6c9 | ||
|
c1c9f7a587 | ||
|
b397ae55e7 | ||
|
7e672a7bab | ||
|
3e82e98057 | ||
|
50a5852df8 | ||
|
a8faf271b9 | ||
|
be2b2906b0 | ||
|
ae8fed98cf | ||
|
919d1ae985 | ||
|
1f19619df0 | ||
|
3f7a98c4c1 | ||
|
6fbc12cddd | ||
|
8d25e9c4ef | ||
|
82ea97945a | ||
|
a58016e64e | ||
|
bfafa8c1f3 | ||
|
e6cad63983 | ||
|
7514c4ff66 | ||
|
5be38a3b02 | ||
|
90354ff788 | ||
|
e79e408baa | ||
|
8969c51976 | ||
|
a06e5c6e61 | ||
|
2090a8b59e | ||
|
acd319da16 | ||
|
4e145419e7 | ||
|
a28744f34b | ||
|
bca4665e36 | ||
|
28a9c8855b | ||
|
3f4948eb39 | ||
|
7ac839fad6 | ||
|
d38272e67a | ||
|
904a6da4df | ||
|
a66e5bb100 | ||
|
cbc84a25d4 | ||
|
e661afc1cf | ||
|
f7109daa59 | ||
|
cc5a737f21 | ||
|
4a26fca3b7 | ||
|
09cb50ce4b | ||
|
b4bbba2d37 | ||
|
25d861f7e0 | ||
|
62fa87e272 | ||
|
2003b7bfae | ||
|
6c67393d88 | ||
|
7e64373340 | ||
|
17890b5d8e | ||
|
2aec255fcd | ||
|
ffae75bc34 | ||
|
d4b36a7d4c | ||
|
ba5901168d | ||
|
05898dc06c | ||
|
12492b7084 | ||
|
acb74e6ace | ||
|
ff890596c7 | ||
|
0a59e3581b | ||
|
71b843464b | ||
|
298e933373 | ||
|
b232414407 | ||
|
049d66a936 | ||
|
dbbc5f457d | ||
|
b04471e693 | ||
|
cb5b350b66 | ||
|
419d0f036c | ||
|
f6abd0cb55 | ||
|
f543ba5477 | ||
|
79f85e92b0 | ||
|
42041f3663 | ||
|
1c9754f02f | ||
|
7c34c27d3e | ||
|
97fb610569 | ||
|
59a62b8552 | ||
|
20a2342de1 | ||
|
006aa1196c | ||
|
b05d19ebda | ||
|
12b9dc6cc3 | ||
|
1abe1cfef1 | ||
|
2b3b0403fe | ||
|
e5e172b69a | ||
|
acc625cd20 | ||
|
7ac70efecd | ||
|
7d000f97f2 | ||
|
23d903e0eb | ||
|
e862909a1b | ||
|
f3ea74df83 | ||
|
598544e422 | ||
|
95eab61130 | ||
|
d0ec5dacaf | ||
|
9ff06b8a2f | ||
|
96d55e3ffb | ||
|
dd9539e93f | ||
|
996590deeb | ||
|
05c24c418e | ||
|
95bd26d6cd | ||
|
2b211360a6 | ||
|
b697a2dd7e | ||
|
e96c1301d3 | ||
|
90c1f1125d | ||
|
4782f43007 | ||
|
f99d65b8d3 | ||
|
278a8eace5 | ||
|
9e011c16b1 | ||
|
f9c48e5e1f | ||
|
60fdd7fa78 | ||
|
012d01f5c7 | ||
|
10f614e4a3 | ||
|
2b430e15dd | ||
|
787260bae6 | ||
|
d3377424ce | ||
|
d447bfb875 | ||
|
10611e379e | ||
|
a815ce84d2 | ||
|
10e5f914d7 | ||
|
62edebc451 | ||
|
bf370d4102 | ||
|
e8b18dddae | ||
|
e4acd03c02 | ||
|
29f0c3dcf9 | ||
|
6b243592c6 | ||
|
f66af4b9f8 | ||
|
4138d08227 | ||
|
6a82e60dc2 | ||
|
63e16cf7e3 | ||
|
8fdfc5beaf | ||
|
9c686151c1 | ||
|
7cf8ec8f96 | ||
|
4af5d94c52 | ||
|
11f36a6b71 | ||
|
e96a8532d4 | ||
|
ff64385e0d | ||
|
022fba49d2 | ||
|
8ecaee041d | ||
|
ebb42f0227 | ||
|
d87059b16f | ||
|
a9d360b1bd | ||
|
566c9f0d02 | ||
|
90bf7ca1ce | ||
|
62d0c54d03 | ||
|
7ba4b0b980 | ||
|
84c3e6df70 | ||
|
099dcc9ccf | ||
|
038682b6b0 | ||
|
65089be0a6 | ||
|
456d22bf6a | ||
|
b9e4a5da60 | ||
|
0cc17d8c85 | ||
|
a8f40acb9a | ||
|
f2d30fd573 | ||
|
aec018d627 | ||
|
5acfd1305f | ||
|
93f16b945c | ||
|
c375b29602 | ||
|
a4c4dcf1fa | ||
|
7bf868c498 | ||
|
b06104ecf6 | ||
|
0d45fb6204 | ||
|
a9ecc9b88e | ||
|
0bb4cc20b5 | ||
|
3d24258692 | ||
|
c066d4efe7 | ||
|
4ed24aa859 | ||
|
be6e8516b4 | ||
|
354668568e | ||
|
479005909c | ||
|
64d514cfc7 | ||
|
a11204394f | ||
|
134d0e96dc | ||
|
a42fe6344f | ||
|
411774bd5b | ||
|
773623a6b8 | ||
|
bbc0d9ba17 | ||
|
76872115fd | ||
|
711a1ba259 | ||
|
c236c91de1 | ||
|
7b6b360357 | ||
|
51fe880533 | ||
|
fb1f4b3c96 | ||
|
dfc4a052de | ||
|
4856854e17 | ||
|
979aa38318 | ||
|
9f796716f3 | ||
|
d29f86c7ba | ||
|
5f71d65914 | ||
|
b33352a426 | ||
|
40c9ec24ad | ||
|
2089a3c929 | ||
|
0073b0da44 | ||
|
8893e9bbaa | ||
|
f0d0b75753 | ||
|
28e8395182 | ||
|
3d511e9ef0 | ||
|
fd95fe304c | ||
|
36aa3a25eb | ||
|
4d0f22f36e | ||
|
d147ab0e28 | ||
|
3d5c16d284 | ||
|
285ae92852 | ||
|
ed3dca6cb0 | ||
|
3244d34c57 | ||
|
fd05688c97 | ||
|
41a7038be1 | ||
|
7e6c3a1798 | ||
|
dfdaadcf05 | ||
|
c0ffcbd407 | ||
|
213eca3074 | ||
|
de02f09465 | ||
|
59e4555215 | ||
|
a0a2b8ae9d | ||
|
abc3d9aaf6 | ||
|
48e1867aa3 | ||
|
dbfbef8684 | ||
|
bccf5c1330 | ||
|
d2c7639d3c | ||
|
cfcadaaf7f | ||
|
fd759155b3 | ||
|
0a3f59b0f6 | ||
|
1548cc5c2f | ||
|
29af084d11 | ||
|
0a74a89f38 | ||
|
cb2c72b7bb | ||
|
b43dd41cc2 | ||
|
3fc350abc5 | ||
|
a14a92b017 | ||
|
19f1c54856 | ||
|
9982260c51 | ||
|
b4f551b49c | ||
|
75240c6686 | ||
|
dc25cbf0ee | ||
|
9dd278aa98 | ||
|
59b06567fc | ||
|
53c2017538 | ||
|
a70a12db15 | ||
|
9a417e54dc | ||
|
c30320111e | ||
|
54a35e162f | ||
|
9dfb90c94e | ||
|
105f0745b7 | ||
|
362d4f5a7c | ||
|
ae553500c3 | ||
|
31cb11d152 | ||
|
8f7c7de8d4 | ||
|
a03924ea0e | ||
|
246082803d | ||
|
488362bd6d | ||
|
8ad56b88e7 | ||
|
69022e9b09 | ||
|
19e90e210f | ||
|
f4e6a887f9 | ||
|
764ccb5946 | ||
|
f5a690a5fc | ||
|
ecfada11ef | ||
|
01432f796d | ||
|
223724c043 | ||
|
1b0a746fbb | ||
|
664cc0c58f | ||
|
fdd55f4af4 | ||
|
5e4efe2de8 | ||
|
18050acb14 | ||
|
08284e53e6 | ||
|
80a5a0dfc4 | ||
|
3755d4edfb | ||
|
7fddffb94b | ||
|
2b4fe1b19e | ||
|
247b802109 | ||
|
3405ed347e | ||
|
7f438ac104 | ||
|
5f81203659 | ||
|
c3fa5c6a98 | ||
|
704213557d | ||
|
b209eb6841 | ||
|
fb00ec7f37 | ||
|
3da6f5822a | ||
|
e4ecc68b4e | ||
|
a16f0cb060 | ||
|
32fe805c19 | ||
|
e14269a7e8 | ||
|
d66a7689c2 | ||
|
3bf069f84c | ||
|
d472ff1375 | ||
|
714ef36829 | ||
|
60d1222bca | ||
|
192c0ddfdf | ||
|
48a0c9261e | ||
|
d3b0592298 | ||
|
dec825bbf4 | ||
|
451ba42638 | ||
|
7c05c71fa3 | ||
|
84357b6fc3 | ||
|
ba2bdd9e4e | ||
|
ff7dec6d78 | ||
|
e2e44ff425 | ||
|
481d7f9cca | ||
|
4d84923f64 | ||
|
3f79768ef6 | ||
|
bbbb2cfbd7 | ||
|
5b38895ca7 | ||
|
6273a3efc3 | ||
|
ee8628b1f5 | ||
|
403d147118 | ||
|
6acc5d4498 | ||
|
207d1fa22f | ||
|
5404e92058 | ||
|
1a279e0b8e | ||
|
5e0e34e7da | ||
|
881cd55424 | ||
|
3568cf2028 | ||
|
a39c34bbde | ||
|
25b4b05f77 | ||
|
2450c440a8 | ||
|
46b2854004 | ||
|
404910dd67 | ||
|
732678303d | ||
|
2254a505d5 | ||
|
5799c32120 | ||
|
eed598113e | ||
|
ced1a80c19 | ||
|
fe75a3f639 | ||
|
e81e65a8c2 | ||
|
04f7e8bf92 | ||
|
756fab832a | ||
|
cafe822c1a | ||
|
2dfe996cf6 | ||
|
752125a479 | ||
|
b6c719e920 | ||
|
cb392256bf | ||
|
26d7b2cb34 | ||
|
01b49bffe5 | ||
|
b3a1b29aa9 | ||
|
c3c558a150 | ||
|
6458f99cf5 | ||
|
ef97c6e160 | ||
|
7c448ca64e | ||
|
c7c640aa22 |
3
.gitignore
vendored
@ -11,4 +11,5 @@ src/config.rs
|
||||
subprojects/libadwaita
|
||||
subprojects/gtksourceview
|
||||
.vscode
|
||||
.fenv
|
||||
.fenv
|
||||
.zed
|
||||
|
@ -11,24 +11,15 @@ variables:
|
||||
|
||||
workflow:
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH || $CI_COMMIT_TAG
|
||||
- if: $CI_COMMIT_BRANCH
|
||||
|
||||
include:
|
||||
- local: .gitlab-ci/run_checks.yml
|
||||
rules:
|
||||
- if: $CI_COMMIT_TAG == null
|
||||
- local: .gitlab-ci/build.yml
|
||||
rules:
|
||||
- if: $CI_COMMIT_TAG == null
|
||||
- local: .gitlab-ci/test.yml
|
||||
rules:
|
||||
- if: $CI_COMMIT_TAG == null
|
||||
- local: .gitlab-ci/publish_docs.yml
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == "main"
|
||||
- component: "gitlab.gnome.org/GNOME/citemplates/basic-deploy-docs@master"
|
||||
inputs:
|
||||
docs-job-name: "build-docs"
|
||||
- local: .gitlab-ci/publish_nightly.yml
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == "main"
|
||||
- local: .gitlab-ci/create_release.yml
|
||||
rules:
|
||||
- if: $CI_COMMIT_TAG
|
||||
|
@ -1,6 +1,9 @@
|
||||
# Build the Flatpak
|
||||
|
||||
include: 'https://gitlab.gnome.org/GNOME/citemplates/-/raw/master/flatpak/flatpak_ci_initiative.yml'
|
||||
include:
|
||||
- project: "GNOME/citemplates"
|
||||
file: "flatpak/flatpak_ci_initiative.yml"
|
||||
- local: '.gitlab-ci/utils.yml'
|
||||
|
||||
variables:
|
||||
RUNTIME_REPO: "https://nightly.gnome.org/gnome-nightly.flatpakrepo"
|
||||
@ -10,16 +13,41 @@ build@x86_64:
|
||||
extends:
|
||||
- .flatpak@x86_64
|
||||
stage: build
|
||||
after_script:
|
||||
# Copy the metainfo file to the current directory.
|
||||
- cp flatpak_app/files/share/metainfo/*.metainfo.xml .
|
||||
artifacts:
|
||||
paths:
|
||||
- $BUNDLE
|
||||
- 'repo.tar'
|
||||
- '.flatpak-builder/build/${FLATPAK_MODULE}/_flatpak_build/meson-logs/meson-log.txt'
|
||||
- '.flatpak-builder/build/${FLATPAK_MODULE}/_flatpak_build/meson-logs/testlog.txt'
|
||||
- '.flatpak-builder/build/${FLATPAK_MODULE}/_flatpak_build/meson-logs/'
|
||||
# Add the metainfo file for the corresponding test.
|
||||
- '.flatpak-builder/build/${FLATPAK_MODULE}/_flatpak_build/data/${APP_ID}.metainfo.xml'
|
||||
- '${APP_ID}.metainfo.xml'
|
||||
|
||||
build@aarch64:
|
||||
extends:
|
||||
- .flatpak@aarch64
|
||||
stage: build
|
||||
|
||||
# Test builds with the stable runtime to make sure that the Flatpak will build on Flathub.
|
||||
# Should be run manually before tagging a new release.
|
||||
.build_stable:
|
||||
extends:
|
||||
- .stable_runtime
|
||||
variables:
|
||||
RUNTIME_REPO: "https://flathub.org/repo/flathub.flatpakrepo"
|
||||
RUN_TESTS: 0
|
||||
|
||||
build-stable@x86_64:
|
||||
extends:
|
||||
- .flatpak@x86_64
|
||||
- .build_stable
|
||||
stage: build
|
||||
when: manual
|
||||
|
||||
build-stable@aarch64:
|
||||
extends:
|
||||
- .flatpak@aarch64
|
||||
- .build_stable
|
||||
stage: build
|
||||
when: manual
|
||||
|
@ -1,72 +0,0 @@
|
||||
# Generate a tarball, test it, then publish it to the package registry and create a release
|
||||
|
||||
variables:
|
||||
PACKAGE_REGISTRY_URL: "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/release-tarballs/${CI_COMMIT_TAG}"
|
||||
RELEASE_TARBALL: "fractal-${CI_COMMIT_TAG}.tar.xz"
|
||||
RELEASE_TARBALL_SHA: "fractal-${CI_COMMIT_TAG}.tar.xz.sha256sum"
|
||||
METAINFO_PATH: "data/org.gnome.Fractal.metainfo.xml.in.in"
|
||||
TARBALL_TEST_MANIFEST_PATH: ".gitlab-ci/org.gnome.Fractal.CiTest.json"
|
||||
|
||||
release-tarball:
|
||||
stage: build
|
||||
image: 'quay.io/gnome_infrastructure/gnome-runtime-images:gnome-master'
|
||||
variables:
|
||||
LANG: "C.UTF-8"
|
||||
tags:
|
||||
- flatpak
|
||||
script:
|
||||
# Create tarball with vendored rust dependencies
|
||||
- flatpak-builder --keep-build-dirs --user --disable-rofiles-fuse --stop-at=${FLATPAK_MODULE} flatpak_app --repo=repo ${BRANCH:+--default-branch=$BRANCH} ${MANIFEST_PATH}
|
||||
- echo "meson dist --no-test --allow-dirty" | flatpak-builder --disable-rofiles-fuse --build-shell=${FLATPAK_MODULE} flatpak_app ${MANIFEST_PATH}
|
||||
- mv .flatpak-builder/build/${FLATPAK_MODULE}/_flatpak_build/meson-dist tarball
|
||||
|
||||
# Extract release notes of latest version from appstream
|
||||
- appstreamcli metainfo-to-news --format markdown ${METAINFO_PATH} - |
|
||||
awk '{ x[NR] = $0 } END { for ( i=5 ; i<=NR ; i++ ) { if (x[i] ~ /^$/ && x[i+1] ~ /^Version/ && x[i+2] ~ /^-/ && x[i+3] ~ /^Released:/) exit; else print x[i]; }}' >
|
||||
release_notes.md
|
||||
artifacts:
|
||||
paths:
|
||||
- tarball
|
||||
- release_notes.md
|
||||
|
||||
test-tarball:
|
||||
stage: test
|
||||
image: 'quay.io/gnome_infrastructure/gnome-runtime-images:gnome-46'
|
||||
tags:
|
||||
- flatpak
|
||||
script:
|
||||
- TARBALL_SHA=$(cut -f 1 -d " " tarball/${RELEASE_TARBALL_SHA})
|
||||
- sed -i "s|%%TARBALL_PATH%%|${PWD}/tarball/${RELEASE_TARBALL}|g" ${TARBALL_TEST_MANIFEST_PATH}
|
||||
- sed -i "s|%%TARBALL_SHA%%|${TARBALL_SHA}|g" ${TARBALL_TEST_MANIFEST_PATH}
|
||||
- flatpak-builder --user --disable-rofiles-fuse --sandbox flatpak_app --repo=repo ${BRANCH:+--default-branch=$BRANCH} ${TARBALL_TEST_MANIFEST_PATH}
|
||||
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/271534 The default URL needs authentication right now, which is less than optimal...
|
||||
# Get the package_files ID instead and use that as download URL in the "release" step.
|
||||
create-release:
|
||||
stage: deploy
|
||||
image: registry.gitlab.com/gitlab-org/release-cli:latest
|
||||
script:
|
||||
- apk add curl jq
|
||||
|
||||
# Upload tarball to package registry
|
||||
- |
|
||||
curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file tarball/${RELEASE_TARBALL} "${PACKAGE_REGISTRY_URL}/${RELEASE_TARBALL}"
|
||||
- |
|
||||
curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file tarball/${RELEASE_TARBALL_SHA} "${PACKAGE_REGISTRY_URL}/${RELEASE_TARBALL_SHA}"
|
||||
|
||||
# Get package IDs and build URLs
|
||||
- |
|
||||
export PACKAGE_ID=$(curl "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages?sort=desc" | jq -c .[0].id)
|
||||
- |
|
||||
export RELEASE_TARBALL_DOWNLOAD_ID=$(curl "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/${PACKAGE_ID}/package_files" | jq -c .[0].id)
|
||||
export RELEASE_TARBALL_SHA_DOWNLOAD_ID=$(curl "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/${PACKAGE_ID}/package_files" | jq -c .[1].id)
|
||||
- export RELEASE_TARBALL_URL="${CI_PROJECT_URL}/-/package_files/$RELEASE_TARBALL_DOWNLOAD_ID/download"
|
||||
- export RELEASE_TARBALL_SHA_URL="${CI_PROJECT_URL}/-/package_files/$RELEASE_TARBALL_SHA_DOWNLOAD_ID/download"
|
||||
- cat release_notes.md
|
||||
|
||||
# Create release
|
||||
- |
|
||||
release-cli create --name "$CI_COMMIT_TAG" --tag-name $CI_COMMIT_TAG \
|
||||
--assets-link "{\"name\":\"Tarball for ${CI_COMMIT_TAG}\",\"url\":\"${RELEASE_TARBALL_URL}\", \"filepath\": \"/tarball/${RELEASE_TARBALL}\"}" \
|
||||
--assets-link "{\"name\":\"Checksum for ${CI_COMMIT_TAG}\",\"url\":\"${RELEASE_TARBALL_SHA_URL}\", \"filepath\": \"/tarball/${RELEASE_TARBALL_SHA}\"}" \
|
||||
--description release_notes.md
|
17
.gitlab-ci/nextest.module.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "cargo-nextest",
|
||||
"buildsystem": "simple",
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://github.com/nextest-rs/nextest/releases/download/cargo-nextest-0.9.100/cargo-nextest-0.9.100-x86_64-unknown-linux-gnu.tar.gz",
|
||||
"sha256": "de8843f9d4cd72ba7ff3995679536b8a5638ebc8f94848cb988c7549d9dc4e7d",
|
||||
"dest": "cargo-nextest",
|
||||
"strip-components": 0
|
||||
}
|
||||
],
|
||||
"build-commands": [
|
||||
"mkdir /app/bin",
|
||||
"mv cargo-nextest/cargo-nextest /app/bin/"
|
||||
]
|
||||
}
|
5
.gitlab-ci/nextest.toml
Normal file
@ -0,0 +1,5 @@
|
||||
[profile.default]
|
||||
fail-fast = false
|
||||
|
||||
[profile.default.junit]
|
||||
path = "junit.xml"
|
@ -1,82 +0,0 @@
|
||||
{
|
||||
"app-id": "org.gnome.Fractal",
|
||||
"runtime": "org.gnome.Platform",
|
||||
"runtime-version": "46",
|
||||
"sdk": "org.gnome.Sdk",
|
||||
"sdk-extensions": [
|
||||
"org.freedesktop.Sdk.Extension.rust-stable",
|
||||
"org.freedesktop.Sdk.Extension.llvm16"
|
||||
],
|
||||
"command": "fractal",
|
||||
"finish-args": [
|
||||
"--share=network",
|
||||
"--share=ipc",
|
||||
"--socket=fallback-x11",
|
||||
"--socket=wayland",
|
||||
"--socket=pulseaudio",
|
||||
"--device=dri",
|
||||
"--env=RUST_LOG=fractal=info,warn"
|
||||
],
|
||||
"build-options": {
|
||||
"append-ld-library-path": "/usr/lib/sdk/llvm16/lib",
|
||||
"append-path": "/usr/lib/sdk/llvm16/bin:/usr/lib/sdk/rust-stable/bin"
|
||||
},
|
||||
"modules": [
|
||||
{
|
||||
"name" : "protobuf",
|
||||
"buildsystem" : "autotools",
|
||||
"config-opts": [ "DIST_LANG=cpp" ],
|
||||
"cleanup" : [
|
||||
"/bin/protoc*",
|
||||
"/lib/libprotoc*",
|
||||
"/lib/libprotobuf-lite*"
|
||||
],
|
||||
"sources" : [
|
||||
{
|
||||
"type" : "archive",
|
||||
"url" : "https://github.com/protocolbuffers/protobuf/releases/download/v3.17.3/protobuf-all-3.17.3.tar.gz",
|
||||
"sha256" : "77ad26d3f65222fd96ccc18b055632b0bfedf295cb748b712a98ba1ac0b704b2"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "protobuf-c",
|
||||
"buildsystem" : "autotools",
|
||||
"sources" : [
|
||||
{
|
||||
"type" : "archive",
|
||||
"url" : "https://github.com/protobuf-c/protobuf-c/releases/download/v1.4.0/protobuf-c-1.4.0.tar.gz",
|
||||
"sha256" : "26d98ee9bf18a6eba0d3f855ddec31dbe857667d269bc0b6017335572f85bbcb"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "libshumate",
|
||||
"buildsystem": "meson",
|
||||
"config-opts": [
|
||||
"-Dgir=false",
|
||||
"-Dvapi=false",
|
||||
"-Dgtk_doc=false",
|
||||
"-Dvector_renderer=true"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://download.gnome.org/sources/libshumate/1.2/libshumate-1.2.0.tar.xz",
|
||||
"sha256": "4f8413a707cd00f84cee39ca49f58c48fc436f008ea80d6532ac37dafd0ba96b"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "fractal",
|
||||
"buildsystem": "meson",
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"path": "%%TARBALL_PATH%%",
|
||||
"sha256": "%%TARBALL_SHA%%"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
# Build and publish the docs
|
||||
|
||||
pages:
|
||||
stage: deploy
|
||||
image: 'quay.io/gnome_infrastructure/gnome-runtime-images:gnome-master'
|
||||
tags:
|
||||
- flatpak
|
||||
script:
|
||||
- flatpak install --user --noninteractive org.freedesktop.Sdk.Extension.rust-nightly//23.08
|
||||
# We want to use rust-nightly to build the app, but mold is in rust-stable
|
||||
- sed -i 's|"org.freedesktop.Sdk.Extension.rust-stable"|"org.freedesktop.Sdk.Extension.rust-stable","org.freedesktop.Sdk.Extension.rust-nightly"|g' ${MANIFEST_PATH}
|
||||
- sed -i 's|:/usr/lib/sdk/rust-stable/bin|:/usr/lib/sdk/rust-nightly/bin|g' ${MANIFEST_PATH}
|
||||
- flatpak-builder --keep-build-dirs --user --disable-rofiles-fuse --stop-at=${FLATPAK_MODULE} flatpak_app --repo=repo ${BRANCH:+--default-branch=$BRANCH} ${MANIFEST_PATH}
|
||||
- echo "ninja src/doc" | flatpak-builder --disable-rofiles-fuse --build-shell=${FLATPAK_MODULE} flatpak_app ${MANIFEST_PATH}
|
||||
- mv .flatpak-builder/build/${FLATPAK_MODULE}/_flatpak_build/src/doc public
|
||||
- chmod -R a=rwx public
|
||||
dependencies: []
|
||||
artifacts:
|
||||
paths:
|
||||
- 'public'
|
||||
rules:
|
||||
- changes:
|
||||
- src/**/*
|
||||
- Cargo.lock
|
||||
- Cargo.toml
|
||||
when: always
|
||||
- when: manual
|
||||
allow_failure: true
|
@ -1,6 +1,8 @@
|
||||
# Publish the nightly (Devel) version
|
||||
|
||||
include: 'https://gitlab.gnome.org/GNOME/citemplates/-/raw/master/flatpak/flatpak_ci_initiative.yml'
|
||||
include:
|
||||
- project: "GNOME/citemplates"
|
||||
file: "flatpak/flatpak_ci_initiative.yml"
|
||||
|
||||
publish_nightly@x86_64:
|
||||
extends: .publish_nightly
|
||||
|
@ -1,18 +1,24 @@
|
||||
# Configure and run code checks
|
||||
|
||||
include: '.gitlab-ci/utils.yml'
|
||||
|
||||
# Custom checks and lints
|
||||
checks:
|
||||
stage: check
|
||||
image: "rustlang/rust:nightly-slim"
|
||||
interruptible: true
|
||||
script:
|
||||
- scripts/checks.sh --verbose --force-install
|
||||
- hooks/checks.sh --verbose --force-install
|
||||
|
||||
# Lint the code
|
||||
cargo-clippy:
|
||||
extends:
|
||||
- .remove_build_only_modules
|
||||
stage: check
|
||||
image: 'quay.io/gnome_infrastructure/gnome-runtime-images:gnome-master'
|
||||
tags:
|
||||
- flatpak
|
||||
interruptible: true
|
||||
script:
|
||||
- flatpak-builder --keep-build-dirs --user --disable-rofiles-fuse --stop-at=${FLATPAK_MODULE} flatpak_app --repo=repo ${BRANCH:+--default-branch=$BRANCH} ${MANIFEST_PATH}
|
||||
- echo "cargo clippy -- -D warnings" | flatpak-builder --disable-rofiles-fuse --build-shell=${FLATPAK_MODULE} flatpak_app ${MANIFEST_PATH}
|
||||
|
@ -1,16 +1,69 @@
|
||||
# Tests after the app is built.
|
||||
|
||||
include: '.gitlab-ci/utils.yml'
|
||||
|
||||
# Validate the metainfo with Flathub's tool.
|
||||
metainfo:
|
||||
lint-metainfo:
|
||||
stage: test
|
||||
image:
|
||||
name: "ghcr.io/flathub/flatpak-builder-lint:latest"
|
||||
entrypoint: [""]
|
||||
variables:
|
||||
METAINFO: ".flatpak-builder/build/${FLATPAK_MODULE}/_flatpak_build/data/${APP_ID}.metainfo.xml"
|
||||
METAINFO: "${APP_ID}.metainfo.xml"
|
||||
interruptible: true
|
||||
script:
|
||||
# This tool has extra tests on top of appstreamcli and is required to pass for Flathub.
|
||||
- flatpak-builder-lint appstream ${METAINFO}
|
||||
# Test also with the pedantic and strict flags.
|
||||
- appstreamcli validate --pedantic --explain --strict ${METAINFO}
|
||||
needs: ["build@x86_64"]
|
||||
|
||||
# Run the Rust tests.
|
||||
rust-tests:
|
||||
extends:
|
||||
- .remove_build_only_modules
|
||||
stage: test
|
||||
image: 'quay.io/gnome_infrastructure/gnome-runtime-images:gnome-master'
|
||||
tags:
|
||||
- flatpak
|
||||
interruptible: true
|
||||
script:
|
||||
# Create a temporary file.
|
||||
- TMP_FILE=$(mktemp)
|
||||
# Add a module for nextest to the Flatpak manifest and write it to the temporary file.
|
||||
- jq --slurpfile nextest .gitlab-ci/nextest.module.json '.modules = [$nextest[], .modules[]]' ${MANIFEST_PATH} > ${TMP_FILE}
|
||||
# Replace the manifest with the temporary file.
|
||||
- mv $TMP_FILE ${MANIFEST_PATH}
|
||||
# Initialize the Flatpak sandbox.
|
||||
- flatpak-builder --keep-build-dirs --user --disable-rofiles-fuse --stop-at=${FLATPAK_MODULE} flatpak_app --repo=repo ${BRANCH:+--default-branch=$BRANCH} ${MANIFEST_PATH}
|
||||
# Run the tests.
|
||||
- echo "cargo-nextest nextest run --config-file ../.gitlab-ci/nextest.toml" | flatpak-builder --disable-rofiles-fuse --build-shell=${FLATPAK_MODULE} flatpak_app ${MANIFEST_PATH}
|
||||
dependencies: []
|
||||
artifacts:
|
||||
reports:
|
||||
junit: '.flatpak-builder/build/${FLATPAK_MODULE}/target/nextest/default/junit.xml'
|
||||
|
||||
# Test that there are no errors in the docs.
|
||||
build-docs:
|
||||
extends:
|
||||
- .remove_build_only_modules
|
||||
- .stable_runtime
|
||||
stage: test
|
||||
tags:
|
||||
- flatpak
|
||||
interruptible: true
|
||||
before_script:
|
||||
- !reference [.remove_build_only_modules, before_script]
|
||||
- !reference [.stable_runtime, before_script]
|
||||
script:
|
||||
- flatpak install --user --noninteractive org.freedesktop.Sdk.Extension.rust-nightly//${FREEDESKTOP_RUNTIME_BRANCH}
|
||||
# We want to use the nightly toolchain inside the build terminal.
|
||||
- sed -i 's|"org.freedesktop.Sdk.Extension.rust-stable"|"org.freedesktop.Sdk.Extension.rust-nightly"|g' ${MANIFEST_PATH}
|
||||
- sed -i 's|/rust-stable/bin|/rust-nightly/extra/sdk/rust-nightly/bin|g' ${MANIFEST_PATH}
|
||||
- flatpak-builder --keep-build-dirs --user --disable-rofiles-fuse --stop-at=${FLATPAK_MODULE} flatpak_app --repo=repo ${BRANCH:+--default-branch=$BRANCH} ${MANIFEST_PATH}
|
||||
- echo "ninja src/doc" | flatpak-builder --disable-rofiles-fuse --build-shell=${FLATPAK_MODULE} flatpak_app ${MANIFEST_PATH}
|
||||
- tar --auto-compress --create --file "${CI_PROJECT_DIR}/${CI_PROJECT_NAME}-docs.tar.gz" --directory ".flatpak-builder/build/${FLATPAK_MODULE}/_flatpak_build/src/doc" .
|
||||
dependencies: []
|
||||
artifacts:
|
||||
paths:
|
||||
- ${CI_PROJECT_NAME}-docs.tar.gz
|
||||
|
34
.gitlab-ci/utils.yml
Normal file
@ -0,0 +1,34 @@
|
||||
# Utilities to include in other jobs.
|
||||
|
||||
# Remove the Flatpak modules that are only necessary when building the app with meson.
|
||||
.remove_build_only_modules:
|
||||
variables:
|
||||
# JSON array of the names of the Flatpak modules to remove.
|
||||
MODULES_TO_REMOVE: '["grass", "glycin-loaders"]'
|
||||
before_script:
|
||||
# Create a temporary file.
|
||||
- TMP_FILE=$(mktemp)
|
||||
# Remove the modules in the manifest and write the output to the temporary file.
|
||||
- jq --argjson modules_to_remove "${MODULES_TO_REMOVE}" 'del(.modules[] | select(IN(.name; $modules_to_remove | .[])))' ${MANIFEST_PATH} > $TMP_FILE
|
||||
# Replace the manifest with the temporary file.
|
||||
- mv $TMP_FILE ${MANIFEST_PATH}
|
||||
# Use meson's build-env profile.
|
||||
- sed -i "s|-Dprofile=development|-Dprofile=build-env|g" ${MANIFEST_PATH}
|
||||
|
||||
# Update the Flatpak manifest to use the latest stable GNOME runtime and use the corresponding
|
||||
# container image.
|
||||
#
|
||||
# To get a list of available GNOME and LLVM versions, see:
|
||||
# https://gitlab.gnome.org/GNOME/gnome-runtime-images/-/blob/master/.gitlab-ci.yml
|
||||
.stable_runtime:
|
||||
image: 'quay.io/gnome_infrastructure/gnome-runtime-images:gnome-${GNOME_STABLE_VERSION}'
|
||||
variables:
|
||||
GNOME_STABLE_VERSION: "48"
|
||||
FREEDESKTOP_RUNTIME_BRANCH: "24.08"
|
||||
LLVM_NIGHTLY_VERSION: "20"
|
||||
LLVM_STABLE_VERSION: "20"
|
||||
before_script:
|
||||
# We want to use the latest stable GNOME runtime instead of the nightly runtime.
|
||||
- sed -i "s|master|${GNOME_STABLE_VERSION}|g" ${MANIFEST_PATH}
|
||||
# We want to use the latest LLVM extension for the stable runtime.
|
||||
- sed -i "s|llvm${LLVM_NIGHTLY_VERSION}|llvm${LLVM_STABLE_VERSION}|g" ${MANIFEST_PATH}
|
@ -12,14 +12,15 @@ or videos showing the issue.
|
||||
## Information
|
||||
|
||||
* [ ] This bug is reproducible from the latest nightly build <!-- Check this box if the bug happens on Fractal's development version -->
|
||||
* [ ] This bug is reproducible with an [officially supported flatpak](https://gitlab.gnome.org/World/fractal#installation-instructions)
|
||||
<!-- ⚠️ Issue with third party packages (distribution repository, AUR, snap, Fedora flatpak…) should be reported to your distributor -->
|
||||
* **Fractal Version**: <!-- The version of Fractal you were using when the bug occurred. Check the "About Fractal" dialog for this information -->
|
||||
* **OS Version**: <!-- Operating system version, e.g. Fedora 36 -->
|
||||
* **Installation Source**: <!-- Where you installed Fractal from, e.g. Flathub, GNOME Apps Nightly, AUR, or distro repositories -->
|
||||
* **Homeserver**: <!-- The homeserver for your matrix account, e.g. matrix.org, gnome.org, … You can mention several of them if this is reproducible on multiple ones. -->
|
||||
|
||||
<!-- If you have error logs or a crash report, use the "Attach A File" button in the issue editor to attach it, or paste it in a code block below.
|
||||
|
||||
To access the logs, you can run `journalctl -e -o cat _COMM=fractal`
|
||||
To access the logs, you can run `SYSTEMD_LESS=FRXMK journalctl -e -o cat _COMM=fractal`
|
||||
|
||||
To generate a stack trace in case of a crash, you can follow this guide: https://handbook.gnome.org/issues/stack-traces.html
|
||||
|
||||
@ -30,3 +31,5 @@ code goes here
|
||||
```
|
||||
|
||||
-->
|
||||
|
||||
/label ~"1. Bug"
|
||||
|
@ -1,7 +1,10 @@
|
||||
<!-- Please note that some features missing in the stable release are already available in the
|
||||
development version. To avoid duplicates and unnecessary issues, please check that your request is
|
||||
for something that is not yet implemented, and doesn’t have an existing issue that is open or that
|
||||
was closed as out of scope. -->
|
||||
was closed as out of scope.
|
||||
We also recommend talking to us in the #fractal:gnome.org Matrix room first. We do not intend to
|
||||
implement everything and don’t want our issue tracker to become a giant wishlist, but rather a
|
||||
curated list of known problems and planned features. -->
|
||||
|
||||
Detailed description of the feature. Provide as much information as you can.
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
[default.extend-words]
|
||||
gir = "gir"
|
||||
inout = "inout"
|
||||
numer = "numer" # Short for numerator in GStreamer
|
||||
ue = "ue" # End of word after mnemonic
|
||||
|
||||
[type.po]
|
||||
extend-glob = ["*.po"]
|
||||
|
@ -22,7 +22,7 @@ can also provide general help about using Rust in GNOME.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Fractal is written in Rust, so you will need to have at least Rust 1.76 and Cargo available on your
|
||||
Fractal is written in Rust, so you will need to have at least Rust 1.80 and Cargo available on your
|
||||
system. You will also need to install the Rust nightly toolchain to be able to run our
|
||||
[pre-commit hook](#pre-commit), which can be done with:
|
||||
|
||||
@ -35,21 +35,21 @@ manually add the necessary remotes and install the required freedesktop.org exte
|
||||
|
||||
```sh
|
||||
# Add Flathub and the gnome-nightly repo
|
||||
flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
|
||||
flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
|
||||
flatpak remote-add --user --if-not-exists gnome-nightly https://nightly.gnome.org/gnome-nightly.flatpakrepo
|
||||
|
||||
# Install the gnome-nightly Sdk and Platform runtime
|
||||
flatpak install --user gnome-nightly org.gnome.Sdk//master org.gnome.Platform//master
|
||||
|
||||
# Install the required rust-stable extension from Flathub
|
||||
flatpak install --user flathub org.freedesktop.Sdk.Extension.rust-stable//23.08
|
||||
flatpak install --user flathub org.freedesktop.Sdk.Extension.rust-stable//25.08beta
|
||||
|
||||
# Install the required llvm extension from Flathub
|
||||
flatpak install --user flathub org.freedesktop.Sdk.Extension.llvm16//23.08
|
||||
flatpak install --user flathub org.freedesktop.Sdk.Extension.llvm20//25.08beta
|
||||
```
|
||||
|
||||
If you are building the flatpak manually you will also need flatpak-builder on your system, or the
|
||||
`org.flatpak.Builder` flatpak.
|
||||
`org.flatpak.Builder` flatpak from Flathub.
|
||||
|
||||
### GNOME Builder
|
||||
|
||||
@ -66,42 +66,29 @@ environment from the command line and execute commands in that environment.
|
||||
First, install fenv:
|
||||
|
||||
```sh
|
||||
# Clone the project somewhere on your system
|
||||
git clone https://gitlab.gnome.org/ZanderBrown/fenv.git
|
||||
|
||||
# Move into the folder
|
||||
cd fenv
|
||||
|
||||
# Install fenv with Cargo
|
||||
cargo install --path .
|
||||
cargo install --git https://gitlab.gnome.org/ZanderBrown/fenv fenv
|
||||
```
|
||||
|
||||
You can now discard the `fenv` directory if you want.
|
||||
|
||||
After that, move into the directory where you cloned Fractal and setup the project:
|
||||
After that, setup the project:
|
||||
|
||||
```sh
|
||||
# Setup the flatpak environment
|
||||
# Set up the flatpak environment
|
||||
fenv gen build-aux/org.gnome.Fractal.Devel.json
|
||||
|
||||
# Initialize the build system
|
||||
fenv exec -- meson setup -Dprofile=development --prefix=/app _build
|
||||
```
|
||||
|
||||
Finally, build and run the application:
|
||||
|
||||
```sh
|
||||
# Build the project
|
||||
fenv exec -- ninja -C _build
|
||||
|
||||
# Install the application in the flatpak environment
|
||||
fenv exec -- ninja -C _build install
|
||||
fenv build
|
||||
|
||||
# Launch Fractal
|
||||
fenv exec ./_build/src/fractal
|
||||
fenv run
|
||||
```
|
||||
|
||||
To test changes you make to the code, re-run these three last commands.
|
||||
_Note that fenv will use `_build` as build directory._
|
||||
|
||||
To test changes you make to the code, re-run these two last commands.
|
||||
|
||||
### Install the flatpak
|
||||
|
||||
@ -113,7 +100,7 @@ GNOME Builder can export a flatpak of the app after it has been successfully bui
|
||||
Fractal can then be installed with:
|
||||
|
||||
```sh
|
||||
flatpak install --user --bundle path/to/org.gnome.Fractal.Devel.flatpak
|
||||
flatpak install --user --bundle path/to/org.gnome.Fractal.Devel.flatpak
|
||||
```
|
||||
|
||||
Alternatively, it can be built and installed with flatpak-builder:
|
||||
@ -144,7 +131,7 @@ sudo ninja -C _build install
|
||||
## Pre-commit
|
||||
|
||||
We expect all code contributions to be correctly formatted. To help with that, a pre-commit hook
|
||||
should get installed as part of the building process. It runs the `scripts/checks.sh` script. It's a
|
||||
should get installed as part of the building process. It runs the `hooks/checks.sh` script. It's a
|
||||
quick script that makes sure that the code is correctly formatted with `rustfmt`, among other
|
||||
things. Make sure that this script is effectively run before submitting your merge request,
|
||||
otherwise CI will probably fail right away.
|
||||
@ -156,7 +143,7 @@ submissions and is once again checked by our CI.
|
||||
|
||||
Please follow the [GNOME commit message guidelines](https://handbook.gnome.org/development/commit-messages.html).
|
||||
We enforce the use of a tag as a prefix for the summary line. It should be the area of the app that
|
||||
is changed.
|
||||
is changed.
|
||||
|
||||
## Merge Request
|
||||
|
||||
|
4714
Cargo.lock
generated
114
Cargo.toml
@ -1,11 +1,14 @@
|
||||
[package]
|
||||
name = "fractal"
|
||||
version = "7.0.0-beta"
|
||||
version = "12.0.0"
|
||||
authors = ["Julian Sparber <julian@sparber.net>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.76"
|
||||
edition = "2024"
|
||||
rust-version = "1.85"
|
||||
publish = false
|
||||
|
||||
[package.metadata.cargo-machete]
|
||||
ignored = ["serde_bytes"] # Used by the SecretFile API.
|
||||
|
||||
[profile.release]
|
||||
debug = true
|
||||
lto = "thin"
|
||||
@ -21,91 +24,110 @@ codegen-units = 16
|
||||
|
||||
# Please keep dependencies sorted.
|
||||
[dependencies]
|
||||
blurhash = "0.2"
|
||||
cfg-if = "1"
|
||||
diff = "0.1"
|
||||
djb_hash = "0.1"
|
||||
eyeball-im = "0.4"
|
||||
futures-channel = "0.3"
|
||||
futures-util = "0.3"
|
||||
geo-uri = "0.2"
|
||||
gettext-rs = { version = "0.7", features = ["gettext-system"] }
|
||||
html-escape = "0.2"
|
||||
html2pango = "0.6"
|
||||
html5gum = "0.5"
|
||||
image = "0.24"
|
||||
indexmap = "2"
|
||||
linkify = "0.10.0"
|
||||
mime = "0.3"
|
||||
mime_guess = "2"
|
||||
once_cell = "1"
|
||||
pulldown-cmark = "0.10"
|
||||
qrcode = "0.13"
|
||||
rand = "0.8"
|
||||
numeric-sort = "0.1"
|
||||
pulldown-cmark = "0.13"
|
||||
qrcode = { version = "0.14", default-features = false }
|
||||
rand = "0.9"
|
||||
regex = "1"
|
||||
rmp-serde = "1"
|
||||
rqrr = "0.6"
|
||||
secular = { version = "1", features = ["bmp", "normalization"] }
|
||||
serde = "1"
|
||||
serde_bytes = "0.11"
|
||||
serde_json = "1"
|
||||
strum = { version = "0.26", features = ["derive"] }
|
||||
thiserror = "1"
|
||||
strum = { version = "0.27.1", features = ["derive"] }
|
||||
tempfile = "3"
|
||||
thiserror = "2"
|
||||
tld = "2"
|
||||
tokio = { version = "1", features = ["rt", "rt-multi-thread", "sync"] }
|
||||
tokio-stream = { version = "0.1", features = ["sync"] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
url = "2"
|
||||
webp = { version = "0.3", default-features = false }
|
||||
wtinylfu = "0.2"
|
||||
zeroize = "1"
|
||||
|
||||
# gtk-rs project and dependents. These usually need to be updated together.
|
||||
adw = { package = "libadwaita", version = "0.6", features = ["v1_5"] }
|
||||
gst = { version = "0.22", package = "gstreamer" }
|
||||
gst_base = { version = "0.22", package = "gstreamer-base" }
|
||||
gst_gtk = { version = "0.12", package = "gst-plugin-gtk4" }
|
||||
gst_pbutils = { version = "0.22", package = "gstreamer-pbutils" }
|
||||
gst_play = { version = "0.22", package = "gstreamer-play" }
|
||||
gst_video = { version = "0.22", package = "gstreamer-video" }
|
||||
gtk = { package = "gtk4", version = "0.8", features = ["gnome_45"] }
|
||||
shumate = { package = "libshumate", version = "0.5" }
|
||||
sourceview = { package = "sourceview5", version = "0.8" }
|
||||
adw = { package = "libadwaita", version = "0.7", features = ["v1_7"] }
|
||||
glycin = { version = "3.0.0-beta.1", default-features = false, features = ["tokio", "gdk4"] }
|
||||
gst = { version = "0.23", package = "gstreamer" }
|
||||
gst_app = { version = "0.23", package = "gstreamer-app" }
|
||||
gst_pbutils = { version = "0.23", package = "gstreamer-pbutils" }
|
||||
gst_play = { version = "0.23", package = "gstreamer-play" }
|
||||
gst_video = { version = "0.23", package = "gstreamer-video" }
|
||||
gtk = { package = "gtk4", version = "0.9", features = ["gnome_47"] }
|
||||
shumate = { package = "libshumate", version = "0.6" }
|
||||
sourceview = { package = "sourceview5", version = "0.9" }
|
||||
|
||||
[dependencies.matrix-sdk]
|
||||
# version = "0.13"
|
||||
git = "https://github.com/matrix-org/matrix-rust-sdk.git"
|
||||
rev = "ab9e4f73b1b43e0779080b714e0916496a052cc3"
|
||||
features = [
|
||||
"socks",
|
||||
"sso-login",
|
||||
"markdown",
|
||||
"qrcode",
|
||||
"image-rayon",
|
||||
]
|
||||
rev = "a9ce1c6e5822b8eb8411c5bc257049d9a9d15884"
|
||||
features = ["socks", "sso-login", "markdown", "qrcode"]
|
||||
|
||||
[dependencies.matrix-sdk-store-encryption]
|
||||
# version = "0.13"
|
||||
git = "https://github.com/matrix-org/matrix-rust-sdk.git"
|
||||
rev = "a9ce1c6e5822b8eb8411c5bc257049d9a9d15884"
|
||||
|
||||
[dependencies.matrix-sdk-ui]
|
||||
# version = "0.13"
|
||||
git = "https://github.com/matrix-org/matrix-rust-sdk.git"
|
||||
rev = "ab9e4f73b1b43e0779080b714e0916496a052cc3"
|
||||
default-features = false
|
||||
features = ["e2e-encryption", "native-tls"]
|
||||
rev = "a9ce1c6e5822b8eb8411c5bc257049d9a9d15884"
|
||||
|
||||
[dependencies.ruma]
|
||||
# version = "0.9.4"
|
||||
# version = "0.12.5"
|
||||
git = "https://github.com/ruma/ruma.git"
|
||||
rev = "4c00bd010dbdca6005bd599b52e90a0b7015d056"
|
||||
rev = "a2fe858133ba932b4bda730dc7472c9c985739a0"
|
||||
features = [
|
||||
"unstable-unspecified",
|
||||
"client-api-c",
|
||||
"compat-key-id",
|
||||
"compat-user-id",
|
||||
"markdown",
|
||||
"html-matrix",
|
||||
"compat-arbitrary-length-ids",
|
||||
"compat-server-signing-key-version",
|
||||
"compat-empty-string-null",
|
||||
"compat-null",
|
||||
"compat-optional",
|
||||
"compat-unset-avatar",
|
||||
"compat-get-3pids",
|
||||
"html",
|
||||
"compat-lax-room-create-deser",
|
||||
"compat-lax-room-topic-deser",
|
||||
]
|
||||
|
||||
# Linux-only dependencies.
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
ashpd = { version = "0.8", default-features = false, features = [
|
||||
"pipewire",
|
||||
aperture = "0.9"
|
||||
ashpd = { version = "0.11", default-features = false, features = [
|
||||
"tracing",
|
||||
"tokio",
|
||||
] }
|
||||
oo7 = { version = "0.3", default-features = false, features = [
|
||||
oo7 = { version = "0.4", default-features = false, features = [
|
||||
"openssl_crypto",
|
||||
"tokio",
|
||||
"tracing",
|
||||
] }
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches2 = "0.1"
|
||||
|
||||
[lints.clippy]
|
||||
pedantic = { level = "warn", priority = -1 }
|
||||
cast_possible_truncation = "allow"
|
||||
cast_precision_loss = "allow"
|
||||
default_trait_access = "allow"
|
||||
module_name_repetitions = "allow"
|
||||
new_without_default = "allow"
|
||||
struct_field_names = "allow"
|
||||
unsafe_derive_deserialize = "allow"
|
||||
wildcard_imports = "allow"
|
||||
|
33
README.md
@ -8,7 +8,12 @@
|
||||
Fractal is a Matrix messaging app for GNOME written in Rust. Its interface is optimized for
|
||||
collaboration in large groups, such as free software projects, and will fit all screens, big or small.
|
||||
|
||||

|
||||
<img
|
||||
src="https://gitlab.gnome.org/World/fractal/raw/main/screenshots/main.png"
|
||||
alt="Fractal’s main window"
|
||||
width="882"
|
||||
height="672"
|
||||
/>
|
||||
|
||||
Highlights:
|
||||
|
||||
@ -33,7 +38,7 @@ development version while keeping the stable release around for daily use.
|
||||
|
||||
### Stable version
|
||||
|
||||
The current stable version is 6 (released January 18th 2024).
|
||||
The current stable version is 12 (released August 11th 2025).
|
||||
|
||||
You can get the official Fractal Flatpak from Flathub.
|
||||
|
||||
@ -48,7 +53,7 @@ You can get the official Fractal Flatpak from Flathub.
|
||||
|
||||
### Beta version
|
||||
|
||||
The current beta version is 6, same as the stable version (released January 18th 2024).
|
||||
The current beta version is 12 (same as stable).
|
||||
|
||||
It is available as a Flatpak on Flathub Beta.
|
||||
|
||||
@ -140,6 +145,24 @@ flatpak install --user gnome-nightly org.gnome.Fractal.Devel
|
||||
|
||||
### Runtime Dependencies
|
||||
|
||||
On top of the dependencies required at build time and checked by Meson, Fractal depends on the
|
||||
following dependencies at runtime:
|
||||
|
||||
* xdg-desktop-portal and its backends: some functionalities are dependant on the following portals,
|
||||
and a permission will be asked when necessary, but Fractal should work without them:
|
||||
* Secret: this portal or a Secret Service is required, see [storing secrets](#storing-secrets).
|
||||
* Camera: scan QR codes during verification.
|
||||
* Location: send the user’s location in a conversation.
|
||||
* Settings: get the 12h/24h time format system preference.
|
||||
* GStreamer plugins:
|
||||
* gst-plugin-gtk4 (gstgtk4): required to preview videos in the timeline and to present the output
|
||||
of the camera.
|
||||
* libgstpipewire with the `pipewiredeviceprovider`: used to list and access the cameras.
|
||||
* glycin: all images are loaded with this library so loaders for the different image formats need to
|
||||
be installed.
|
||||
|
||||
#### Storing secrets
|
||||
|
||||
Fractal doesn’t store your **password**, but it stores your **access token** and the **passphrase**
|
||||
used to encrypt the database and the local cache.
|
||||
|
||||
@ -147,12 +170,12 @@ The Fractal Flatpaks use the [Secret **Portal**](https://docs.flatpak.org/en/lat
|
||||
to store those secrets. If you are using GNOME this should just work. If you are using a different
|
||||
desktop environment or are facing issues, make sure `xdg-desktop-portal` is installed along with a
|
||||
service that provides the [Secret portal backend interface](https://docs.flatpak.org/en/latest/portal-api-reference.html#gdbus-org.freedesktop.impl.portal.Secret),
|
||||
which is currently only implemented by gnome-keyring.
|
||||
like gnome-keyring or KWallet (since version 6.2).
|
||||
|
||||
Any version that is not sandboxed relies on software that implements the [Secret **Service** API](https://www.freedesktop.org/wiki/Specifications/secret-storage-spec/)
|
||||
to store those secrets. Therefore, you need to have software providing that service on your system,
|
||||
like gnome-keyring, KeepassXC ([setup guide](https://avaldes.co/2020/01/28/secret-service-keepassxc.html)),
|
||||
or KWallet (since version 5.97). Once again, if you are using GNOME this should just work.
|
||||
or KWallet. Once again, if you are using GNOME this should just work.
|
||||
|
||||
If you prefer to use software that only implements the Secret Service API while using the Flatpaks,
|
||||
you need to make sure that no service implementing the Secret portal backend interface is running,
|
||||
|
115
RELEASING.md
Normal file
@ -0,0 +1,115 @@
|
||||
# Releasing Fractal
|
||||
|
||||
## Before making a new release
|
||||
|
||||
- Update the dependencies (crates or system libraries) and migrate from deprecated APIs.
|
||||
- Make the `build-stable` CI jobs use the latest stable GNOME runtime.
|
||||
|
||||
## Making a new stable release
|
||||
|
||||
1. If this is a new major version, create a new `fractal-M` branch, where `M` is the major version
|
||||
number.
|
||||
2. Create a [release merge request](#release-merge-request-content) against the major version
|
||||
branch.
|
||||
3. After the MR is merged, [create a tag](#creating-a-signed-tag) on the last commit of the major
|
||||
version branch.
|
||||
4. Create a release on GitLab for that tag.
|
||||
5. Make a fast-forward merge of the major version branch to `main`.
|
||||
6. [Publish the new version on Flathub and Flathub beta](#publishing-a-version-on-flathub).
|
||||
7. [Get the stable branch added to Damned Lies](#getting-a-branch-added-to-damned-lies).
|
||||
|
||||
## Making a new beta release
|
||||
|
||||
1. Create a [release merge request](#release-merge-request-content) against `main`.
|
||||
2. After the MR is merged, [create a tag](#creating-a-signed-tag) on the last commit of `main`.
|
||||
3. Create a release on GitLab for that tag.
|
||||
4. [Publish the new version on Flathub beta](#publishing-a-version-on-flathub).
|
||||
|
||||
## Release merge request content
|
||||
|
||||
_To represent conditional list items, this section will start items with "**stable.**" to mean "if
|
||||
this is a stable release"._
|
||||
|
||||
Make a single release commit containing the following changes:
|
||||
|
||||
- Update `/meson.build`:
|
||||
- Change the version on L3, it must look the same as it would in the app, with a
|
||||
`major_version.pre_release_version` format.
|
||||
- Change the `major_version` and `pre_release_version` on L13-14. For stable versions,
|
||||
`pre_release_version` should be an empty string.
|
||||
- Update `/Cargo.toml`: change the `version`, using a semver format.
|
||||
- Update `/README.md`:
|
||||
- **stable.** update the current stable version and its release date.
|
||||
- Update the current beta version. For stable versions, put `(same as stable)` instead of the
|
||||
release date.
|
||||
- Update `/data/org.gnome.Fractal.metainfo.xml.in.in`:
|
||||
- Add a new `release` entry at the top of the `releases`:
|
||||
- Its `version` should use the `major_version~pre_release_version` format.
|
||||
- For stable versions, its `type` should be `stable`, otherwise it should be `development`.
|
||||
- **stable.** remove all the `development` entries.
|
||||
- **stable.** update the paths of the screenshots to point to the major version branch.
|
||||
- **stable.** If there were visible changes in the UI, update the screenshots in `/screenshots`.
|
||||
They should follow [Flathub's quality guidelines](https://docs.flathub.org/docs/for-app-authors/metainfo-guidelines/quality-guidelines#screenshots),
|
||||
with the following window sizes:
|
||||
- `main.png`: 760×550.
|
||||
- `adaptive.png`: 360×600.
|
||||
- `media-history.png`: 500×540.
|
||||
|
||||
A good practice in this merge request is to launch the `build-stable` CI jobs to make sure that
|
||||
Fractal builds with the stable Flatpak runtime.
|
||||
|
||||
## Creating a signed tag
|
||||
|
||||
Creating a signed tag is not mandatory but is good practice. To do so, use this command:
|
||||
|
||||
```sh
|
||||
git tag -s V
|
||||
```
|
||||
|
||||
With `V` being the version to tag, in the format `major_version.pre_release_version`.
|
||||
|
||||
You will be prompted for a tag message. This message doesn't really matter so something like
|
||||
`Release Fractal V` should suffice.
|
||||
|
||||
## Publishing a version on Flathub
|
||||
|
||||
Publishing a version of Fractal on Flathub is done via its [Flathub repository on GitHub](https://github.com/flathub/org.gnome.Fractal/).
|
||||
A permission from the Flathub team granted to your GitHub account is necessary to merge PRs on this
|
||||
repository, but anyone can open a PR.
|
||||
|
||||
1. Open a PR against the correct branch. For a stable build, work against the `master` branch, for a
|
||||
beta build, work against the `beta` branch.
|
||||
|
||||
It must contain a commit that updates the manifest to:
|
||||
|
||||
- Use the latest GNOME runtime.
|
||||
- Make sure that the Flatpak dependencies are the same as in the nightly manifest, and using the
|
||||
same version.
|
||||
- Build the latest version of Fractal, identified by its tag _and_ commit hash.
|
||||
|
||||
If the list of Rust modules to build changes, the `MODULES` variable in the
|
||||
`update-cargo-sources.sh` script must also be updated.
|
||||
2. When the PR is opened, a CI job will update the `*-cargo-sources.json` files with the latest
|
||||
dependencies for the Rust modules and add a commit to the PR if necessary.
|
||||
3. Trigger a test build by posting a comment saying `bot, build`.
|
||||
|
||||
If the build succeeds, test the generated Flatpak as instructed and watch for obvious errors. If
|
||||
there are no issues, merge the PR.
|
||||
4. Merging the PR will trigger an "official" build that will then be published on Flathub or Flathub
|
||||
beta within 1 to 2 hours. If this build fails, an issue will be opened on the GitHub repository.
|
||||
The Flathub admins need to be contacted to launch it again.
|
||||
|
||||
More details about these steps can be found in the Flathub docs about [maintenance](https://docs.flathub.org/docs/for-app-authors/maintenance)
|
||||
and [updates](https://docs.flathub.org/docs/for-app-authors/updates).
|
||||
|
||||
## Getting a branch added to Damned Lies
|
||||
|
||||
Damned Lies is the GNOME translation management platform. It provides translation workflows, but
|
||||
also statistics. Even though we don’t publish any release from stable branches after the initial
|
||||
one, we add them there so we can keep track of the evolution of translation coverage.
|
||||
|
||||
1. Go to https://l10n.gnome.org/module/fractal/ and log in.
|
||||
2. Click on the pencil icon next to the branch list.
|
||||
3. In the entry at the bottom, type in the name of the new branch, then click on the Save button.
|
||||
4. Assign the newly added branch to the “Other Apps (stable)” Release, unassign the previous one.
|
||||
5. Hit Save again for the assignments to take effect.
|
@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
export DIST="$1"
|
||||
export SOURCE_ROOT="$2"
|
||||
|
||||
cd "$SOURCE_ROOT"
|
||||
mkdir "$DIST"/.cargo
|
||||
cargo vendor | sed 's/^directory = ".*"/directory = "vendor"/g' >> $DIST/.cargo/config.toml
|
||||
# Move vendor into dist tarball directory
|
||||
mv vendor "$DIST"
|
@ -1,11 +1,11 @@
|
||||
{
|
||||
"app-id": "org.gnome.Fractal.Devel",
|
||||
"id": "org.gnome.Fractal.Devel",
|
||||
"runtime": "org.gnome.Platform",
|
||||
"runtime-version": "master",
|
||||
"sdk": "org.gnome.Sdk",
|
||||
"sdk-extensions": [
|
||||
"org.freedesktop.Sdk.Extension.rust-stable",
|
||||
"org.freedesktop.Sdk.Extension.llvm16"
|
||||
"org.freedesktop.Sdk.Extension.llvm20"
|
||||
],
|
||||
"command": "fractal",
|
||||
"finish-args": [
|
||||
@ -20,9 +20,10 @@
|
||||
"--env=RUST_BACKTRACE=1"
|
||||
],
|
||||
"build-options": {
|
||||
"append-ld-library-path": "/usr/lib/sdk/llvm16/lib",
|
||||
"append-path": "/usr/lib/sdk/llvm16/bin:/usr/lib/sdk/rust-stable/bin",
|
||||
"append-ld-library-path": "/usr/lib/sdk/llvm20/lib",
|
||||
"append-path": "/usr/lib/sdk/llvm20/bin:/usr/lib/sdk/rust-stable/bin",
|
||||
"env": {
|
||||
"RUSTFLAGS": "-C force-frame-pointers=yes",
|
||||
"CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER": "clang",
|
||||
"CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS": "-C link-arg=-fuse-ld=/usr/lib/sdk/rust-stable/bin/mold --cfg=ruma_identifiers_storage=\"Arc\"",
|
||||
"CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER": "clang",
|
||||
@ -38,30 +39,41 @@
|
||||
},
|
||||
"modules": [
|
||||
{
|
||||
"name" : "protobuf",
|
||||
"buildsystem" : "autotools",
|
||||
"config-opts": [ "DIST_LANG=cpp" ],
|
||||
"cleanup" : [
|
||||
"/bin/protoc*",
|
||||
"/lib/libprotoc*",
|
||||
"/lib/libprotobuf-lite*"
|
||||
"name": "grass",
|
||||
"buildsystem": "simple",
|
||||
"build-options": {
|
||||
"env": {
|
||||
"CARGO_HOME": "/run/build/grass/cargo"
|
||||
}
|
||||
},
|
||||
"build-commands": [
|
||||
"cargo build --release --locked",
|
||||
"mkdir -p /app/bin",
|
||||
"install -D ./target/release/grass /app/bin/"
|
||||
],
|
||||
"sources" : [
|
||||
"cleanup": ["*"],
|
||||
"sources": [
|
||||
{
|
||||
"type" : "archive",
|
||||
"url" : "https://github.com/protocolbuffers/protobuf/releases/download/v3.17.3/protobuf-all-3.17.3.tar.gz",
|
||||
"sha256" : "77ad26d3f65222fd96ccc18b055632b0bfedf295cb748b712a98ba1ac0b704b2"
|
||||
"type": "git",
|
||||
"url": "https://github.com/connorskees/grass",
|
||||
"tag": "0.13.4",
|
||||
"commit": "e0bb9e2eabfc3a58e42b03089cd7b22c68d09d0b",
|
||||
"disable-submodules": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "protobuf-c",
|
||||
"buildsystem" : "autotools",
|
||||
"sources" : [
|
||||
"name": "protobuf-c",
|
||||
"buildsystem": "autotools",
|
||||
"config-opts": [
|
||||
"--disable-protoc"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type" : "archive",
|
||||
"url" : "https://github.com/protobuf-c/protobuf-c/releases/download/v1.4.0/protobuf-c-1.4.0.tar.gz",
|
||||
"sha256" : "26d98ee9bf18a6eba0d3f855ddec31dbe857667d269bc0b6017335572f85bbcb"
|
||||
"type": "git",
|
||||
"url": "https://github.com/protobuf-c/protobuf-c.git",
|
||||
"tag": "v1.5.2",
|
||||
"commit": "4719fdd7760624388c2c5b9d6759eb6a47490626"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -76,9 +88,10 @@
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://download.gnome.org/sources/libshumate/1.2/libshumate-1.2.0.tar.xz",
|
||||
"sha256": "4f8413a707cd00f84cee39ca49f58c48fc436f008ea80d6532ac37dafd0ba96b"
|
||||
"type": "git",
|
||||
"url": "https://gitlab.gnome.org/GNOME/libshumate.git",
|
||||
"tag": "1.4.0",
|
||||
"commit": "06021e35f0d479612fb1a3af91a73ba562175e03"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -35,7 +35,7 @@ if profile == 'Devel'
|
||||
appstream_version += '-' + devel_version
|
||||
|
||||
development_release = '''
|
||||
<release version="@0@" type="development" date="@1@">
|
||||
<release version="@0@" type="snapshot" date="@1@">
|
||||
<description>
|
||||
<p>Development release.</p>
|
||||
</description>
|
||||
|
@ -22,8 +22,8 @@
|
||||
</ul>
|
||||
</description>
|
||||
<branding>
|
||||
<color type="primary" scheme_preference="light">#a5c8f2</color>
|
||||
<color type="primary" scheme_preference="dark">#1b549b</color>
|
||||
<color type="primary" scheme_preference="light">#bdfbff</color>
|
||||
<color type="primary" scheme_preference="dark">#1a5fb4</color>
|
||||
</branding>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>GPL-3.0+</project_license>
|
||||
@ -35,15 +35,15 @@
|
||||
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<image type="source">https://gitlab.gnome.org/World/fractal/raw/main/screenshots/main.png</image>
|
||||
<image type="source">https://gitlab.gnome.org/World/fractal/raw/fractal-12/screenshots/main.png</image>
|
||||
<caption>Fractal’s main window</caption>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image type="source">https://gitlab.gnome.org/World/fractal/raw/main/screenshots/media-history.png</image>
|
||||
<image type="source">https://gitlab.gnome.org/World/fractal/raw/fractal-12/screenshots/media-history.png</image>
|
||||
<caption>View the media history of a Matrix room</caption>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image type="source">https://gitlab.gnome.org/World/fractal/raw/main/screenshots/adaptive.png</image>
|
||||
<image type="source">https://gitlab.gnome.org/World/fractal/raw/fractal-12/screenshots/adaptive.png</image>
|
||||
<caption>Fractal’s interface adapts to small screens</caption>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
@ -71,19 +71,275 @@
|
||||
</content_rating>
|
||||
|
||||
<releases>@development-release@
|
||||
<release version="7~beta" type="development" date="2024-03-28">
|
||||
<release version="12" type="stable" date="2025-08-11">
|
||||
<description>
|
||||
<p>
|
||||
Spring is here in Fractal land. Birds chirping, flowers blooming, and a new beta for you
|
||||
to try!
|
||||
</p>
|
||||
<p>
|
||||
Staff’s picks:
|
||||
Knock, knock, knock… on rooms, baby 🎵 Ooh ooh ooh ooh ooh ooh 🎶 That's right, Fractal 12
|
||||
adds support for knocking, among other things. Read all about the improvements since 11.2:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
Encryption support has been extended, with server-side key backup and account recovery.
|
||||
Requesting invites to rooms (aka knocking) is now possible, as is enabling such requests
|
||||
for room admins.
|
||||
</li>
|
||||
<li>
|
||||
The upcoming room version 12 is supported, with the special power level of room
|
||||
creators.
|
||||
</li>
|
||||
<li>
|
||||
A room can be marked as unread via the context menu in the sidebar.
|
||||
</li>
|
||||
<li>
|
||||
You can now see if a section in the sidebar has any notifications or activity when it is
|
||||
collapsed.
|
||||
</li>
|
||||
<li>
|
||||
Clicking on the name of the sender of a message adds a mention to them in the composer.
|
||||
</li>
|
||||
<li>
|
||||
The safety setting to hide media previews in rooms is now synced between Matrix clients
|
||||
and we added another safety setting (which is also synced) to hide avatars in invites.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
As usual, this release includes other improvements, fixes and new translations thanks to
|
||||
all our contributors, and our upstream projects.
|
||||
</p>
|
||||
<p>
|
||||
We want to address special thanks to the translators who worked on this version. We know
|
||||
this is a huge undertaking and have a deep appreciation for what you’ve done. If you want
|
||||
to help with this effort, head over to Damned Lies.
|
||||
</p>
|
||||
</description>
|
||||
</release>
|
||||
<release version="11.2" type="stable" date="2025-06-10">
|
||||
<description>
|
||||
<p>
|
||||
This version updates the matrix-sdk-crypto dependency to include a fix for a high severity
|
||||
security issue.
|
||||
</p>
|
||||
</description>
|
||||
</release>
|
||||
<release version="11.1" type="stable" date="2025-05-15">
|
||||
<description>
|
||||
<p>
|
||||
Due to a pesky bug that makes Fractal crash when our users attempt to start a
|
||||
verification, we are releasing Fractal 11.1 only 2 weeks after Fractal 11. And while we’re
|
||||
at it we also backported a few fixes for smaller paper cuts!
|
||||
</p>
|
||||
</description>
|
||||
</release>
|
||||
<release version="11" type="stable" date="2025-05-01">
|
||||
<description>
|
||||
<p>
|
||||
A new version of Fractal numbered Eleven? Stranger things have happened… Features come
|
||||
running up that hill:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
Support for login using the OAuth 2.0 API (as used by matrix.org, which recently made
|
||||
the switch to Matrix Authentication Service)
|
||||
</li>
|
||||
<li>
|
||||
Overhaul of the page that lists user sessions, with details moved to subpages, for a
|
||||
less cluttered feel, and allowing to rename sessions!
|
||||
</li>
|
||||
<li>
|
||||
Rearranged account settings, with a new Safety tab that includes a setting to toggle
|
||||
media preview visibility
|
||||
</li>
|
||||
<li>
|
||||
BlurHashes for images and videos, that are used as placeholders while the media is
|
||||
loading or if the preview is disabled
|
||||
</li>
|
||||
<li>
|
||||
Contiguous state events are grouped behind a single item
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
As usual, this release includes other improvements and fixes thanks to all our
|
||||
contributors, and our upstream projects.
|
||||
</p>
|
||||
<p>
|
||||
We want to address special thanks to the translators who worked on this version. We know
|
||||
this is a huge undertaking and have a deep appreciation for what you’ve done. If you want
|
||||
to help with this effort, head over to l10n.gnome.org.
|
||||
</p>
|
||||
</description>
|
||||
</release>
|
||||
<release version="10.1" type="stable" date="2025-02-10">
|
||||
<description>
|
||||
<p>
|
||||
Due to a couple of unfortunate but important regressions in Fractal 10, we are releasing
|
||||
Fractal 10.1 so our users don’t have to wait too long for them to be addressed. This minor
|
||||
version fixes the following issues:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
Some rooms were stuck in an unread state, even after reading them or marking them as
|
||||
read.
|
||||
</li>
|
||||
<li>
|
||||
Joining or creating a room would crash the app.
|
||||
</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="10" type="stable" date="2025-01-30">
|
||||
<description>
|
||||
<p>
|
||||
How are you going to find your friends and coordinate end of day drinks when you’re lost
|
||||
in the middle of a large crowd in a big city? With the new version of your favorite Matrix
|
||||
client, of course! Here is Fractal 10.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
The QR code scanning code has been ported to libaperture, the library behind GNOME
|
||||
Camera. This should result in better performance and more reliability.
|
||||
</li>
|
||||
<li>
|
||||
OAuth 2.0 compatibility was added, to make sure that we are ready for the upcoming
|
||||
authentication changes for matrix.org.
|
||||
</li>
|
||||
<li>
|
||||
Pills for users and rooms mentions show consistently in the right place instead of
|
||||
seemingly random places, getting rid of one of our oldest and most annoying bug.
|
||||
</li>
|
||||
<li>
|
||||
Attachments go through the send queue, ensuring correct order of all messages and
|
||||
improving the visual feedback.
|
||||
</li>
|
||||
<li>
|
||||
Videos were often not playing after loading in the room history. This was fixed, and we
|
||||
also show properly when an error occurred.
|
||||
</li>
|
||||
<li>
|
||||
We were downloading too many different sizes for avatar images, which would fill the
|
||||
media cache needlessly. We now only download a couple of sizes. This has the extra
|
||||
benefit of fixing blurry or missing thumbnails in notifications.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
As usual, this release includes other improvements and fixes thanks to all our
|
||||
contributors, and our upstream projects.
|
||||
</p>
|
||||
<p>
|
||||
We want to address special thanks to the translators who worked on this version. We know
|
||||
this is a huge undertaking and have a deep appreciation for what you’ve done. If you want
|
||||
to help with this effort, head over to l10n.gnome.org.
|
||||
</p>
|
||||
</description>
|
||||
</release>
|
||||
<release version="9" type="stable" date="2024-10-30">
|
||||
<description>
|
||||
<p>
|
||||
What’s that behind you⁉️ 😱 Oh, that’s a new Fractal release❣️ 😁 🎃
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
We switched to the glycin library (the same one used by GNOME Image Viewer) to load
|
||||
images, allowing us to fix several issues, like supporting more animated formats and
|
||||
SVGs and respecting EXIF orientation.
|
||||
</li>
|
||||
<li>
|
||||
The annoying bug where some rooms would stay as unread even after opening them is now a
|
||||
distant memory.
|
||||
</li>
|
||||
<li>
|
||||
The media cache uses its own database that you can delete if you want to free some space
|
||||
on your system. It will also soon be able to clean up unused media files to prevent it
|
||||
from growing indefinitely.
|
||||
</li>
|
||||
<li>
|
||||
Sometimes the day separators would show up with the wrong date, not anymore!
|
||||
</li>
|
||||
<li>
|
||||
We migrated to the new GTK 4.16 and libadwaita 1.6 APIs, including CSS variables,
|
||||
AdwButtonRow and AdwSpinner.
|
||||
</li>
|
||||
<li>
|
||||
We used to only rely on the secrets provider to tell us which Matrix accounts are
|
||||
logged-in, which caused issues for people sharing their secrets between devices. Now we
|
||||
also make sure that there is a data folder for a given session before trying to restore
|
||||
it.
|
||||
</li>
|
||||
<li>
|
||||
Our notifications are categorized as coming from an instant messenger, so graphical
|
||||
shells that support it, such as Phosh, can play a sound for them.
|
||||
</li>
|
||||
<li>
|
||||
Some room settings are hidden for direct chats, because it does not make sense to change
|
||||
them in this type of room.
|
||||
</li>
|
||||
<li>
|
||||
The size of the headerbar would change depending on whether the room has a topic or not.
|
||||
This will not happen anymore.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
As usual, this release includes other improvements and fixes thanks to all our
|
||||
contributors, and our upstream projects.
|
||||
</p>
|
||||
<p>
|
||||
We want to address special thanks to the translators who worked on this version. We know
|
||||
this is a huge undertaking and have a deep appreciation for what you’ve done. If you want
|
||||
to help with this effort, head over to Damned Lies.
|
||||
</p>
|
||||
</description>
|
||||
</release>
|
||||
<release version="8" type="stable" date="2024-08-01">
|
||||
<description>
|
||||
<p>
|
||||
Let’s see the main improvements:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
Mentions are sent intentionally
|
||||
</li>
|
||||
<li>
|
||||
Authenticated media are supported
|
||||
</li>
|
||||
<li>
|
||||
Draft messages are kept per-room and persisted across restarts
|
||||
</li>
|
||||
<li>
|
||||
More links are detected in messages and room descriptions
|
||||
</li>
|
||||
<li>
|
||||
Collapsed categories in the sidebar are remembered between restarts, with the
|
||||
“Historical” category collapsed by default
|
||||
</li>
|
||||
<li>
|
||||
A banner appears when synchronization with the homeserver fails too many times in a row
|
||||
</li>
|
||||
<li>
|
||||
The verification and account recovery processes have been polished
|
||||
</li>
|
||||
<li>
|
||||
HTML rendering has been improved, with the support of new elements and attributes
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
As usual, this release includes other improvements and fixes thanks to all our
|
||||
contributors, and our upstream projects.
|
||||
</p>
|
||||
<p>
|
||||
We want to address special thanks to the translators who worked on this version. We know
|
||||
this is a huge undertaking and have a deep appreciation for what you’ve done. If you want
|
||||
to help with this effort, head over to Damned Lies.
|
||||
</p>
|
||||
</description>
|
||||
</release>
|
||||
<release version="7" type="stable" date="2024-05-02">
|
||||
<description>
|
||||
<p>
|
||||
Here comes Fractal 7, with extended encryption support and improved accessibility.
|
||||
Server-side key backup and account recovery have been added, bringing greater security.
|
||||
Third-party verification has received some bug fixes and improvements. Amongst the many
|
||||
accessibility improvements, navigability has increased, especially in the room history.
|
||||
But that’s not all we’ve been up to in the past three months:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
Messages that failed to send can now be retried or discarded.
|
||||
</li>
|
||||
@ -92,7 +348,7 @@
|
||||
</li>
|
||||
<li>
|
||||
Room details are now considered complete, with the addition of room address management,
|
||||
permissions, and version upgrade.
|
||||
permissions, and room upgrade.
|
||||
</li>
|
||||
<li>
|
||||
A new member menu appears when clicking on an avatar in the room history. It offers a
|
||||
@ -102,18 +358,15 @@
|
||||
<li>
|
||||
Pills are clickable and allow to directly go to a room or member profile.
|
||||
</li>
|
||||
<li>
|
||||
Many more improvements on the accessibility front, for better navigability with a screen
|
||||
reader.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
As usual, this release includes other improvements, fixes and new translations thanks to
|
||||
all our contributors, and our upstream projects.
|
||||
</p>
|
||||
<p>
|
||||
As the version implies, there might be a slight risk of regressions, but it should be
|
||||
mostly stable. If all goes well the next step is the release candidate!
|
||||
We want to address special thanks to the translators who worked on this version. We know
|
||||
this is a huge undertaking and have a deep appreciation for what you’ve done. If you want
|
||||
to help with this effort, head over to Damned Lies.
|
||||
</p>
|
||||
</description>
|
||||
</release>
|
||||
|
@ -5,47 +5,15 @@
|
||||
viewBox="0 0 89.958331 52.916668"
|
||||
version="1.1"
|
||||
id="svg8662"
|
||||
sodipodi:docname="welcome-export.svg"
|
||||
inkscape:version="1.1-rc (52f87abb86, 2021-05-02)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<sodipodi:namedview
|
||||
id="namedview47"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
objecttolerance="10.0"
|
||||
gridtolerance="10.0"
|
||||
guidetolerance="10.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.99583586"
|
||||
inkscape:cx="303.76492"
|
||||
inkscape:cy="15.062723"
|
||||
inkscape:current-layer="svg8662"
|
||||
inkscape:object-paths="true"
|
||||
inkscape:snap-intersection-paths="true"
|
||||
inkscape:snap-midpoints="true"
|
||||
inkscape:snap-smooth-nodes="true"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-text-baseline="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid1470" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs8656">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient39832">
|
||||
<stop
|
||||
style="stop-color:#3584e4;stop-opacity:1"
|
||||
@ -57,7 +25,6 @@
|
||||
id="stop39830" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient24559">
|
||||
<stop
|
||||
style="stop-color:#ed333b;stop-opacity:1;"
|
||||
@ -69,7 +36,6 @@
|
||||
id="stop24557" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient12858">
|
||||
<stop
|
||||
style="stop-color:#f66151;stop-opacity:1"
|
||||
@ -103,7 +69,6 @@
|
||||
id="stop8143" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient976"
|
||||
id="radialGradient1221"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
@ -113,7 +78,6 @@
|
||||
fy="212"
|
||||
r="60" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient976">
|
||||
<stop
|
||||
style="stop-color:#f8e45c;stop-opacity:1"
|
||||
@ -125,7 +89,6 @@
|
||||
id="stop974" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1117"
|
||||
id="radialGradient1223"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
@ -136,7 +99,6 @@
|
||||
fy="224"
|
||||
r="16" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient1117">
|
||||
<stop
|
||||
style="stop-color:#5e5c64;stop-opacity:1"
|
||||
@ -148,7 +110,6 @@
|
||||
id="stop1115" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1117"
|
||||
id="radialGradient1225"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
@ -159,7 +120,6 @@
|
||||
fy="224"
|
||||
r="16" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient1325">
|
||||
<stop
|
||||
style="stop-color:#f66151;stop-opacity:1"
|
||||
@ -175,7 +135,6 @@
|
||||
id="stop1323" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient976"
|
||||
id="radialGradient1393-0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
@ -186,7 +145,6 @@
|
||||
r="60"
|
||||
gradientTransform="matrix(0.26458333,0,0,0.26458333,10.972649,-67.464743)" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1325"
|
||||
id="radialGradient1395-6"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
@ -197,7 +155,6 @@
|
||||
fy="29.856375"
|
||||
r="16.084499" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1325"
|
||||
id="radialGradient1397-9-9"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
@ -208,7 +165,6 @@
|
||||
fy="29.856375"
|
||||
r="16.084499" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient12858"
|
||||
id="linearGradient12860"
|
||||
x1="3467.3748"
|
||||
@ -217,7 +173,6 @@
|
||||
y2="-383.00339"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient8145"
|
||||
id="linearGradient15076"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
@ -227,7 +182,6 @@
|
||||
x2="-3272.5"
|
||||
y2="-438.48035" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient39832"
|
||||
id="radialGradient39804"
|
||||
cx="3496.6987"
|
||||
@ -238,7 +192,6 @@
|
||||
gradientTransform="matrix(0.61314223,0,0,0.49927324,-2207.2336,232.19657)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient8161"
|
||||
id="linearGradient40220"
|
||||
x1="428.48035"
|
||||
@ -248,7 +201,6 @@
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.85000059,0,0,0.85000059,64.271801,-492.37307)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient24559"
|
||||
id="linearGradient63749"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
@ -337,14 +289,10 @@
|
||||
id="g137264"
|
||||
style="stroke-width:0.666667">
|
||||
<path
|
||||
sodipodi:nodetypes="sssscccsssss"
|
||||
style="display:inline;fill:#62a0ea;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
|
||||
d="m -3912.0003,-233.37879 c -4.4319,0 -8,3.56799 -8,8 v 27.89844 c 0,4.43202 3.5681,8 8,8 h 36.3333 l 12.6667,12.66667 v -12.66667 h 8.3333 c 4.432,0 8,-3.56798 8,-8 v -27.89844 c 0,-4.43201 -3.568,-8 -8,-8 z"
|
||||
id="path137260"
|
||||
inkscape:connector-curvature="0" />
|
||||
id="path137260" />
|
||||
<path
|
||||
sodipodi:nodetypes="cssccccsccsccssc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path137262"
|
||||
d="m -3920.0003,-199.48035 v 2 c 0,4.43202 3.5681,8 8,8 h 36.3333 l 12.6667,12.66667 v -2 l -12.6667,-12.66667 h -36.3333 c -4.4319,0 -8,-3.56798 -8,-8 z m 73.3333,0 c 0,4.43202 -3.568,8 -8,8 h -8.3333 v 2 h 8.3333 c 4.432,0 8,-3.56798 8,-8 z"
|
||||
style="display:inline;fill:#3584e4;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new" />
|
||||
@ -374,17 +322,13 @@
|
||||
id="g137282"
|
||||
style="stroke-width:0.666667">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path137272"
|
||||
d="m -3913.6666,-217.48035 c -4.4319,0 -8,3.56798 -8,8 v 24 c 0,4.43202 3.5681,8 8,8 h 15.3333 v 12.66667 l 12.6667,-12.66667 h 45.9999 c 4.432,0 8,-3.56798 8,-8 v -24 c 0,-4.43202 -3.568,-8 -8,-8 z"
|
||||
style="display:inline;fill:#3d3846;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
|
||||
sodipodi:nodetypes="sssscccsssss" />
|
||||
style="display:inline;fill:#3d3846;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path137274"
|
||||
d="m -3921.6666,-187.48035 v 2 c 0,4.43202 3.5681,8 8,8 h 15.3333 v -2 h -15.3333 c -4.4319,0 -8,-3.56798 -8,-8 z m 89.9999,0 c 0,4.43202 -3.568,8 -8,8 h -45.9999 l -12.6667,12.66667 v 2 l 12.6667,-12.66667 h 45.9999 c 4.432,0 8,-3.56798 8,-8 z"
|
||||
style="display:inline;fill:#241f31;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
|
||||
sodipodi:nodetypes="cssccsccsccccssc" />
|
||||
style="display:inline;fill:#241f31;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new" />
|
||||
<rect
|
||||
style="opacity:1;fill:#77767b;fill-opacity:1;stroke:none;stroke-width:9.33333;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
id="rect137276"
|
||||
@ -469,13 +413,12 @@
|
||||
ry="3.175" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.23333px;line-height:1.25;font-family:Cantarell;-inkscape-font-specification:'Cantarell Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#ffffff;stroke-width:0.264583"
|
||||
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-size:4.23333px;line-height:1.25;font-family:'Adwaita Sans';-inkscape-font-specification:'Adwaita Sans Ultra-Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#ffffff;stroke-width:0.264583"
|
||||
x="25.548162"
|
||||
y="7.6729164"
|
||||
id="text63745"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan63743"
|
||||
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-size:4.23333px;font-family:Cantarell;-inkscape-font-specification:'Cantarell Ultra-Bold';fill:#ffffff;stroke-width:0.264583"
|
||||
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-size:4.23333px;font-family:'Adwaita Sans';-inkscape-font-specification:'Adwaita Sans Ultra-Bold';fill:#ffffff;stroke-width:0.264583"
|
||||
x="25.548162"
|
||||
y="7.6729164">999+</tspan></text>
|
||||
</g>
|
||||
@ -551,22 +494,12 @@
|
||||
rx="2"
|
||||
ry="2" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="opacity:1;vector-effect:none;fill:#e5a50a;fill-opacity:1;stroke:none;stroke-width:12.8571;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M 123.82422,231.53906 A 60,60 0 0 1 64,288 60,60 0 0 1 4.17578,232.46094 60,60 0 0 0 4,236 a 60,60 0 0 0 60,60 60,60 0 0 0 60,-60 60,60 0 0 0 -0.17578,-4.46094 z"
|
||||
id="path1193" />
|
||||
<path
|
||||
sodipodi:open="true"
|
||||
sodipodi:end="3.1415927"
|
||||
sodipodi:start="0"
|
||||
sodipodi:ry="7.0068064"
|
||||
sodipodi:rx="7.6309938"
|
||||
sodipodi:cy="236.99103"
|
||||
sodipodi:cx="64"
|
||||
sodipodi:type="arc"
|
||||
id="path1195"
|
||||
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#3d3846;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
sodipodi:arc-type="arc"
|
||||
d="m 71.630994,236.99103 a 7.6309938,7.0068064 0 0 1 -3.815497,6.06807 7.6309938,7.0068064 0 0 1 -7.630994,0 7.6309938,7.0068064 0 0 1 -3.815497,-6.06807" />
|
||||
<rect
|
||||
ry="8"
|
||||
@ -580,16 +513,7 @@
|
||||
<path
|
||||
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#241f31;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
id="path1199"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="64"
|
||||
sodipodi:cy="-216"
|
||||
sodipodi:rx="8"
|
||||
sodipodi:ry="8"
|
||||
sodipodi:start="0"
|
||||
sodipodi:end="3.1415927"
|
||||
sodipodi:open="true"
|
||||
transform="scale(1,-1)"
|
||||
sodipodi:arc-type="arc"
|
||||
d="m 72,-216 a 8,8 0 0 1 -4,6.9282 8,8 0 0 1 -8,0 A 8,8 0 0 1 56,-216" />
|
||||
<rect
|
||||
style="opacity:1;vector-effect:none;fill:#241f31;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
@ -640,17 +564,13 @@
|
||||
transform="matrix(1.5,0,0,1.5,2465.0001,-75.912199)"
|
||||
id="g137294">
|
||||
<path
|
||||
sodipodi:nodetypes="sssccssss"
|
||||
style="display:inline;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
|
||||
d="m -3905.3334,-234.37879 c -4.4319,0 -8,3.56799 -8,8 v 27.33333 c 0,4.43202 3.5681,8 8,8 H -3848 c 4.432,0 8,-3.56798 8,-8 v -27.33333 c 0,-4.43201 -3.568,-8 -8,-8 z"
|
||||
id="path137290"
|
||||
inkscape:connector-curvature="0" />
|
||||
id="path137290" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path137292"
|
||||
d="m -3913.3334,-201.04546 v 2 c 0,4.43202 3.5681,8 8,8 h 19 l 12.6667,12.66667 v -2 l -12.6667,-12.66667 h -19 c -4.4319,0 -8,-3.56798 -8,-8 z m 73.3334,0 c 0,4.43202 -3.568,8 -8,8 h -25.6667 v 2 H -3848 c 4.432,0 8,-3.56798 8,-8 z"
|
||||
style="display:inline;fill:#deddda;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
|
||||
sodipodi:nodetypes="cssccccsccsccssc" />
|
||||
style="display:inline;fill:#deddda;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new" />
|
||||
</g>
|
||||
<rect
|
||||
y="-412.48035"
|
||||
@ -689,42 +609,28 @@
|
||||
cy="-5.023077"
|
||||
r="15.875" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="display:inline;fill:#e5a50a;fill-opacity:1;stroke:none;stroke-width:3.40177;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;enable-background:new"
|
||||
d="M 43.734473,-6.2033675 A 15.875,15.875 0 0 1 27.905982,8.735256 15.875,15.875 0 0 1 12.077491,-5.9594534 a 15.875,15.875 0 0 0 -0.04651,0.9363763 15.875,15.875 0 0 0 15.875,15.8750001 15.875,15.875 0 0 0 15.875,-15.8750001 15.875,15.875 0 0 0 -0.04651,-1.1802904 z"
|
||||
id="path1369-2" />
|
||||
<path
|
||||
sodipodi:open="true"
|
||||
sodipodi:end="3.1415927"
|
||||
sodipodi:start="0"
|
||||
sodipodi:ry="1.8538842"
|
||||
sodipodi:rx="2.0190337"
|
||||
sodipodi:cy="-4.7608676"
|
||||
sodipodi:cx="27.905983"
|
||||
sodipodi:type="arc"
|
||||
id="path1371-3"
|
||||
style="display:inline;opacity:1;fill:none;fill-opacity:1;stroke:#3d3846;stroke-width:1.05833;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;enable-background:new"
|
||||
sodipodi:arc-type="arc"
|
||||
d="m 29.925017,-4.7608676 a 2.0190337,1.8538842 0 0 1 -1.009517,1.6055108 2.0190337,1.8538842 0 0 1 -2.019034,0 2.0190337,1.8538842 0 0 1 -1.009517,-1.6055108" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path1373-7"
|
||||
d="m 24.707728,-13.490777 a 2.1168783,2.1168783 0 0 0 -1.459859,0.642337 l -0.620118,0.620117 -0.620117,-0.620117 a 2.1168783,2.1168783 0 0 0 -1.51877,-0.641302 2.1168783,2.1168783 0 0 0 -1.474329,3.6349196 l 2.865975,2.8659745 v -0.00212 a 1.0583333,1.0583333 0 0 0 1.49655,0 l 2.863907,-2.8639082 A 2.1168783,2.1168783 0 0 0 24.707728,-13.49083 Z"
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#radialGradient1395-6);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.23333;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#radialGradient1397-9-9);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.23333;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 35.291109,-13.490777 a 2.1168783,2.1168783 0 0 0 -1.459859,0.642337 l -0.620118,0.620117 -0.620117,-0.620117 a 2.1168783,2.1168783 0 0 0 -1.51877,-0.641302 2.1168783,2.1168783 0 0 0 -1.474329,3.6349196 l 2.865975,2.8659745 v -0.00212 a 1.0583333,1.0583333 0 0 0 1.49655,0 l 2.863908,-2.8639082 a 2.1168783,2.1168783 0 0 0 -1.53324,-3.6359539 z"
|
||||
id="path1375-3-5" />
|
||||
<path
|
||||
id="path1377-9"
|
||||
d="m 18.412505,-11.77202 a 2.1168783,2.1168783 0 0 0 0.60203,1.9171976 l 2.865975,2.8659745 v -0.00212 a 1.0583333,1.0583333 0 0 0 1.49655,0 l 2.863907,-2.8639109 a 2.1168783,2.1168783 0 0 0 0.604615,-1.9166782 2.1168783,2.1168783 0 0 1 -0.604615,1.122929 l -2.863907,2.8639074 a 1.0583333,1.0583333 0 0 1 -1.49655,0 v 0.00212 l -2.865975,-2.8659714 a 2.1168783,2.1168783 0 0 1 -0.60203,-1.123448 z"
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#c01c28;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.23333;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
inkscape:connector-curvature="0" />
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#c01c28;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.23333;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
<path
|
||||
id="path1379-6-2"
|
||||
d="m 28.995838,-11.77202 a 2.1168783,2.1168783 0 0 0 0.60203,1.9171976 l 2.865975,2.8659745 v -0.00212 a 1.0583333,1.0583333 0 0 0 1.49655,0 l 2.863908,-2.8639109 a 2.1168783,2.1168783 0 0 0 0.604614,-1.9166782 2.1168783,2.1168783 0 0 1 -0.604614,1.122929 l -2.863908,2.8639074 a 1.0583333,1.0583333 0 0 1 -1.49655,0 v 0.00212 l -2.865975,-2.8659714 a 2.1168783,2.1168783 0 0 1 -0.60203,-1.123448 z"
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#c01c28;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.23333;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
inkscape:connector-curvature="0" />
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#c01c28;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.23333;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 33 KiB |
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 3 2 c -1.660156 0 -3 1.339844 -3 3 v 8 c 0 1.660156 1.339844 3 3 3 h 8 c 1.660156 0 3 -1.339844 3 -3 v -4 c 0 -0.550781 -0.449219 -1 -1 -1 s -1 0.449219 -1 1 v 4 c 0 0.554688 -0.445312 1 -1 1 h -8 c -0.554688 0 -1 -0.445312 -1 -1 v -8 c 0 -0.554688 0.445312 -1 1 -1 h 4 c 0.550781 0 1 -0.449219 1 -1 s -0.449219 -1 -1 -1 z m 7 -2 c -0.550781 0 -1 0.449219 -1 1 s 0.449219 1 1 1 h 2.585938 l -5.292969 5.289062 c -0.390625 0.394532 -0.390625 1.027344 0 1.417969 s 1.023437 0.390625 1.414062 0 l 5.292969 -5.292969 v 2.585938 c 0 0.550781 0.449219 1 1 1 s 1 -0.449219 1 -1 v -5 c 0 -0.085938 -0.011719 -0.171875 -0.035156 -0.257812 c -0.023438 -0.085938 -0.054688 -0.167969 -0.101563 -0.242188 c -0.042969 -0.074219 -0.09375 -0.144531 -0.15625 -0.207031 c -0.015625 -0.011719 -0.03125 -0.023438 -0.046875 -0.035157 c -0.054687 -0.050781 -0.117187 -0.09375 -0.183594 -0.128906 c -0.035156 -0.019531 -0.074218 -0.035156 -0.113281 -0.0468748 c -0.050781 -0.0234374 -0.101562 -0.0390624 -0.15625 -0.0507812 c -0.039062 -0.0117188 -0.082031 -0.015625 -0.121093 -0.0195312 c -0.03125 -0.00781255 -0.058594 -0.00781255 -0.085938 -0.0117188 z m 0 0" fill="#222222"/></svg>
|
After Width: | Height: | Size: 1.3 KiB |
2
data/resources/icons/scalable/actions/hide-symbolic.svg
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 13.980469 1.988281 c -0.261719 0.007813 -0.507813 0.117188 -0.6875 0.304688 l -0.984375 0.984375 c -1.285156 -0.828125 -2.78125 -1.273438 -4.308594 -1.277344 c -3.648438 0.003906 -6.832031 2.476562 -7.738281 6.011719 c 0.460937 1.746093 1.496093 3.285156 2.941406 4.371093 l -0.910156 0.910157 c -0.261719 0.25 -0.367188 0.625 -0.273438 0.972656 c 0.089844 0.351563 0.363281 0.625 0.714844 0.714844 c 0.347656 0.09375 0.722656 -0.011719 0.972656 -0.273438 l 11 -11 c 0.296875 -0.289062 0.382813 -0.726562 0.222657 -1.105469 c -0.160157 -0.382812 -0.539063 -0.625 -0.949219 -0.613281 z m -5.980469 2.011719 c 0.957031 0 1.886719 0.347656 2.609375 0.976562 l -1.417969 1.417969 c -0.34375 -0.257812 -0.761718 -0.394531 -1.191406 -0.394531 c -1.105469 0 -2 0.894531 -2 2 c 0 0.429688 0.140625 0.847656 0.394531 1.1875 l -1.417969 1.421875 c -0.628906 -0.726563 -0.972656 -1.652344 -0.976562 -2.609375 c 0 -2.210938 1.789062 -4 4 -4 z m 7.027344 2.207031 l -3.34375 3.34375 c -0.402344 0.960938 -1.167969 1.722657 -2.125 2.128907 l -2.28125 2.277343 c 0.242187 0.027344 0.480468 0.039063 0.722656 0.042969 c 3.648438 -0.003906 6.832031 -2.476562 7.738281 -6.011719 c -0.164062 -0.617187 -0.402343 -1.214843 -0.710937 -1.78125 z m -7.527344 0.792969 c 0.277344 0 0.5 0.222656 0.5 0.5 s -0.222656 0.5 -0.5 0.5 s -0.5 -0.222656 -0.5 -0.5 s 0.222656 -0.5 0.5 -0.5 z m 0 0" fill="#222222"/></svg>
|
After Width: | Height: | Size: 1.5 KiB |
55
data/resources/icons/scalable/apps/org.gnome.Fractal.svg
Normal file
@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg height="128px" viewBox="0 0 128 128" width="128px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="58" y1="69.999985" y2="69.999985">
|
||||
<stop offset="0" stop-color="#4aaac9"/>
|
||||
<stop offset="0.16" stop-color="#8bddf7"/>
|
||||
<stop offset="0.32" stop-color="#4aaac9"/>
|
||||
<stop offset="1" stop-color="#4aaac9"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" gradientUnits="userSpaceOnUse" x1="31.462524" x2="39" y1="113.997253" y2="113.997253">
|
||||
<stop offset="0" stop-color="#4aaac9"/>
|
||||
<stop offset="0.469318" stop-color="#74d7f7"/>
|
||||
<stop offset="1" stop-color="#4aaac9"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="c" gradientUnits="userSpaceOnUse" x1="104" x2="120" y1="84" y2="84">
|
||||
<stop offset="0" stop-color="#1a5fb4"/>
|
||||
<stop offset="0.5" stop-color="#4296ff"/>
|
||||
<stop offset="1" stop-color="#1a5fb4"/>
|
||||
</linearGradient>
|
||||
<clipPath id="d">
|
||||
<path d="m 8 24 h 97 v 84 h -97 z m 0 0"/>
|
||||
</clipPath>
|
||||
<clipPath id="e">
|
||||
<path d="m 24 24 h 80 c 8.835938 0 16 7.164062 16 16 v 52 c 0 8.835938 -7.164062 16 -16 16 h -80 c -8.835938 0 -16 -7.164062 -16 -16 v -52 c 0 -8.835938 7.164062 -16 16 -16 z m 0 0"/>
|
||||
</clipPath>
|
||||
<linearGradient id="f" gradientUnits="userSpaceOnUse" x1="55.608135" x2="71.783539" y1="100" y2="48.532928">
|
||||
<stop offset="0" stop-color="#81dffe"/>
|
||||
<stop offset="1" stop-color="#9bf8fe"/>
|
||||
</linearGradient>
|
||||
<filter id="g" height="100%" width="100%" x="0%" y="0%">
|
||||
<feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<mask id="h">
|
||||
<g filter="url(#g)">
|
||||
<rect fill-opacity="0.35" height="128" width="128"/>
|
||||
</g>
|
||||
</mask>
|
||||
<clipPath id="i">
|
||||
<rect height="152" width="192"/>
|
||||
</clipPath>
|
||||
<path d="m 24 28 h 72 c 8.835938 0 16 7.164062 16 16 v 52 c 0 8.835938 -7.164062 16 -16 16 h -72 c -8.835938 0 -16 -7.164062 -16 -16 v -52 c 0 -8.835938 7.164062 -16 16 -16 z m 0 0" fill="url(#a)"/>
|
||||
<path d="m 24 28 h 80 c 8.835938 0 16 7.164062 16 16 v 48 c 0 8.835938 -7.164062 16 -16 16 h -80 c -8.835938 0 -16 -7.164062 -16 -16 v -48 c 0 -8.835938 7.164062 -16 16 -16 z m 0 0" fill="#53bde0"/>
|
||||
<path d="m 24 100 v 12 h 4 c 2.210938 0 4 1.789062 4 4 v 7 c 0 1.992188 1.183594 3.792969 3.011719 4.585938 c 1.828125 0.789062 3.953125 0.417968 5.40625 -0.945313 l 13.523437 -12.707031 c 1.324219 -1.242188 3.070313 -1.933594 4.882813 -1.933594 h 9.175781 v -12 z m 0 0" fill="url(#b)" fill-rule="evenodd"/>
|
||||
<path d="m 102 58.566406 h 2 c 8.835938 0 16 7.164063 16 16 v 21.433594 c 0 8.835938 -7.164062 16 -16 16 h -2 c -8.835938 0 -16 -7.164062 -16 -16 v -21.433594 c 0 -8.835937 7.164062 -16 16 -16 z m 0 0" fill="url(#c)"/>
|
||||
<path d="m 86 87 h 18 v 25 h -18 z m 0 0" fill="#1a5fb4"/>
|
||||
<path d="m 48 24 h 56 c 8.835938 0 16 7.164062 16 16 v 52 c 0 8.835938 -7.164062 16 -16 16 h -56 c -8.835938 0 -16 -7.164062 -16 -16 v -52 c 0 -8.835938 7.164062 -16 16 -16 z m 0 0" fill="#3584e4"/>
|
||||
<g clip-path="url(#d)">
|
||||
<g clip-path="url(#e)">
|
||||
<path d="m 78.804688 16.023438 l 0.527343 2.460937 c -1.207031 -0.082031 -2.417969 4.964844 -3.621093 4.988281 c -19.335938 0.371094 -38.003907 14.230469 -39.148438 34.546875 c -0.835938 14.761719 9.570312 29.839844 25.15625 30.488281 c 10.371094 0.433594 20.96875 -6.957031 21.242188 -17.925781 c 0.179687 -7.078125 -4.953126 -14.3125 -12.488282 -14.355469 c -4.683594 -0.027343 -9.484375 3.425782 -9.398437 8.429688 c 0.074219 2.980469 2.300781 6.042969 5.511719 5.902344 c 1.8125 -0.082032 3.691406 -1.488282 3.539062 -3.453125 c -0.078125 -1.042969 -0.921875 -2.128907 -2.0625 -1.996094 c -0.5625 0.066406 -1.148438 0.539063 -1.046875 1.15625 c 0.070313 0.273437 0.285156 0.570313 0.597656 0.5 c 0.121094 -0.03125 0.25 -0.144531 0.214844 -0.28125 c 0 -0.042969 -0.070313 -0.09375 -0.113281 -0.074219 c 0 0.003906 -0.070313 0.023438 0 0.035156 v 0.007813 v -0.003906 v 0.035156 c 0 0.050781 -0.09375 0.050781 -0.136719 0.03125 c -0.121094 -0.066406 -0.113281 -0.242187 -0.070313 -0.347656 c 0.164063 -0.265625 0.542969 -0.230469 0.777344 -0.074219 c 0.519532 0.367188 0.429688 1.117188 0.070313 1.558594 c -0.710938 0.898437 -2.074219 0.726562 -2.867188 0.042968 c -1.5 -1.28125 -1.167969 -3.601562 0.070313 -4.941406 c 2.167968 -2.367187 5.929687 -1.792968 8.074218 0.28125 c 3.601563 3.476563 2.652344 9.308594 -0.675781 12.597656 c -5.359375 5.292969 -14.109375 3.800782 -18.992187 -1.324218 c -7.570313 -7.953125 -5.304688 -20.664063 2.335937 -27.6875 c 11.480469 -10.550782 29.507813 -7.242188 39.363281 3.785156 c 14.414063 16.121094 9.6875 41.066406 -5.855468 54.582031 c -11.121094 9.226563 -22.246094 15.429688 -32.949219 19.4375 c -13.058594 75.445313 -75.230469 6.835938 -81.039063 -4.195312 l 0.285157 -105.054688 z m 0 0" fill="url(#f)"/>
|
||||
</g>
|
||||
</g>
|
||||
<path d="m 24 106 v 2 h 4 c 2.210938 0 4 1.789062 4 4 v 7 c 0 1.992188 1.183594 3.792969 3.011719 4.585938 c 1.828125 0.789062 3.953125 0.417968 5.40625 -0.945313 l 13.523437 -12.707031 c 1.324219 -1.242188 3.070313 -1.933594 4.882813 -1.933594 h 9.175781 v -2 z m 0 0" fill="#81dffe" fill-rule="evenodd"/>
|
||||
<g clip-path="url(#i)" mask="url(#h)" transform="matrix(1 0 0 1 -8 -16)">
|
||||
<path d="m 173 17 h 8 c 1.65625 0 3 1.34375 3 3 v 7 c 0 1.65625 -1.34375 3 -3 3 h -8 c -1.65625 0 -3 -1.34375 -3 -3 v -7 c 0 -1.65625 1.34375 -3 3 -3 z m 0 0" fill="#241f31"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.5 KiB |
2
data/resources/icons/scalable/status/dot-symbolic.svg
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 11.019531 7.996094 c 0 1.65625 -1.34375 3 -3 3 s -3 -1.34375 -3 -3 s 1.34375 -3 3 -3 s 3 1.34375 3 3 z m 0 0" fill="#222222"/></svg>
|
After Width: | Height: | Size: 270 B |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
2
data/resources/icons/scalable/status/info-symbolic.svg
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 8 0 c -4.410156 0 -8 3.589844 -8 8 s 3.589844 8 8 8 s 8 -3.589844 8 -8 s -3.589844 -8 -8 -8 z m 0 2 c 3.332031 0 6 2.667969 6 6 s -2.667969 6 -6 6 s -6 -2.667969 -6 -6 s 2.667969 -6 6 -6 z m 0 1.875 c -0.621094 0 -1.125 0.503906 -1.125 1.125 s 0.503906 1.125 1.125 1.125 s 1.125 -0.503906 1.125 -1.125 s -0.503906 -1.125 -1.125 -1.125 z m -1.523438 3.125 c -0.265624 0.011719 -0.476562 0.230469 -0.476562 0.5 c 0 0.277344 0.222656 0.5 0.5 0.5 h 0.5 v 3 h -0.5 c -0.277344 0 -0.5 0.222656 -0.5 0.5 s 0.222656 0.5 0.5 0.5 h 3 c 0.277344 0 0.5 -0.222656 0.5 -0.5 s -0.222656 -0.5 -0.5 -0.5 h -0.5 v -4 h -2.5 c -0.007812 0 -0.015625 0 -0.023438 0 z m 0 0" fill="#222222"/></svg>
|
After Width: | Height: | Size: 813 B |
4
data/resources/icons/scalable/status/person-symbolic.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m 8 1 c -1.65625 0 -3 1.34375 -3 3 s 1.34375 3 3 3 s 3 -1.34375 3 -3 s -1.34375 -3 -3 -3 z m -1.5 7 c -2.492188 0 -4.5 2.007812 -4.5 4.5 v 0.5 c 0 1.109375 0.890625 2 2 2 h 8 c 1.109375 0 2 -0.890625 2 -2 v -0.5 c 0 -2.492188 -2.007812 -4.5 -4.5 -4.5 z m 0 0" fill="#2e3436"/>
|
||||
</svg>
|
After Width: | Height: | Size: 424 B |
4
data/resources/icons/scalable/status/safety-symbolic.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m 8 0 c 0.554688 0 1 0.445312 1 1 v 6.5 s 0 0.5 0.5 0.5 s 0.5 -0.5 0.5 -0.5 v -4.5 c 0 -0.554688 0.445312 -1 1 -1 s 1 0.445312 1 1 v 8.5 c 0 0.5 0.5 0.5 0.5 0.5 l 1.792969 -1.707031 c 0.1875 -0.195313 0.445312 -0.300781 0.71875 -0.304688 c 1.082031 0.085938 1.144531 1.269531 0.695312 1.71875 l -3 3 c -0.707031 0.792969 -1.757812 1.289063 -2.707031 1.292969 h -6 c -3 0 -3 -3 -3 -3 v -8 c 0 -0.554688 0.445312 -1 1 -1 s 1 0.445312 1 1 v 3.5 s 0 0.5 0.5 0.5 s 0.5 -0.5 0.5 -0.5 v -6.5 c 0 -0.554688 0.445312 -1 1 -1 s 1 0.445312 1 1 v 5.5 s 0 0.5 0.5 0.5 s 0.5 -0.5 0.5 -0.5 v -6.5 c 0 -0.554688 0.445312 -1 1 -1 z m 0 0" fill="#2e3436"/>
|
||||
</svg>
|
After Width: | Height: | Size: 786 B |
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g color="#bebebe" fill="#2e3436"><path d="M6 0a3 3 0 100 6 3 3 0 000-6zM4.5 7A4.49 4.49 0 000 11.5v.5c0 1 1 1 1 1h6V8.875c0-.83.587-1.554 1.355-1.79A4.532 4.532 0 007.5 7zM9 9v4h1V9z" style="marker:none" overflow="visible"/><path d="M8.875 8A.863.863 0 008 8.875v6.25c0 .492.383.875.875.875h6.25a.863.863 0 00.875-.875v-6.25A.863.863 0 0015.125 8zM11 9h2v1h-2zm0 2h2v4h-2z" style="marker:none" overflow="visible"/></g></svg>
|
After Width: | Height: | Size: 488 B |
@ -1,3 +1,48 @@
|
||||
# Stylesheets
|
||||
# We accept grass (a Rust SASS compiler) or sass (the official dart SASS compiler).
|
||||
sass_bin = find_program('grass', required: false)
|
||||
sass_options = []
|
||||
if not sass_bin.found()
|
||||
# Require SASS as we need at least one compiler.
|
||||
sass_bin = find_program('sass')
|
||||
|
||||
if sass_bin.found()
|
||||
# these options are not supported by grass.
|
||||
sass_options += ['--no-error-css', '--no-source-map']
|
||||
endif
|
||||
endif
|
||||
|
||||
scss_files = [
|
||||
'style',
|
||||
'style-hc',
|
||||
]
|
||||
|
||||
# Keep ordered alphabetically.
|
||||
scss_deps = files([
|
||||
'stylesheet/_common.scss',
|
||||
'stylesheet/_components.scss',
|
||||
'stylesheet/_config.scss',
|
||||
'stylesheet/_login.scss',
|
||||
'stylesheet/_room_details.scss',
|
||||
'stylesheet/_room_history.scss',
|
||||
'stylesheet/_session_view.scss',
|
||||
'stylesheet/_vendor.scss',
|
||||
])
|
||||
|
||||
stylesheet_deps = []
|
||||
|
||||
foreach scss: scss_files
|
||||
stylesheet_deps += custom_target('@0@.scss'.format(scss),
|
||||
input: '@0@.scss'.format(scss),
|
||||
output: '@0@.css'.format(scss),
|
||||
command: [
|
||||
sass_bin, sass_options, '@INPUT@', '@OUTPUT@',
|
||||
],
|
||||
depend_files: scss_deps,
|
||||
)
|
||||
endforeach
|
||||
|
||||
|
||||
# Resources
|
||||
resources = gnome.compile_resources(
|
||||
'resources',
|
||||
@ -5,4 +50,5 @@ resources = gnome.compile_resources(
|
||||
gresource_bundle: true,
|
||||
install: true,
|
||||
install_dir: pkgdatadir,
|
||||
dependencies: stylesheet_deps,
|
||||
)
|
||||
|
@ -13,10 +13,12 @@
|
||||
<file preprocess="xml-stripblanks">icons/scalable/actions/edit-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/actions/emoji-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/actions/expander-arrow-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/actions/external-link-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/actions/fullscreen-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/actions/go-bottom-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/actions/go-next-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/actions/go-previous-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/actions/hide-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/actions/idp-apple-dark.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/actions/idp-apple.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/actions/idp-facebook.svg</file>
|
||||
@ -41,24 +43,30 @@
|
||||
<file preprocess="xml-stripblanks">icons/scalable/actions/settings-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/actions/system-search-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/actions/user-add-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/apps/org.gnome.Fractal.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/audio-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/blocked-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/checkmark-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/devices-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/document-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/done-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/dot-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/empty-page-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/encryption-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/error-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/explore-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/home-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/image-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/info-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/key-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/no-camera-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/notifications-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/security-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/person-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/safety-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/sync-off-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/sync-on-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/sync-partial-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/user-info-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/users-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/verified-danger-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/status/verified-symbolic.svg</file>
|
||||
@ -98,6 +106,7 @@
|
||||
<file compressed="true">sas-emoji/uk.json</file>
|
||||
<file compressed="true">sas-emoji/vi.json</file>
|
||||
<file compressed="true">sas-emoji/zh_Hans.json</file>
|
||||
<file compressed="true">style-hc.css</file>
|
||||
<file compressed="true">style.css</file>
|
||||
</gresource>
|
||||
</gresources>
|
||||
|
11
data/resources/style-hc.scss
Normal file
@ -0,0 +1,11 @@
|
||||
// High contrast variant.
|
||||
|
||||
// Initialize contrast variable.
|
||||
@use 'stylesheet/config' with (
|
||||
$contrast: 'high',
|
||||
);
|
||||
|
||||
@use 'stylesheet/common';
|
||||
@use 'stylesheet/components';
|
||||
@use 'stylesheet/login';
|
||||
@use 'stylesheet/session_view';
|
5
data/resources/style.scss
Normal file
@ -0,0 +1,5 @@
|
||||
@use 'stylesheet/common';
|
||||
|
||||
@use 'stylesheet/components';
|
||||
@use 'stylesheet/login';
|
||||
@use 'stylesheet/session_view';
|
121
data/resources/stylesheet/_common.scss
Normal file
@ -0,0 +1,121 @@
|
||||
// Global selectors.
|
||||
|
||||
@use 'vendor';
|
||||
|
||||
:root {
|
||||
--focus-outline-color: color-mix(in srgb, var(--accent-color) 50%, transparent);
|
||||
}
|
||||
|
||||
textview, text {
|
||||
color: inherit;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
button.pill.large {
|
||||
padding: 12px 40px;
|
||||
}
|
||||
|
||||
.emoji {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
headerbar .suggested-action, .standalone-button {
|
||||
min-width: 70px;
|
||||
}
|
||||
|
||||
.extra-large-icon {
|
||||
-gtk-icon-size: 128px;
|
||||
}
|
||||
|
||||
.form-page {
|
||||
scrolledwindow > viewport > clamp > box {
|
||||
margin: 42px 12px;
|
||||
border-spacing: 24px;
|
||||
}
|
||||
|
||||
levelbar.discrete block {
|
||||
min-height: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
box.paragraphs {
|
||||
// Set the spacing between paragraphs.
|
||||
border-spacing: 12px;
|
||||
}
|
||||
|
||||
.content .label-button {
|
||||
min-width: 86px;
|
||||
}
|
||||
|
||||
button.overlaid {
|
||||
// Make sure the outline is fully visible.
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
.avatar-row-list, .string-row-list {
|
||||
contents {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
viewport, listview {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
list, listview {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
row {
|
||||
border-radius: vendor.$menu_radius;
|
||||
margin: 3px 0px;
|
||||
padding: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-row-list {
|
||||
row {
|
||||
&:first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.string-row-list row {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.entry-row-error-revealer {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
padding: 12px;
|
||||
border-radius: 100%;
|
||||
-gtk-icon-size: 20px;
|
||||
background-color: color-mix(in srgb, var(--accent-bg-color) 30%, transparent);
|
||||
}
|
||||
|
||||
.card.command {
|
||||
border-radius: vendor.$menu_radius;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
scrolledwindow.card {
|
||||
@include vendor.focus-ring($offset: -1px, $focus-state: ':focus-within');
|
||||
|
||||
> textview {
|
||||
padding: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.padded-top-bar {
|
||||
padding: 0 12px;
|
||||
}
|
168
data/resources/stylesheet/_components.scss
Normal file
@ -0,0 +1,168 @@
|
||||
// Components.
|
||||
|
||||
@use "sass:math";
|
||||
@use 'vendor';
|
||||
|
||||
inline-pill {
|
||||
border-radius: 9999px;
|
||||
background-color: vendor.$button_color;
|
||||
|
||||
@include vendor.focus-ring();
|
||||
|
||||
&.activatable {
|
||||
&:hover {
|
||||
background-color: vendor.$button_hover_color;
|
||||
|
||||
image {
|
||||
filter: brightness(1.07) ;
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: vendor.$button_active_color;
|
||||
|
||||
image {
|
||||
filter: brightness(1.16) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.selected-avatar avatar {
|
||||
border: 2px solid var(--accent-bg-color);
|
||||
}
|
||||
|
||||
.blue-checkmark {
|
||||
color: var(--accent-fg-color);
|
||||
border-radius: 9999px;
|
||||
border: solid var(--accent-bg-color) 2px;
|
||||
background-color: var(--accent-bg-color);
|
||||
}
|
||||
|
||||
role-badge {
|
||||
color: var(--dark-5);
|
||||
background-color: var(--light-3);
|
||||
border-radius: 0.4em;
|
||||
padding: 0.1em 0.5em;
|
||||
font-size: 0.8em;
|
||||
|
||||
&.creator {
|
||||
color: var(--accent-fg-color);
|
||||
background-color: var(--accent-purple);
|
||||
}
|
||||
|
||||
&.admin {
|
||||
color: var(--accent-fg-color);
|
||||
background-color: var(--accent-red);
|
||||
}
|
||||
|
||||
&.mod {
|
||||
color: var(--accent-fg-color);
|
||||
background-color: var(--accent-yellow);
|
||||
}
|
||||
|
||||
&.muted {
|
||||
color: var(--light-1);
|
||||
background-color: var(--dark-2);
|
||||
}
|
||||
}
|
||||
|
||||
media-viewer toolbarview headerbar {
|
||||
background: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
media-content-viewer controls {
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
location-viewer .map-marker {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
entry inline-pill {
|
||||
margin-bottom: -0.5em;
|
||||
}
|
||||
|
||||
editable-avatar {
|
||||
.cutout {
|
||||
background-color: var(--window-bg-color);
|
||||
border-radius: 9999px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.osd.circular {
|
||||
$size: 64px;
|
||||
min-width: $size;
|
||||
min-height: $size;
|
||||
border-radius: math.div($size, 2);
|
||||
}
|
||||
}
|
||||
|
||||
.substring-entry-row .header {
|
||||
.subtitle {
|
||||
margin-top: 4px;
|
||||
margin-bottom: -4px;
|
||||
}
|
||||
|
||||
text placeholder {
|
||||
opacity: var(--dim-opacity);
|
||||
}
|
||||
}
|
||||
|
||||
.role-selection-popover {
|
||||
viewport > box {
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
list row {
|
||||
margin: 0;
|
||||
padding: 2px 6px;
|
||||
border-radius: vendor.$menu_radius;
|
||||
|
||||
&.spin {
|
||||
padding: 0;
|
||||
|
||||
> box {
|
||||
min-height: 30px;
|
||||
}
|
||||
|
||||
spinbutton > button {
|
||||
margin-top: 6px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
button.spin-confirm {
|
||||
min-height: 22px;
|
||||
min-width: 22px;
|
||||
padding: 0;
|
||||
margin-left: 2px;
|
||||
|
||||
&:dir(rtl) {
|
||||
margin-left: 0;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
image {
|
||||
padding: 7px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
row.button loading-bin label.title {
|
||||
margin-left: 12px;
|
||||
margin-right: 12px;
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
crop-circle > .mask {
|
||||
background: black;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
user-page scrolledwindow > viewport > clamp > box {
|
||||
margin: 12px;
|
||||
border-spacing: 24px;
|
||||
}
|
3
data/resources/stylesheet/_config.scss
Normal file
@ -0,0 +1,3 @@
|
||||
// Configuration file with global variable.
|
||||
|
||||
$contrast: 'default' !default;
|
26
data/resources/stylesheet/_login.scss
Normal file
@ -0,0 +1,26 @@
|
||||
// Login and setup.
|
||||
|
||||
login {
|
||||
min-width: 250px;
|
||||
}
|
||||
|
||||
qrcode {
|
||||
background-color: white;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.sso-button {
|
||||
padding: 4px;
|
||||
-gtk-icon-size: 26px;
|
||||
min-height: 34px;
|
||||
}
|
||||
|
||||
setup-view {
|
||||
clamp {
|
||||
margin: 12px;
|
||||
}
|
||||
|
||||
.text-button {
|
||||
min-width: 200px;
|
||||
}
|
||||
}
|
117
data/resources/stylesheet/_room_details.scss
Normal file
@ -0,0 +1,117 @@
|
||||
// Room details.
|
||||
|
||||
@use 'vendor';
|
||||
|
||||
visual-media-history-viewer {
|
||||
background: black;
|
||||
color: white;
|
||||
|
||||
headerbar {
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
gridview {
|
||||
background: none;
|
||||
padding: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
visual-media-history-viewer-item {
|
||||
background-color: var(--border-color);
|
||||
transition: vendor.$ease-out-quad;
|
||||
|
||||
&:hover, &:focus {
|
||||
transform: scale(1.03);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
> overlay > image {
|
||||
border-radius: 100%;
|
||||
padding: 12px;
|
||||
-gtk-icon-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
file-history-viewer, audio-history-viewer {
|
||||
listview > row {
|
||||
border-radius: 0;
|
||||
padding: 6px;
|
||||
|
||||
&:last-child {
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.room-details listview {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
members-list {
|
||||
listview > row, members-list list > row {
|
||||
padding: 8px 12px;
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
listview > row {
|
||||
margin-bottom: 6px;
|
||||
border-radius: vendor.$card_radius;
|
||||
}
|
||||
|
||||
row .icon {
|
||||
&:dir(ltr) {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
&:dir(rtl) {
|
||||
margin-left: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dragoverlay statuspage {
|
||||
background-color: color-mix(in srgb, var(--accent-bg-color) var(--dim-opacity), transparent);
|
||||
color: var(--accent-fg-color);
|
||||
}
|
||||
|
||||
.public-address-tag {
|
||||
color: var(--accent-fg-color);
|
||||
background-color: var(--accent-bg-color);
|
||||
border-radius: 0.4em;
|
||||
padding: 0.3em 0.5em;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
.permissions-member-list > row {
|
||||
min-height: 0;
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
margin: 6px;
|
||||
}
|
||||
|
||||
permissions-member-row {
|
||||
padding: 8px;
|
||||
border-radius: vendor.$card_radius;
|
||||
|
||||
@include vendor.focus-ring();
|
||||
|
||||
&:hover, &.has-open-popup {
|
||||
background-color: vendor.$hover_color;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: vendor.$active_color;
|
||||
}
|
||||
}
|
||||
|
||||
.user-search-results {
|
||||
padding: 12px 0px;
|
||||
|
||||
> row {
|
||||
border-radius: vendor.$menu_radius;
|
||||
}
|
||||
}
|
487
data/resources/stylesheet/_room_history.scss
Normal file
@ -0,0 +1,487 @@
|
||||
// Room history.
|
||||
|
||||
@use 'config';
|
||||
@use 'vendor';
|
||||
|
||||
%nested-effect {
|
||||
border-left: 2px solid var(--accent-bg-color);
|
||||
padding-left: 6px;
|
||||
opacity: if(config.$contrast == 'high', 90%, 70%);
|
||||
}
|
||||
|
||||
room-title {
|
||||
margin-top: -6px;
|
||||
margin-bottom: -6px;
|
||||
min-height: 12px;
|
||||
padding: 3px 0;
|
||||
|
||||
.title {
|
||||
padding: 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
padding: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
&.with-subtitle {
|
||||
button {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.title, .subtitle {
|
||||
margin-top: -0.2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.room-history .room-history-list {
|
||||
padding-bottom: 0;
|
||||
|
||||
> row {
|
||||
min-height: 0;
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.room-history-row {
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
border-radius: vendor.$menu_radius;
|
||||
|
||||
@include vendor.focus-ring();
|
||||
|
||||
&.has-avatar {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
&:not(.has-avatar) {
|
||||
.event-content {
|
||||
&:dir(ltr) {
|
||||
margin-left: 54px;
|
||||
}
|
||||
|
||||
&:dir(rtl) {
|
||||
margin-right: 54px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.has-open-popup {
|
||||
background-color: vendor.$hover_color;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background-color: vendor.$selected_color;
|
||||
|
||||
&.has-open-popup {
|
||||
background-color: vendor.$selected_hover_color;
|
||||
}
|
||||
}
|
||||
|
||||
&.highlight {
|
||||
background-color: color-mix(in srgb, var(--accent-bg-color) 20%, transparent);
|
||||
|
||||
&.has-open-popup {
|
||||
background-color: color-mix(in srgb, var(--accent-bg-color) 25%, transparent);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background-color: color-mix(in srgb, var(--accent-bg-color) 30%, transparent);
|
||||
|
||||
&.has-open-popup {
|
||||
background-color: color-mix(in srgb, var(--accent-bg-color) 33%, transparent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sender-avatar {
|
||||
padding: 5px;
|
||||
border-radius: 100%;
|
||||
|
||||
@include vendor.focus-ring();
|
||||
|
||||
&:hover {
|
||||
background-color: vendor.$hover_color;
|
||||
|
||||
image {
|
||||
filter: brightness(1.07) ;
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: vendor.$active_color;
|
||||
|
||||
image {
|
||||
filter: brightness(1.16) ;
|
||||
}
|
||||
}
|
||||
|
||||
&:checked {
|
||||
background-color: vendor.$selected_color;
|
||||
|
||||
image {
|
||||
filter: brightness(1.1) ;
|
||||
}
|
||||
}
|
||||
|
||||
popover button.text-button {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
message-sender {
|
||||
@include vendor.focus-ring($offset: -1px, $focus-state: ':focus-within');
|
||||
|
||||
border-radius: 3px;
|
||||
|
||||
&.activatable {
|
||||
&:hover, &:focus {
|
||||
label {
|
||||
text-decoration-line: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.event-content {
|
||||
.h1 {
|
||||
font-weight: 800;
|
||||
font-size: 15pt;
|
||||
}
|
||||
|
||||
.h2 {
|
||||
font-weight: 800;
|
||||
font-size: 14pt;
|
||||
}
|
||||
|
||||
.h3 {
|
||||
font-weight: 700;
|
||||
font-size: 14pt;
|
||||
}
|
||||
|
||||
.h4 {
|
||||
font-weight: 700;
|
||||
font-size: 13pt;
|
||||
}
|
||||
|
||||
.h5 {
|
||||
font-weight: 700;
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
.h6 {
|
||||
font-weight: 700;
|
||||
font-size: 11pt;
|
||||
}
|
||||
|
||||
.emoji-message {
|
||||
font-size: 3em;
|
||||
}
|
||||
|
||||
.emote {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
.quote {
|
||||
@extend %nested-effect;
|
||||
}
|
||||
|
||||
expander-widget > box > {
|
||||
title {
|
||||
border-spacing: 6px;
|
||||
}
|
||||
|
||||
:not(title) {
|
||||
padding: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.codeview {
|
||||
border-radius: vendor.$menu_radius;
|
||||
padding: 6px;
|
||||
font-family: monospace;
|
||||
background-color: var(--text-view-bg);
|
||||
color: var(--view-fg-color);
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
min-width: 36px;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
state-group-row.room-history-row {
|
||||
&:not(.has-avatar) {
|
||||
.event-content {
|
||||
&:dir(ltr) {
|
||||
margin-left: 42px;
|
||||
}
|
||||
|
||||
&:dir(rtl) {
|
||||
margin-right: 42px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.expander-title {
|
||||
padding: 6px 12px;
|
||||
border-radius: vendor.$menu_radius;
|
||||
|
||||
&:hover {
|
||||
background-color: vendor.$button_hover_color;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: vendor.$button_active_color;
|
||||
}
|
||||
}
|
||||
|
||||
image.arrow {
|
||||
transition: 200ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
}
|
||||
|
||||
&:not(:checked) image.arrow {
|
||||
&:dir(ltr) {
|
||||
transform: rotate(-0.5turn);
|
||||
}
|
||||
|
||||
&:dir(rtl) {
|
||||
transform: rotate(0.5turn);
|
||||
}
|
||||
}
|
||||
|
||||
.expander-content {
|
||||
padding: 3px 6px;
|
||||
background-color: color-mix(in srgb, var(--view-fg-color) 4%, transparent);
|
||||
border-radius: vendor.$menu_radius;
|
||||
}
|
||||
|
||||
state-group-item-row {
|
||||
padding: 6px 12px;
|
||||
margin: 2px 0;
|
||||
border-radius: vendor.$menu_radius;
|
||||
|
||||
@include vendor.focus-ring();
|
||||
|
||||
&.has-open-popup {
|
||||
background-color: vendor.$hover_color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
message-visual-media {
|
||||
border-radius: vendor.$menu_radius;
|
||||
|
||||
@include vendor.focus-ring();
|
||||
}
|
||||
|
||||
.visual-content {
|
||||
&.opaque-bg {
|
||||
background-color: var(--border-color);
|
||||
}
|
||||
|
||||
> .overlaid {
|
||||
margin: 6px;
|
||||
}
|
||||
|
||||
> .instructions {
|
||||
padding: 12px;
|
||||
border-radius: vendor.$menu_radius;
|
||||
}
|
||||
|
||||
&.compact {
|
||||
> .instructions {
|
||||
padding: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
// Copied from .osd button style in https://gitlab.gnome.org/GNOME/libadwaita/-/blob/main/src/stylesheet/widgets/_buttons.scss
|
||||
&.has-placeholder {
|
||||
> .instructions {
|
||||
color: vendor.$osd_fg_color;
|
||||
background-color: rgb(0 0 0 / 65%);
|
||||
|
||||
@if config.$contrast == 'high' {
|
||||
box-shadow: 0 0 0 1px currentColor;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.compact) {
|
||||
&:hover {
|
||||
> .instructions {
|
||||
color: white;
|
||||
background-color: color-mix(in srgb, black calc(0.85 * 65%), currentColor calc(0.15 * 65%));
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
> .instructions {
|
||||
color: white;
|
||||
background-color: color-mix(in srgb, black calc(0.75 * 65%), currentColor calc(0.25 * 65%));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .spinner {
|
||||
min-width: 32px;
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
> button {
|
||||
// Leave enough space at the start to click to be able to view small images.
|
||||
&:dir(ltr) {
|
||||
margin-left: 64px;
|
||||
}
|
||||
&:dir(rtl) {
|
||||
margin-right: 64px;
|
||||
}
|
||||
}
|
||||
|
||||
> image.osd.circular {
|
||||
min-width: 64px;
|
||||
min-height: 64px;
|
||||
border-radius: 32px;
|
||||
-gtk-icon-size: 32px;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
border-radius: 4px;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
|
||||
&.compact {
|
||||
> .spinner {
|
||||
min-width: 16px;
|
||||
min-height: 16px;
|
||||
}
|
||||
|
||||
> image.osd.circular {
|
||||
min-width: 32px;
|
||||
min-height: 32px;
|
||||
-gtk-icon-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
message-reactions {
|
||||
flowboxchild {
|
||||
&:hover, &:active {
|
||||
// Cancel effect under .navigation-sidebar from libadwaita
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
&:dir(ltr) .toggle {
|
||||
padding: 1px 0 1px 6px;
|
||||
}
|
||||
|
||||
&:dir(rtl) .toggle {
|
||||
padding: 1px 6px 1px 0;
|
||||
}
|
||||
|
||||
.reaction-key-text {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.reaction-key-emoji {
|
||||
font-size: 1.1em;
|
||||
padding-right: 2px;
|
||||
padding-left: 2px;
|
||||
}
|
||||
|
||||
.reaction-count {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
}
|
||||
|
||||
.reaction-chooser {
|
||||
margin: 6px;
|
||||
|
||||
button {
|
||||
font-size: 1.3em;
|
||||
-gtk-icon-size: 1.3em;
|
||||
padding: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
read-receipts-list {
|
||||
min-height: 20px;
|
||||
min-width: 20px;
|
||||
padding: 6px;
|
||||
border-radius: 9999px;
|
||||
|
||||
@include vendor.focus-ring();
|
||||
|
||||
&:hover {
|
||||
background-color: vendor.$hover_color;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: vendor.$active_color;
|
||||
}
|
||||
|
||||
&:checked {
|
||||
background-color: vendor.$selected_color;
|
||||
}
|
||||
}
|
||||
|
||||
divider-row {
|
||||
font-size: 0.9em;
|
||||
font-weight: bold;
|
||||
|
||||
label {
|
||||
opacity: var(--dim-opacity);
|
||||
}
|
||||
|
||||
&.new-messages {
|
||||
color: var(--accent-color);
|
||||
|
||||
label {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
separator {
|
||||
min-height: 2px;
|
||||
background-color: var(--accent-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typing-row {
|
||||
padding: 0 6px;
|
||||
min-height: 30px;
|
||||
}
|
||||
|
||||
.related-event-toolbar {
|
||||
padding: 0 6px 0 12px;
|
||||
|
||||
button {
|
||||
margin: 12px 6px;
|
||||
min-height: 24px;
|
||||
min-width: 24px;
|
||||
}
|
||||
|
||||
.event-content {
|
||||
@extend %nested-effect;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
button.send-text-message-button image {
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
.composer-replacement {
|
||||
margin: 12px;
|
||||
}
|
224
data/resources/stylesheet/_session_view.scss
Normal file
@ -0,0 +1,224 @@
|
||||
// Session view.
|
||||
|
||||
@use 'vendor';
|
||||
@use 'room_history';
|
||||
@use 'room_details';
|
||||
|
||||
// Account switcher
|
||||
.account-switcher {
|
||||
list {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.account-switcher-row {
|
||||
border-radius: vendor.$menu_radius;
|
||||
margin: 3px 0px;
|
||||
padding: 6px 12px;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
button.account-switcher-row {
|
||||
font-weight: normal;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#new-login-icon {
|
||||
padding: 12px; // 2 * padding + pixel-size = size (of avatar)
|
||||
background-color: vendor.$button_color;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
// Sidebar
|
||||
sidebar {
|
||||
.collapse-spacing {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
searchbar > revealer > box {
|
||||
padding-top: 0;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.sidebar-list row {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
|
||||
// Reset focus effect.
|
||||
&:focus-within {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
&:hover, &:selected {
|
||||
background: none;
|
||||
}
|
||||
|
||||
&:active sidebar-row > *:not(popover) {
|
||||
background-color: vendor.$active_color;
|
||||
}
|
||||
|
||||
&:selected sidebar-row > *:not(popover) {
|
||||
background-color: vendor.$selected_color;
|
||||
}
|
||||
|
||||
&:selected sidebar-row:not(.drop-mode) > *:not(popover):hover {
|
||||
background-color: vendor.$selected_hover_color;
|
||||
}
|
||||
|
||||
&:selected:active sidebar-row > *:not(popover) {
|
||||
background-color: vendor.$selected_active_color;
|
||||
}
|
||||
}
|
||||
|
||||
sidebar-row {
|
||||
> *:not(popover) {
|
||||
margin: 2px 6px 0;
|
||||
padding: 9px;
|
||||
border-radius: vendor.$menu_radius;
|
||||
}
|
||||
|
||||
@include vendor.focus-ring($target: '> *:not(popover)');
|
||||
|
||||
&:not(.drop-mode) > *:not(popover):hover {
|
||||
background-color: vendor.$hover_color;
|
||||
}
|
||||
|
||||
icon-item {
|
||||
background: none;
|
||||
font-weight: bold;
|
||||
|
||||
image {
|
||||
min-width: 24px; /* Same width as avatars, so the text is aligned */
|
||||
}
|
||||
}
|
||||
|
||||
sidebar-section {
|
||||
margin-top: 6px;
|
||||
font-size: 0.8em;
|
||||
font-weight: bold;
|
||||
|
||||
image.arrow {
|
||||
transition: 200ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
}
|
||||
|
||||
&:not(:checked) image.arrow {
|
||||
&:dir(ltr) {
|
||||
transform: rotate(-0.5turn);
|
||||
}
|
||||
|
||||
&:dir(rtl) {
|
||||
transform: rotate(0.5turn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notification-count {
|
||||
font-weight: bold;
|
||||
font-size: 0.8em;
|
||||
border-radius: 9999px;
|
||||
min-width: 0.8em;
|
||||
min-height: 0.8em;
|
||||
line-height: 0.8em;
|
||||
padding: 0.4em 5px;
|
||||
color: currentColor;
|
||||
background-color: color-mix(in srgb, currentColor 15%, transparent);
|
||||
}
|
||||
|
||||
.highlight {
|
||||
color: var(--accent-fg-color);
|
||||
background-color: var(--accent-bg-color);
|
||||
}
|
||||
|
||||
&.drag > * {
|
||||
color: var(--accent-fg-color);
|
||||
background-color: var(--accent-bg-color);
|
||||
opacity: var(--disabled-opacity);
|
||||
}
|
||||
|
||||
&.drop-disabled > *:not(popover) {
|
||||
opacity: var(--disabled-opacity);
|
||||
}
|
||||
|
||||
&.drop-empty > *:not(popover) {
|
||||
color: var(--accent--color);
|
||||
}
|
||||
|
||||
icon-item.forget {
|
||||
color: var(--error-color);
|
||||
background: none;
|
||||
}
|
||||
|
||||
&.drop-active {
|
||||
background-color: color-mix(in srgb, var(--accent-color) 10%, transparent);
|
||||
|
||||
category {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
.dimmed, &.drop-empty .dimmed {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Explore
|
||||
.explore {
|
||||
.padded-button {
|
||||
min-width: 64px;
|
||||
}
|
||||
|
||||
.public-rooms row {
|
||||
border-radius: vendor.$menu_radius;
|
||||
margin: 6px 0;
|
||||
padding: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.explore-servers-popover list {
|
||||
background-color: transparent;
|
||||
color: inherit;
|
||||
|
||||
row {
|
||||
min-height: 36px;
|
||||
padding: 0 8px;
|
||||
border-radius: vendor.$menu_radius;
|
||||
margin: 0 0 2px;
|
||||
|
||||
button {
|
||||
min-height: 24px;
|
||||
min-width: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Invite
|
||||
.invite-room-name {
|
||||
font-size: 1.6em;
|
||||
}
|
||||
|
||||
// Event details dialog
|
||||
.event-details-dialog .sourceview {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
// Account settings
|
||||
.account-settings listview {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
// Account chooser dialog
|
||||
.account-chooser list {
|
||||
background: transparent;
|
||||
|
||||
row {
|
||||
margin-bottom: 6px;
|
||||
padding: 8px 12px;
|
||||
min-height: 32px;
|
||||
border-radius: vendor.$card_radius;
|
||||
}
|
||||
}
|
41
data/resources/stylesheet/_vendor.scss
Normal file
@ -0,0 +1,41 @@
|
||||
// SCSS reused directly from other projects.
|
||||
|
||||
@use 'config';
|
||||
|
||||
// https://gitlab.gnome.org/GNOME/libadwaita/-/blob/1.6.1/src/stylesheet/_colors.scss
|
||||
$focus_border_opacity: if(config.$contrast == 'high', 80%, 50%);
|
||||
$focus_border_color: color-mix(in srgb, var(--accent-color) $focus_border_opacity, transparent);
|
||||
$hover_color: color-mix(in srgb, currentColor 7%, transparent);
|
||||
$active_color: color-mix(in srgb, currentColor 16%, transparent);
|
||||
$selected_color: color-mix(in srgb, currentColor 10%, transparent);
|
||||
$selected_hover_color: color-mix(in srgb, currentColor 13%, transparent);
|
||||
$selected_active_color: color-mix(in srgb, currentColor 19%, transparent);
|
||||
$osd_fg_color: RGB(255 255 255 / 90%);
|
||||
|
||||
// https://gitlab.gnome.org/GNOME/libadwaita/-/blob/1.6.1/src/stylesheet/widgets/_buttons.scss
|
||||
$button_color: color-mix(in srgb, currentColor 10%, transparent);
|
||||
$button_hover_color: color-mix(in srgb, currentColor 15%, transparent);
|
||||
$button_active_color: color-mix(in srgb, currentColor 30%, transparent);
|
||||
|
||||
// https://gitlab.gnome.org/GNOME/libadwaita/-/blob/1.6.1/src/stylesheet/_common.scss
|
||||
$ease-out-quad: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
$focus_transition: outline-color 200ms $ease-out-quad,
|
||||
outline-width 200ms $ease-out-quad,
|
||||
outline-offset 200ms $ease-out-quad;
|
||||
$card_radius: 12px;
|
||||
$menu_radius: 9px;
|
||||
|
||||
// https://gitlab.gnome.org/GNOME/libadwaita/-/blob/1.6.1/src/stylesheet/_drawing.scss
|
||||
@mixin focus-ring($target: null, $width: 2px, $offset: -$width, $outer: false, $focus-state: ':focus:focus-visible', $transition: null) {
|
||||
& #{$target} {
|
||||
outline: 0 solid transparent;
|
||||
outline-offset: if($outer, $offset + 4px, $offset + $width + 4px);
|
||||
transition: $focus_transition, $transition;
|
||||
}
|
||||
|
||||
&#{$focus-state} #{$target} {
|
||||
outline-color: $focus_border_color;
|
||||
outline-width: $width;
|
||||
outline-offset: $offset;
|
||||
}
|
||||
}
|
34
deny.toml
Normal file
@ -0,0 +1,34 @@
|
||||
[advisories]
|
||||
yanked = "deny"
|
||||
ignore = [
|
||||
{ id = "RUSTSEC-2024-0436", reason = "paste is unmaintained but used by various dependencies" },
|
||||
]
|
||||
|
||||
[bans]
|
||||
multiple-versions = "allow"
|
||||
|
||||
# To check if a license if compatible with our GPL v3.0 license, see: https://www.gnu.org/licenses/license-list.html
|
||||
# Keep list sorted alphabetically.
|
||||
[licenses]
|
||||
unused-allowed-license = "deny"
|
||||
allow = [
|
||||
"Apache-2.0",
|
||||
"Apache-2.0 WITH LLVM-exception",
|
||||
"BSD-2-Clause",
|
||||
"BSD-3-Clause",
|
||||
"BSL-1.0",
|
||||
"GPL-3.0",
|
||||
"LGPL-2.1",
|
||||
"ISC",
|
||||
"MIT",
|
||||
"MPL-2.0",
|
||||
"Unicode-3.0",
|
||||
"Zlib",
|
||||
]
|
||||
|
||||
[sources]
|
||||
required-git-spec="rev"
|
||||
allow-git = [
|
||||
"https://github.com/ruma/ruma",
|
||||
"https://github.com/matrix-org/matrix-rust-sdk",
|
||||
]
|
18
fractal.doap
@ -6,7 +6,7 @@
|
||||
|
||||
<name xml:lang="en">Fractal</name>
|
||||
<shortdesc xml:lang="en">Chat on Matrix</shortdesc>
|
||||
<description xmi:lang="en">
|
||||
<description xml:lang="en">
|
||||
Fractal is a Matrix messaging app for GNOME written in Rust. Its interface is optimized for
|
||||
collaboration in large groups, such as free software projects, and will fit all screens, big or small.
|
||||
|
||||
@ -18,8 +18,10 @@
|
||||
- See who has read messages, and who is typing
|
||||
- Log into multiple accounts at once (with Single-Sign On support)
|
||||
</description>
|
||||
|
||||
|
||||
<homepage rdf:resource="https://gitlab.gnome.org/World/fractal" />
|
||||
<support-forum rdf:resource="https://discourse.gnome.org/tag/fractal" />
|
||||
<bug-database rdf:resource="https://gitlab.gnome.org/GNOME/fractal/issues" />
|
||||
|
||||
<programming-language>Rust</programming-language>
|
||||
<platform>GTK 4</platform>
|
||||
@ -27,12 +29,12 @@
|
||||
|
||||
<maintainer>
|
||||
<foaf:Person>
|
||||
<foaf:name>Julian Sparber</foaf:name>
|
||||
<gnome:userid>jsparber</gnome:userid>
|
||||
<foaf:name>Kévin Commaille</foaf:name>
|
||||
<gnome:userid>kcommaille</gnome:userid>
|
||||
<foaf:account>
|
||||
<foaf:OnlineAccount>
|
||||
<foaf:accountServiceHomepage rdf:resource="https://gitlab.gnome.org"/>
|
||||
<foaf:accountName>jsparber</foaf:accountName>
|
||||
<foaf:accountName>kcommaille</foaf:accountName>
|
||||
</foaf:OnlineAccount>
|
||||
</foaf:account>
|
||||
</foaf:Person>
|
||||
@ -51,12 +53,12 @@
|
||||
</maintainer>
|
||||
<maintainer>
|
||||
<foaf:Person>
|
||||
<foaf:name>Kévin Commaille</foaf:name>
|
||||
<gnome:userid>kcommaille</gnome:userid>
|
||||
<foaf:name>Julian Sparber</foaf:name>
|
||||
<gnome:userid>jsparber</gnome:userid>
|
||||
<foaf:account>
|
||||
<foaf:OnlineAccount>
|
||||
<foaf:accountServiceHomepage rdf:resource="https://gitlab.gnome.org"/>
|
||||
<foaf:accountName>kcommaille</foaf:accountName>
|
||||
<foaf:accountName>jsparber</foaf:accountName>
|
||||
</foaf:OnlineAccount>
|
||||
</foaf:account>
|
||||
</foaf:Person>
|
||||
|
@ -1,5 +1,4 @@
|
||||
#!/bin/bash
|
||||
# Source: https://gitlab.gnome.org/World/fractal/blob/master/hooks/pre-commit.hook
|
||||
#!/usr/bin/env bash
|
||||
|
||||
export LC_ALL=C
|
||||
|
||||
@ -122,12 +121,12 @@ check_cargo() {
|
||||
elif [ ! -t 1 ]; then
|
||||
exit 2
|
||||
elif check_rustup; then
|
||||
echo -e "$error rustup is installed but the cargo command isn't available"
|
||||
echo -e "$error rustup is installed but the cargo command isn’t available"
|
||||
exit 2
|
||||
else
|
||||
echo ""
|
||||
echo "y: Install cargo via rustup"
|
||||
echo "N: Don't install cargo and abort checks"
|
||||
echo "N: Don’t install cargo and abort checks"
|
||||
echo ""
|
||||
while true; do
|
||||
echo -n "Install cargo? [y/N]: "; read yn < /dev/tty
|
||||
@ -177,7 +176,7 @@ run_rustfmt() {
|
||||
echo "Rustfmt is needed to check Fractal’s code style, but it isn’t available"
|
||||
echo ""
|
||||
echo "y: Install rustfmt via rustup"
|
||||
echo "N: Don't install rustfmt and abort checks"
|
||||
echo "N: Don’t install rustfmt and abort checks"
|
||||
echo ""
|
||||
while true; do
|
||||
echo -n "Install rustfmt? [y/N]: "; read yn < /dev/tty
|
||||
@ -256,7 +255,7 @@ run_typos() {
|
||||
echo "Typos is needed to check spelling mistakes, but it isn’t available"
|
||||
echo ""
|
||||
echo "y: Install typos via cargo"
|
||||
echo "N: Don't install typos and abort checks"
|
||||
echo "N: Don’t install typos and abort checks"
|
||||
echo ""
|
||||
while true; do
|
||||
echo -n "Install typos? [y/N]: "; read yn < /dev/tty
|
||||
@ -295,6 +294,124 @@ run_typos() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Install machete with cargo.
|
||||
install_machete() {
|
||||
echo -e "$Installing cargo-machete…"
|
||||
cargo install cargo-machete
|
||||
if ! cargo machete --version>/dev/null 2>&1; then
|
||||
echo -e "$Could not install cargo-machete"
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
# Run machete to check for unused dependencies.
|
||||
run_machete() {
|
||||
if ! cargo machete --version >/dev/null 2>&1; then
|
||||
if [[ $force_install -eq 1 ]]; then
|
||||
install_machete
|
||||
elif [ ! -t 1 ]; then
|
||||
echo "Could not check for unused dependencies, because cargo-machete could not be run"
|
||||
exit 2
|
||||
else
|
||||
echo "cargo-machete is needed to check for unused dependencies, but it isn’t available"
|
||||
echo ""
|
||||
echo "y: Install cargo-machete via cargo"
|
||||
echo "N: Don’t install cargo-machete and abort checks"
|
||||
echo ""
|
||||
while true; do
|
||||
echo -n "Install cargo-machete? [y/N]: "; read yn < /dev/tty
|
||||
case $yn in
|
||||
[Yy]* )
|
||||
install_machete
|
||||
break
|
||||
;;
|
||||
[Nn]* | "" )
|
||||
exit 2
|
||||
;;
|
||||
* )
|
||||
echo $invalid
|
||||
;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -e "$Checking for unused dependencies…"
|
||||
|
||||
if [[ $verbose -eq 1 ]]; then
|
||||
echo ""
|
||||
cargo machete --version
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if ! cargo machete --with-metadata; then
|
||||
echo -e " Checking for unused dependencies result: $fail"
|
||||
echo "Please fix the above issues, either by removing the dependencies, or by adding the necessary configuration option in Cargo.toml (see cargo-machete documentation)"
|
||||
exit 1
|
||||
else
|
||||
echo -e " Checking for unused dependencies result: $ok"
|
||||
fi
|
||||
}
|
||||
|
||||
# Install cargo-deny with cargo.
|
||||
install_cargo_deny() {
|
||||
echo -e "$Installing cargo-deny…"
|
||||
cargo install cargo-deny
|
||||
if ! cargo deny --version>/dev/null 2>&1; then
|
||||
echo -e "$Could not install cargo-deny"
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
# Run cargo-deny to check Rust dependencies.
|
||||
run_cargo_deny() {
|
||||
if ! cargo deny --version >/dev/null 2>&1; then
|
||||
if [[ $force_install -eq 1 ]]; then
|
||||
install_cargo_deny
|
||||
elif [ ! -t 1 ]; then
|
||||
echo "Could not check Rust dependencies, because cargo-deny could not be run"
|
||||
exit 2
|
||||
else
|
||||
echo "cargo-deny is needed to check the Rust dependencies, but it isn’t available"
|
||||
echo ""
|
||||
echo "y: Install cargo-deny via cargo"
|
||||
echo "N: Don’t install cargo-deny and abort checks"
|
||||
echo ""
|
||||
while true; do
|
||||
echo -n "Install cargo-deny? [y/N]: "; read yn < /dev/tty
|
||||
case $yn in
|
||||
[Yy]* )
|
||||
install_cargo_deny
|
||||
break
|
||||
;;
|
||||
[Nn]* | "" )
|
||||
exit 2
|
||||
;;
|
||||
* )
|
||||
echo $invalid
|
||||
;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -e "$Checking Rust dependencies…"
|
||||
|
||||
if [[ $verbose -eq 1 ]]; then
|
||||
echo ""
|
||||
cargo deny --version
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if ! cargo deny check; then
|
||||
echo -e " Checking Rust dependencies result: $fail"
|
||||
echo "Please fix the above issues, either by removing the dependencies, or by adding the necessary configuration option in deny.toml (see cargo-deny documentation)"
|
||||
exit 1
|
||||
else
|
||||
echo -e " Checking Rust dependencies result: $ok"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if files in POTFILES.in are correct.
|
||||
#
|
||||
# This checks, in that order:
|
||||
@ -521,7 +638,7 @@ run_cargo_sort() {
|
||||
echo "Cargo-sort is needed to check the sorting in Cargo.toml, but it isn’t available"
|
||||
echo ""
|
||||
echo "y: Install cargo-sort via cargo"
|
||||
echo "N: Don't install cargo-sort and abort checks"
|
||||
echo "N: Don’t install cargo-sort and abort checks"
|
||||
echo ""
|
||||
while true; do
|
||||
echo -n "Install cargo-sort? [y/N]: "; read yn < /dev/tty
|
||||
@ -596,6 +713,10 @@ run_rustfmt
|
||||
echo ""
|
||||
run_typos
|
||||
echo ""
|
||||
run_machete
|
||||
echo ""
|
||||
run_cargo_deny
|
||||
echo ""
|
||||
check_potfiles
|
||||
echo ""
|
||||
if [[ $git_staged -eq 1 ]]; then
|
@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
# Depends on: scripts/checks.sh
|
||||
# Depends on: hooks/checks.sh
|
||||
|
||||
# Style helpers
|
||||
act="\e[1;32m"
|
||||
@ -11,7 +11,7 @@ res="\e[0m"
|
||||
echo "-- Pre-commit checks --"
|
||||
echo "To ignore these checks next time, run: git commit --no-verify"
|
||||
echo ""
|
||||
if scripts/checks.sh --git-staged; then
|
||||
if hooks/checks.sh --git-staged; then
|
||||
echo ""
|
||||
echo -e "Pre-commit checks result: ${pos}ok${res}"
|
||||
elif [[ $? -eq 2 ]]; then
|
||||
|
51
meson.build
@ -1,8 +1,8 @@
|
||||
project('fractal',
|
||||
'rust',
|
||||
version: '7.beta',
|
||||
version: '12',
|
||||
license: 'GPL-3.0-or-later',
|
||||
meson_version: '>= 0.59')
|
||||
meson_version: '>= 1.1')
|
||||
|
||||
i18n = import('i18n')
|
||||
gnome = import('gnome')
|
||||
@ -10,8 +10,8 @@ gnome = import('gnome')
|
||||
base_id = 'org.gnome.Fractal'
|
||||
application_id = base_id
|
||||
|
||||
major_version = '7'
|
||||
pre_release_version = 'beta'
|
||||
major_version = '12'
|
||||
pre_release_version = ''
|
||||
|
||||
version = major_version
|
||||
if pre_release_version != ''
|
||||
@ -21,32 +21,29 @@ full_version = version
|
||||
|
||||
dependency('glib-2.0', version: '>= 2.76') # update when changing gtk version
|
||||
dependency('gio-2.0', version: '>= 2.76') # always same version as glib
|
||||
dependency('gtk4', version: '>= 4.12.0')
|
||||
dependency(
|
||||
'libadwaita-1', version: '>= 1.5',
|
||||
fallback: ['libadwaita', 'libadwaita_dep'],
|
||||
default_options: ['tests=false', 'examples=false', 'vapi=false']
|
||||
)
|
||||
dependency('gtk4', version: '>= 4.16')
|
||||
dependency('libadwaita-1', version: '>= 1.7')
|
||||
|
||||
# Please keep these dependencies sorted.
|
||||
dependency('gstreamer-1.0', version: '>= 1.20')
|
||||
dependency('gstreamer-app-1.0', version: '>= 1.20')
|
||||
dependency('gstreamer-base-1.0', version: '>= 1.20')
|
||||
dependency('gstreamer-pbutils-1.0', version: '>= 1.20')
|
||||
dependency('gstreamer-play-1.0', version: '>= 1.20')
|
||||
dependency('gstreamer-video-1.0', version: '>= 1.20')
|
||||
dependency(
|
||||
'gtksourceview-5', version: '>= 5.0.0',
|
||||
fallback: ['gtksourceview', 'gtksource_dep'],
|
||||
default_options: ['gtk_doc=false', 'sysprof=false', 'gir=false', 'vapi=false', 'install_tests=false']
|
||||
)
|
||||
dependency('openssl', version: '>= 1.0.1')
|
||||
dependency('gtksourceview-5', version: '>= 5.0.0')
|
||||
dependency('libwebp', version: '>= 1.0.0')
|
||||
dependency('openssl', version: '>= 3.0.0')
|
||||
dependency('shumate-1.0', version: '>= 1.0.0')
|
||||
dependency('sqlite3', version: '>= 3.24.0')
|
||||
|
||||
# Required by glycin crate
|
||||
dependency('lcms2', version: '>=2.12.0')
|
||||
|
||||
# Linux-only dependencies
|
||||
if build_machine.system() == 'linux'
|
||||
dependency('libpipewire-0.3', version: '>= 0.3.0')
|
||||
dependency('xdg-desktop-portal', version: '>= 1.14.1')
|
||||
# Required by glycin crate
|
||||
dependency('libseccomp', version: '>= 2.5.0')
|
||||
endif
|
||||
|
||||
glib_compile_resources = find_program('glib-compile-resources', required: true)
|
||||
@ -69,7 +66,12 @@ iconsdir = datadir / 'icons'
|
||||
podir = meson.project_source_root() / 'po'
|
||||
gettext_package = meson.project_name()
|
||||
|
||||
if get_option('profile') == 'development'
|
||||
# When the `build-env` profile is used, we only want to set up the build
|
||||
# environment for the sandbox, we will not try to compile the app, so we can
|
||||
# remove some build steps.
|
||||
build_env_only = get_option('profile') == 'build-env'
|
||||
|
||||
if get_option('profile') == 'development' or build_env_only
|
||||
profile = 'Devel'
|
||||
application_id += '.Devel'
|
||||
elif get_option('profile') == 'hack'
|
||||
@ -97,14 +99,11 @@ if profile == 'Devel'
|
||||
run_command('cp', '-f', 'hooks/pre-commit.hook', '.git/hooks/pre-commit')
|
||||
endif
|
||||
|
||||
meson.add_dist_script(
|
||||
'build-aux/dist-vendor.sh',
|
||||
meson.project_build_root() / 'meson-dist' / meson.project_name() + '-' + version,
|
||||
meson.project_source_root()
|
||||
)
|
||||
if not build_env_only
|
||||
subdir('data')
|
||||
subdir('po')
|
||||
endif
|
||||
|
||||
subdir('data')
|
||||
subdir('po')
|
||||
subdir('src')
|
||||
|
||||
gnome.post_install(
|
||||
|
@ -6,7 +6,15 @@ option(
|
||||
'beta',
|
||||
'development',
|
||||
'hack',
|
||||
'build-env',
|
||||
],
|
||||
value: 'default',
|
||||
description: 'The build profile for Fractal. One of "default", "beta", "development" or "hack".'
|
||||
)
|
||||
option(
|
||||
'disable-glycin-sandbox',
|
||||
type : 'boolean',
|
||||
value : false,
|
||||
description: 'Whether the sandbox of glycin should be disabled.' +
|
||||
'This is only useful during development.'
|
||||
)
|
@ -1,5 +1,6 @@
|
||||
# please keep this list sorted alphabetically
|
||||
#
|
||||
bg
|
||||
ca
|
||||
cs
|
||||
da
|
||||
@ -16,6 +17,7 @@ fur
|
||||
fy
|
||||
gl
|
||||
he
|
||||
hi
|
||||
hr
|
||||
hu
|
||||
id
|
||||
@ -36,6 +38,8 @@ sl
|
||||
sr
|
||||
sr@latin
|
||||
sv
|
||||
th
|
||||
tr
|
||||
uk
|
||||
uz
|
||||
zh_CN
|
||||
|
127
po/POTFILES.in
@ -10,28 +10,34 @@ src/account_switcher/account_switcher_popover.ui
|
||||
src/account_switcher/session_item.ui
|
||||
src/application.rs
|
||||
src/components/action_button.ui
|
||||
src/components/auth_dialog.rs
|
||||
src/components/auth_dialog.ui
|
||||
src/components/avatar/editable.rs
|
||||
src/components/avatar/editable.ui
|
||||
src/components/camera/qrcode_scanner.rs
|
||||
src/components/camera/qrcode_scanner.ui
|
||||
src/components/camera/viewfinder.rs
|
||||
src/components/crypto/identity_setup_view.rs
|
||||
src/components/crypto/identity_setup_view.ui
|
||||
src/components/crypto/recovery_setup_view.rs
|
||||
src/components/crypto/recovery_setup_view.ui
|
||||
src/components/editable_avatar.rs
|
||||
src/components/editable_avatar.ui
|
||||
src/components/dialogs/auth/in_browser_page.ui
|
||||
src/components/dialogs/auth/mod.rs
|
||||
src/components/dialogs/auth/mod.ui
|
||||
src/components/dialogs/auth/password_page.ui
|
||||
src/components/dialogs/message_dialogs.rs
|
||||
src/components/dialogs/room_preview.rs
|
||||
src/components/dialogs/room_preview.ui
|
||||
src/components/dialogs/user_profile.ui
|
||||
src/components/offline_banner.rs
|
||||
src/components/join_room_dialog.rs
|
||||
src/components/join_room_dialog.ui
|
||||
src/components/location_viewer.rs
|
||||
src/components/media_content_viewer.rs
|
||||
src/components/reaction_chooser.ui
|
||||
src/components/media/content_viewer.rs
|
||||
src/components/media/location_viewer.rs
|
||||
src/components/pill/at_room.rs
|
||||
src/components/power_level_selection/popover.ui
|
||||
src/components/power_level_selection/row.ui
|
||||
src/components/rows/loading_row.ui
|
||||
src/components/spinner.rs
|
||||
src/components/user_page.rs
|
||||
src/components/user_page.ui
|
||||
src/components/user_profile_dialog.ui
|
||||
src/contrib/qr_code_scanner/mod.ui
|
||||
src/contrib/qr_code.rs
|
||||
src/error_page.rs
|
||||
src/error_page.ui
|
||||
src/identity_verification_view/accept_request_page.rs
|
||||
src/identity_verification_view/accept_request_page.ui
|
||||
@ -43,11 +49,13 @@ src/identity_verification_view/completed_page.rs
|
||||
src/identity_verification_view/completed_page.ui
|
||||
src/identity_verification_view/confirm_qr_code_page.rs
|
||||
src/identity_verification_view/confirm_qr_code_page.ui
|
||||
src/identity_verification_view/mod.rs
|
||||
src/identity_verification_view/mod.ui
|
||||
src/identity_verification_view/no_supported_methods_page.rs
|
||||
src/identity_verification_view/no_supported_methods_page.ui
|
||||
src/identity_verification_view/qr_code_scanned_page.rs
|
||||
src/identity_verification_view/qr_code_scanned_page.ui
|
||||
src/identity_verification_view/room_left_page.ui
|
||||
src/identity_verification_view/sas_page.rs
|
||||
src/identity_verification_view/sas_page.ui
|
||||
src/identity_verification_view/scan_qr_code_page.rs
|
||||
@ -58,21 +66,28 @@ src/login/advanced_dialog.ui
|
||||
src/login/greeter.ui
|
||||
src/login/homeserver_page.rs
|
||||
src/login/homeserver_page.ui
|
||||
src/login/idp_button.rs
|
||||
src/login/in_browser_page.rs
|
||||
src/login/in_browser_page.ui
|
||||
src/login/method_page.rs
|
||||
src/login/method_page.ui
|
||||
src/login/mod.rs
|
||||
src/login/mod.ui
|
||||
src/login/session_setup_view.ui
|
||||
src/login/sso_page.ui
|
||||
src/login/sso_idp_button.rs
|
||||
src/secret/linux.rs
|
||||
src/session/model/session.rs
|
||||
src/session/model/notifications/mod.rs
|
||||
src/session/model/room/join_rule.rs
|
||||
src/session/model/room/mod.rs
|
||||
src/session/model/room/permissions.rs
|
||||
src/session/model/room_list/mod.rs
|
||||
src/session/model/sidebar_data/category/category_type.rs
|
||||
src/session/model/sidebar_data/section/name.rs
|
||||
src/session/model/sidebar_data/icon_item.rs
|
||||
src/session/model/user_sessions_list/user_session.rs
|
||||
src/session/view/account_settings/encryption_page/import_export_keys_subpage.rs
|
||||
src/session/view/account_settings/encryption_page/import_export_keys_subpage.ui
|
||||
src/session/view/account_settings/encryption_page/mod.rs
|
||||
src/session/view/account_settings/encryption_page/mod.ui
|
||||
src/session/view/account_settings/general_page/change_password_subpage.rs
|
||||
src/session/view/account_settings/general_page/change_password_subpage.ui
|
||||
src/session/view/account_settings/general_page/deactivate_account_subpage.rs
|
||||
@ -84,65 +99,76 @@ src/session/view/account_settings/general_page/mod.ui
|
||||
src/session/view/account_settings/mod.ui
|
||||
src/session/view/account_settings/notifications_page.rs
|
||||
src/session/view/account_settings/notifications_page.ui
|
||||
src/session/view/account_settings/security_page/ignored_users_subpage/ignored_user_row.rs
|
||||
src/session/view/account_settings/security_page/ignored_users_subpage/ignored_user_row.ui
|
||||
src/session/view/account_settings/security_page/ignored_users_subpage/mod.ui
|
||||
src/session/view/account_settings/security_page/import_export_keys_subpage.rs
|
||||
src/session/view/account_settings/security_page/import_export_keys_subpage.ui
|
||||
src/session/view/account_settings/security_page/mod.rs
|
||||
src/session/view/account_settings/security_page/mod.ui
|
||||
src/session/view/account_settings/user_sessions_page/mod.ui
|
||||
src/session/view/account_settings/user_sessions_page/user_session_row.rs
|
||||
src/session/view/account_settings/user_sessions_page/user_session_row.ui
|
||||
src/session/view/account_settings/safety_page/ignored_users_subpage/ignored_user_row.rs
|
||||
src/session/view/account_settings/safety_page/ignored_users_subpage/ignored_user_row.ui
|
||||
src/session/view/account_settings/safety_page/ignored_users_subpage/mod.ui
|
||||
src/session/view/account_settings/safety_page/mod.rs
|
||||
src/session/view/account_settings/safety_page/mod.ui
|
||||
src/session/view/account_settings/user_session/user_session_list_subpage.ui
|
||||
src/session/view/account_settings/user_session/user_session_row.ui
|
||||
src/session/view/account_settings/user_session/user_session_subpage.rs
|
||||
src/session/view/account_settings/user_session/user_session_subpage.ui
|
||||
src/session/view/content/explore/mod.ui
|
||||
src/session/view/content/explore/public_room_row.rs
|
||||
src/session/view/content/explore/servers_popover.ui
|
||||
src/session/view/content/explore/server_row.ui
|
||||
src/session/view/content/invite.rs
|
||||
src/session/view/content/invite.ui
|
||||
src/session/view/content/invite_request.rs
|
||||
src/session/view/content/invite_request.ui
|
||||
src/session/view/content/mod.ui
|
||||
src/session/view/content/room_details/addresses_subpage/completion_popover.ui
|
||||
src/session/view/content/room_details/addresses_subpage/mod.rs
|
||||
src/session/view/content/room_details/addresses_subpage/mod.ui
|
||||
src/session/view/content/room_details/general_page/mod.rs
|
||||
src/session/view/content/room_details/general_page/mod.ui
|
||||
src/session/view/content/room_details/edit_details_subpage.rs
|
||||
src/session/view/content/room_details/edit_details_subpage.ui
|
||||
src/session/view/content/room_details/general_page.rs
|
||||
src/session/view/content/room_details/general_page.ui
|
||||
src/session/view/content/room_details/history_viewer/audio_row.rs
|
||||
src/session/view/content/room_details/history_viewer/audio_row.ui
|
||||
src/session/view/content/room_details/history_viewer/audio.ui
|
||||
src/session/view/content/room_details/history_viewer/file_row.rs
|
||||
src/session/view/content/room_details/history_viewer/file_row.ui
|
||||
src/session/view/content/room_details/history_viewer/file.ui
|
||||
src/session/view/content/room_details/history_viewer/media.ui
|
||||
src/session/view/content/room_details/invite_subpage/invitee_list.rs
|
||||
src/session/view/content/room_details/history_viewer/visual_media.ui
|
||||
src/session/view/content/room_details/invite_subpage/list.rs
|
||||
src/session/view/content/room_details/invite_subpage/mod.rs
|
||||
src/session/view/content/room_details/invite_subpage/mod.ui
|
||||
src/session/view/content/room_details/members_page/members_list_view/extra_lists.rs
|
||||
src/session/view/content/room_details/members_page/members_list_view/member_row.ui
|
||||
src/session/view/content/room_details/join_rule_subpage.rs
|
||||
src/session/view/content/room_details/join_rule_subpage.ui
|
||||
src/session/view/content/room_details/member_row.ui
|
||||
src/session/view/content/room_details/members_page/members_list_view/membership_subpage_row.rs
|
||||
src/session/view/content/room_details/members_page/members_list_view/mod.rs
|
||||
src/session/view/content/room_details/members_page/members_list_view/mod.ui
|
||||
src/session/view/content/room_details/members_page/mod.rs
|
||||
src/session/view/content/room_details/mod.rs
|
||||
src/session/view/content/room_details/mod.ui
|
||||
src/session/view/content/room_details/permissions/add_members_subpage.ui
|
||||
src/session/view/content/room_details/permissions/members_subpage.rs
|
||||
src/session/view/content/room_details/permissions/members_subpage.ui
|
||||
src/session/view/content/room_details/permissions/permissions_subpage.rs
|
||||
src/session/view/content/room_details/permissions/permissions_subpage.ui
|
||||
src/session/view/content/room_details/room_upgrade_dialog.rs
|
||||
src/session/view/content/room_history/event_actions.ui
|
||||
src/session/view/content/room_history/item_row.rs
|
||||
src/session/view/content/room_details/upgrade_dialog/mod.rs
|
||||
src/session/view/content/room_details/upgrade_dialog/mod.ui
|
||||
src/session/view/content/room_history/divider_row.rs
|
||||
src/session/view/content/room_history/event_actions/context_menu.rs
|
||||
src/session/view/content/room_history/event_actions/context_menu.ui
|
||||
src/session/view/content/room_history/event_actions/group.rs
|
||||
src/session/view/content/room_history/event_actions/quick_reaction_chooser.ui
|
||||
src/session/view/content/room_history/message_row/audio.rs
|
||||
src/session/view/content/room_history/message_row/content.rs
|
||||
src/session/view/content/room_history/message_row/file.rs
|
||||
src/session/view/content/room_history/message_row/file.ui
|
||||
src/session/view/content/room_history/message_row/location.rs
|
||||
src/session/view/content/room_history/message_row/location.ui
|
||||
src/session/view/content/room_history/message_row/media.rs
|
||||
src/session/view/content/room_history/message_row/message_state_stack.rs
|
||||
src/session/view/content/room_history/message_row/message_state_stack.ui
|
||||
src/session/view/content/room_history/message_row/mod.rs
|
||||
src/session/view/content/room_history/message_row/reaction/mod.rs
|
||||
src/session/view/content/room_history/message_row/reaction_list.ui
|
||||
src/session/view/content/room_history/message_row/reply.ui
|
||||
src/session/view/content/room_history/message_row/sender_name.rs
|
||||
src/session/view/content/room_history/message_row/text/widgets.rs
|
||||
src/session/view/content/room_history/message_row/visual_media.rs
|
||||
src/session/view/content/room_history/message_row/visual_media.ui
|
||||
src/session/view/content/room_history/message_toolbar/attachment_dialog.ui
|
||||
src/session/view/content/room_history/message_toolbar/completion/completion_popover.rs
|
||||
src/session/view/content/room_history/message_toolbar/mod.rs
|
||||
@ -153,30 +179,31 @@ src/session/view/content/room_history/mod.ui
|
||||
src/session/view/content/room_history/read_receipts_list/mod.rs
|
||||
src/session/view/content/room_history/sender_avatar/mod.rs
|
||||
src/session/view/content/room_history/sender_avatar/mod.ui
|
||||
src/session/view/content/room_history/state_row/creation.rs
|
||||
src/session/view/content/room_history/state_row/creation.ui
|
||||
src/session/view/content/room_history/state_row/mod.rs
|
||||
src/session/view/content/room_history/state_row/tombstone.rs
|
||||
src/session/view/content/room_history/state_row/tombstone.ui
|
||||
src/session/view/content/room_history/state/content.rs
|
||||
src/session/view/content/room_history/state/creation.rs
|
||||
src/session/view/content/room_history/state/creation.ui
|
||||
src/session/view/content/room_history/state/group_row.rs
|
||||
src/session/view/content/room_history/title.ui
|
||||
src/session/view/content/room_history/typing_row.rs
|
||||
src/session/view/content/room_history/verification_info_bar.rs
|
||||
src/session/view/create_dm_dialog/mod.rs
|
||||
src/session/view/create_dm_dialog/mod.ui
|
||||
src/session/view/create_direct_chat_dialog/mod.rs
|
||||
src/session/view/create_direct_chat_dialog/mod.ui
|
||||
src/session/view/create_room_dialog.rs
|
||||
src/session/view/create_room_dialog.ui
|
||||
src/session/view/event_details_dialog.rs
|
||||
src/session/view/event_details_dialog.ui
|
||||
src/session/view/media_viewer.rs
|
||||
src/session/view/media_viewer.ui
|
||||
src/session/view/room_creation.rs
|
||||
src/session/view/room_creation.ui
|
||||
src/session/view/sidebar/category_row.rs
|
||||
src/session/view/sidebar/mod.rs
|
||||
src/session/view/sidebar/mod.ui
|
||||
src/session/view/sidebar/room_row.rs
|
||||
src/session/view/sidebar/row.rs
|
||||
src/session/view/sidebar/section_row.rs
|
||||
src/session_list/mod.rs
|
||||
src/shortcuts.ui
|
||||
src/user_facing_error.rs
|
||||
src/utils/matrix.rs
|
||||
src/utils/media.rs
|
||||
src/utils/message_dialog.rs
|
||||
src/utils/matrix/media_message.rs
|
||||
src/utils/matrix/mod.rs
|
||||
src/utils/media/image/mod.rs
|
||||
src/utils/media/mod.rs
|
||||
src/window.ui
|
||||
|
@ -1,4 +1,4 @@
|
||||
# These are files that we don't want to translate
|
||||
# Please keep this file sorted alphabetically.
|
||||
src/i18n.rs
|
||||
src/utils/macros.rs
|
||||
src/utils/toast.rs
|
||||
|
5915
po/en_GB.po
7057
po/pt_BR.po
6279
po/zh_CN.po
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 115 KiB |
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 116 KiB |
@ -1,7 +1,7 @@
|
||||
use gtk::{self, glib, prelude::*, subclass::prelude::*, CompositeTemplate};
|
||||
use gtk::{self, CompositeTemplate, glib, prelude::*, subclass::prelude::*};
|
||||
|
||||
use crate::{
|
||||
components::{Avatar, AvatarData, Spinner},
|
||||
components::{Avatar, AvatarData},
|
||||
prelude::*,
|
||||
session::model::Session,
|
||||
session_list::{FailedSession, SessionInfo},
|
||||
@ -41,8 +41,6 @@ mod imp {
|
||||
type ParentType = gtk::ListBoxRow;
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
Spinner::ensure_type();
|
||||
|
||||
Self::bind_template(klass);
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
<object class="GtkBox">
|
||||
<property name="spacing">10</property>
|
||||
<child>
|
||||
<object class="ComponentsAvatar" id="avatar">
|
||||
<object class="Avatar" id="avatar">
|
||||
<property name="size">40</property>
|
||||
</object>
|
||||
</child>
|
||||
@ -25,7 +25,7 @@
|
||||
<property name="xalign">0.0</property>
|
||||
<property name="hexpand">True</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
<class name="dimmed"/>
|
||||
<class name="caption"/>
|
||||
</style>
|
||||
</object>
|
||||
@ -38,7 +38,9 @@
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">loading</property>
|
||||
<property name="child">
|
||||
<object class="Spinner"/>
|
||||
<object class="AdwSpinner">
|
||||
<property name="width-request">24</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
|
@ -1,6 +1,6 @@
|
||||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use futures_channel::oneshot;
|
||||
use gtk::{glib, CompositeTemplate};
|
||||
use gtk::{CompositeTemplate, glib};
|
||||
use tracing::error;
|
||||
|
||||
mod account_row;
|
||||
@ -60,13 +60,13 @@ mod imp {
|
||||
|
||||
impl AccountChooserDialog {
|
||||
/// Set the list of logged-in sessions.
|
||||
fn set_session_list(&self, session_list: SessionList) {
|
||||
self.accounts.bind_model(Some(&session_list), |session| {
|
||||
fn set_session_list(&self, session_list: &SessionList) {
|
||||
self.accounts.bind_model(Some(session_list), |session| {
|
||||
let row = AccountRow::new(session.downcast_ref().unwrap());
|
||||
row.upcast()
|
||||
});
|
||||
|
||||
self.session_list.set(Some(&session_list));
|
||||
self.session_list.set(Some(session_list));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -92,18 +92,19 @@ impl AccountChooserDialog {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.imp().sender.replace(Some(sender));
|
||||
|
||||
self.present(parent);
|
||||
self.present(Some(parent));
|
||||
|
||||
receiver.await.ok().flatten()
|
||||
}
|
||||
|
||||
/// Select the given row in the session list.
|
||||
#[template_callback]
|
||||
fn select_row(&self, row: gtk::ListBoxRow) {
|
||||
fn select_row(&self, row: >k::ListBoxRow) {
|
||||
if let Some(sender) = self.imp().sender.take() {
|
||||
// The index is -1 when it is not in a GtkListBox, but we just got it from the
|
||||
// GtkListBox so we can safely assume it's a valid u32.
|
||||
let index = row.index() as u32;
|
||||
let index = row
|
||||
.index()
|
||||
.try_into()
|
||||
.expect("selected row should have an index");
|
||||
|
||||
let session_id = self
|
||||
.session_list()
|
||||
|
@ -1,30 +1,25 @@
|
||||
use gtk::{
|
||||
glib::{self, clone, closure},
|
||||
prelude::*,
|
||||
subclass::prelude::*,
|
||||
CompositeTemplate,
|
||||
};
|
||||
use gtk::{CompositeTemplate, glib, glib::clone, prelude::*, subclass::prelude::*};
|
||||
|
||||
use super::AccountSwitcherPopover;
|
||||
use crate::{
|
||||
Window,
|
||||
components::Avatar,
|
||||
session_list::SessionInfo,
|
||||
utils::{template_callbacks::TemplateCallbacks, BoundObjectWeakRef},
|
||||
Window,
|
||||
utils::{BoundObjectWeakRef, TemplateCallbacks},
|
||||
};
|
||||
|
||||
mod imp {
|
||||
use std::cell::RefCell;
|
||||
|
||||
use glib::subclass::InitializingObject;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, CompositeTemplate)]
|
||||
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
|
||||
#[template(resource = "/org/gnome/Fractal/ui/account_switcher/account_switcher_button.ui")]
|
||||
#[properties(wrapper_type = super::AccountSwitcherButton)]
|
||||
pub struct AccountSwitcherButton {
|
||||
pub popover: BoundObjectWeakRef<AccountSwitcherPopover>,
|
||||
pub watch: RefCell<Option<gtk::ExpressionWatch>>,
|
||||
/// The popover of this button.
|
||||
#[property(get, set = Self::set_popover, explicit_notify, nullable)]
|
||||
popover: BoundObjectWeakRef<AccountSwitcherPopover>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
@ -38,6 +33,7 @@ mod imp {
|
||||
SessionInfo::ensure_type();
|
||||
|
||||
Self::bind_template(klass);
|
||||
Self::bind_template_callbacks(klass);
|
||||
TemplateCallbacks::bind_template_callbacks(klass);
|
||||
}
|
||||
|
||||
@ -46,40 +42,85 @@ mod imp {
|
||||
}
|
||||
}
|
||||
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for AccountSwitcherButton {
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
let obj = self.obj();
|
||||
|
||||
obj.connect_toggled(|obj| {
|
||||
obj.handle_toggled();
|
||||
});
|
||||
|
||||
let watch = obj
|
||||
.property_expression("root")
|
||||
.chain_property::<Window>("session-selection")
|
||||
.chain_property::<gtk::SingleSelection>("n-items")
|
||||
.chain_closure::<bool>(closure!(|_: Option<glib::Object>, n_items: u32| {
|
||||
n_items > 0
|
||||
}))
|
||||
.bind(&*obj, "visible", glib::Object::NONE);
|
||||
self.watch.replace(Some(watch));
|
||||
}
|
||||
|
||||
fn dispose(&self) {
|
||||
if let Some(watch) = self.watch.take() {
|
||||
watch.unwatch();
|
||||
}
|
||||
self.reset();
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for AccountSwitcherButton {}
|
||||
impl ButtonImpl for AccountSwitcherButton {}
|
||||
impl ToggleButtonImpl for AccountSwitcherButton {}
|
||||
|
||||
#[gtk::template_callbacks]
|
||||
impl AccountSwitcherButton {
|
||||
/// Set the popover of this button.
|
||||
fn set_popover(&self, popover: Option<&AccountSwitcherPopover>) {
|
||||
if self.popover.obj().as_ref() == popover {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset the state.
|
||||
self.reset();
|
||||
let obj = self.obj();
|
||||
|
||||
if let Some(popover) = popover {
|
||||
// We need to remove the popover from the previous button, if any.
|
||||
if let Some(parent) = popover
|
||||
.parent()
|
||||
.and_downcast::<super::AccountSwitcherButton>()
|
||||
{
|
||||
parent.set_popover(None::<AccountSwitcherPopover>);
|
||||
}
|
||||
|
||||
let closed_handler = popover.connect_closed(clone!(
|
||||
#[weak]
|
||||
obj,
|
||||
move |_| {
|
||||
obj.set_active(false);
|
||||
}
|
||||
));
|
||||
|
||||
popover.set_parent(&*obj);
|
||||
self.popover.set(popover, vec![closed_handler]);
|
||||
}
|
||||
|
||||
obj.notify_popover();
|
||||
}
|
||||
|
||||
/// Toggle the popover of this button.
|
||||
#[template_callback]
|
||||
fn toggle_popover(&self) {
|
||||
let obj = self.obj();
|
||||
|
||||
if obj.is_active() {
|
||||
let Some(window) = obj.root().and_downcast::<Window>() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let popover = window.account_switcher();
|
||||
self.set_popover(Some(popover));
|
||||
|
||||
popover.popup();
|
||||
} else if let Some(popover) = self.popover.obj() {
|
||||
popover.popdown();
|
||||
}
|
||||
}
|
||||
|
||||
/// Reset the state of this button.
|
||||
fn reset(&self) {
|
||||
if let Some(popover) = self.popover.obj() {
|
||||
popover.unparent();
|
||||
}
|
||||
self.popover.disconnect_signals();
|
||||
self.obj().set_active(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
/// A button showing the currently selected account and opening the account switcher popover.
|
||||
/// A button showing the currently selected session and opening the account switcher popover.
|
||||
pub struct AccountSwitcherButton(ObjectSubclass<imp::AccountSwitcherButton>)
|
||||
@extends gtk::Widget, gtk::Button, gtk::ToggleButton, @implements gtk::Accessible;
|
||||
}
|
||||
@ -89,56 +130,6 @@ impl AccountSwitcherButton {
|
||||
pub fn new() -> Self {
|
||||
glib::Object::new()
|
||||
}
|
||||
|
||||
pub fn popover(&self) -> Option<AccountSwitcherPopover> {
|
||||
self.imp().popover.obj()
|
||||
}
|
||||
|
||||
pub fn set_popover(&self, popover: Option<&AccountSwitcherPopover>) {
|
||||
let old_popover = self.popover();
|
||||
|
||||
if old_popover.as_ref() == popover {
|
||||
return;
|
||||
}
|
||||
|
||||
let imp = self.imp();
|
||||
|
||||
// Reset the state.
|
||||
if let Some(popover) = old_popover {
|
||||
popover.unparent();
|
||||
}
|
||||
imp.popover.disconnect_signals();
|
||||
self.set_active(false);
|
||||
|
||||
if let Some(popover) = popover {
|
||||
// We need to remove the popover from the previous button, if any.
|
||||
if let Some(parent) = popover.parent().and_downcast::<AccountSwitcherButton>() {
|
||||
parent.set_popover(None);
|
||||
}
|
||||
|
||||
let closed_handler = popover.connect_closed(clone!(@weak self as obj => move |_| {
|
||||
obj.set_active(false);
|
||||
}));
|
||||
|
||||
popover.set_parent(self);
|
||||
imp.popover.set(popover, vec![closed_handler]);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_toggled(&self) {
|
||||
if self.is_active() {
|
||||
let Some(window) = self.root().and_downcast::<Window>() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let popover = window.account_switcher();
|
||||
self.set_popover(Some(popover));
|
||||
|
||||
popover.popup();
|
||||
} else if let Some(popover) = self.popover() {
|
||||
popover.popdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AccountSwitcherButton {
|
||||
|
@ -1,9 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="AccountSwitcherButton" parent="GtkToggleButton">
|
||||
<signal name="toggled" handler="toggle_popover" swapped="yes"/>
|
||||
<binding name="visible">
|
||||
<closure type="gboolean" function="invert_boolean">
|
||||
<lookup name="is-empty" type="FixedSelection">
|
||||
<lookup name="session-selection" type="Window">
|
||||
<lookup name="root">AccountSwitcherButton</lookup>
|
||||
</lookup>
|
||||
</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
<binding name="tooltip-text">
|
||||
<lookup name="user-id-string" type="SessionInfo">
|
||||
<lookup name="selected-item" type="GtkSingleSelection">
|
||||
<lookup name="selected-item" type="FixedSelection">
|
||||
<lookup name="session-selection" type="Window">
|
||||
<lookup name="root">AccountSwitcherButton</lookup>
|
||||
</lookup>
|
||||
@ -16,13 +26,14 @@
|
||||
</accessibility>
|
||||
<style>
|
||||
<class name="image-button"/>
|
||||
<class name="circular"/>
|
||||
</style>
|
||||
<property name="child">
|
||||
<object class="ComponentsAvatar">
|
||||
<object class="Avatar">
|
||||
<property name="size">24</property>
|
||||
<binding name="data">
|
||||
<lookup name="avatar-data" type="SessionInfo">
|
||||
<lookup name="selected-item" type="GtkSingleSelection">
|
||||
<lookup name="selected-item" type="FixedSelection">
|
||||
<lookup name="session-selection" type="Window">
|
||||
<lookup name="root">AccountSwitcherButton</lookup>
|
||||
</lookup>
|
||||
|
@ -1,12 +1,12 @@
|
||||
use gtk::{
|
||||
CompositeTemplate,
|
||||
glib::{self, clone},
|
||||
prelude::*,
|
||||
subclass::prelude::*,
|
||||
CompositeTemplate,
|
||||
};
|
||||
|
||||
use super::session_item::SessionItemRow;
|
||||
use crate::utils::BoundObjectWeakRef;
|
||||
use crate::utils::{BoundObjectWeakRef, FixedSelection};
|
||||
|
||||
mod imp {
|
||||
use glib::subclass::InitializingObject;
|
||||
@ -18,12 +18,12 @@ mod imp {
|
||||
#[properties(wrapper_type = super::AccountSwitcherPopover)]
|
||||
pub struct AccountSwitcherPopover {
|
||||
#[template_child]
|
||||
pub sessions: TemplateChild<gtk::ListBox>,
|
||||
sessions: TemplateChild<gtk::ListBox>,
|
||||
/// The model containing the logged-in sessions selection.
|
||||
#[property(get, set = Self::set_session_selection, explicit_notify, nullable)]
|
||||
pub session_selection: BoundObjectWeakRef<gtk::SingleSelection>,
|
||||
session_selection: BoundObjectWeakRef<FixedSelection>,
|
||||
/// The selected row.
|
||||
pub selected_row: glib::WeakRef<SessionItemRow>,
|
||||
selected_row: glib::WeakRef<SessionItemRow>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
@ -34,7 +34,7 @@ mod imp {
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
Self::bind_template(klass);
|
||||
Self::Type::bind_template_callbacks(klass);
|
||||
Self::bind_template_callbacks(klass);
|
||||
|
||||
klass.install_action("account-switcher.close", None, |obj, _, _| {
|
||||
obj.popdown();
|
||||
@ -52,92 +52,94 @@ mod imp {
|
||||
impl WidgetImpl for AccountSwitcherPopover {}
|
||||
impl PopoverImpl for AccountSwitcherPopover {}
|
||||
|
||||
#[gtk::template_callbacks]
|
||||
impl AccountSwitcherPopover {
|
||||
/// Set the model containing the logged-in sessions selection.
|
||||
fn set_session_selection(&self, selection: Option<gtk::SingleSelection>) {
|
||||
if selection == self.session_selection.obj() {
|
||||
fn set_session_selection(&self, selection: Option<&FixedSelection>) {
|
||||
if selection == self.session_selection.obj().as_ref() {
|
||||
return;
|
||||
}
|
||||
let obj = self.obj();
|
||||
|
||||
self.session_selection.disconnect_signals();
|
||||
|
||||
self.sessions.bind_model(selection.as_ref(), |session| {
|
||||
let row = SessionItemRow::new(session.downcast_ref().unwrap());
|
||||
self.sessions.bind_model(selection, |session| {
|
||||
let row = SessionItemRow::new(
|
||||
session
|
||||
.downcast_ref()
|
||||
.expect("sessions list box item should be a Session"),
|
||||
);
|
||||
row.upcast()
|
||||
});
|
||||
|
||||
if let Some(selection) = &selection {
|
||||
let selected_handler =
|
||||
selection.connect_selected_item_notify(clone!(@weak obj => move |selection| {
|
||||
obj.update_selected_item(selection.selected());
|
||||
}));
|
||||
obj.update_selected_item(selection.selected());
|
||||
if let Some(selection) = selection {
|
||||
let selected_handler = selection.connect_selected_item_notify(clone!(
|
||||
#[weak(rename_to = imp)]
|
||||
self,
|
||||
move |selection| {
|
||||
imp.update_selected_item(selection.selected());
|
||||
}
|
||||
));
|
||||
self.update_selected_item(selection.selected());
|
||||
|
||||
self.session_selection
|
||||
.set(selection, vec![selected_handler]);
|
||||
}
|
||||
|
||||
obj.notify_session_selection();
|
||||
self.obj().notify_session_selection();
|
||||
}
|
||||
|
||||
/// Select the given row in the session list.
|
||||
#[template_callback]
|
||||
fn select_row(&self, row: >k::ListBoxRow) {
|
||||
self.obj().popdown();
|
||||
|
||||
let Some(selection) = self.session_selection.obj() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let index = u32::try_from(row.index()).expect("selected row has an index");
|
||||
selection.set_selected(index);
|
||||
}
|
||||
|
||||
/// Update the selected item in the session list.
|
||||
fn update_selected_item(&self, selected: u32) {
|
||||
let old_selected = self.selected_row.upgrade();
|
||||
let new_selected = if selected == gtk::INVALID_LIST_POSITION {
|
||||
None
|
||||
} else {
|
||||
let index = selected.try_into().expect("item index should fit into i32");
|
||||
self.sessions
|
||||
.row_at_index(index)
|
||||
.and_downcast::<SessionItemRow>()
|
||||
};
|
||||
|
||||
if old_selected == new_selected {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(row) = &old_selected {
|
||||
row.set_selected(false);
|
||||
}
|
||||
if let Some(row) = &new_selected {
|
||||
row.set_selected(true);
|
||||
}
|
||||
|
||||
self.selected_row.set(new_selected.as_ref());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
/// A popover allowing to switch between the available sessions, to open their
|
||||
/// account settings, or to log into a new account.
|
||||
pub struct AccountSwitcherPopover(ObjectSubclass<imp::AccountSwitcherPopover>)
|
||||
@extends gtk::Widget, gtk::Popover, @implements gtk::Accessible;
|
||||
}
|
||||
|
||||
#[gtk::template_callbacks]
|
||||
impl AccountSwitcherPopover {
|
||||
pub fn new() -> Self {
|
||||
glib::Object::new()
|
||||
}
|
||||
|
||||
fn selected_row(&self) -> Option<SessionItemRow> {
|
||||
self.imp().selected_row.upgrade()
|
||||
}
|
||||
|
||||
/// Select the given row in the session list.
|
||||
#[template_callback]
|
||||
fn select_row(&self, row: gtk::ListBoxRow) {
|
||||
self.popdown();
|
||||
|
||||
let Some(selection) = self.session_selection() else {
|
||||
return;
|
||||
};
|
||||
|
||||
// The index is -1 when it is not in a GtkListBox, but we just got it from the
|
||||
// GtkListBox so we can safely assume it's a valid u32.
|
||||
selection.set_selected(row.index() as u32);
|
||||
}
|
||||
|
||||
/// Update the selected item in the session list.
|
||||
fn update_selected_item(&self, selected: u32) {
|
||||
let imp = self.imp();
|
||||
|
||||
let old_selected = self.selected_row();
|
||||
let new_selected = if selected == gtk::INVALID_LIST_POSITION {
|
||||
None
|
||||
} else {
|
||||
imp.sessions
|
||||
.row_at_index(selected as i32)
|
||||
.and_downcast::<SessionItemRow>()
|
||||
};
|
||||
|
||||
if old_selected == new_selected {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(row) = &old_selected {
|
||||
row.set_selected(false);
|
||||
}
|
||||
if let Some(row) = &new_selected {
|
||||
row.set_selected(true);
|
||||
}
|
||||
|
||||
imp.selected_row.set(new_selected.as_ref());
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AccountSwitcherPopover {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use adw::subclass::prelude::*;
|
||||
use gtk::{glib, prelude::*, CompositeTemplate};
|
||||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gtk::{CompositeTemplate, glib};
|
||||
|
||||
use crate::components::{Avatar, AvatarData};
|
||||
|
||||
@ -15,18 +15,18 @@ mod imp {
|
||||
#[properties(wrapper_type = super::AvatarWithSelection)]
|
||||
pub struct AvatarWithSelection {
|
||||
#[template_child]
|
||||
pub child_avatar: TemplateChild<Avatar>,
|
||||
child_avatar: TemplateChild<Avatar>,
|
||||
#[template_child]
|
||||
pub checkmark: TemplateChild<gtk::Image>,
|
||||
checkmark: TemplateChild<gtk::Image>,
|
||||
/// The [`AvatarData`] displayed by this widget.
|
||||
#[property(get = Self::data, set = Self::set_data, explicit_notify, nullable)]
|
||||
pub data: PhantomData<Option<AvatarData>>,
|
||||
data: PhantomData<Option<AvatarData>>,
|
||||
/// The size of the Avatar.
|
||||
#[property(get = Self::size, set = Self::set_size, minimum = -1, default = -1)]
|
||||
pub size: PhantomData<i32>,
|
||||
size: PhantomData<i32>,
|
||||
/// Whether this avatar is selected.
|
||||
#[property(get = Self::is_selected, set = Self::set_selected, explicit_notify)]
|
||||
pub selected: PhantomData<bool>,
|
||||
selected: PhantomData<bool>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
@ -103,7 +103,7 @@ mod imp {
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
/// A widget displaying an `Avatar` for a `Room` or `User` and an optional selected effect.
|
||||
/// A widget displaying an [`Avatar`] and an optional selected effect.
|
||||
pub struct AvatarWithSelection(ObjectSubclass<imp::AvatarWithSelection>)
|
||||
@extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
|
||||
}
|
||||
@ -112,8 +112,4 @@ impl AvatarWithSelection {
|
||||
pub fn new() -> Self {
|
||||
glib::Object::new()
|
||||
}
|
||||
|
||||
pub fn avatar(&self) -> &Avatar {
|
||||
&self.imp().child_avatar
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
<property name="child">
|
||||
<object class="GtkOverlay">
|
||||
<child>
|
||||
<object class="ComponentsAvatar" id="child_avatar"></object>
|
||||
<object class="Avatar" id="child_avatar"></object>
|
||||
</child>
|
||||
<child type="overlay">
|
||||
<object class="GtkImage" id="checkmark">
|
||||
|
@ -3,7 +3,7 @@ mod account_switcher_popover;
|
||||
mod avatar_with_selection;
|
||||
mod session_item;
|
||||
|
||||
pub use self::{
|
||||
pub(crate) use self::{
|
||||
account_switcher_button::AccountSwitcherButton,
|
||||
account_switcher_popover::AccountSwitcherPopover,
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
use gtk::{self, glib, prelude::*, subclass::prelude::*, CompositeTemplate};
|
||||
use gtk::{CompositeTemplate, glib, prelude::*, subclass::prelude::*};
|
||||
|
||||
use super::avatar_with_selection::AvatarWithSelection;
|
||||
use crate::{
|
||||
@ -20,22 +20,22 @@ mod imp {
|
||||
#[properties(wrapper_type = super::SessionItemRow)]
|
||||
pub struct SessionItemRow {
|
||||
#[template_child]
|
||||
pub avatar: TemplateChild<AvatarWithSelection>,
|
||||
avatar: TemplateChild<AvatarWithSelection>,
|
||||
#[template_child]
|
||||
pub display_name: TemplateChild<gtk::Label>,
|
||||
display_name: TemplateChild<gtk::Label>,
|
||||
#[template_child]
|
||||
pub user_id: TemplateChild<gtk::Label>,
|
||||
user_id: TemplateChild<gtk::Label>,
|
||||
#[template_child]
|
||||
pub state_stack: TemplateChild<gtk::Stack>,
|
||||
state_stack: TemplateChild<gtk::Stack>,
|
||||
#[template_child]
|
||||
pub error_image: TemplateChild<gtk::Image>,
|
||||
error_image: TemplateChild<gtk::Image>,
|
||||
/// The session this item represents.
|
||||
#[property(get, set = Self::set_session, explicit_notify)]
|
||||
pub session: glib::WeakRef<SessionInfo>,
|
||||
pub user_bindings: RefCell<Vec<glib::Binding>>,
|
||||
session: glib::WeakRef<SessionInfo>,
|
||||
user_bindings: RefCell<Vec<glib::Binding>>,
|
||||
/// Whether this session is selected.
|
||||
#[property(get = Self::is_selected, set = Self::set_selected, explicit_notify)]
|
||||
pub selected: PhantomData<bool>,
|
||||
selected: PhantomData<bool>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
@ -46,7 +46,7 @@ mod imp {
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
Self::bind_template(klass);
|
||||
Self::Type::bind_template_callbacks(klass);
|
||||
Self::bind_template_callbacks(klass);
|
||||
}
|
||||
|
||||
fn instance_init(obj: &InitializingObject<Self>) {
|
||||
@ -66,6 +66,7 @@ mod imp {
|
||||
impl WidgetImpl for SessionItemRow {}
|
||||
impl ListBoxRowImpl for SessionItemRow {}
|
||||
|
||||
#[gtk::template_callbacks]
|
||||
impl SessionItemRow {
|
||||
/// Whether this session is selected.
|
||||
fn is_selected(&self) -> bool {
|
||||
@ -145,6 +146,23 @@ mod imp {
|
||||
self.session.set(session);
|
||||
self.obj().notify_session();
|
||||
}
|
||||
|
||||
/// Show the account settings for the session of this row.
|
||||
#[template_callback]
|
||||
fn show_account_settings(&self) {
|
||||
let Some(session) = self.session.upgrade() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let obj = self.obj();
|
||||
obj.activate_action("account-switcher.close", None)
|
||||
.expect("`account-switcher.close` action should exist");
|
||||
obj.activate_action(
|
||||
"win.open-account-settings",
|
||||
Some(&session.session_id().to_variant()),
|
||||
)
|
||||
.expect("`win.open-account-settings` action should exist");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,24 +172,8 @@ glib::wrapper! {
|
||||
@extends gtk::Widget, gtk::ListBoxRow, @implements gtk::Accessible;
|
||||
}
|
||||
|
||||
#[gtk::template_callbacks]
|
||||
impl SessionItemRow {
|
||||
pub fn new(session: &SessionInfo) -> Self {
|
||||
glib::Object::builder().property("session", session).build()
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
pub fn show_account_settings(&self) {
|
||||
let Some(session) = self.session() else {
|
||||
return;
|
||||
};
|
||||
|
||||
self.activate_action("account-switcher.close", None)
|
||||
.unwrap();
|
||||
self.activate_action(
|
||||
"win.open-account-settings",
|
||||
Some(&session.session_id().to_variant()),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -21,14 +21,16 @@
|
||||
<object class="GtkLabel" id="display_name">
|
||||
<property name="xalign">0.0</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="ellipsize">end</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="user_id">
|
||||
<property name="xalign">0.0</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="ellipsize">end</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
<class name="dimmed"/>
|
||||
<class name="caption"/>
|
||||
</style>
|
||||
</object>
|
||||
@ -41,7 +43,11 @@
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">loading</property>
|
||||
<property name="child">
|
||||
<object class="Spinner"/>
|
||||
<object class="AdwSpinner">
|
||||
<property name="valign">center</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="height-request">24</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
@ -54,9 +60,6 @@
|
||||
<property name="valign">center</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="tooltip-text" translatable="yes">Account Settings</property>
|
||||
<accessibility>
|
||||
<property name="label" translatable="yes">Account Settings</property>
|
||||
</accessibility>
|
||||
<signal name="clicked" handler="show_account_settings" swapped="true"/>
|
||||
<style>
|
||||
<class name="circular"/>
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::{cell::RefCell, fmt, rc::Rc};
|
||||
use std::{borrow::Cow, cell::RefCell, fmt, rc::Rc};
|
||||
|
||||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gettextrs::gettext;
|
||||
@ -6,28 +6,39 @@ use gtk::{gio, glib, glib::clone};
|
||||
use tracing::{debug, error, info, warn};
|
||||
|
||||
use crate::{
|
||||
config, intent,
|
||||
GETTEXT_PACKAGE, Window, config,
|
||||
intent::SessionIntent,
|
||||
prelude::*,
|
||||
session::model::{Session, SessionState},
|
||||
session_list::{FailedSession, SessionInfo, SessionList},
|
||||
spawn,
|
||||
system_settings::SystemSettings,
|
||||
toast,
|
||||
utils::{matrix::MatrixIdUri, BoundObjectWeakRef, LoadingState},
|
||||
Window,
|
||||
utils::{BoundObjectWeakRef, LoadingState, matrix::MatrixIdUri},
|
||||
};
|
||||
|
||||
/// The key for the current session setting.
|
||||
pub(crate) const SETTINGS_KEY_CURRENT_SESSION: &str = "current-session";
|
||||
/// The name of the application.
|
||||
pub(crate) const APP_NAME: &str = "Fractal";
|
||||
/// The URL of the homepage of the application.
|
||||
pub(crate) const APP_HOMEPAGE_URL: &str = "https://gitlab.gnome.org/World/fractal/";
|
||||
|
||||
mod imp {
|
||||
use std::cell::Cell;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Application {
|
||||
/// The application settings.
|
||||
pub settings: gio::Settings,
|
||||
pub(super) settings: gio::Settings,
|
||||
/// The system settings.
|
||||
pub system_settings: SystemSettings,
|
||||
pub(super) system_settings: SystemSettings,
|
||||
/// The list of logged-in sessions.
|
||||
pub session_list: SessionList,
|
||||
pub intent_handler: BoundObjectWeakRef<glib::Object>,
|
||||
pub(super) session_list: SessionList,
|
||||
intent_handler: BoundObjectWeakRef<glib::Object>,
|
||||
last_network_state: Cell<NetworkState>,
|
||||
}
|
||||
|
||||
impl Default for Application {
|
||||
@ -37,6 +48,7 @@ mod imp {
|
||||
system_settings: Default::default(),
|
||||
session_list: Default::default(),
|
||||
intent_handler: Default::default(),
|
||||
last_network_state: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -52,43 +64,77 @@ mod imp {
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
let app = self.obj();
|
||||
app.set_up_gactions();
|
||||
app.set_up_accels();
|
||||
// Initialize actions and accelerators.
|
||||
self.set_up_gactions();
|
||||
self.set_up_accels();
|
||||
|
||||
self.session_list
|
||||
.connect_error_notify(clone!(@weak app => move |session_list| {
|
||||
// Listen to errors in the session list.
|
||||
self.session_list.connect_error_notify(clone!(
|
||||
#[weak(rename_to = imp)]
|
||||
self,
|
||||
move |session_list| {
|
||||
if let Some(message) = session_list.error() {
|
||||
let window = app.present_main_window();
|
||||
let window = imp.present_main_window();
|
||||
window.show_secret_error(&message);
|
||||
}
|
||||
}));
|
||||
spawn!(
|
||||
clone!(@weak self.session_list as session_list => async move {
|
||||
}
|
||||
));
|
||||
|
||||
// Restore the sessions.
|
||||
spawn!(clone!(
|
||||
#[weak(rename_to = session_list)]
|
||||
self.session_list,
|
||||
async move {
|
||||
session_list.restore_sessions().await;
|
||||
})
|
||||
);
|
||||
}
|
||||
));
|
||||
|
||||
// Watch the network to log its state.
|
||||
let network_monitor = gio::NetworkMonitor::default();
|
||||
network_monitor.connect_network_changed(clone!(
|
||||
#[weak(rename_to = imp)]
|
||||
self,
|
||||
move |network_monitor, _| {
|
||||
let network_state = NetworkState::with_monitor(network_monitor);
|
||||
|
||||
if imp.last_network_state.get() == network_state {
|
||||
return;
|
||||
}
|
||||
|
||||
network_state.log();
|
||||
imp.last_network_state.set(network_state);
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplicationImpl for Application {
|
||||
fn activate(&self) {
|
||||
self.parent_activate();
|
||||
|
||||
debug!("Application::activate");
|
||||
|
||||
self.obj().present_main_window();
|
||||
self.present_main_window();
|
||||
}
|
||||
|
||||
fn startup(&self) {
|
||||
self.parent_startup();
|
||||
|
||||
// Set icons for shell
|
||||
gtk::Window::set_default_icon_name(crate::APP_ID);
|
||||
}
|
||||
|
||||
fn open(&self, files: &[gio::File], _hint: &str) {
|
||||
debug!("Application::open");
|
||||
|
||||
self.obj().present_main_window();
|
||||
self.present_main_window();
|
||||
|
||||
if files.len() > 1 {
|
||||
warn!("Trying to open several URIs, only the first one will be processed");
|
||||
}
|
||||
|
||||
if let Some(uri) = files.first().map(|f| f.uri()) {
|
||||
self.obj().process_uri(&uri);
|
||||
if let Some(uri) = files.first().map(FileExt::uri) {
|
||||
self.process_uri(&uri);
|
||||
} else {
|
||||
debug!("No URI to open");
|
||||
}
|
||||
@ -97,9 +143,336 @@ mod imp {
|
||||
|
||||
impl GtkApplicationImpl for Application {}
|
||||
impl AdwApplicationImpl for Application {}
|
||||
|
||||
impl Application {
|
||||
/// Get or create the main window and make sure it is visible.
|
||||
///
|
||||
/// Returns the main window.
|
||||
fn present_main_window(&self) -> Window {
|
||||
let window = if let Some(window) = self.obj().active_window().and_downcast() {
|
||||
window
|
||||
} else {
|
||||
Window::new(&self.obj())
|
||||
};
|
||||
|
||||
window.present();
|
||||
window
|
||||
}
|
||||
|
||||
/// Set up the application actions.
|
||||
fn set_up_gactions(&self) {
|
||||
self.obj().add_action_entries([
|
||||
// Quit
|
||||
gio::ActionEntry::builder("quit")
|
||||
.activate(|obj: &super::Application, _, _| {
|
||||
if let Some(window) = obj.active_window() {
|
||||
// This is needed to trigger the close request and save the window
|
||||
// state.
|
||||
window.close();
|
||||
}
|
||||
|
||||
obj.quit();
|
||||
})
|
||||
.build(),
|
||||
// About
|
||||
gio::ActionEntry::builder("about")
|
||||
.activate(|obj: &super::Application, _, _| {
|
||||
obj.imp().show_about_dialog();
|
||||
})
|
||||
.build(),
|
||||
// Show a room. This is the action triggered when clicking a notification about a
|
||||
// message.
|
||||
gio::ActionEntry::builder(SessionIntent::SHOW_MATRIX_ID_ACTION_NAME)
|
||||
.parameter_type(Some(&SessionIntent::static_variant_type()))
|
||||
.activate(|obj: &super::Application, _, variant| {
|
||||
debug!(
|
||||
"`{}` action activated",
|
||||
SessionIntent::SHOW_MATRIX_ID_APP_ACTION_NAME
|
||||
);
|
||||
|
||||
let Some((session_id, intent)) =
|
||||
variant.and_then(SessionIntent::show_matrix_id_from_variant)
|
||||
else {
|
||||
error!(
|
||||
"Activated `{}` action without the proper payload",
|
||||
SessionIntent::SHOW_MATRIX_ID_APP_ACTION_NAME
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
obj.imp().process_session_intent(session_id, intent);
|
||||
})
|
||||
.build(),
|
||||
// Show an identity verification. This is the action triggered when clicking a
|
||||
// notification about a new verification.
|
||||
gio::ActionEntry::builder(SessionIntent::SHOW_IDENTITY_VERIFICATION_ACTION_NAME)
|
||||
.parameter_type(Some(&SessionIntent::static_variant_type()))
|
||||
.activate(|obj: &super::Application, _, variant| {
|
||||
debug!(
|
||||
"`{}` action activated",
|
||||
SessionIntent::SHOW_IDENTITY_VERIFICATION_APP_ACTION_NAME
|
||||
);
|
||||
|
||||
let Some((session_id, intent)) = variant
|
||||
.and_then(SessionIntent::show_identity_verification_from_variant)
|
||||
else {
|
||||
error!(
|
||||
"Activated `{}` action without the proper payload",
|
||||
SessionIntent::SHOW_IDENTITY_VERIFICATION_APP_ACTION_NAME
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
obj.imp().process_session_intent(session_id, intent);
|
||||
})
|
||||
.build(),
|
||||
]);
|
||||
}
|
||||
|
||||
/// Sets up keyboard shortcuts for application and window actions.
|
||||
fn set_up_accels(&self) {
|
||||
let obj = self.obj();
|
||||
obj.set_accels_for_action("app.quit", &["<Control>q"]);
|
||||
obj.set_accels_for_action("win.show-help-overlay", &["<Control>question"]);
|
||||
obj.set_accels_for_action("window.close", &["<Control>w"]);
|
||||
}
|
||||
|
||||
/// Show the dialog with information about the application.
|
||||
fn show_about_dialog(&self) {
|
||||
let dialog = adw::AboutDialog::builder()
|
||||
.application_name(APP_NAME)
|
||||
.application_icon(config::APP_ID)
|
||||
.developer_name(gettext("The Fractal Team"))
|
||||
.license_type(gtk::License::Gpl30)
|
||||
.website(APP_HOMEPAGE_URL)
|
||||
.issue_url("https://gitlab.gnome.org/World/fractal/-/issues")
|
||||
.support_url("https://matrix.to/#/#fractal:gnome.org")
|
||||
.version(config::VERSION)
|
||||
.copyright(gettext("© The Fractal Team"))
|
||||
.developers([
|
||||
"Alejandro Domínguez",
|
||||
"Alexandre Franke",
|
||||
"Bilal Elmoussaoui",
|
||||
"Christopher Davis",
|
||||
"Daniel García Moreno",
|
||||
"Eisha Chen-yen-su",
|
||||
"Jordan Petridis",
|
||||
"Julian Sparber",
|
||||
"Kévin Commaille",
|
||||
"Saurav Sachidanand",
|
||||
])
|
||||
.designers(["Tobias Bernard"])
|
||||
.translator_credits(gettext("translator-credits"))
|
||||
.build();
|
||||
|
||||
// This can't be added via the builder
|
||||
dialog.add_credit_section(Some(&gettext("Name by")), &["Regina Bíró"]);
|
||||
|
||||
// If the user wants our support room, try to open it ourselves.
|
||||
dialog.connect_activate_link(clone!(
|
||||
#[weak(rename_to = imp)]
|
||||
self,
|
||||
#[weak]
|
||||
dialog,
|
||||
#[upgrade_or]
|
||||
false,
|
||||
move |_, uri| {
|
||||
if uri == "https://matrix.to/#/#fractal:gnome.org"
|
||||
&& imp.session_list.has_session_ready()
|
||||
{
|
||||
imp.process_uri(uri);
|
||||
dialog.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
));
|
||||
|
||||
dialog.present(Some(&self.present_main_window()));
|
||||
}
|
||||
|
||||
/// Process the given URI.
|
||||
fn process_uri(&self, uri: &str) {
|
||||
debug!(uri, "Processing URI…");
|
||||
match MatrixIdUri::parse(uri) {
|
||||
Ok(matrix_id) => {
|
||||
self.select_session_for_intent(SessionIntent::ShowMatrixId(matrix_id));
|
||||
}
|
||||
Err(error) => warn!("Invalid Matrix URI: {error}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Select a session to handle the given intent as soon as possible.
|
||||
fn select_session_for_intent(&self, intent: SessionIntent) {
|
||||
debug!(?intent, "Selecting session for intent…");
|
||||
|
||||
// We only handle a single intent at time, the latest one.
|
||||
self.intent_handler.disconnect_signals();
|
||||
|
||||
if self.session_list.state() == LoadingState::Ready {
|
||||
match self.session_list.n_items() {
|
||||
0 => {
|
||||
warn!("Cannot process intent with no logged in session");
|
||||
}
|
||||
1 => {
|
||||
let session = self
|
||||
.session_list
|
||||
.first()
|
||||
.expect("there should be one session");
|
||||
self.process_session_intent(session.session_id(), intent);
|
||||
}
|
||||
_ => {
|
||||
spawn!(clone!(
|
||||
#[weak(rename_to = imp)]
|
||||
self,
|
||||
async move {
|
||||
imp.ask_session_for_intent(intent).await;
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
debug!(?intent, "Session list is not ready, queuing intent…");
|
||||
// Wait for the list to be ready.
|
||||
let cell = Rc::new(RefCell::new(Some(intent)));
|
||||
let handler = self.session_list.connect_state_notify(clone!(
|
||||
#[weak(rename_to = imp)]
|
||||
self,
|
||||
#[strong]
|
||||
cell,
|
||||
move |session_list| {
|
||||
if session_list.state() == LoadingState::Ready {
|
||||
imp.intent_handler.disconnect_signals();
|
||||
|
||||
if let Some(intent) = cell.take() {
|
||||
imp.select_session_for_intent(intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
self.intent_handler
|
||||
.set(self.session_list.upcast_ref(), vec![handler]);
|
||||
}
|
||||
}
|
||||
|
||||
/// Ask the user to choose a session to process the given Matrix ID URI.
|
||||
///
|
||||
/// The session list needs to be ready.
|
||||
async fn ask_session_for_intent(&self, intent: SessionIntent) {
|
||||
debug!(?intent, "Asking to select a session to process intent…");
|
||||
let main_window = self.present_main_window();
|
||||
|
||||
let Some(session_id) = main_window.ask_session().await else {
|
||||
warn!("No session selected to show intent");
|
||||
return;
|
||||
};
|
||||
|
||||
self.process_session_intent(session_id, intent);
|
||||
}
|
||||
|
||||
/// Process the given intent for the given session, as soon as the
|
||||
/// session is ready.
|
||||
fn process_session_intent(&self, session_id: String, intent: SessionIntent) {
|
||||
let Some(session_info) = self.session_list.get(&session_id) else {
|
||||
warn!(
|
||||
session = session_id,
|
||||
?intent,
|
||||
"Could not find session to process intent"
|
||||
);
|
||||
toast!(self.present_main_window(), gettext("Session not found"));
|
||||
return;
|
||||
};
|
||||
|
||||
debug!(session = session_id, ?intent, "Processing session intent…");
|
||||
|
||||
if session_info.is::<FailedSession>() {
|
||||
// We can't do anything, it should show an error screen.
|
||||
warn!(
|
||||
session = session_id,
|
||||
?intent,
|
||||
"Could not process intent for failed session"
|
||||
);
|
||||
} else if let Some(session) = session_info.downcast_ref::<Session>() {
|
||||
if session.state() == SessionState::Ready {
|
||||
self.present_main_window()
|
||||
.process_session_intent(session.session_id(), intent);
|
||||
} else {
|
||||
debug!(
|
||||
session = session_id,
|
||||
?intent,
|
||||
"Session is not ready, queuing intent…"
|
||||
);
|
||||
// Wait for the session to be ready.
|
||||
let cell = Rc::new(RefCell::new(Some((session_id, intent))));
|
||||
let handler = session.connect_ready(clone!(
|
||||
#[weak(rename_to = imp)]
|
||||
self,
|
||||
#[strong]
|
||||
cell,
|
||||
move |_| {
|
||||
imp.intent_handler.disconnect_signals();
|
||||
|
||||
if let Some((session_id, intent)) = cell.take() {
|
||||
imp.present_main_window()
|
||||
.process_session_intent(&session_id, intent);
|
||||
}
|
||||
}
|
||||
));
|
||||
self.intent_handler.set(session.upcast_ref(), vec![handler]);
|
||||
}
|
||||
} else {
|
||||
debug!(
|
||||
session = session_id,
|
||||
?intent,
|
||||
"Session is still loading, queuing intent…"
|
||||
);
|
||||
// Wait for the session to be a `Session`.
|
||||
let cell = Rc::new(RefCell::new(Some((session_id, intent))));
|
||||
let handler = self.session_list.connect_items_changed(clone!(
|
||||
#[weak(rename_to = imp)]
|
||||
self,
|
||||
#[strong]
|
||||
cell,
|
||||
move |session_list, pos, _, added| {
|
||||
if added == 0 {
|
||||
return;
|
||||
}
|
||||
let Some(session_id) = cell
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.map(|(session_id, _)| session_id.clone())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
for i in pos..pos + added {
|
||||
let Some(session_info) =
|
||||
session_list.item(i).and_downcast::<SessionInfo>()
|
||||
else {
|
||||
break;
|
||||
};
|
||||
|
||||
if session_info.session_id() == session_id {
|
||||
imp.intent_handler.disconnect_signals();
|
||||
|
||||
if let Some((session_id, intent)) = cell.take() {
|
||||
imp.process_session_intent(session_id, intent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
self.intent_handler
|
||||
.set(self.session_list.upcast_ref(), vec![handler]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
/// The Fractal application.
|
||||
pub struct Application(ObjectSubclass<imp::Application>)
|
||||
@extends gio::Application, gtk::Application, adw::Application,
|
||||
@implements gio::ActionMap, gio::ActionGroup;
|
||||
@ -114,255 +487,23 @@ impl Application {
|
||||
.build()
|
||||
}
|
||||
|
||||
/// Get or create the main window and make sure it is visible.
|
||||
///
|
||||
/// Returns the main window.
|
||||
fn present_main_window(&self) -> Window {
|
||||
let window = if let Some(window) = self.active_window().and_downcast() {
|
||||
window
|
||||
} else {
|
||||
Window::new(self)
|
||||
};
|
||||
|
||||
window.present();
|
||||
window
|
||||
}
|
||||
|
||||
/// The application settings.
|
||||
pub fn settings(&self) -> gio::Settings {
|
||||
pub(crate) fn settings(&self) -> gio::Settings {
|
||||
self.imp().settings.clone()
|
||||
}
|
||||
|
||||
/// The system settings.
|
||||
pub fn system_settings(&self) -> SystemSettings {
|
||||
pub(crate) fn system_settings(&self) -> SystemSettings {
|
||||
self.imp().system_settings.clone()
|
||||
}
|
||||
|
||||
/// The list of logged-in sessions.
|
||||
pub fn session_list(&self) -> &SessionList {
|
||||
pub(crate) fn session_list(&self) -> &SessionList {
|
||||
&self.imp().session_list
|
||||
}
|
||||
|
||||
/// Set up the application actions.
|
||||
fn set_up_gactions(&self) {
|
||||
self.add_action_entries([
|
||||
// Quit
|
||||
gio::ActionEntry::builder("quit")
|
||||
.activate(|app: &Application, _, _| {
|
||||
if let Some(window) = app.active_window() {
|
||||
// This is needed to trigger the delete event
|
||||
// and saving the window state
|
||||
window.close();
|
||||
}
|
||||
|
||||
app.quit();
|
||||
})
|
||||
.build(),
|
||||
// About
|
||||
gio::ActionEntry::builder("about")
|
||||
.activate(|app: &Application, _, _| {
|
||||
app.show_about_dialog();
|
||||
})
|
||||
.build(),
|
||||
// Show a room. This is the action triggered when clicking a notification.
|
||||
gio::ActionEntry::builder("show-room")
|
||||
.parameter_type(Some(&intent::ShowRoomPayload::static_variant_type()))
|
||||
.activate(|app: &Application, _, v| {
|
||||
let Some(payload) = v.and_then(|v| v.get::<intent::ShowRoomPayload>()) else {
|
||||
error!("Triggered `show-room` action without the proper payload");
|
||||
return;
|
||||
};
|
||||
|
||||
app.process_intent(intent::SessionIntent::ShowRoom(payload));
|
||||
})
|
||||
.build(),
|
||||
]);
|
||||
}
|
||||
|
||||
/// Sets up keyboard shortcuts for application and window actions.
|
||||
fn set_up_accels(&self) {
|
||||
self.set_accels_for_action("app.quit", &["<Control>q"]);
|
||||
self.set_accels_for_action("win.show-help-overlay", &["<Control>question"]);
|
||||
}
|
||||
|
||||
fn show_about_dialog(&self) {
|
||||
let dialog = adw::AboutDialog::builder()
|
||||
.application_name("Fractal")
|
||||
.application_icon(config::APP_ID)
|
||||
.developer_name(gettext("The Fractal Team"))
|
||||
.license_type(gtk::License::Gpl30)
|
||||
.website("https://gitlab.gnome.org/World/fractal/")
|
||||
.issue_url("https://gitlab.gnome.org/World/fractal/-/issues")
|
||||
.support_url("https://matrix.to/#/#fractal:gnome.org")
|
||||
.version(config::VERSION)
|
||||
.copyright(gettext("© 2017-2024 The Fractal Team"))
|
||||
.developers(vec![
|
||||
"Alejandro Domínguez".to_string(),
|
||||
"Alexandre Franke".to_string(),
|
||||
"Bilal Elmoussaoui".to_string(),
|
||||
"Christopher Davis".to_string(),
|
||||
"Daniel García Moreno".to_string(),
|
||||
"Eisha Chen-yen-su".to_string(),
|
||||
"Jordan Petridis".to_string(),
|
||||
"Julian Sparber".to_string(),
|
||||
"Kévin Commaille".to_string(),
|
||||
"Saurav Sachidanand".to_string(),
|
||||
])
|
||||
.designers(vec!["Tobias Bernard".to_string()])
|
||||
.translator_credits(gettext("translator-credits"))
|
||||
.build();
|
||||
|
||||
// This can't be added via the builder
|
||||
dialog.add_credit_section(Some(&gettext("Name by")), &["Regina Bíró"]);
|
||||
|
||||
dialog.present(&self.present_main_window());
|
||||
}
|
||||
|
||||
/// Process the given URI.
|
||||
fn process_uri(&self, uri: &str) {
|
||||
match MatrixIdUri::parse(uri) {
|
||||
Ok(matrix_id) => self.process_intent(matrix_id),
|
||||
Err(error) => warn!("Invalid Matrix URI: {error}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Process the given intent, as soon as possible.
|
||||
fn process_intent(&self, intent: impl Into<intent::AppIntent>) {
|
||||
let intent = intent.into();
|
||||
debug!("Processing intent {intent:?}");
|
||||
|
||||
// We only handle a single intent at time, the latest one.
|
||||
self.imp().intent_handler.disconnect_signals();
|
||||
|
||||
let session_list = self.session_list();
|
||||
|
||||
if session_list.state() == LoadingState::Ready {
|
||||
match intent {
|
||||
intent::AppIntent::WithSession(session_intent) => {
|
||||
self.process_session_intent(session_intent);
|
||||
}
|
||||
intent::AppIntent::ShowMatrixId(matrix_uri) => match session_list.n_items() {
|
||||
0 => {
|
||||
warn!("Cannot open URI with no logged in session");
|
||||
}
|
||||
1 => {
|
||||
let session = session_list.first().expect("There should be one session");
|
||||
let session_intent = intent::SessionIntent::with_matrix_uri(
|
||||
session.session_id(),
|
||||
matrix_uri,
|
||||
);
|
||||
self.process_session_intent(session_intent);
|
||||
}
|
||||
_ => {
|
||||
spawn!(clone!(@weak self as obj => async move {
|
||||
obj.choose_session_for_uri(matrix_uri).await;
|
||||
}));
|
||||
}
|
||||
},
|
||||
}
|
||||
} else {
|
||||
// Wait for the list to be ready.
|
||||
let cell = Rc::new(RefCell::new(Some(intent)));
|
||||
let handler = session_list.connect_state_notify(
|
||||
clone!(@weak self as app, @strong cell => move |session_list| {
|
||||
if session_list.state() == LoadingState::Ready {
|
||||
app.imp().intent_handler.disconnect_signals();
|
||||
|
||||
if let Some(intent) = cell.take() {
|
||||
app.process_intent(intent);
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
self.imp()
|
||||
.intent_handler
|
||||
.set(session_list.upcast_ref(), vec![handler]);
|
||||
}
|
||||
}
|
||||
|
||||
/// Ask the user to choose a session to process the given Matrix ID URI.
|
||||
///
|
||||
/// The session list needs to be ready.
|
||||
async fn choose_session_for_uri(&self, matrix_uri: MatrixIdUri) {
|
||||
let main_window = self.present_main_window();
|
||||
|
||||
let Some(session_id) = main_window.choose_session_for_uri().await else {
|
||||
warn!("No session selected to show URI");
|
||||
return;
|
||||
};
|
||||
|
||||
let session_intent = intent::SessionIntent::with_matrix_uri(session_id, matrix_uri);
|
||||
self.process_session_intent(session_intent);
|
||||
}
|
||||
|
||||
/// Process the given for a session, as soon as the session is ready.
|
||||
fn process_session_intent(&self, intent: intent::SessionIntent) {
|
||||
let Some(session_info) = self.session_list().get(intent.session_id()) else {
|
||||
warn!("Could not find session to process intent {intent:?}");
|
||||
toast!(self.present_main_window(), gettext("Session not found"));
|
||||
return;
|
||||
};
|
||||
if session_info.is::<FailedSession>() {
|
||||
// We can't do anything, it should show an error screen.
|
||||
warn!("Could not process intent {intent:?} for failed session");
|
||||
} else if let Some(session) = session_info.downcast_ref::<Session>() {
|
||||
if session.state() == SessionState::Ready {
|
||||
self.present_main_window()
|
||||
.process_session_intent_ready(intent);
|
||||
} else {
|
||||
// Wait for the session to be ready.
|
||||
let cell = Rc::new(RefCell::new(Some(intent)));
|
||||
let handler = session.connect_state_notify(
|
||||
clone!(@weak self as app, @strong cell => move |session| {
|
||||
if session.state() == SessionState::Ready {
|
||||
app.imp().intent_handler.disconnect_signals();
|
||||
|
||||
if let Some(intent) = cell.take() {
|
||||
app.present_main_window().process_session_intent_ready(intent);
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
self.imp()
|
||||
.intent_handler
|
||||
.set(session.upcast_ref(), vec![handler]);
|
||||
}
|
||||
} else {
|
||||
// Wait for the session to be a `Session`.
|
||||
let session_list = self.session_list();
|
||||
let cell = Rc::new(RefCell::new(Some(intent)));
|
||||
let handler = session_list.connect_items_changed(
|
||||
clone!(@weak self as app, @strong cell => move |session_list, pos, _, added| {
|
||||
if added == 0 {
|
||||
return;
|
||||
}
|
||||
let Some(session_id) = cell.borrow().as_ref().map(|i| i.session_id().to_owned()) else {
|
||||
return;
|
||||
};
|
||||
|
||||
for i in pos..pos + added {
|
||||
let Some(session_info) = session_list.item(i).and_downcast::<SessionInfo>() else {
|
||||
break;
|
||||
};
|
||||
|
||||
if session_info.session_id() == session_id {
|
||||
app.imp().intent_handler.disconnect_signals();
|
||||
|
||||
if let Some(intent) = cell.take() {
|
||||
app.process_session_intent(intent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
self.imp()
|
||||
.intent_handler
|
||||
.set(session_list.upcast_ref(), vec![handler]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&self) {
|
||||
/// Run Fractal.
|
||||
pub(crate) fn run(&self) {
|
||||
info!("Fractal ({})", config::APP_ID);
|
||||
info!("Version: {} ({})", config::VERSION, config::PROFILE);
|
||||
info!("Datadir: {}", config::PKGDATADIR);
|
||||
@ -375,14 +516,14 @@ impl Default for Application {
|
||||
fn default() -> Self {
|
||||
gio::Application::default()
|
||||
.and_downcast::<Application>()
|
||||
.unwrap()
|
||||
.expect("application should always be available")
|
||||
}
|
||||
}
|
||||
|
||||
/// The profile that was built.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[allow(dead_code)]
|
||||
pub enum AppProfile {
|
||||
pub(crate) enum AppProfile {
|
||||
/// A stable release.
|
||||
Stable,
|
||||
/// A beta release.
|
||||
@ -393,7 +534,7 @@ pub enum AppProfile {
|
||||
|
||||
impl AppProfile {
|
||||
/// The string representation of this `AppProfile`.
|
||||
pub fn as_str(&self) -> &str {
|
||||
pub(crate) fn as_str(&self) -> &str {
|
||||
match self {
|
||||
Self::Stable => "stable",
|
||||
Self::Beta => "beta",
|
||||
@ -402,9 +543,17 @@ impl AppProfile {
|
||||
}
|
||||
|
||||
/// Whether this `AppProfile` should use the `.devel` CSS class on windows.
|
||||
pub fn should_use_devel_class(&self) -> bool {
|
||||
pub(crate) fn should_use_devel_class(self) -> bool {
|
||||
matches!(self, Self::Devel)
|
||||
}
|
||||
|
||||
/// The name of the directory where to put data for this profile.
|
||||
pub(crate) fn dir_name(self) -> Cow<'static, str> {
|
||||
match self {
|
||||
AppProfile::Stable => Cow::Borrowed(GETTEXT_PACKAGE),
|
||||
_ => Cow::Owned(format!("{GETTEXT_PACKAGE}-{self}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for AppProfile {
|
||||
@ -412,3 +561,41 @@ impl fmt::Display for AppProfile {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
/// The state of the network.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum NetworkState {
|
||||
/// The network is available.
|
||||
Unavailable,
|
||||
/// The network is available with the given connectivity.
|
||||
Available(gio::NetworkConnectivity),
|
||||
}
|
||||
|
||||
impl NetworkState {
|
||||
/// Construct the network state with the given network monitor.
|
||||
fn with_monitor(monitor: &gio::NetworkMonitor) -> Self {
|
||||
if monitor.is_network_available() {
|
||||
Self::Available(monitor.connectivity())
|
||||
} else {
|
||||
Self::Unavailable
|
||||
}
|
||||
}
|
||||
|
||||
/// Log this network state.
|
||||
fn log(self) {
|
||||
match self {
|
||||
Self::Unavailable => {
|
||||
info!("Network is unavailable");
|
||||
}
|
||||
Self::Available(connectivity) => {
|
||||
info!("Network connectivity is {connectivity:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for NetworkState {
|
||||
fn default() -> Self {
|
||||
Self::Available(gio::NetworkConnectivity::Full)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
use adw::subclass::prelude::*;
|
||||
use gtk::{glib, glib::closure_local, prelude::*, CompositeTemplate};
|
||||
|
||||
use super::Spinner;
|
||||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gtk::{CompositeTemplate, glib, glib::closure_local};
|
||||
|
||||
#[derive(Debug, Default, Hash, Eq, PartialEq, Clone, Copy, glib::Enum)]
|
||||
#[repr(u32)]
|
||||
@ -35,10 +33,10 @@ mod imp {
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
marker::PhantomData,
|
||||
sync::LazyLock,
|
||||
};
|
||||
|
||||
use glib::subclass::{InitializingObject, Signal};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::*;
|
||||
|
||||
@ -46,41 +44,39 @@ mod imp {
|
||||
#[template(resource = "/org/gnome/Fractal/ui/components/action_button.ui")]
|
||||
#[properties(wrapper_type = super::ActionButton)]
|
||||
pub struct ActionButton {
|
||||
#[template_child]
|
||||
stack: TemplateChild<gtk::Stack>,
|
||||
#[template_child]
|
||||
button_default: TemplateChild<gtk::Button>,
|
||||
/// The icon used in the default state.
|
||||
#[property(get, set = Self::set_icon_name, explicit_notify)]
|
||||
pub icon_name: RefCell<String>,
|
||||
/// The extra classes applied to the button in the default state.
|
||||
pub extra_classes: RefCell<Vec<String>>,
|
||||
icon_name: RefCell<String>,
|
||||
/// The extra CSS classes applied to the button in the default state.
|
||||
extra_classes: RefCell<Vec<&'static str>>,
|
||||
/// The action emitted by the button.
|
||||
#[property(get = Self::action_name, set = Self::set_action_name, override_interface = gtk::Actionable)]
|
||||
pub action_name: RefCell<Option<glib::GString>>,
|
||||
action_name: RefCell<Option<glib::GString>>,
|
||||
/// The target value of the action of the button.
|
||||
#[property(get = Self::action_target_value, set = Self::set_action_target, override_interface = gtk::Actionable)]
|
||||
pub action_target: RefCell<Option<glib::Variant>>,
|
||||
action_target: RefCell<Option<glib::Variant>>,
|
||||
/// The state of the button.
|
||||
#[property(get, set = Self::set_state, explicit_notify, builder(ActionState::default()))]
|
||||
pub state: Cell<ActionState>,
|
||||
state: Cell<ActionState>,
|
||||
/// The tooltip text of the button of the default state.
|
||||
#[property(set = Self::set_default_state_tooltip_text)]
|
||||
pub default_state_tooltip_text: PhantomData<Option<String>>,
|
||||
#[template_child]
|
||||
pub stack: TemplateChild<gtk::Stack>,
|
||||
#[template_child]
|
||||
pub button_default: TemplateChild<gtk::Button>,
|
||||
#[template_child]
|
||||
pub spinner: TemplateChild<Spinner>,
|
||||
default_state_tooltip_text: PhantomData<Option<String>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for ActionButton {
|
||||
const NAME: &'static str = "ComponentsActionButton";
|
||||
const NAME: &'static str = "ActionButton";
|
||||
type Type = super::ActionButton;
|
||||
type ParentType = adw::Bin;
|
||||
type Interfaces = (gtk::Actionable,);
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
Self::bind_template(klass);
|
||||
Self::Type::bind_template_callbacks(klass);
|
||||
Self::bind_template_callbacks(klass);
|
||||
|
||||
klass.set_css_name("action-button");
|
||||
}
|
||||
@ -93,8 +89,8 @@ mod imp {
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for ActionButton {
|
||||
fn signals() -> &'static [Signal] {
|
||||
static SIGNALS: Lazy<Vec<Signal>> =
|
||||
Lazy::new(|| vec![Signal::builder("clicked").build()]);
|
||||
static SIGNALS: LazyLock<Vec<Signal>> =
|
||||
LazyLock::new(|| vec![Signal::builder("clicked").build()]);
|
||||
SIGNALS.as_ref()
|
||||
}
|
||||
}
|
||||
@ -120,6 +116,7 @@ mod imp {
|
||||
}
|
||||
}
|
||||
|
||||
#[gtk::template_callbacks]
|
||||
impl ActionButton {
|
||||
/// Set the icon used in the default state.
|
||||
fn set_icon_name(&self, icon_name: &str) {
|
||||
@ -131,6 +128,27 @@ mod imp {
|
||||
self.obj().notify_icon_name();
|
||||
}
|
||||
|
||||
/// Set the extra CSS classes applied to the button in the default
|
||||
/// state.
|
||||
pub(super) fn set_extra_classes(&self, classes: &[&'static str]) {
|
||||
let mut extra_classes = self.extra_classes.borrow_mut();
|
||||
|
||||
if *extra_classes == classes {
|
||||
// Nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
for class in extra_classes.drain(..) {
|
||||
self.button_default.remove_css_class(class);
|
||||
}
|
||||
|
||||
for class in classes {
|
||||
self.button_default.add_css_class(class);
|
||||
}
|
||||
|
||||
extra_classes.extend(classes);
|
||||
}
|
||||
|
||||
/// Set the state of the button.
|
||||
fn set_state(&self, state: ActionState) {
|
||||
if self.state.get() == state {
|
||||
@ -148,8 +166,13 @@ mod imp {
|
||||
}
|
||||
|
||||
/// Set the tooltip text of the button of the default state.
|
||||
fn set_default_state_tooltip_text(&self, text: Option<String>) {
|
||||
self.button_default.set_tooltip_text(text.as_deref());
|
||||
fn set_default_state_tooltip_text(&self, text: Option<&str>) {
|
||||
self.button_default.set_tooltip_text(text);
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn button_clicked(&self) {
|
||||
self.obj().emit_by_name::<()>("clicked", &[]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -166,25 +189,12 @@ impl ActionButton {
|
||||
glib::Object::new()
|
||||
}
|
||||
|
||||
pub fn extra_classes(&self) -> Vec<String> {
|
||||
self.imp().extra_classes.borrow().clone()
|
||||
}
|
||||
|
||||
pub fn set_extra_classes(&self, classes: &[&str]) {
|
||||
let imp = self.imp();
|
||||
for class in imp.extra_classes.borrow_mut().drain(..) {
|
||||
imp.button_default.remove_css_class(&class);
|
||||
}
|
||||
|
||||
for class in classes.iter() {
|
||||
imp.button_default.add_css_class(class);
|
||||
}
|
||||
|
||||
self.imp()
|
||||
.extra_classes
|
||||
.replace(classes.iter().map(ToString::to_string).collect());
|
||||
/// Set the extra CSS classes applied to the button in the default state.
|
||||
pub(crate) fn set_extra_classes(&self, classes: &[&'static str]) {
|
||||
self.imp().set_extra_classes(classes);
|
||||
}
|
||||
|
||||
/// Connect to the signal emitted when the button is clicked.
|
||||
pub fn connect_clicked<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_closure(
|
||||
"clicked",
|
||||
@ -194,9 +204,4 @@ impl ActionButton {
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn button_clicked(&self) {
|
||||
self.emit_by_name::<()>("clicked", &[]);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="ComponentsActionButton" parent="AdwBin">
|
||||
<template class="ActionButton" parent="AdwBin">
|
||||
<child>
|
||||
<object class="GtkStack" id="stack">
|
||||
<property name="transition-type">crossfade</property>
|
||||
@ -13,9 +13,9 @@
|
||||
<class name="circular"/>
|
||||
</style>
|
||||
<property name="valign">center</property>
|
||||
<property name="icon-name" bind-source="ComponentsActionButton" bind-property="icon-name" bind-flags="sync-create"/>
|
||||
<property name="action-name" bind-source="ComponentsActionButton" bind-property="action-name" bind-flags="sync-create"/>
|
||||
<property name="action-target" bind-source="ComponentsActionButton" bind-property="action-target" bind-flags="sync-create"/>
|
||||
<property name="icon-name" bind-source="ActionButton" bind-property="icon-name" bind-flags="sync-create"/>
|
||||
<property name="action-name" bind-source="ActionButton" bind-property="action-name" bind-flags="sync-create"/>
|
||||
<property name="action-target" bind-source="ActionButton" bind-property="action-target" bind-flags="sync-create"/>
|
||||
<signal name="clicked" handler="button_clicked" swapped="true"/>
|
||||
</object>
|
||||
</property>
|
||||
@ -27,15 +27,14 @@
|
||||
<property name="child">
|
||||
<object class="GtkButton">
|
||||
<style>
|
||||
<class name="opaque"/>
|
||||
<class name="circular"/>
|
||||
<class name="suggested-action"/>
|
||||
</style>
|
||||
<property name="tooltip-text" translatable="yes">Confirm Change</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="icon-name">checkmark-symbolic</property>
|
||||
<property name="action-name" bind-source="ComponentsActionButton" bind-property="action-name" bind-flags="sync-create"/>
|
||||
<property name="action-target" bind-source="ComponentsActionButton" bind-property="action-target" bind-flags="sync-create"/>
|
||||
<property name="action-name" bind-source="ActionButton" bind-property="action-name" bind-flags="sync-create"/>
|
||||
<property name="action-target" bind-source="ActionButton" bind-property="action-target" bind-flags="sync-create"/>
|
||||
<signal name="clicked" handler="button_clicked" swapped="true"/>
|
||||
</object>
|
||||
</property>
|
||||
@ -47,15 +46,14 @@
|
||||
<property name="child">
|
||||
<object class="GtkButton">
|
||||
<style>
|
||||
<class name="opaque"/>
|
||||
<class name="circular"/>
|
||||
<class name="suggested-action"/>
|
||||
</style>
|
||||
<property name="tooltip-text" translatable="yes">Try Again</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="icon-name">refresh-symbolic</property>
|
||||
<property name="action-name" bind-source="ComponentsActionButton" bind-property="action-name" bind-flags="sync-create"/>
|
||||
<property name="action-target" bind-source="ComponentsActionButton" bind-property="action-target" bind-flags="sync-create"/>
|
||||
<property name="action-name" bind-source="ActionButton" bind-property="action-name" bind-flags="sync-create"/>
|
||||
<property name="action-target" bind-source="ActionButton" bind-property="action-target" bind-flags="sync-create"/>
|
||||
<signal name="clicked" handler="button_clicked" swapped="true"/>
|
||||
</object>
|
||||
</property>
|
||||
@ -74,9 +72,12 @@
|
||||
<property name="can-target">false</property>
|
||||
<property name="valign">center</property>
|
||||
<child>
|
||||
<object class="Spinner" id="spinner">
|
||||
<object class="AdwSpinner">
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
<property name="halign">center</property>
|
||||
<property name="height-request">20</property>
|
||||
<property name="width-request">20</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
@ -89,7 +90,7 @@
|
||||
<property name="child">
|
||||
<object class="GtkButton">
|
||||
<style>
|
||||
<class name="opaque"/>
|
||||
<class name="suggested-action"/>
|
||||
<class name="circular"/>
|
||||
<class name="success"/>
|
||||
</style>
|
||||
|
@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="ComponentsAudioPlayer" parent="AdwBin">
|
||||
<child>
|
||||
<object class="GtkMediaControls">
|
||||
<property name="media-stream" bind-source="ComponentsAudioPlayer" bind-property="media-file" bind-flags="sync-create"/>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|