Compare commits
779 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14c60eb954 | ||
|
|
0234cd74f7 | ||
|
|
f637e81c8c | ||
|
|
ea82830a9f | ||
|
|
8b62c38e20 | ||
|
|
4667b4704d | ||
|
|
fe60614b41 | ||
|
|
1e833f542f | ||
|
|
faabfa11c2 | ||
|
|
959782835d | ||
|
|
412762ae9d | ||
|
|
6e4f4126b6 | ||
|
|
5dc212d1e5 | ||
|
|
a02c9b6589 | ||
|
|
18e18f240f | ||
|
|
96bdc7c74a | ||
|
|
812b96a571 | ||
|
|
a51145f9db | ||
|
|
36fc042e27 | ||
|
|
54f39318ba | ||
|
|
cbd5c2438d | ||
|
|
29e032d89c | ||
|
|
72a9696249 | ||
|
|
9ca89cdf63 | ||
|
|
227c900d1c | ||
|
|
49b304f3b7 | ||
|
|
ac77f0b879 | ||
|
|
54e02b1925 | ||
|
|
1489e42c46 | ||
|
|
684004bd89 | ||
|
|
80e68be9fa | ||
|
|
06af10e0d3 | ||
|
|
95b8aa38cb | ||
|
|
b010bff5d9 | ||
|
|
a8b73861a6 | ||
|
|
bd002fa96d | ||
|
|
5712dd05d9 | ||
|
|
694b0552c2 | ||
|
|
d9f5726d45 | ||
|
|
3c5a56b440 | ||
|
|
725cf83551 | ||
|
|
f91275ffd2 | ||
|
|
f0e46f4b12 | ||
|
|
57f7f21ecb | ||
|
|
9f673f27ef | ||
|
|
08db193b31 | ||
|
|
0be8b10995 | ||
|
|
b35df9062c | ||
|
|
f6f34c6341 | ||
|
|
22e4ca8925 | ||
|
|
4fd2be744e | ||
|
|
31e48437ec | ||
|
|
78f1bb3cc9 | ||
|
|
14cdb5fa16 | ||
|
|
533cd07cfd | ||
|
|
7232118978 | ||
|
|
7a1286ec1c | ||
|
|
a51a52268e | ||
|
|
6db43f9603 | ||
|
|
f7b5836c75 | ||
|
|
ffb2b27477 | ||
|
|
dd3c524c49 | ||
|
|
b92973df00 | ||
|
|
0b7e8f9720 | ||
|
|
f6342d3b52 | ||
|
|
013b3fb73c | ||
|
|
69527857f8 | ||
|
|
e0128a7817 | ||
|
|
62628b25c6 | ||
|
|
3f89b57c96 | ||
|
|
01537b401f | ||
|
|
30e846274c | ||
|
|
d340922fea | ||
|
|
8dd8d6f561 | ||
|
|
cdaa837d48 | ||
|
|
7576b5c602 | ||
|
|
fac3d8b8c4 | ||
|
|
cecf778381 | ||
|
|
0663a989f4 | ||
|
|
7b2d9d0b73 | ||
|
|
f220e11bce | ||
|
|
af4165d3e5 | ||
|
|
c82ca68c03 | ||
|
|
02e5029eb6 | ||
|
|
d98153ba9e | ||
|
|
4d913adcec | ||
|
|
5729d0b84a | ||
|
|
4a26cecd7d | ||
|
|
e5a3fa4cfa | ||
|
|
6268e82f35 | ||
|
|
348a5777d3 | ||
|
|
594af0c412 | ||
|
|
d6a81150b6 | ||
|
|
38b3413e94 | ||
|
|
762ec75601 | ||
|
|
6da9a39959 | ||
|
|
2938a1a312 | ||
|
|
eb517c8517 | ||
|
|
a79d2afb2d | ||
|
|
396f4ef566 | ||
|
|
c26b4803c3 | ||
|
|
4ccf12e988 | ||
|
|
74121fce78 | ||
|
|
0a7491d747 | ||
|
|
68bf3e7ff7 | ||
|
|
93dd8f535d | ||
|
|
0b993525f5 | ||
|
|
5dfdb68c75 | ||
|
|
443d1dc42b | ||
|
|
34f6c9514a | ||
|
|
2f2619403a | ||
|
|
d25cc847f3 | ||
|
|
769ad859e6 | ||
|
|
7ba0e5b42c | ||
|
|
201339345d | ||
|
|
6ee4205f1e | ||
|
|
2307e2cabe | ||
|
|
4becaa28ce | ||
|
|
cc45bd63ab | ||
|
|
757bcee4e2 | ||
|
|
1340b209f9 | ||
|
|
8f362d57fe | ||
|
|
d0b9c9b54a | ||
|
|
6284b0c489 | ||
|
|
8a211f98fd | ||
|
|
d6633397b7 | ||
|
|
0d06cc685e | ||
|
|
8de89a44a4 | ||
|
|
f3b1f10f6c | ||
|
|
2bced47762 | ||
|
|
7ba9b87064 | ||
|
|
a642c439ce | ||
|
|
d421bbfa60 | ||
|
|
3539ce1139 | ||
|
|
94b907f46d | ||
|
|
1e04622eb4 | ||
|
|
519269be9d | ||
|
|
7d6670ce3c | ||
|
|
50bd597baa | ||
|
|
4403b54fbc | ||
|
|
d6648b0b5c | ||
|
|
861e5c0be6 | ||
|
|
bf86b84d6c | ||
|
|
605dd02217 | ||
|
|
b33a62f2dc | ||
|
|
8a320eb0a1 | ||
|
|
83c4ced407 | ||
|
|
e094dead91 | ||
|
|
6cdf86b6b3 | ||
|
|
3e8bbb07ea | ||
|
|
e3ddd8e7d0 | ||
|
|
aa67982129 | ||
|
|
8aa645ae5d | ||
|
|
20540cb843 | ||
|
|
d338930d69 | ||
|
|
35c1763792 | ||
|
|
7a87bdcb1b | ||
|
|
50b6bd1884 | ||
|
|
db853d9023 | ||
|
|
5be44705f7 | ||
|
|
fdfa46099b | ||
|
|
f987e5f13d | ||
|
|
c766230118 | ||
|
|
bb2b1204b4 | ||
|
|
cf3690a434 | ||
|
|
7062acf10f | ||
|
|
7ace9c4d51 | ||
|
|
23887ce2a3 | ||
|
|
e8b8677bfe | ||
|
|
3603bc7c6a | ||
|
|
1e44941db6 | ||
|
|
f71da9e843 | ||
|
|
1b4a20a8af | ||
|
|
d8e179ed09 | ||
|
|
8016cf7ae9 | ||
|
|
7774426eb9 | ||
|
|
9fa522c29a | ||
|
|
b92295ad2c | ||
|
|
d7c8a5d193 | ||
|
|
0d8d6dceb0 | ||
|
|
8a5e793b3d | ||
|
|
8fcbed6481 | ||
|
|
ac2ed286e1 | ||
|
|
3c3c1f702d | ||
|
|
c33be29f56 | ||
|
|
d8ae2dcba2 | ||
|
|
3b14c0a04e | ||
|
|
0e3d6465eb | ||
|
|
d381a0b89b | ||
|
|
5df0fa145b | ||
|
|
f0ff0e1400 | ||
|
|
53f5a92dc8 | ||
|
|
d5c31273ee | ||
|
|
22ea58a849 | ||
|
|
67d96993ce | ||
|
|
96dd4f9835 | ||
|
|
3bef6be7c1 | ||
|
|
b83d38a72e | ||
|
|
5b73960f34 | ||
|
|
505a7f4ac9 | ||
|
|
e8acfb2b51 | ||
|
|
dcd5dc4c7f | ||
|
|
2702cdf889 | ||
|
|
669e7c32a2 | ||
|
|
294eb0feb5 | ||
|
|
a7ddbd0d53 | ||
|
|
c745dd6362 | ||
|
|
a4cf9f956e | ||
|
|
02aa68b24a | ||
|
|
4e731e1dce | ||
|
|
eee341e907 | ||
|
|
aad96bb1c4 | ||
|
|
50d3ee5703 | ||
|
|
08dfb78815 | ||
|
|
6949a5d075 | ||
|
|
893d3b0473 | ||
|
|
f5128e13f2 | ||
|
|
8318ea919f | ||
|
|
bde20e78f0 | ||
|
|
02db9525e7 | ||
|
|
8d4586bd57 | ||
|
|
8872659621 | ||
|
|
46c7677643 | ||
|
|
4b9f4b1b63 | ||
|
|
b64c93897b | ||
|
|
a7939f18d1 | ||
|
|
15efbc29be | ||
|
|
6e5466a4ec | ||
|
|
737c897624 | ||
|
|
c317a876dd | ||
|
|
72c67aacc4 | ||
|
|
ba7e3fc0b5 | ||
|
|
61cf679b8c | ||
|
|
ba55bacab4 | ||
|
|
9445bd2205 | ||
|
|
6ed1f45ffd | ||
|
|
81be1d2e2f | ||
|
|
52abf74088 | ||
|
|
623bc6dbf3 | ||
|
|
04b23ec68f | ||
|
|
b65f362f0d | ||
|
|
7c30f35b12 | ||
|
|
9e976f06b4 | ||
|
|
369801dbeb | ||
|
|
2c3c7e8a73 | ||
|
|
bb8f34de83 | ||
|
|
6c98658fe1 | ||
|
|
3f06c2bc04 | ||
|
|
9e38b091a9 | ||
|
|
62d7b883f0 | ||
|
|
e56a9c5681 | ||
|
|
03aa180472 | ||
|
|
bbb0775e1a | ||
|
|
c41d285131 | ||
|
|
cc63876b7e | ||
|
|
08769ec0b6 | ||
|
|
5615c21797 | ||
|
|
48d2b4c902 | ||
|
|
a43db7233d | ||
|
|
25143c92f4 | ||
|
|
c4ab7e9bbf | ||
|
|
1d082fc026 | ||
|
|
75919911f5 | ||
|
|
bf1f129854 | ||
|
|
59bcc0a6a7 | ||
|
|
61a54add2e | ||
|
|
17d07239c1 | ||
|
|
bf8a31a6c8 | ||
|
|
1d2fdf5c19 | ||
|
|
f6ada58f5f | ||
|
|
ebfbc397be | ||
|
|
02290e8bba | ||
|
|
1537ac39d4 | ||
|
|
e39c3b4561 | ||
|
|
80030687d9 | ||
|
|
3859c09318 | ||
|
|
a41cd3c19a | ||
|
|
fcbdeba8c5 | ||
|
|
116f074e3b | ||
|
|
9a78c7e4f4 | ||
|
|
9a2d6d68ff | ||
|
|
bc49778100 | ||
|
|
67d2e2f623 | ||
|
|
eff625fcb8 | ||
|
|
3742f79d4b | ||
|
|
b6a3503f5e | ||
|
|
f449ab31c9 | ||
|
|
920b925b12 | ||
|
|
ad5e70b03b | ||
|
|
7beebc11f6 | ||
|
|
1391acd71f | ||
|
|
761fdf1036 | ||
|
|
7f2c39cdb2 | ||
|
|
b5ff860118 | ||
|
|
aeaba3f379 | ||
|
|
f96943a4dc | ||
|
|
60ad2e37ff | ||
|
|
8c53fd9590 | ||
|
|
867ace0b0a | ||
|
|
a74862e745 | ||
|
|
c16f9828bd | ||
|
|
608a8b1639 | ||
|
|
7bc9b282a2 | ||
|
|
9db7b96f24 | ||
|
|
c87cfc822a | ||
|
|
ff94c5a143 | ||
|
|
81f999f761 | ||
|
|
17b5c0400a | ||
|
|
ac937b9549 | ||
|
|
0c223de045 | ||
|
|
564235d44c | ||
|
|
92a2361306 | ||
|
|
9f27654729 | ||
|
|
f4013c7a5c | ||
|
|
9e7eff4a3e | ||
|
|
5b2bdd138a | ||
|
|
90d3c5c51d | ||
|
|
b00e531626 | ||
|
|
b1c1b01184 | ||
|
|
739b1e11a0 | ||
|
|
a3541ff0d9 | ||
|
|
8fb5476a1c | ||
|
|
fc8a46f15a | ||
|
|
2e21f7cf72 | ||
|
|
a317d79016 | ||
|
|
4722b89cc3 | ||
|
|
5a4b71ba90 | ||
|
|
a6ee142f21 | ||
|
|
35427ed4f1 | ||
|
|
bce63d3168 | ||
|
|
0426d1d8d5 | ||
|
|
f9aaa732b2 | ||
|
|
7b82c5e12b | ||
|
|
db305687d5 | ||
|
|
f527fe3774 | ||
|
|
3428bebb67 | ||
|
|
73822dc5f5 | ||
|
|
961757f717 | ||
|
|
7d5c985004 | ||
|
|
cd16995503 | ||
|
|
3a3b5bac85 | ||
|
|
9f6b83eece | ||
|
|
4456b440d8 | ||
|
|
f61e2f5f5c | ||
|
|
a8eecac0ff | ||
|
|
9a6b4da33c | ||
|
|
cb53d0b85c | ||
|
|
318b9b18af | ||
|
|
cdf970d4b6 | ||
|
|
1ee3803265 | ||
|
|
b25c4d07a7 | ||
|
|
330e7b312d | ||
|
|
01f921541f | ||
|
|
cc52f4a260 | ||
|
|
eb71474c95 | ||
|
|
69681cdc9a | ||
|
|
c958e2d0fd | ||
|
|
9c964eaf1b | ||
|
|
0500480d75 | ||
|
|
37a09b7be1 | ||
|
|
d8c734124d | ||
|
|
25b6b562d9 | ||
|
|
f2315d28f9 | ||
|
|
9b93931de6 | ||
|
|
47a2fd36e1 | ||
|
|
be19802f78 | ||
|
|
35746251fd | ||
|
|
a2b014ccbf | ||
|
|
95131dc252 | ||
|
|
f3acb27d61 | ||
|
|
a904d51cf7 | ||
|
|
30ad3d670f | ||
|
|
63615cb657 | ||
|
|
a894897770 | ||
|
|
1b2c0a4b6c | ||
|
|
8e8b0392a2 | ||
|
|
aea57f0305 | ||
|
|
6462af30ee | ||
|
|
8d658f7e0d | ||
|
|
05bcd981cd | ||
|
|
ecf608ead8 | ||
|
|
4ad50ab035 | ||
|
|
934d17db4b | ||
|
|
b25e9541ce | ||
|
|
3204fd7842 | ||
|
|
808f75e998 | ||
|
|
56ecc684cc | ||
|
|
67ceca8f70 | ||
|
|
953445d6cf | ||
|
|
cb834f9fa1 | ||
|
|
ca2cd844b2 | ||
|
|
98b3da41e8 | ||
|
|
c00c749c0b | ||
|
|
e11c90365b | ||
|
|
c812fdc134 | ||
|
|
9299db49fb | ||
|
|
3a15dffe76 | ||
|
|
3df4e5fdbb | ||
|
|
c0a5d0d091 | ||
|
|
d36a196540 | ||
|
|
9077687c0a | ||
|
|
fcbfaec53e | ||
|
|
7c170e7e90 | ||
|
|
ed7a292805 | ||
|
|
eb79f5b512 | ||
|
|
eceff51ba1 | ||
|
|
63c07bdc73 | ||
|
|
fdc3dda484 | ||
|
|
fca516e58a | ||
|
|
c14f7f6fb8 | ||
|
|
2497dc6d31 | ||
|
|
a1f38d818d | ||
|
|
9646149f9a | ||
|
|
b6d02f8e1f | ||
|
|
5f96b3c11c | ||
|
|
e217f23767 | ||
|
|
579a290bae | ||
|
|
2dc4325a90 | ||
|
|
376502e952 | ||
|
|
91c9982d7f | ||
|
|
c721121a2d | ||
|
|
4007b61cf2 | ||
|
|
2655419880 | ||
|
|
9f1b52c852 | ||
|
|
d375cac32e | ||
|
|
66e2b32c8f | ||
|
|
95dd651b29 | ||
|
|
1534e50a15 | ||
|
|
23781abead | ||
|
|
8065d38fdd | ||
|
|
05faa0763b | ||
|
|
e88cd44b3c | ||
|
|
85fa282153 | ||
|
|
119da3291b | ||
|
|
6675d1c55d | ||
|
|
be1a22069a | ||
|
|
2b9162b79d | ||
|
|
6aab09bd06 | ||
|
|
c336e7d70e | ||
|
|
f486dfa112 | ||
|
|
80de26dc16 | ||
|
|
af6f5fea54 | ||
|
|
9743054174 | ||
|
|
827af154b8 | ||
|
|
f9b87f9b44 | ||
|
|
a41bcd4f10 | ||
|
|
1a13f29b0c | ||
|
|
b6a6de9bb5 | ||
|
|
5b7631898c | ||
|
|
f8bae05036 | ||
|
|
60c75e5fcf | ||
|
|
c6d2063f4d | ||
|
|
52f373fb70 | ||
|
|
283a1ec5c2 | ||
|
|
c516f05927 | ||
|
|
e836a9e5e0 | ||
|
|
d1674c5f75 | ||
|
|
9fa283877c | ||
|
|
f1a154207f | ||
|
|
5b07dfded9 | ||
|
|
9f9dbd579b | ||
|
|
c9477dd94d | ||
|
|
5a30ebe403 | ||
|
|
a5f347ba10 | ||
|
|
dab326c17e | ||
|
|
bdf6af3ee3 | ||
|
|
1532c15325 | ||
|
|
cbd1b7d983 | ||
|
|
4b9c53ff2e | ||
|
|
6eb3a62e2b | ||
|
|
0469fc6aa9 | ||
|
|
1f6f22010e | ||
|
|
ae1b17d29c | ||
|
|
3fa750ce17 | ||
|
|
56e2a570e3 | ||
|
|
167f0be6b4 | ||
|
|
80eb29bd51 | ||
|
|
f2b261c573 | ||
|
|
6c49ca825c | ||
|
|
6a25971366 | ||
|
|
dc3523a344 | ||
|
|
2ef6dba0a5 | ||
|
|
92db87f7cb | ||
|
|
82d2011061 | ||
|
|
545b811562 | ||
|
|
d99d0a06bd | ||
|
|
6105eecff2 | ||
|
|
d1254d9b57 | ||
|
|
2db4589dea | ||
|
|
dfeb99fc0a | ||
|
|
48ea80d391 | ||
|
|
b138e218bb | ||
|
|
829440f546 | ||
|
|
14859b4009 | ||
|
|
3a596054ad | ||
|
|
65191d83b4 | ||
|
|
8499b0e254 | ||
|
|
f166ca501f | ||
|
|
a36331522f | ||
|
|
25e43a5d08 | ||
|
|
ad914441a2 | ||
|
|
200d337769 | ||
|
|
e56f1a976e | ||
|
|
6a66cd4fa6 | ||
|
|
55c493389a | ||
|
|
06f1303834 | ||
|
|
3d5687ae3c | ||
|
|
cdd3fba593 | ||
|
|
f11c101878 | ||
|
|
7c412ca7d9 | ||
|
|
e3a6e8f82c | ||
|
|
ce10ad64c4 | ||
|
|
5b43d4733c | ||
|
|
3671d5a299 | ||
|
|
a024d9f005 | ||
|
|
d94a3500f6 | ||
|
|
78141e6433 | ||
|
|
dd53fb8eb7 | ||
|
|
6cd2be452b | ||
|
|
36abd576b5 | ||
|
|
730547b3e1 | ||
|
|
e7e6b882a3 | ||
|
|
53f92f2910 | ||
|
|
a26380d56e | ||
|
|
ab334d95e0 | ||
|
|
7e5726e632 | ||
|
|
95b40069bf | ||
|
|
bd9f1cfd91 | ||
|
|
81548ced69 | ||
|
|
a378e5fc34 | ||
|
|
56d19ad480 | ||
|
|
a071685c13 | ||
|
|
599428d292 | ||
|
|
1f1f9664a6 | ||
|
|
fb039c0fb5 | ||
|
|
6e63edd737 | ||
|
|
55ecd3939e | ||
|
|
d83d6e857a | ||
|
|
db35d7ae7c | ||
|
|
d34b5030b9 | ||
|
|
7f8f1234ae | ||
|
|
9b25a227c6 | ||
|
|
78580bc3a8 | ||
|
|
b50074fd37 | ||
|
|
d5a1044244 | ||
|
|
11cad1f3b3 | ||
|
|
2582ab4fd0 | ||
|
|
5a06de606d | ||
|
|
4671c24b78 | ||
|
|
0d41643866 | ||
|
|
d8f7d70c10 | ||
|
|
cbd981351e | ||
|
|
434185ba03 | ||
|
|
abc8877d11 | ||
|
|
a7ff9f401e | ||
|
|
bab4be1a59 | ||
|
|
47beeb1a8e | ||
|
|
945eddbc26 | ||
|
|
c37ba0878a | ||
|
|
3861ffae31 | ||
|
|
2081309679 | ||
|
|
08a04b2f02 | ||
|
|
7b363fd659 | ||
|
|
85112394b3 | ||
|
|
190243ee89 | ||
|
|
1141d996d9 | ||
|
|
f7cbd0d600 | ||
|
|
c704f0de55 | ||
|
|
c8a1cd9c99 | ||
|
|
ff79588d3b | ||
|
|
8ae84703a4 | ||
|
|
f18b281020 | ||
|
|
75503b5f3c | ||
|
|
1d96f83362 | ||
|
|
b3f009017c | ||
|
|
05d961dfe1 | ||
|
|
8a2b9abc09 | ||
|
|
3effc9cfc2 | ||
|
|
1be7061da0 | ||
|
|
f75d1d75af | ||
|
|
67f9e4c419 | ||
|
|
aaeff6d12b | ||
|
|
912ba1e651 | ||
|
|
b741b1bbe2 | ||
|
|
d7414025a5 | ||
|
|
a317d75f70 | ||
|
|
194e294d2c | ||
|
|
a092da2527 | ||
|
|
4655cde002 | ||
|
|
0a979f5bfa | ||
|
|
d2046bb2c6 | ||
|
|
a2f40952b3 | ||
|
|
29917bcb1b | ||
|
|
1b881e1eaa | ||
|
|
40a8ab60f4 | ||
|
|
b684e756e2 | ||
|
|
7eae32eb76 | ||
|
|
34c20e6176 | ||
|
|
62818e71e9 | ||
|
|
ee85839d60 | ||
|
|
d9932b8f55 | ||
|
|
efa882080f | ||
|
|
132b3b0509 | ||
|
|
e5a0616a0a | ||
|
|
fbeb3cf1e9 | ||
|
|
072fbee7cb | ||
|
|
e9928b3c08 | ||
|
|
2af737a8c2 | ||
|
|
321702fd15 | ||
|
|
60493faf96 | ||
|
|
821cbf9328 | ||
|
|
cc0f167c0e | ||
|
|
d731534d04 | ||
|
|
94eba47358 | ||
|
|
d34933de0b | ||
|
|
9410bdfad6 | ||
|
|
d424b0f645 | ||
|
|
c0454db0c6 | ||
|
|
913b2ba691 | ||
|
|
ba51869b93 | ||
|
|
ffe6110ea9 | ||
|
|
8b1bcc9fed | ||
|
|
ebad80c64d | ||
|
|
39caf951e0 | ||
|
|
ecf28e13b4 | ||
|
|
a01b44f7de | ||
|
|
6b1926bab3 | ||
|
|
64986740e3 | ||
|
|
6583eae622 | ||
|
|
9add688abf | ||
|
|
a85764a0ab | ||
|
|
eaa2c7857e | ||
|
|
6e1b5bc346 | ||
|
|
f59ab1272d | ||
|
|
f25ffe6f63 | ||
|
|
6746061898 | ||
|
|
3dc9f2e4ff | ||
|
|
682d6a8fd1 | ||
|
|
d3ea985e14 | ||
|
|
496ae16cf1 | ||
|
|
0554e0deb9 | ||
|
|
c0c938977c | ||
|
|
8ee207d74d | ||
|
|
0e625bedba | ||
|
|
127b5e6c25 | ||
|
|
64c17acc0a | ||
|
|
0102b2800a | ||
|
|
7c66434616 | ||
|
|
f9ae518c9d | ||
|
|
55ba3c086a | ||
|
|
d22e603686 | ||
|
|
3e0d8bd56f | ||
|
|
45ac2cd403 | ||
|
|
a808380d04 | ||
|
|
5c0ef12d13 | ||
|
|
b80aa8ee11 | ||
|
|
7de345fc8b | ||
|
|
ff3adb8429 | ||
|
|
3398e74005 | ||
|
|
f9ad051367 | ||
|
|
ae65a82fdd | ||
|
|
2dbe692a31 | ||
|
|
7de28858e1 | ||
|
|
8881fbcc9d | ||
|
|
3b5a1486de | ||
|
|
0721ed6504 | ||
|
|
ec98cff9d9 | ||
|
|
9ecd6bbb13 | ||
|
|
3b6cd04a59 | ||
|
|
3284fe81d7 | ||
|
|
39274f286c | ||
|
|
eba3cfccfe | ||
|
|
f5c69cbfdc | ||
|
|
c668392462 | ||
|
|
22e416c09b | ||
|
|
9dc183af23 | ||
|
|
6a0a247668 | ||
|
|
2aaf4370b6 | ||
|
|
ba27cc4a64 | ||
|
|
acb44de469 | ||
|
|
48399d46c0 | ||
|
|
30e7ff5a8f | ||
|
|
968ae427f1 | ||
|
|
4ee30a5445 | ||
|
|
d3bbeb1b0c | ||
|
|
9d5b149428 | ||
|
|
b90c99b87c | ||
|
|
ba38019049 | ||
|
|
406f4cd170 | ||
|
|
a2c63a46d3 | ||
|
|
3ab01aebec | ||
|
|
c6ecf2c0df | ||
|
|
a26f9e34c7 | ||
|
|
21315d74c3 | ||
|
|
d86044f383 | ||
|
|
077a292952 | ||
|
|
8bd35eeb09 | ||
|
|
eb425ac8a2 | ||
|
|
724249dd29 | ||
|
|
76526a9c2d | ||
|
|
29ea5ca1a9 | ||
|
|
40c6b8b1d8 | ||
|
|
2460ac9df8 | ||
|
|
8c551ba8ee | ||
|
|
99295c438d | ||
|
|
f840f70052 | ||
|
|
ef99394ffb | ||
|
|
778eebc421 | ||
|
|
97d1cbca19 | ||
|
|
a0cae3d130 | ||
|
|
01893ca048 | ||
|
|
c86a67823c | ||
|
|
726be0692e | ||
|
|
f339001e22 | ||
|
|
530b165ddf | ||
|
|
e479068d84 | ||
|
|
824e79d83e | ||
|
|
0d18c3f70d | ||
|
|
a682b66bbf | ||
|
|
f4510b9b04 | ||
|
|
c1143fa716 | ||
|
|
37b95a8498 | ||
|
|
cccef0e857 | ||
|
|
592b4ebacf | ||
|
|
3a799d519b | ||
|
|
8b09c17684 | ||
|
|
f00ffcaa3d | ||
|
|
82cc175df1 | ||
|
|
eda9eeef18 | ||
|
|
fb1d4faece | ||
|
|
3b1d7428df | ||
|
|
dca18f3614 | ||
|
|
6a9b3a9369 | ||
|
|
373c3660a1 | ||
|
|
74fdac5dfd | ||
|
|
49c00f3282 | ||
|
|
b9a32a8f64 | ||
|
|
bf2074cd82 | ||
|
|
a659c3e586 | ||
|
|
959dfee54e | ||
|
|
714162fdd3 | ||
|
|
856bda4270 | ||
|
|
5530229dd2 | ||
|
|
cdc3894400 | ||
|
|
f8f66f628e | ||
|
|
f8b9afda07 | ||
|
|
617904e69f | ||
|
|
6e4be83dec | ||
|
|
66f0972a43 | ||
|
|
e6bdbb2c68 | ||
|
|
7d3915c30d | ||
|
|
a04ad6acd7 | ||
|
|
8778c16f2a | ||
|
|
2917deead1 | ||
|
|
08fc96c7ee | ||
|
|
35c01d7223 | ||
|
|
71ba8b04d7 | ||
|
|
c74077239e | ||
|
|
2c2d6b69ca | ||
|
|
dcf08375c1 | ||
|
|
15085df41f | ||
|
|
29780d495d | ||
|
|
69700dc29b | ||
|
|
0a91893b02 | ||
|
|
dceebeaa7f | ||
|
|
a2508106f4 | ||
|
|
2716430310 | ||
|
|
4fa73145b4 | ||
|
|
f7b2a1ac58 | ||
|
|
8ac0313c24 | ||
|
|
9d6d8e00ac | ||
|
|
a8544a30e1 | ||
|
|
bde50c021c | ||
|
|
1d5ab3f328 | ||
|
|
31b00faef8 | ||
|
|
6e81f240dc | ||
|
|
74643a62d1 | ||
|
|
29a9c7582d | ||
|
|
cce2f10fba |
3
.eslintrc.yml
Normal file
3
.eslintrc.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
extends:
|
||||
- ./lint/eslintrc-gjs.yml
|
||||
- ./lint/eslintrc-shell.yml
|
||||
171
.gitlab-ci.yml
Normal file
171
.gitlab-ci.yml
Normal file
@@ -0,0 +1,171 @@
|
||||
include:
|
||||
- remote: 'https://gitlab.freedesktop.org/freedesktop/ci-templates/-/raw/bbe5232986c9b98eb1efe62484e07216f7d1a4df/templates/fedora.yml'
|
||||
- remote: "https://gitlab.freedesktop.org/freedesktop/ci-templates/-/raw/6f86b8bcb0cd5168c32779c4fea9a893c4a0c046/templates/ci-fairy.yml"
|
||||
|
||||
image: registry.gitlab.gnome.org/gnome/gnome-shell/fedora/35:2022-02-18.0
|
||||
|
||||
stages:
|
||||
- pre_review
|
||||
- prepare
|
||||
- review
|
||||
- build
|
||||
- deploy
|
||||
|
||||
default:
|
||||
# Cancel jobs if newer commits are pushed to the branch
|
||||
interruptible: true
|
||||
# Auto-retry jobs in case of infra failures
|
||||
retry:
|
||||
max: 1
|
||||
when:
|
||||
- 'runner_system_failure'
|
||||
- 'stuck_or_timeout_failure'
|
||||
- 'scheduler_failure'
|
||||
- 'api_failure'
|
||||
|
||||
variables:
|
||||
FDO_UPSTREAM_REPO: GNOME/gnome-shell-extensions
|
||||
LINT_LOG: "eslint-report.xml"
|
||||
|
||||
workflow:
|
||||
rules:
|
||||
- if: '$CI_MERGE_REQUEST_IID'
|
||||
- if: '$CI_COMMIT_TAG'
|
||||
- if: '$CI_COMMIT_BRANCH'
|
||||
|
||||
.pipeline_guard: &pipeline_guard
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||
- if: '$CI_COMMIT_TAG'
|
||||
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
||||
- if: '$CI_COMMIT_BRANCH =~ /^gnome-[0-9-]+$/'
|
||||
- when: 'manual'
|
||||
|
||||
.gnome-shell-extensions.fedora:34:
|
||||
variables:
|
||||
FDO_DISTRIBUTION_VERSION: 34
|
||||
FDO_DISTRIBUTION_TAG: '2021-08-31.0'
|
||||
FDO_DISTRIBUTION_PACKAGES: >
|
||||
meson git gettext sassc
|
||||
|
||||
.prereview_req: &prereview_req
|
||||
needs:
|
||||
- check_commit_log
|
||||
- check-merge-request
|
||||
|
||||
check_commit_log:
|
||||
extends:
|
||||
- .fdo.ci-fairy
|
||||
stage: pre_review
|
||||
script:
|
||||
- if [[ x"$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" != "x" ]] ;
|
||||
then
|
||||
ci-fairy check-commits --junit-xml=commit-message-junit-report.xml ;
|
||||
else
|
||||
echo "Not a merge request" ;
|
||||
fi
|
||||
<<: *pipeline_guard
|
||||
artifacts:
|
||||
expire_in: 1 week
|
||||
paths:
|
||||
- commit-message-junit-report.xml
|
||||
reports:
|
||||
junit: commit-message-junit-report.xml
|
||||
|
||||
check-merge-request:
|
||||
extends:
|
||||
- .fdo.ci-fairy
|
||||
stage: pre_review
|
||||
script:
|
||||
- if [[ x"$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" != "x" ]] ;
|
||||
then
|
||||
ci-fairy check-merge-request --require-allow-collaboration --junit-xml=check-merge-request-report.xml ;
|
||||
else
|
||||
echo "Not a merge request" ;
|
||||
fi
|
||||
<<: *pipeline_guard
|
||||
artifacts:
|
||||
expire_in: 1 week
|
||||
paths:
|
||||
- check-merge-request-report.xml
|
||||
reports:
|
||||
junit: check-merge-request-report.xml
|
||||
|
||||
build-fedora-container:
|
||||
extends:
|
||||
- .fdo.container-build@fedora@x86_64
|
||||
- .gnome-shell-extensions.fedora:34
|
||||
stage: prepare
|
||||
<<: *prereview_req
|
||||
|
||||
js_check:
|
||||
stage: review
|
||||
<<: *prereview_req
|
||||
script:
|
||||
- gjs-check-syntax
|
||||
|
||||
eslint:
|
||||
stage: review
|
||||
<<: *prereview_req
|
||||
script:
|
||||
- eslint -o $LINT_LOG -f junit --resolve-plugins-relative-to $(npm root -g) extensions
|
||||
artifacts:
|
||||
paths:
|
||||
- ${LINT_LOG}
|
||||
reports:
|
||||
junit: ${LINT_LOG}
|
||||
|
||||
build-bundles:
|
||||
stage: build
|
||||
<<: *prereview_req
|
||||
script:
|
||||
- ./export-zips.sh
|
||||
artifacts:
|
||||
name: 'Extension bundles'
|
||||
expose_as: 'Get Extension bundles here'
|
||||
paths:
|
||||
- zip-files/
|
||||
|
||||
fedora-build:
|
||||
extends:
|
||||
- .fdo.distribution-image@fedora
|
||||
- .gnome-shell-extensions.fedora:34
|
||||
stage: build
|
||||
needs:
|
||||
- build-fedora-container
|
||||
before_script:
|
||||
- git submodule update --init
|
||||
script:
|
||||
- meson setup build --werror -Dextension_set=all -Dclassic_mode=true
|
||||
- meson compile -C build
|
||||
- meson test -C build
|
||||
- meson install -C build
|
||||
artifacts:
|
||||
paths:
|
||||
- build
|
||||
|
||||
fedora-dist:
|
||||
extends:
|
||||
- .fdo.distribution-image@fedora
|
||||
- .gnome-shell-extensions.fedora:34
|
||||
stage: deploy
|
||||
needs:
|
||||
- fedora-build
|
||||
before_script:
|
||||
- git submodule update --init
|
||||
script:
|
||||
- meson dist -C build
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||
changes:
|
||||
- "**/meson.build"
|
||||
- meson/*
|
||||
|
||||
fedora-dist-tarball:
|
||||
extends: fedora-dist
|
||||
artifacts:
|
||||
expose_as: 'Get tarball here'
|
||||
paths:
|
||||
- build/meson-dist/$CI_PROJECT_NAME-$CI_COMMIT_TAG.tar.xz
|
||||
rules:
|
||||
- if: '$CI_COMMIT_TAG'
|
||||
13
.gitlab-ci/commit-rules.yml
Normal file
13
.gitlab-ci/commit-rules.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
patterns:
|
||||
deny:
|
||||
- regex: '^$CI_MERGE_REQUEST_PROJECT_URL/(-/)?merge_requests/$CI_MERGE_REQUEST_IID$'
|
||||
message: Commit message must not contain a link to its own merge request
|
||||
- regex: '^extensions/'
|
||||
message: Commit message subject should not be prefixed with 'extensions/', use the extension name instead
|
||||
where: subject
|
||||
- regex: '^[^:]+: [a-z]'
|
||||
message: "Commit message subject should be properly Capitalized. E.g. 'window: Marginalize extradicity'"
|
||||
where: subject
|
||||
- regex: '^\S*\.js:'
|
||||
message: Commit message subject prefix should not include .js
|
||||
where: subject
|
||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
||||
[submodule "data/gnome-shell-sass"]
|
||||
path = data/gnome-shell-sass
|
||||
url = https://git.gnome.org/browse/gnome-shell-sass/
|
||||
url = https://gitlab.gnome.org/GNOME/gnome-shell-sass.git
|
||||
|
||||
31
HACKING
31
HACKING
@@ -1,31 +0,0 @@
|
||||
--- Creating a New Extension ---
|
||||
|
||||
To create a new extension, add a subdirectory in extensions.
|
||||
Then create a Makefile.am like the one in example, replacing
|
||||
the EXTENSION_ID with the basename of your extension, which
|
||||
must match the UUID in metadata.json.
|
||||
If you need additional files, add them to EXTENSION_EXTRA.
|
||||
|
||||
Then modify extensions/Makefile.am and configure.ac. It should
|
||||
be pretty self-explanatory.
|
||||
|
||||
Don't forget to add any translatable file to po/POTFILES.in, and
|
||||
then you're done.
|
||||
The Gettext domain you should choose is gnome-shell-extensions,
|
||||
not gnome-shell, unless you're sure there is the string you
|
||||
need in gnome-shell.
|
||||
|
||||
--- Coding Style ---
|
||||
|
||||
Generally, we follow GJS coding style (you can find it at
|
||||
http://git.gnome.org/browse/gjs/tree/doc/Style_Guide.txt), which
|
||||
in short is: indent 4 spaces, no tabs, space after comma, no space
|
||||
after function call.
|
||||
|
||||
The Emacs mode line for this
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
Imports should be at the top, in two groups, one for standard
|
||||
imports (like imports.lang or imports.dbus) and introspection,
|
||||
the other for Shell API. Within the same group, put everything
|
||||
in alphabetic order.
|
||||
31
HACKING.md
Normal file
31
HACKING.md
Normal file
@@ -0,0 +1,31 @@
|
||||
## Creating a New Extension
|
||||
|
||||
To create a new extension, add a subdirectory in extensions. Then create
|
||||
a meson.build from the provided [template](extensions/meson.build.template).
|
||||
If you need additional sources, add them to extension_sources. Similarily add
|
||||
GSettings schemas to extension_schemas and other files to extension_data.
|
||||
|
||||
Then modify the [toplevel Meson file](meson.build) to add the new
|
||||
extension name in the appropriate set (that is one of classic_extensions,
|
||||
default_extensions or all_extensions).
|
||||
|
||||
Don't forget to add any translatable file to po/POTFILES.in, and
|
||||
then you're done.
|
||||
The Gettext domain you should choose is gnome-shell-extensions,
|
||||
not gnome-shell, unless you're sure there is the string you
|
||||
need in gnome-shell.
|
||||
|
||||
## Coding Style
|
||||
|
||||
Generally, we follow [GJS coding style][coding-style], which in short is:
|
||||
indent 4 spaces, no tabs, space after comma, no space after function call.
|
||||
|
||||
The Emacs mode line for this
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
Imports should be at the top, in two groups, one for standard
|
||||
imports (like imports.lang or imports.dbus) and introspection,
|
||||
the other for Shell API. Within the same group, put everything
|
||||
in alphabetic order.
|
||||
|
||||
[coding-style]: https://gitlab.gnome.org/GNOME/gjs/blob/HEAD/doc/Style_Guide.md
|
||||
52
Makefile.am
52
Makefile.am
@@ -1,52 +0,0 @@
|
||||
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
|
||||
|
||||
DIST_SUBDIRS = data extensions po
|
||||
|
||||
SUBDIRS = extensions po
|
||||
|
||||
if CLASSIC_MODE
|
||||
SUBDIRS += data
|
||||
endif
|
||||
|
||||
EXTRA_DIST = lib/convenience.js
|
||||
|
||||
DISTCHECK_CONFIGURE_FLAGS = --enable-extensions=all
|
||||
|
||||
include include.mk
|
||||
|
||||
zip-file: all
|
||||
-rm -fR $(builddir)/_build
|
||||
-rm -fR $(builddir)/zip-files
|
||||
$(MKDIR_P) $(builddir)/_build; \
|
||||
$(MKDIR_P) $(builddir)/zip-files; \
|
||||
$(MAKE) install DESTDIR="$(abs_builddir)/_build"; \
|
||||
for i in $(ENABLED_EXTENSIONS); do \
|
||||
mv "$(builddir)/_build$(topextensiondir)/$${i}$(extensionbase)" "$(builddir)/_build/"; \
|
||||
cp -r "$(builddir)/_build$(datadir)/locale" "$(builddir)/_build/$${i}$(extensionbase)"; \
|
||||
cp "$(srcdir)/COPYING" -t "$(builddir)/_build/$${i}$(extensionbase)"; \
|
||||
cp "$(srcdir)/NEWS" -t "$(builddir)/_build/$${i}$(extensionbase)"; \
|
||||
if [ -f "$(builddir)/_build$(datadir)/glib-2.0/schemas/$(gschemabase).$${i}.gschema.xml" ]; then \
|
||||
$(MKDIR_P) "$(builddir)/_build/$${i}$(extensionbase)/schemas"; \
|
||||
mv "$(builddir)/_build$(datadir)/glib-2.0/schemas/$(gschemabase).$${i}.gschema.xml" "$(builddir)/_build/$${i}$(extensionbase)/schemas"; \
|
||||
glib-compile-schemas "$(builddir)/_build/$${i}$(extensionbase)/schemas"; \
|
||||
fi; \
|
||||
(cd "$(builddir)/_build/$${i}$(extensionbase)"; \
|
||||
zip -qr "$(abs_builddir)/zip-files/$${i}$(extensionbase).shell-extension.zip" .; \
|
||||
); \
|
||||
done
|
||||
-rm -fR $(builddir)/_build
|
||||
|
||||
localprefix = $(HOME)/.local/share/gnome-shell/extensions
|
||||
|
||||
local-install: zip-file
|
||||
for i in $(ENABLED_EXTENSIONS); do \
|
||||
uuid="$${i}$(extensionbase)"; \
|
||||
zip_file="$(abs_builddir)/zip-files/$${uuid}.shell-extension.zip"; \
|
||||
if [ -d "$(localprefix)/$${uuid}" ]; then \
|
||||
rm -fR "$(localprefix)/$${uuid}"; \
|
||||
fi; \
|
||||
$(MKDIR_P) $(localprefix)/$${uuid}; \
|
||||
(cd $(localprefix)/$${uuid}; \
|
||||
unzip -q $${zip_file}; \
|
||||
); \
|
||||
done
|
||||
589
NEWS
589
NEWS
@@ -1,3 +1,592 @@
|
||||
43.alpha
|
||||
========
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
Translators:
|
||||
Marco Ciampa [it]
|
||||
|
||||
42.3
|
||||
====
|
||||
* screenshot-window-sizer: Fix reported sizes on wayland [Florian; !232]
|
||||
* window-list: Improve touch support [Florian; !233]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
42.2
|
||||
====
|
||||
* native-window-placement: Adjust to gnome-shell 42 changes [Florian; !229]
|
||||
* window-list: Fix visibility on non-primary monitors [Jason; !230]
|
||||
|
||||
Contributors:
|
||||
Jason Lynch, Florian Müllner
|
||||
|
||||
Translators:
|
||||
Cheng-Chia Tseng [zh_TW]
|
||||
|
||||
42.1
|
||||
====
|
||||
* Misc. bug fixes and cleanups [Florian; !223, !222, !225]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
Translators:
|
||||
Milo Casagrande [it], Rūdolfs Mazurs [lv], Nathan Follens [nl],
|
||||
Ngọc Quân Trần [vi], Zurab Kargareteli [ka]
|
||||
|
||||
42.0
|
||||
====
|
||||
|
||||
Translators:
|
||||
Philipp Kiemle [de], Balázs Úr [hu], Марко Костић [sr], sicklylife [ja],
|
||||
Baurzhan Muftakhidinov [kk]
|
||||
|
||||
42.rc
|
||||
=====
|
||||
* Misc. bug fixes and cleanups [Florian; !215, !218]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
Translators:
|
||||
Marek Černocký [cs], Dušan Kazik [sk], Piotr Drąg [pl], Jiri Grönroos [fi],
|
||||
Luna Jernberg [sv], Alan Mortensen [da], Charles Monzat [fr],
|
||||
Changwoo Ryu [ko]
|
||||
|
||||
42.beta
|
||||
=======
|
||||
* workspace-indicator: Fix cancelling editing with Esc [Florian; !208]
|
||||
* window-list: Update window tracking to avoid missing icons [Florian; !207]
|
||||
* Use libadwaita for preferences [Florian; !209, !213]
|
||||
* Adapt to Clutter grab API changes [Florian; !212]
|
||||
* Misc. bug fixes and cleanups [Jan, Florian; !210, !214]
|
||||
|
||||
Contributors:
|
||||
Jan Beich, Florian Müllner, Naala Nanba
|
||||
|
||||
Translators:
|
||||
Boyuan Yang [zh_CN], Matej Urbančič [sl], Naala Nanba [ab],
|
||||
Alexander Shopov [bg], Emin Tufan Çetin [tr]
|
||||
|
||||
42.alpha
|
||||
========
|
||||
* native-window-placement: Fix distorted layout in app grid [Sebastian; !189]
|
||||
* window-list: Fix on-screen keyboard [Florian; !199]
|
||||
* Misc. bug fixes and cleanups [Neal; Just; !195, !197]
|
||||
|
||||
Contributors:
|
||||
Piotr Drąg, Neal Gompa, Sebastian Keller, Florian Müllner, Just Perfection
|
||||
|
||||
Translators:
|
||||
Goran Vidović [hr], Sveinn í Felli [is], Yuri Chornoivan [uk],
|
||||
Fabio Tomat [fur], Quentin PAGÈS [oc], Hugo Carvalho [pt],
|
||||
Yaron Shahrabani [he], Jordi Mas i Hernandez [ca], MohammadSaleh Kamyab [fa],
|
||||
Fran Dieguez [gl], Daniel Mustieles [es], Aleksandr Melman [ru],
|
||||
Aurimas Černius [lt], Asier Sarasua Garmendia [eu], Kukuh Syafaat [id],
|
||||
Rafael Fontenelle [pt_BR]
|
||||
|
||||
41.0
|
||||
====
|
||||
* Bump version
|
||||
|
||||
41.rc.1
|
||||
=======
|
||||
* Fix pre-generating stylesheets in tarball [Florian; !190]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
41.rc
|
||||
=====
|
||||
* window-list: Adapt to overview-on-startup [Florian; !185]
|
||||
* apps-menu: Use a custom 'toggle-menu' shortcut [Florian; !173]
|
||||
* Misc. bug fixes and cleanups [Florian; !186]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
41.beta
|
||||
=======
|
||||
* window-list: Extend reactive area of minimap to screen edges [Adam; !171]
|
||||
* drive-menu: Improve detection of network mounts [Florian; !27, !176]
|
||||
* Use distinct gettext domain for e.g.o uploads [Florian; #335]
|
||||
* Misc. bug fixes and cleanups [Florian; !172, !174, !177, !167, !178, !180,
|
||||
!181, !182, !183]
|
||||
|
||||
Contributors:
|
||||
Marco Trevisan (Treviño), Adam Goode, Florian Müllner
|
||||
|
||||
Translators:
|
||||
Hugo Carvalho [pt], Juliano de Souza Camargo [pt], Alexander Shopov [bg]
|
||||
|
||||
40.1
|
||||
====
|
||||
* Disable welcome dialog in classic session [Florian; !169]
|
||||
* windowsNavigator: Adjust to a late gnome-shell change [Florian; !170]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
Translators:
|
||||
Ngọc Quân Trần [vi], Anders Jonsson [sv], Carmen Bianca BAKKER [eo],
|
||||
Pawan Chitrakar [ne], Quentin PAGÈS [oc]
|
||||
|
||||
40.0
|
||||
====
|
||||
|
||||
Translators:
|
||||
Jiri Grönroos [fi]
|
||||
|
||||
40.rc
|
||||
=====
|
||||
* native-window-placement: Adjust to gnome-shell changes [Florian; !164]
|
||||
* windows-navigator: Adjust to gnome-shell changes [Florian; !163]
|
||||
* window-list, workspace-indicator: Only show previews for up to six workspaces
|
||||
[Florian; !165]
|
||||
* window-list, workspace-indicator: Improve workspace preview appearance
|
||||
[Florian; !166]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
Translators:
|
||||
Fran Dieguez [gl]
|
||||
|
||||
40.beta
|
||||
=======
|
||||
* Add tooltips to workspace thumbnails [Florian; !155]
|
||||
* Drop arrows from top bar menus [Florian; !156]
|
||||
* drive-menu: Mark mounts that can be unmounted as removable [Michael; !152]
|
||||
* Remove horizontal-workspaces extension [Florian; !158]
|
||||
* Adjust to shell overview changes [Florian; !159, !160]
|
||||
* Fix crashes [Daniel; !157]
|
||||
* Misc. bug fixes and cleanups [Florian; !154, !161]
|
||||
|
||||
Contributors:
|
||||
Michael Lawton, Florian Müllner, Daniel van Vugt
|
||||
|
||||
Translators:
|
||||
Аляксей [be], A S Alam [pa]
|
||||
|
||||
40.alpha.1
|
||||
==========
|
||||
* Don't depend on sassc when building from tarball [Florian; !150]
|
||||
* Port extensions preferences to GTK4 [Florian; !148]
|
||||
* Misc. bug fixes and cleanups [Florian, Jonas; !149, !151, !153]
|
||||
|
||||
Contributors:
|
||||
Jonas Dreßler, Florian Müllner
|
||||
|
||||
40.alpha
|
||||
========
|
||||
* window-list: Honor changes in skip-taskbar property [Sergio; !130]
|
||||
* window-list, workspace-indicator: Adjust to 3.38 changes [Florian; !133]
|
||||
* window-list, workspace-indicator: Improve previews in workspace thumbs
|
||||
[Florian; #260, !142]
|
||||
* auto-move: Improve behavior on multi-monitor setups [Florian; !135]
|
||||
* windowNavigator: Adjust to 3.38 changes [Thun; #259]
|
||||
* Misc. bug fixes and cleanups [Florian, Jonas Å, Jordan, Ray; !131, !136,
|
||||
!137, !140, !141, !144, !146, !145]
|
||||
|
||||
Contributors:
|
||||
Sergio Costas, Florian Müllner, Jordan Petridis, Thun Pin, Ray Strode,
|
||||
Jonas Ådahl
|
||||
|
||||
Translators:
|
||||
Fabio Tomat [fur], Jordi Mas [ca]
|
||||
|
||||
3.38.1
|
||||
======
|
||||
|
||||
Contributors:
|
||||
Yacine Bouklif, Florian Müllner
|
||||
|
||||
Translators:
|
||||
Yacine Bouklif [kab], Cheng-Chia Tseng [zh_TW], Stas Solovey [ru],
|
||||
Yosef Or Boczko [he]
|
||||
|
||||
3.38.0
|
||||
======
|
||||
|
||||
Translators:
|
||||
Balázs Meskó [hu], Alan Mortensen [da], Juliano Camargo [pt], Tim Sabsch [de],
|
||||
Milo Casagrande [it], Rūdolfs Mazurs [lv]
|
||||
|
||||
3.37.92
|
||||
=======
|
||||
|
||||
Translators:
|
||||
Nathan Follens [nl], Zander Brown [en_GB], Aurimas Černius [lt],
|
||||
Marek Černocký [cs], Changwoo Ryu [ko], Dušan Kazik [sk]
|
||||
|
||||
3.37.91
|
||||
=======
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
Translators:
|
||||
Fran Dieguez [gl], Akarshan Biswas [bn_IN], Kukuh Syafaat [id],
|
||||
Piotr Drąg [pl], Rafael Fontenelle [pt_BR], Jiri Grönroos [fi],
|
||||
Марко Костић [sr], Goran Vidović [hr]
|
||||
|
||||
3.37.90
|
||||
=======
|
||||
* Misc. bug fixes and cleanups [Florian, Piotr; !126, !128]
|
||||
|
||||
Contributors:
|
||||
Piotr Drąg, Florian Müllner
|
||||
|
||||
Translators:
|
||||
Fabio Tomat [fur], Efstathios Iosifidis [el], Anders Jonsson [sv],
|
||||
Asier Sarasua Garmendia [eu], Alexandre Franke [fr]
|
||||
|
||||
3.37.3
|
||||
======
|
||||
* window-list, native-window-placement: Adjust to shell changes [Florian; !124]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
Translators:
|
||||
Jordi Mas [ca], sicklylife [ja], Boyuan Yang [zh_CN],
|
||||
Baurzhan Muftakhidinov [kk]
|
||||
|
||||
3.37.2
|
||||
======
|
||||
* window-list, auto-move: Modernize preference dialogs [Florian; !121]
|
||||
* Adjust to gnome-shell changes [Florian; !122]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
Translators:
|
||||
Cheng-Chia Tseng [zh_TW], Yuri Chornoivan [uk], Daniel Mustieles [es],
|
||||
Emin Tufan Çetin [tr], Danial Behzadi [fa], Daniel Șerbănescu [ro],
|
||||
Matej Urbančič [sl]
|
||||
|
||||
3.37.1
|
||||
======
|
||||
* drive-menu: Emphasize eject buttons [Florian; #223]
|
||||
* user-theme: Add preference dialog [Florian; !117]
|
||||
* window-list: Fix inconsistent state in preference dialog [Milan; !119]
|
||||
* workspace-indicator: Overhaul preference dialog [Florian; !120]
|
||||
* user-theme: Support session mode styles [Florian; !118]
|
||||
* Misc. bug fixes and cleanups [Florian, Xiaoguang; !113, !106, !114, !116]
|
||||
|
||||
Contributors:
|
||||
Milan Crha, Florian Müllner, Xiaoguang Wang
|
||||
|
||||
Translators:
|
||||
Daniel Korostil [uk], Yosef Or Boczko [he], Kristjan SCHMIDT [eo],
|
||||
Dz Chen [zh_CN], Danial Behzadi [fa], Yuri Chornoivan [uk],
|
||||
Anders Jonsson [sv], Daniel Mustieles [es]
|
||||
|
||||
3.36.0
|
||||
======
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
3.35.91
|
||||
=======
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
Translators:
|
||||
Zander Brown [en_GB]
|
||||
|
||||
3.35.90
|
||||
=======
|
||||
* Adjust to gnome-shell changes [Florian; !100, !101, !102]
|
||||
* Force single-line window titles in window list [Florian; #202]
|
||||
* Misc. bug fixes and cleanup [Florian; !104, !105]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
Translators:
|
||||
sicklylife [ja], Umarzuki Bin Mochlis Moktar [ms]
|
||||
|
||||
3.35.3
|
||||
======
|
||||
|
||||
Translators:
|
||||
Fran Dieguez [gl]
|
||||
|
||||
3.35.2
|
||||
======
|
||||
* Adjust to gnome-shell changes [Marco, Florian; !89, !95, !96]
|
||||
* window-list, workspace-indicator: Exclude DESKTOP windows from previews
|
||||
[Florian; !93]
|
||||
* screenshot-window-sizer: Fix cycling through all valid sizes [Willy; !97]
|
||||
|
||||
Contributors:
|
||||
Marco Trevisan (Treviño), Florian Müllner, Willy Stadnick
|
||||
|
||||
3.34.1
|
||||
======
|
||||
* Adjust to gnome-settings-daemon plugin removals [Xiaoguang; !94]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner, Xiaoguang Wang
|
||||
|
||||
Translators:
|
||||
Nathan Follens [nl], Dušan Kazik [sk], Ask Hjorth Larsen [da],
|
||||
Yi-Jyun Pan [zh_TW]
|
||||
|
||||
3.34.0
|
||||
======
|
||||
|
||||
Translators:
|
||||
Rafael Fontenelle [pt_BR], Efstathios Iosifidis [el], Milo Casagrande [it],
|
||||
Sabri Ünal [tr]
|
||||
|
||||
3.33.92
|
||||
=======
|
||||
|
||||
Translators:
|
||||
Марко Костић [sr], Tim Sabsch [de], Rūdolfs Mazurs [lv], Matej Urbančič [sl],
|
||||
Balázs Úr [hu], Claude Paroz [fr], Fran Dieguez [gl], Changwoo Ryu [ko],
|
||||
Ryuta Fujii [ja], Fabio Tomat [fur], Goran Vidović [hr]
|
||||
|
||||
3.33.91
|
||||
=======
|
||||
* Misc. bug fixes and cleanups [Florian; !88, !90, !91, !92]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
Translators:
|
||||
Asier Sarasua Garmendia [eu], Anders Jonsson [sv], Marek Černocký [cs],
|
||||
Kukuh Syafaat [id], Jiri Grönroos [fi], Florentina Mușat [ro],
|
||||
Aurimas Černius [lt], Daniel Mustieles [es], Piotr Drąg [pl], Jordi Mas [ca],
|
||||
Danial Behzadi [fa]
|
||||
3.33.90
|
||||
=======
|
||||
* window-list: Support showing windows from all workspaces [Florian; #154]
|
||||
* Misc. bug fixes and cleanups [Florian; !86, !87]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
Translators:
|
||||
Jor Teron [mjw]
|
||||
|
||||
3.33.4
|
||||
======
|
||||
* Make GNOME Classic more classic:
|
||||
- Disable GNOME 3 overview [Florian; !69]
|
||||
- Add window picker button to window list [Florian; !73, !80]
|
||||
- Style improvements and fixes [Jakub; #169, !82]
|
||||
- Support horizontal workspace layout in window list [Florian; !70]
|
||||
- Add draggable previews to window list workspace switcher [Florian; !74]
|
||||
- Arrange workspaces horizontally [Florian; !72]
|
||||
* workspace-indicator: Support horizontal workspace layout [Florian; !71]
|
||||
* workspace-indicator: Add draggable previews [Florian; !77]
|
||||
* Misc. bug fixes and cleanups [Florian; !75, !76, !79, !78, #168, !84]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner, Jakub Steiner, Jor Teron
|
||||
|
||||
Translators:
|
||||
Jor Teron [mjw]
|
||||
|
||||
3.33.3
|
||||
======
|
||||
* Misc. bug fixes [Florian, Marco; !67, !68]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner, Marco Trevisan (Treviño)
|
||||
|
||||
3.33.2
|
||||
======
|
||||
* Misc. bug fixes and cleanups [Florian; !66]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
3.33.1
|
||||
======
|
||||
* Misc. bug fixes [Florian; !64]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
3.32.1
|
||||
======
|
||||
* Fix windowsNavigator extension after ES6 port [Florian; #143]
|
||||
* screenshot-window-sizer: Add phone screenshot sizes [Adrien; !65]
|
||||
* Misc. bug fixes and cleanups [Fabian; !62]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner, Adrien Plazas, Fabian P. Schmidt
|
||||
|
||||
3.32.0
|
||||
======
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
Translations:
|
||||
Victor Ibragimov [tg], Kristjan SCHMIDT [eo], Mart Raudsepp [et]
|
||||
|
||||
3.31.92
|
||||
=======
|
||||
* Misc. bug fixes and cleanups [Florian; !57, !58, !59, !60]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
3.31.91
|
||||
=======
|
||||
* apps-menu: Remove outdated legacy-tray handling [Florian; !53]
|
||||
* user-theme: Allow using XDG user data dir [Tomasz; !55]
|
||||
* Misc. bug fixes and cleanups [Florian; !52, !54, !56]
|
||||
|
||||
Contributors:
|
||||
Tomasz Gąsior, Florian Müllner
|
||||
|
||||
Translators:
|
||||
Matej Urbančič [sl], Gun Chleoc [gd]
|
||||
|
||||
3.31.90
|
||||
=======
|
||||
* Misc. bug fixes and cleanups [Florian; !49, !50, !51]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
Translators:
|
||||
Ryuta Fujii [ja], Charles Monzat [fr], Pieter Schalk Schoeman [af]
|
||||
|
||||
3.31.2
|
||||
======
|
||||
* Remove obsolete alternate-tab extension [Florian; #786496]
|
||||
* Adjust to gnome-shell changes [Florian; #113]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
3.30.1
|
||||
======
|
||||
* apps-menu: Fix height on HiDPI systems [Florian; #102]
|
||||
* window-list: Only switch between windows on active workspace when scrolling
|
||||
[Florian; #78]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
3.30.0
|
||||
======
|
||||
* Bump version
|
||||
|
||||
3.29.91
|
||||
=======
|
||||
* Misc. bug fixes [Florian; #90]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
3.29.90
|
||||
=======
|
||||
* Misc. bug fixes [Florian; #786496]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
3.29.3
|
||||
======
|
||||
* Adjust to global.screen removal [Jonas; #759538]
|
||||
|
||||
Contributors:
|
||||
Jonas Ådahl, Florian Müllner
|
||||
|
||||
3.29.2
|
||||
======
|
||||
* Misc. bug fixes [Florian; #69]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
3.28.1
|
||||
======
|
||||
* Misc. bug fixes [Xiaoguang, Florian; #59, #62]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner, Xiaoguang Wang
|
||||
|
||||
Translators:
|
||||
Dz Chen [zh_CN]
|
||||
|
||||
3.28.0
|
||||
======
|
||||
|
||||
Contributors:
|
||||
Florian Müllner, Xiaoguang Wang
|
||||
|
||||
Translators:
|
||||
Aman Alam [pa], Bruce Cowan [en_GB]
|
||||
|
||||
3.27.92
|
||||
=======
|
||||
|
||||
Contributors:
|
||||
Florian Müllner
|
||||
|
||||
Translators:
|
||||
Piotr Drąg [es], GNOME Translation Robot [gd], Daniel Șerbănescu [ro]
|
||||
|
||||
3.27.91
|
||||
=======
|
||||
* places-menu: Support unmounting ejectable places [Rémy; #17]
|
||||
* apps-menu: Support separators and custom sort order [Florian; #27]
|
||||
* Port to meson [Florian; #31, #45]
|
||||
* window-list: Fix missing icons on wayland [Florian; #10]
|
||||
* places-menu: Fix terminating gnome-shell with recent gjs [Florian; #44]
|
||||
* auto-move: Make it work with wayland windows [Florian; #33]
|
||||
* Classic theme fixes [Florian, Jonas; #26, #41, #39, #40]
|
||||
* Require sassc for classic styling [Florian; !28]
|
||||
* Misc. bug fixes [Piotr, Florian; #772211, #32, #30]
|
||||
|
||||
Contributors:
|
||||
Jeremy Bicha, Piotr Drąg, Jonas Kümmerlin, Rémy Lefevre, Iñigo Martínez,
|
||||
Florian Müllner
|
||||
|
||||
Translators:
|
||||
Matej Urbančič [sl], Kjartan Maraas [nb]
|
||||
|
||||
3.27.1
|
||||
======
|
||||
* updated translations (ca@valencia)
|
||||
|
||||
3.26.1
|
||||
======
|
||||
* native-window-placement: Adjust to gnome-shell changes
|
||||
* updated translations: el, fa, ru, sv
|
||||
|
||||
3.26.0
|
||||
======
|
||||
* updated translations (be, bg, ca, da, eu, fi, is, it, ko, lv, ml,
|
||||
nl, pt_BR, vi, zh_TW)
|
||||
|
||||
3.25.91
|
||||
=======
|
||||
* updated translations (ca, fr, it, pl, pt_BR, sr, sr@latin, tr)
|
||||
|
||||
3.25.90
|
||||
=======
|
||||
* updated translations (es, gl, hr, hu, kk, sl, sv, sv)
|
||||
|
||||
3.25.4
|
||||
======
|
||||
* screenshot-window-sizer: Fix backward cycling
|
||||
* updated translations (ar, be, ca, cs, de, fur, id, lt, pl, sk)
|
||||
|
||||
3.25.3
|
||||
======
|
||||
* places-menu: Use mount operation if necessary
|
||||
|
||||
74
README
74
README
@@ -1,74 +0,0 @@
|
||||
GNOME Shell Extensions is a collection of extensions providing additional
|
||||
and optional functionality to GNOME Shell.
|
||||
|
||||
Since GNOME Shell is not API stable, extensions work only against a very
|
||||
specific version of the shell, usually the same as this package (see
|
||||
"configure --version"). The extensions in this package are supported by GNOME
|
||||
and will be updated to reflect future API changes in GNOME Shell.
|
||||
|
||||
For more information about GNOME Shell Extensions
|
||||
https://wiki.gnome.org/Projects/GnomeShell/Extensions
|
||||
|
||||
For general information about GNOME Shell
|
||||
https://wiki.gnome.org/Projects/GnomeShell
|
||||
|
||||
Bugs should be reported at https://bugzilla.gnome.org against the 'gnome-shell'
|
||||
product, with the 'extensions' component.
|
||||
|
||||
Extensions
|
||||
==========
|
||||
|
||||
alternate-tab
|
||||
|
||||
Lets you use classic Alt+Tab (window-based instead of app-based) in GNOME Shell.
|
||||
|
||||
apps-menu
|
||||
|
||||
Lets you reach an application using gnome 2.x style menu on the panel.
|
||||
|
||||
auto-move-windows
|
||||
|
||||
Lets you manage your workspaces more easily, assigning a specific workspace to
|
||||
each application as soon as it creates a window, in a manner configurable with a
|
||||
GSettings key.
|
||||
|
||||
dock
|
||||
|
||||
Shows a dock-style task switcher on the right side of the screen.
|
||||
|
||||
drive-menu
|
||||
Shows a status menu for rapid unmount and power off of external storage devices
|
||||
(i.e. pendrives)
|
||||
|
||||
example
|
||||
|
||||
A minimal example illustrating how to write extensions.
|
||||
|
||||
gajim
|
||||
|
||||
Integration with Gajim, a Jabber/XMPP instant messaging client.
|
||||
|
||||
native-window-placement
|
||||
|
||||
An alternative algorithm for layouting the thumbnails in the windows overview, that
|
||||
more closely reflects the actual positions and sizes.
|
||||
|
||||
places-menu
|
||||
|
||||
Shows a status Indicator for navigating to Places.
|
||||
|
||||
user-theme
|
||||
|
||||
Loads a shell theme from ~/.themes/<name>/gnome-shell.
|
||||
|
||||
windowsNavigator
|
||||
|
||||
Allow keyboard selection of windows and workspaces in overlay mode.
|
||||
|
||||
License
|
||||
=======
|
||||
GNOME Shell Extensions are distributed under the terms of the GNU General Public License,
|
||||
version 2 or later. See the COPYING file for details.
|
||||
Individual extensions may be licensed under different terms, see each source
|
||||
file for details.
|
||||
|
||||
96
README.md
Normal file
96
README.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# GNOME Shell Extensions
|
||||
|
||||
GNOME Shell Extensions is a collection of extensions providing additional
|
||||
and optional functionality to GNOME Shell.
|
||||
|
||||
Since GNOME Shell is not API stable, extensions work only against a very
|
||||
specific version of the shell, usually the same as this package (see
|
||||
"configure --version"). The extensions in this package are supported by GNOME
|
||||
and will be updated to reflect future API changes in GNOME Shell.
|
||||
|
||||
The GNOME wiki has more information about [GNOME Shell Extensions][project-page],
|
||||
as well as some general information about [GNOME Shell][shell-page].
|
||||
|
||||
Bugs should be reported to the GNOME [bug tracking system][bug-tracker].
|
||||
|
||||
## Extensions
|
||||
|
||||
* alternate-tab (**OBSOLETE**)
|
||||
|
||||
Lets you use classic Alt+Tab (window-based instead of app-based) in GNOME Shell.
|
||||
This extension is obsolete since GNOME 3.30, see [this blogpost][alternatetab-post]
|
||||
for further details.
|
||||
|
||||
* apps-menu
|
||||
|
||||
Lets you reach an application using gnome 2.x style menu on the panel.
|
||||
|
||||
* auto-move-windows
|
||||
|
||||
Lets you manage your workspaces more easily, assigning a specific workspace to
|
||||
each application as soon as it creates a window, in a manner configurable with a
|
||||
GSettings key.
|
||||
|
||||
* drive-menu
|
||||
|
||||
Shows a status menu for rapid unmount and power off of external storage devices
|
||||
(i.e. pendrives)
|
||||
|
||||
* launch-new-instance
|
||||
|
||||
Changes application icons to always launch a new instance when activated.
|
||||
|
||||
* native-window-placement
|
||||
|
||||
An alternative algorithm for layouting the thumbnails in the windows overview, that
|
||||
more closely reflects the actual positions and sizes.
|
||||
|
||||
* places-menu
|
||||
|
||||
Shows a status Indicator for navigating to Places.
|
||||
|
||||
* screenshot-window-sizer
|
||||
|
||||
Adds a shortcut for resizing the focus window to a size that is suitable for GNOME Software screenshots
|
||||
|
||||
* user-theme
|
||||
|
||||
Loads a shell theme from ~/.themes/<name>/gnome-shell.
|
||||
|
||||
* window-list
|
||||
|
||||
Adds a bottom panel with a traditional window list.
|
||||
|
||||
* windowsNavigator
|
||||
|
||||
Allow keyboard selection of windows and workspaces in overlay mode.
|
||||
|
||||
* workspace-indicator
|
||||
|
||||
Adds a simple workspace switcher to the top bar.
|
||||
|
||||
## Default branch
|
||||
|
||||
The default development branch is `main`. If you still have a local
|
||||
checkout under the old name, use:
|
||||
```sh
|
||||
git checkout master
|
||||
git branch -m master main
|
||||
git fetch
|
||||
git branch --unset-upstream
|
||||
git branch -u origin/master
|
||||
git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/main
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
GNOME Shell Extensions are distributed under the terms of the GNU General
|
||||
Public License, version 2 or later. See the [COPYING file][license] for details.
|
||||
Individual extensions may be licensed under different terms, see each source
|
||||
file for details.
|
||||
|
||||
[project-page]: https://wiki.gnome.org/Projects/GnomeShell/Extensions
|
||||
[shell-page]: https://wiki.gnome.org/Projects/GnomeShell
|
||||
[bug-tracker]: https://gitlab.gnome.org/GNOME/gnome-shell-extensions/issues
|
||||
[license]: COPYING
|
||||
[alternatetab-post]: https://blogs.gnome.org/fmuellner/2018/10/11/the-future-of-alternatetab-and-why-you-need-not-worry/
|
||||
26
autogen.sh
26
autogen.sh
@@ -1,26 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Run this to generate all the initial makefiles, etc.
|
||||
|
||||
srcdir=`dirname $0`
|
||||
test -z "$srcdir" && srcdir=.
|
||||
|
||||
test -f $srcdir/configure.ac || {
|
||||
echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
|
||||
echo " top-level gnome-shell-extensions directory"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Fetch submodules if needed
|
||||
if test ! -f data/gnome-shell-sass/COPYING;
|
||||
then
|
||||
echo "+ Setting up submodules"
|
||||
git submodule init
|
||||
fi
|
||||
git submodule update
|
||||
|
||||
which gnome-autogen.sh || {
|
||||
echo "You need to install gnome-common from GNOME Git (or from"
|
||||
echo "your OS vendor's package manager)."
|
||||
exit 1
|
||||
}
|
||||
. gnome-autogen.sh
|
||||
3
config/.gitignore
vendored
3
config/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
install-sh
|
||||
mkinstalldirs
|
||||
missing
|
||||
98
configure.ac
98
configure.ac
@@ -1,98 +0,0 @@
|
||||
AC_PREREQ(2.63)
|
||||
AC_INIT([gnome-shell-extensions],[3.25.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell&component=extensions])
|
||||
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CONFIG_AUX_DIR([config])
|
||||
|
||||
AM_INIT_AUTOMAKE([1.10 dist-xz no-dist-gzip foreign tar-ustar])
|
||||
|
||||
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
|
||||
|
||||
GETTEXT_PACKAGE=gnome-shell-extensions
|
||||
AC_SUBST(GETTEXT_PACKAGE)
|
||||
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
|
||||
[The prefix for our gettext translation domains.])
|
||||
|
||||
AM_GNU_GETTEXT_VERSION([0.19.6])
|
||||
AM_GNU_GETTEXT([external])
|
||||
|
||||
PKG_PROG_PKG_CONFIG([0.22])
|
||||
|
||||
GLIB_GSETTINGS
|
||||
|
||||
SHELL_VERSION="$PACKAGE_VERSION"
|
||||
shell_major=`echo "$PACKAGE_VERSION" | cut -d'.' -f1`
|
||||
shell_minor=`echo "$PACKAGE_VERSION" | cut -d'.' -f2`
|
||||
if test "$(($shell_minor % 2))" -eq 0; then
|
||||
SHELL_VERSION="$shell_major.$shell_minor"
|
||||
fi
|
||||
AC_SUBST([SHELL_VERSION])
|
||||
|
||||
dnl keep this in alphabetic order
|
||||
CLASSIC_EXTENSIONS="apps-menu places-menu alternate-tab launch-new-instance window-list"
|
||||
DEFAULT_EXTENSIONS="$CLASSIC_EXTENSIONS drive-menu screenshot-window-sizer windowsNavigator workspace-indicator"
|
||||
ALL_EXTENSIONS="$DEFAULT_EXTENSIONS auto-move-windows example native-window-placement user-theme"
|
||||
AC_SUBST(CLASSIC_EXTENSIONS, [$CLASSIC_EXTENSIONS])
|
||||
AC_SUBST(ALL_EXTENSIONS, [$ALL_EXTENSIONS])
|
||||
AC_ARG_ENABLE([extensions],
|
||||
[AS_HELP_STRING([--enable-extensions],[Space separated list of extensions to enable.
|
||||
The default is to build and distribute all extensions that can be installed in the home directory and have no external depedencies.
|
||||
Use "all" to enable all available extensions, or "classic-mode" to enable extensions that restore GNOME 2 functionality.])],
|
||||
[],
|
||||
[enable_extensions=$DEFAULT_EXTENSIONS]
|
||||
)
|
||||
|
||||
if test x"$enable_extensions" = xall; then
|
||||
enable_extensions="$ALL_EXTENSIONS"
|
||||
enable_classic_mode_default=yes
|
||||
elif test x"$enable_extensions" = xclassic-mode; then
|
||||
enable_extensions="$CLASSIC_EXTENSIONS"
|
||||
enable_classic_mode_default=yes
|
||||
else
|
||||
enable_classic_mode_default=no
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE([classic-mode],
|
||||
[AS_HELP_STRING([--enable-classic-mode],[Enable installing data files for classic mode.
|
||||
Defaults to yes if enabling all or classic-mode extensions, and no otherwise.])],
|
||||
[],
|
||||
[enable_classic_mode=$enable_classic_mode_default])
|
||||
AM_CONDITIONAL([CLASSIC_MODE], [test x"$enable_classic_mode" != xno])
|
||||
|
||||
ENABLED_EXTENSIONS=
|
||||
for e in $enable_extensions; do
|
||||
case $e in
|
||||
dnl keep this in alphabetic order
|
||||
alternate-tab|apps-menu|auto-move-windows|drive-menu|example|launch-new-instance|native-window-placement|places-menu|screenshot-window-sizer|user-theme|window-list|windowsNavigator|workspace-indicator)
|
||||
ENABLED_EXTENSIONS="$ENABLED_EXTENSIONS $e"
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([invalid extension $e])
|
||||
esac
|
||||
done
|
||||
|
||||
AC_SUBST(ENABLED_EXTENSIONS, [$ENABLED_EXTENSIONS])
|
||||
|
||||
AC_PATH_PROG([SASSC],[sassc],[])
|
||||
|
||||
dnl Please keep this sorted alphabetically
|
||||
AC_CONFIG_FILES([
|
||||
data/Makefile
|
||||
extensions/alternate-tab/Makefile
|
||||
extensions/apps-menu/Makefile
|
||||
extensions/auto-move-windows/Makefile
|
||||
extensions/drive-menu/Makefile
|
||||
extensions/example/Makefile
|
||||
extensions/launch-new-instance/Makefile
|
||||
extensions/native-window-placement/Makefile
|
||||
extensions/places-menu/Makefile
|
||||
extensions/screenshot-window-sizer/Makefile
|
||||
extensions/user-theme/Makefile
|
||||
extensions/window-list/Makefile
|
||||
extensions/windowsNavigator/Makefile
|
||||
extensions/workspace-indicator/Makefile
|
||||
extensions/Makefile
|
||||
Makefile
|
||||
po/Makefile.in
|
||||
])
|
||||
AC_OUTPUT
|
||||
@@ -0,0 +1,9 @@
|
||||
[org.gnome.mutter:GNOME-Classic]
|
||||
dynamic-workspaces=false
|
||||
|
||||
[org.gnome.desktop.wm.preferences:GNOME-Classic]
|
||||
button-layout='appmenu:minimize,maximize,close'
|
||||
|
||||
[org.gnome.desktop.wm.keybindings:GNOME-Classic]
|
||||
switch-applications=[]
|
||||
switch-windows=['<Super>Tab','<Alt>Tab']
|
||||
@@ -1,6 +1,2 @@
|
||||
To generate the css files, from the project directory:
|
||||
|
||||
sassc -a gnome-classic.scss gnome-classic.css
|
||||
|
||||
To update the gnome-shell-sass submodule to latest upstream commit:
|
||||
git submodule update --rebase
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
include $(top_srcdir)/include.mk
|
||||
|
||||
sessiondir = $(datadir)/gnome-session/sessions
|
||||
session_in_files = gnome-classic.session.desktop.in
|
||||
session_DATA = $(session_in_files:.session.desktop.in=.session)
|
||||
|
||||
xsessiondir = $(datadir)/xsessions
|
||||
xsession_in_files = gnome-classic.desktop.in
|
||||
xsession_DATA = $(xsession_in_files:.desktop.in=.desktop)
|
||||
|
||||
modedir = $(datadir)/gnome-shell/modes
|
||||
mode_in_files = classic.json.in
|
||||
mode_DATA = $(mode_in_files:.json.in=.json)
|
||||
|
||||
themedir = $(datadir)/gnome-shell/theme
|
||||
theme_DATA = \
|
||||
$(srcdir)/calendar-today.svg \
|
||||
$(srcdir)/classic-process-working.svg \
|
||||
$(srcdir)/classic-toggle-off-intl.svg \
|
||||
$(srcdir)/classic-toggle-off-us.svg \
|
||||
$(srcdir)/classic-toggle-on-intl.svg \
|
||||
$(srcdir)/classic-toggle-on-us.svg \
|
||||
$(srcdir)/gnome-classic.css \
|
||||
$(srcdir)/gnome-classic-high-contrast.css \
|
||||
$(NULL)
|
||||
|
||||
theme_sources = \
|
||||
$(srcdir)/gnome-classic.scss \
|
||||
$(srcdir)/gnome-shell-sass/_colors.scss \
|
||||
$(srcdir)/gnome-shell-sass/_common.scss \
|
||||
$(srcdir)/gnome-shell-sass/_drawing.scss \
|
||||
$(srcdir)/gnome-shell-sass/_high-contrast-colors.scss \
|
||||
$(NULL)
|
||||
|
||||
%.css: %.scss $(theme_sources)
|
||||
@if test -n "$(SASSC)"; then \
|
||||
if $(AM_V_P); then PS4= set -x; else echo " GEN $@"; fi; \
|
||||
$(SASSC) -a $< $@; \
|
||||
fi
|
||||
|
||||
gsettings_SCHEMAS = org.gnome.shell.extensions.classic-overrides.gschema.xml
|
||||
|
||||
@GSETTINGS_RULES@
|
||||
|
||||
%.desktop:%.desktop.in
|
||||
$(AM_V_GEN) $(MSGFMT) --desktop --template $< -d $(top_srcdir)/po -o $@
|
||||
|
||||
%.session: %.session.desktop.in
|
||||
$(AM_V_GEN) $(MSGFMT) --desktop --template $< -d $(top_srcdir)/po -o $@
|
||||
|
||||
comma:=,
|
||||
empty:=
|
||||
space:= $(empty) $(empty)
|
||||
extensions:=$(patsubst %,\"%$(extensionbase)\",$(CLASSIC_EXTENSIONS))
|
||||
extension_list:=$(subst $(space),$(comma),$(extensions))
|
||||
%.json:%.json.in
|
||||
$(AM_V_GEN) sed \
|
||||
-e "s|\@CLASSIC_EXTENSIONS\@|$(extension_list)|g" \
|
||||
$< > $@
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(session_in_files) \
|
||||
$(xsession_in_files) \
|
||||
$(mode_in_files) \
|
||||
$(gsettings_SCHEMAS) \
|
||||
$(theme_DATA) \
|
||||
$(NULL)
|
||||
|
||||
CLEANFILES = \
|
||||
$(session_DATA) \
|
||||
$(xsession_DATA) \
|
||||
$(mode_DATA) \
|
||||
$(NULL)
|
||||
@@ -1,178 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24"
|
||||
height="24"
|
||||
id="svg10621"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="calendar-today.svg">
|
||||
<defs
|
||||
id="defs10623">
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient99561-1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient34508-1-3">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop34510-1-9" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop34512-4-5" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="42"
|
||||
fy="30"
|
||||
fx="51"
|
||||
cy="30"
|
||||
cx="51"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient10592"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient3770"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient3001"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient3007"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient3067"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient3072"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient2997"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#d3d3d3"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="8"
|
||||
inkscape:cx="-55.349829"
|
||||
inkscape:cy="-31.442864"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="2133"
|
||||
inkscape:window-height="1241"
|
||||
inkscape:window-x="238"
|
||||
inkscape:window-y="89"
|
||||
inkscape:window-maximized="0"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3109"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata10626">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-469.08263,-537.99307)">
|
||||
<circle
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#555753;fill-opacity:0.23756906;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="path7305"
|
||||
cx="481.57138"
|
||||
cy="559.4649"
|
||||
r="1.5" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 5.6 KiB |
@@ -1,262 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="65"
|
||||
height="22"
|
||||
id="svg10865"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="classic-toggle-off-intl.svg">
|
||||
<defs
|
||||
id="defs10867">
|
||||
<linearGradient
|
||||
id="linearGradient62852-6-5">
|
||||
<stop
|
||||
id="stop62854-6-7"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1" />
|
||||
<stop
|
||||
style="stop-color:#eeeeec;stop-opacity:1"
|
||||
offset="0.97726452"
|
||||
id="stop62858-8-0" />
|
||||
<stop
|
||||
id="stop62860-5-3"
|
||||
offset="1"
|
||||
style="stop-color:#f5f5f4;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<inkscape:path-effect
|
||||
is_visible="true"
|
||||
id="path-effect62989-8-0"
|
||||
effect="spiro" />
|
||||
<linearGradient
|
||||
id="linearGradient62821-5-8">
|
||||
<stop
|
||||
id="stop62823-2-4"
|
||||
offset="0"
|
||||
style="stop-color:#d1d3d1;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop62825-3-8"
|
||||
offset="1"
|
||||
style="stop-color:#ebebeb;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient62852-6-5-3">
|
||||
<stop
|
||||
id="stop62854-6-7-6"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1" />
|
||||
<stop
|
||||
style="stop-color:#eeeeec;stop-opacity:1"
|
||||
offset="0.97726452"
|
||||
id="stop62858-8-0-3" />
|
||||
<stop
|
||||
id="stop62860-5-3-9"
|
||||
offset="1"
|
||||
style="stop-color:#f5f5f4;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<inkscape:path-effect
|
||||
effect="spiro"
|
||||
id="path-effect62829-6-8-0"
|
||||
is_visible="true" />
|
||||
<linearGradient
|
||||
id="linearGradient62821-5-8-1">
|
||||
<stop
|
||||
id="stop62823-2-4-2"
|
||||
offset="0"
|
||||
style="stop-color:#d1d3d1;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop62825-3-8-9"
|
||||
offset="1"
|
||||
style="stop-color:#ebebeb;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="507.42715"
|
||||
x2="702.58966"
|
||||
y1="484.49405"
|
||||
x1="702.58966"
|
||||
gradientTransform="matrix(1.3066667,0,0,1,-841.64667,-483)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient11647"
|
||||
xlink:href="#linearGradient62821-5-8-1"
|
||||
inkscape:collect="always" />
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath65663">
|
||||
<rect
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#729fcf;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:new"
|
||||
id="rect65665"
|
||||
width="96.999924"
|
||||
height="24.292892"
|
||||
x="708.71954"
|
||||
y="406.96973"
|
||||
rx="3.1139846"
|
||||
ry="1.9595497"
|
||||
inkscape:export-filename="/home/lapo.fedora/SparkleShare/gnome-mockups/content selection/content-selection.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
</clipPath>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5378-2-5-9-9-0-80-7-6-8-4-5-7-3-6-2-9"
|
||||
id="linearGradient65582"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.5250827,0,0,0.72144839,620.16092,156.4917)"
|
||||
x1="207.17195"
|
||||
y1="497.39584"
|
||||
x2="207.17195"
|
||||
y2="531.48669" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient5378-2-5-9-9-0-80-7-6-8-4-5-7-3-6-2-9">
|
||||
<stop
|
||||
style="stop-color:#f4f6f4;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop5380-6-1-1-9-3-6-3-6-45-6-4-4-9-9-8-9" />
|
||||
<stop
|
||||
style="stop-color:#d7dad7;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop5382-06-3-6-4-2-4-6-8-0-9-6-8-7-7-6-7" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient65584">
|
||||
<stop
|
||||
style="stop-color:#f1f1f1;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop65586" />
|
||||
<stop
|
||||
style="stop-color:#a7aba7;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop65588" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5378-2-5-9-9-0-80-7-6-8-4-5-7-3-6-2-9"
|
||||
id="linearGradient65582-5"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.5250827,0,0,0.72144839,-75.348673,-359.16458)"
|
||||
x1="207.17195"
|
||||
y1="497.39584"
|
||||
x2="207.17195"
|
||||
y2="531.48669" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient65584"
|
||||
id="linearGradient65590-6"
|
||||
x1="716.62506"
|
||||
y1="537.23358"
|
||||
x2="716.62506"
|
||||
y2="535.23358"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0139469,0,0,1.0139469,-705.52354,-521.99915)" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="23.565368"
|
||||
inkscape:cy="19.596892"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g62929"
|
||||
showgrid="false"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:snap-nodes="false"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="true"
|
||||
inkscape:snap-others="false"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1342"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="28"
|
||||
inkscape:window-maximized="1"
|
||||
showborder="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid11512" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata10870">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1030.3622)">
|
||||
<g
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/lapo.fedora/SparkleShare/gnome-mockups/system-settings/network/network-panel-summary.png"
|
||||
style="display:inline"
|
||||
id="g62929"
|
||||
transform="translate(-643.91421,517.29894)">
|
||||
<g
|
||||
transform="translate(0,30)"
|
||||
id="g62931">
|
||||
<rect
|
||||
style="fill:#cecece;fill-opacity:1;stroke:#a7a7a7;stroke-width:1;stroke-linecap:butt;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;color:#000000;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill-rule:nonzero;stroke-linejoin:miter;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:new;clip-rule:nonzero;opacity:1;isolation:auto;mix-blend-mode:normal;solid-color:#000000;solid-opacity:1;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;filter-blend-mode:normal;filter-gaussianBlur-deviation:0;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto"
|
||||
id="rect62935"
|
||||
width="63.856125"
|
||||
height="21.093594"
|
||||
x="644.5"
|
||||
y="483.5"
|
||||
rx="2"
|
||||
ry="2" />
|
||||
<rect
|
||||
style="fill:url(#linearGradient65582);fill-opacity:1;stroke:#a7a7a7;stroke-width:0.97313344;stroke-linecap:butt;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline;color:#000000;clip-rule:nonzero;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill-rule:nonzero;stroke-linejoin:miter;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;filter-blend-mode:normal;filter-gaussianBlur-deviation:0;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect62937"
|
||||
width="29.97398"
|
||||
height="17.022524"
|
||||
x="646.55896"
|
||||
y="485.61429"
|
||||
rx="1"
|
||||
ry="1" />
|
||||
<g
|
||||
transform="translate(-38.048674,-1.9445437)"
|
||||
id="g62939" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:original-d="m 647.78241,486.57192 27.73523,0"
|
||||
inkscape:path-effect="#path-effect62989-8-0"
|
||||
id="path62947"
|
||||
d="m 647.78241,486.57192 27.73523,0"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
id="g11665"
|
||||
transform="matrix(0.78906097,0,0,0.78906097,178.78814,111.57844)" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
id="path3016"
|
||||
sodipodi:cx="50.375"
|
||||
sodipodi:cy="11.875"
|
||||
sodipodi:rx="4.625"
|
||||
sodipodi:ry="4.625"
|
||||
d="M 55,11.875 A 4.625,4.625 0 0 1 50.375,16.5 4.625,4.625 0 0 1 45.75,11.875 4.625,4.625 0 0 1 50.375,7.25 4.625,4.625 0 0 1 55,11.875 Z"
|
||||
transform="translate(642.41421,512.02037)"
|
||||
style="fill:none;stroke:#2e3436;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 9.5 KiB |
@@ -1,222 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="65"
|
||||
height="22"
|
||||
id="svg10865"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="classic-toggle-off-us.svg">
|
||||
<defs
|
||||
id="defs10867">
|
||||
<linearGradient
|
||||
id="linearGradient62852-6-5">
|
||||
<stop
|
||||
id="stop62854-6-7"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1" />
|
||||
<stop
|
||||
style="stop-color:#eeeeec;stop-opacity:1"
|
||||
offset="0.97726452"
|
||||
id="stop62858-8-0" />
|
||||
<stop
|
||||
id="stop62860-5-3"
|
||||
offset="1"
|
||||
style="stop-color:#f5f5f4;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<inkscape:path-effect
|
||||
is_visible="true"
|
||||
id="path-effect62989-8-0"
|
||||
effect="spiro" />
|
||||
<linearGradient
|
||||
id="linearGradient62821-5-8">
|
||||
<stop
|
||||
id="stop62823-2-4"
|
||||
offset="0"
|
||||
style="stop-color:#d1d3d1;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop62825-3-8"
|
||||
offset="1"
|
||||
style="stop-color:#ebebeb;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient62852-6-5-3">
|
||||
<stop
|
||||
id="stop62854-6-7-6"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1" />
|
||||
<stop
|
||||
style="stop-color:#eeeeec;stop-opacity:1"
|
||||
offset="0.97726452"
|
||||
id="stop62858-8-0-3" />
|
||||
<stop
|
||||
id="stop62860-5-3-9"
|
||||
offset="1"
|
||||
style="stop-color:#f5f5f4;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<inkscape:path-effect
|
||||
effect="spiro"
|
||||
id="path-effect62829-6-8-0"
|
||||
is_visible="true" />
|
||||
<linearGradient
|
||||
id="linearGradient62821-5-8-1">
|
||||
<stop
|
||||
id="stop62823-2-4-2"
|
||||
offset="0"
|
||||
style="stop-color:#d1d3d1;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop62825-3-8-9"
|
||||
offset="1"
|
||||
style="stop-color:#ebebeb;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="507.42715"
|
||||
x2="702.58966"
|
||||
y1="484.49405"
|
||||
x1="702.58966"
|
||||
gradientTransform="matrix(1.3066667,0,0,1,-841.64667,-483)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient11647"
|
||||
xlink:href="#linearGradient62821-5-8-1"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5378-2-5-9-9-0-80-7-6-8-4-5-7-3-6-2-9"
|
||||
id="linearGradient65582"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.5250827,0,0,0.72144839,620.16092,156.4917)"
|
||||
x1="207.17195"
|
||||
y1="497.39584"
|
||||
x2="207.17195"
|
||||
y2="531.48669" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient5378-2-5-9-9-0-80-7-6-8-4-5-7-3-6-2-9">
|
||||
<stop
|
||||
style="stop-color:#f4f6f4;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop5380-6-1-1-9-3-6-3-6-45-6-4-4-9-9-8-9" />
|
||||
<stop
|
||||
style="stop-color:#d7dad7;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop5382-06-3-6-4-2-4-6-8-0-9-6-8-7-7-6-7" />
|
||||
</linearGradient>
|
||||
<inkscape:path-effect
|
||||
is_visible="true"
|
||||
id="path-effect62989-8-0-6"
|
||||
effect="spiro" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="78.222729"
|
||||
inkscape:cy="-3.6620078"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g62929"
|
||||
showgrid="false"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:snap-nodes="false"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="true"
|
||||
inkscape:snap-others="false"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1342"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="28"
|
||||
inkscape:window-maximized="1"
|
||||
showborder="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid11512" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata10870">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1030.3622)">
|
||||
<g
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/lapo.fedora/SparkleShare/gnome-mockups/system-settings/network/network-panel-summary.png"
|
||||
style="display:inline"
|
||||
id="g62929"
|
||||
transform="translate(-643.91421,517.29894)">
|
||||
<g
|
||||
style="display:inline"
|
||||
transform="translate(0,30)"
|
||||
id="g62931">
|
||||
<rect
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#cecece;fill-opacity:1;fill-rule:nonzero;stroke:#a7a7a7;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:new"
|
||||
id="rect62935"
|
||||
width="63.856125"
|
||||
height="21.093594"
|
||||
x="644.5"
|
||||
y="483.5"
|
||||
rx="2"
|
||||
ry="2" />
|
||||
<rect
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient65582);fill-opacity:1;fill-rule:nonzero;stroke:#a7a7a7;stroke-width:0.97313344;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect62937"
|
||||
width="29.97398"
|
||||
height="17.022524"
|
||||
x="646.55896"
|
||||
y="485.61429"
|
||||
rx="1"
|
||||
ry="1" />
|
||||
<g
|
||||
transform="translate(-38.048674,-1.9445437)"
|
||||
id="g62939" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:original-d="m 647.78241,486.57192 27.73523,0"
|
||||
inkscape:path-effect="#path-effect62989-8-0-6"
|
||||
id="path62947"
|
||||
d="m 647.78241,486.57192 27.73523,0"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
id="g11665"
|
||||
transform="matrix(0.78906097,0,0,0.78906097,167.78814,111.57844)">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:12.69556618px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#2e3436;fill-opacity:1;stroke:none;font-family:Cantarell;-inkscape-font-specification:Cantarell Bold"
|
||||
x="644.46393"
|
||||
y="533.87158"
|
||||
id="text62949"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="scale(1.0113976,0.98873084)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan62951"
|
||||
x="644.46393"
|
||||
y="533.87158">OFF</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 8.0 KiB |
@@ -1,175 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="65"
|
||||
height="22"
|
||||
id="svg10865"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="classic-toggle-on-intl.svg">
|
||||
<defs
|
||||
id="defs10867">
|
||||
<linearGradient
|
||||
id="linearGradient62852-6-5">
|
||||
<stop
|
||||
id="stop62854-6-7"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1" />
|
||||
<stop
|
||||
style="stop-color:#eeeeec;stop-opacity:1"
|
||||
offset="0.97726452"
|
||||
id="stop62858-8-0" />
|
||||
<stop
|
||||
id="stop62860-5-3"
|
||||
offset="1"
|
||||
style="stop-color:#f5f5f4;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<inkscape:path-effect
|
||||
is_visible="true"
|
||||
id="path-effect62989-8-0"
|
||||
effect="spiro" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient38513"
|
||||
id="linearGradient38519"
|
||||
x1="690"
|
||||
y1="506.25049"
|
||||
x2="690"
|
||||
y2="480.93414"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient38513">
|
||||
<stop
|
||||
style="stop-color:#729fcf;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop38515" />
|
||||
<stop
|
||||
style="stop-color:#6f9ccd;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop38517" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient62852-6-5"
|
||||
id="linearGradient62981-1-1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.85639946,0,0,0.81059641,57.747905,92.132229)"
|
||||
x1="740"
|
||||
y1="486.10501"
|
||||
x2="740"
|
||||
y2="505.3204" />
|
||||
<inkscape:path-effect
|
||||
is_visible="true"
|
||||
id="path-effect62989-8-0-8"
|
||||
effect="spiro" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="9.3225163"
|
||||
inkscape:cy="-0.1588306"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g62929"
|
||||
showgrid="false"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:snap-nodes="false"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="true"
|
||||
inkscape:snap-others="false"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1342"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="28"
|
||||
inkscape:window-maximized="1"
|
||||
showborder="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid11512" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata10870">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1030.3622)">
|
||||
<g
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/lapo.fedora/SparkleShare/gnome-mockups/system-settings/network/network-panel-summary.png"
|
||||
style="display:inline"
|
||||
id="g62929"
|
||||
transform="translate(-643.91421,517.29894)">
|
||||
<g
|
||||
style="display:inline"
|
||||
transform="translate(0,30)"
|
||||
id="g62931">
|
||||
<rect
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient38519);fill-opacity:1;fill-rule:nonzero;stroke:#3465a4;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect62935"
|
||||
width="63.856125"
|
||||
height="21.093594"
|
||||
x="644.5"
|
||||
y="483.5"
|
||||
rx="2"
|
||||
ry="2" />
|
||||
<rect
|
||||
style="display:inline;fill:url(#linearGradient62981-1-1);fill-opacity:1;stroke:#3465a4;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect62937"
|
||||
width="29.97398"
|
||||
height="17.022524"
|
||||
x="676.49646"
|
||||
y="485.67679"
|
||||
rx="1"
|
||||
ry="1" />
|
||||
<g
|
||||
transform="translate(-7.9861743,-1.9445437)"
|
||||
id="g62939" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:original-d="m 677.59874,486.57192 27.94632,0"
|
||||
inkscape:path-effect="#path-effect62989-8-0-8"
|
||||
id="path62947"
|
||||
d="m 677.59874,486.57192 27.94632,0"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
</g>
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4444"
|
||||
d="m 661.91421,522.09451 0,7"
|
||||
style="fill:#3465a4;stroke:#3465a4;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;color:#000000;fill-opacity:1;fill-rule:nonzero;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="m 18,7.96875 0,7"
|
||||
id="path3922"
|
||||
inkscape:connector-curvature="0"
|
||||
transform="translate(643.91421,513.06326)" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 6.2 KiB |
@@ -1,209 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="65"
|
||||
height="22"
|
||||
id="svg10865"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="classic-toggle-on-us.svg">
|
||||
<defs
|
||||
id="defs10867">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient62852-6-5"
|
||||
id="linearGradient62981-1-1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.85639946,0,0,0.81059641,57.747905,92.132229)"
|
||||
x1="740"
|
||||
y1="486.10501"
|
||||
x2="740"
|
||||
y2="505.3204" />
|
||||
<linearGradient
|
||||
id="linearGradient62852-6-5">
|
||||
<stop
|
||||
id="stop62854-6-7"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1" />
|
||||
<stop
|
||||
style="stop-color:#eeeeec;stop-opacity:1"
|
||||
offset="0.97726452"
|
||||
id="stop62858-8-0" />
|
||||
<stop
|
||||
id="stop62860-5-3"
|
||||
offset="1"
|
||||
style="stop-color:#f5f5f4;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<inkscape:path-effect
|
||||
is_visible="true"
|
||||
id="path-effect62989-8-0"
|
||||
effect="spiro" />
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath65663">
|
||||
<rect
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#729fcf;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:new"
|
||||
id="rect65665"
|
||||
width="96.999924"
|
||||
height="24.292892"
|
||||
x="708.71954"
|
||||
y="406.96973"
|
||||
rx="3.1139846"
|
||||
ry="1.9595497"
|
||||
inkscape:export-filename="/home/lapo.fedora/SparkleShare/gnome-mockups/content selection/content-selection.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
</clipPath>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient38513"
|
||||
id="linearGradient38519"
|
||||
x1="690"
|
||||
y1="506.25049"
|
||||
x2="690"
|
||||
y2="480.93414"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient38513">
|
||||
<stop
|
||||
style="stop-color:#729fcf;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop38515" />
|
||||
<stop
|
||||
style="stop-color:#6f9ccd;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop38517" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="translate(-644,-483)"
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient38513"
|
||||
id="linearGradient38519-3"
|
||||
x1="690"
|
||||
y1="506.25049"
|
||||
x2="690"
|
||||
y2="480.93414"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="20.775745"
|
||||
inkscape:cy="7.0434022"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g62929"
|
||||
showgrid="false"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:snap-nodes="false"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="true"
|
||||
inkscape:snap-others="false"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1342"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="28"
|
||||
inkscape:window-maximized="1"
|
||||
showborder="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid11512" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata10870">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1030.3622)">
|
||||
<g
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/lapo.fedora/SparkleShare/gnome-mockups/system-settings/network/network-panel-summary.png"
|
||||
style="display:inline"
|
||||
id="g62929"
|
||||
transform="translate(-643.91421,517.29894)">
|
||||
<g
|
||||
transform="translate(0,30)"
|
||||
id="g62931">
|
||||
<rect
|
||||
style="fill:url(#linearGradient38519);fill-opacity:1;stroke:#3465a4;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill-rule:nonzero;stroke-linejoin:miter;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;filter-blend-mode:normal;filter-gaussianBlur-deviation:0;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect62935"
|
||||
width="63.856125"
|
||||
height="21.093594"
|
||||
x="644.5"
|
||||
y="483.5"
|
||||
rx="2"
|
||||
ry="2" />
|
||||
<rect
|
||||
style="fill:url(#linearGradient62981-1-1);fill-opacity:1;stroke:#3465a4;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||
id="rect62937"
|
||||
width="29.97398"
|
||||
height="17.022524"
|
||||
x="676.49646"
|
||||
y="485.67679"
|
||||
rx="1"
|
||||
ry="1" />
|
||||
<g
|
||||
transform="translate(-7.9861743,-1.9445437)"
|
||||
id="g62939" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:original-d="m 677.59874,486.57192 27.94632,0"
|
||||
inkscape:path-effect="#path-effect62989-8-0"
|
||||
id="path62947"
|
||||
d="m 677.59874,486.57192 27.94632,0"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
</g>
|
||||
<text
|
||||
transform="scale(1.0113976,0.98873084)"
|
||||
sodipodi:linespacing="125%"
|
||||
id="text62949-7"
|
||||
y="534.96918"
|
||||
x="646.71399"
|
||||
style="font-size:10.01104736px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#3465a4;fill-opacity:1;stroke:none;display:inline;font-family:Cantarell;-inkscape-font-specification:Cantarell Bold;"
|
||||
xml:space="preserve"><tspan
|
||||
y="534.96918"
|
||||
x="646.71399"
|
||||
id="tspan62951-7"
|
||||
sodipodi:role="line">ON</tspan></text>
|
||||
<text
|
||||
transform="scale(1.0113976,0.98873084)"
|
||||
sodipodi:linespacing="125%"
|
||||
id="text62949"
|
||||
y="533.94482"
|
||||
x="646.71399"
|
||||
style="font-size:10.01104736px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Cantarell;-inkscape-font-specification:Cantarell Bold"
|
||||
xml:space="preserve"><tspan
|
||||
y="533.94482"
|
||||
x="646.71399"
|
||||
id="tspan62951"
|
||||
sodipodi:role="line">ON</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 7.5 KiB |
@@ -1,8 +1,10 @@
|
||||
{
|
||||
"parentMode": "user",
|
||||
"stylesheetName": "gnome-classic.css",
|
||||
"hasOverview": false,
|
||||
"showWelcomeDialog": false,
|
||||
"enabledExtensions": [@CLASSIC_EXTENSIONS@],
|
||||
"panel": { "left": ["activities", "appMenu"],
|
||||
"panel": { "left": ["appMenu"],
|
||||
"center": [],
|
||||
"right": ["a11y", "keyboard", "dateMenu", "aggregateMenu"]
|
||||
}
|
||||
|
||||
8
data/gnome-classic-wayland.desktop.in
Normal file
8
data/gnome-classic-wayland.desktop.in
Normal file
@@ -0,0 +1,8 @@
|
||||
[Desktop Entry]
|
||||
Name=GNOME Classic on Wayland
|
||||
Comment=This session logs you into GNOME Classic
|
||||
Exec=env GNOME_SHELL_SESSION_MODE=classic gnome-session
|
||||
TryExec=gnome-session
|
||||
Type=Application
|
||||
DesktopNames=GNOME-Classic;GNOME;
|
||||
X-GDM-SessionRegisters=true
|
||||
8
data/gnome-classic-xorg.desktop.in
Normal file
8
data/gnome-classic-xorg.desktop.in
Normal file
@@ -0,0 +1,8 @@
|
||||
[Desktop Entry]
|
||||
Name=GNOME Classic on Xorg
|
||||
Comment=This session logs you into GNOME Classic
|
||||
Exec=env GNOME_SHELL_SESSION_MODE=classic gnome-session
|
||||
TryExec=gnome-session
|
||||
Type=Application
|
||||
DesktopNames=GNOME-Classic;GNOME;
|
||||
X-GDM-SessionRegisters=true
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,8 @@
|
||||
[Desktop Entry]
|
||||
Name=GNOME Classic
|
||||
Comment=This session logs you into GNOME Classic
|
||||
Exec=env GNOME_SHELL_SESSION_MODE=classic gnome-session --session gnome-classic
|
||||
Exec=env GNOME_SHELL_SESSION_MODE=classic gnome-session
|
||||
TryExec=gnome-session
|
||||
Type=Application
|
||||
DesktopNames=GNOME-Classic;GNOME;
|
||||
X-GDM-SessionRegisters=true
|
||||
|
||||
@@ -4,10 +4,11 @@ $variant: 'light';
|
||||
@import "gnome-shell-sass/_colors"; //use gtk colors
|
||||
@import "gnome-shell-sass/_drawing";
|
||||
@import "gnome-shell-sass/_common";
|
||||
@import "gnome-shell-sass/_widgets";
|
||||
|
||||
/* Overrides */
|
||||
|
||||
#panel {
|
||||
#panel, #panel.solid {
|
||||
font-weight: normal;
|
||||
background-color: $bg_color;
|
||||
background-gradient-direction: vertical;
|
||||
@@ -31,15 +32,38 @@ $variant: 'light';
|
||||
-minimum-hpadding: 4px;
|
||||
font-weight: normal;
|
||||
color: $fg_color;
|
||||
text-shadow: none;
|
||||
transition-duration: 0ms;
|
||||
border: 0;
|
||||
border-radius: 0px;
|
||||
|
||||
&.clock-display {
|
||||
.clock {
|
||||
transition-duration: 0ms;
|
||||
border: 0;
|
||||
border-radius: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: lighten($fg_color,10%);
|
||||
text-shadow: none;
|
||||
& .system-status-icon { icon-shadow: none; }
|
||||
}
|
||||
&:active, &:overview, &:focus, &:checked {
|
||||
// Trick due to St limitations. It needs a background to draw
|
||||
// a box-shadow
|
||||
background-color: $selected_bg_color !important;
|
||||
color: $selected_fg_color !important;
|
||||
background-color: $selected_bg_color;
|
||||
color: $selected_fg_color;
|
||||
box-shadow: none;
|
||||
& > .system-status-icon { icon-shadow: none; }
|
||||
}
|
||||
|
||||
.app-menu-icon { width: 0; height: 0; margin: 0; } // shell's display:none; :D
|
||||
|
||||
.system-status-icon {
|
||||
icon-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.panel-corner,
|
||||
@@ -51,18 +75,17 @@ $variant: 'light';
|
||||
&.lock-screen,
|
||||
&.unlock-screen,
|
||||
&.login-screen {
|
||||
background-color: transparentize($_bubble_bg_color, 0.5);
|
||||
background-gradient-start: transparentize($_bubble_bg_color, 0.5);
|
||||
background-gradient-end: transparentize($_bubble_bg_color, 0.5);
|
||||
background-color: transparentize($bg_color, 0.5);
|
||||
background-gradient-start: transparentize($bg_color, 0.5);
|
||||
background-gradient-end: transparentize($bg_color, 0.5);
|
||||
border-bottom: none;
|
||||
.panel-button { color: $osd_fg_color; }
|
||||
}
|
||||
.popup-menu-arrow { width: 0; height: 0; } // shell's display: none;
|
||||
}
|
||||
|
||||
#appMenu {
|
||||
padding: 0 8px 0 8px;
|
||||
spinner-image: url("classic-process-working.svg");
|
||||
.panel-status-menu-box { padding: 0; }
|
||||
}
|
||||
.tile-preview-left.on-primary,
|
||||
.tile-preview-right.on-primary,
|
||||
@@ -70,14 +93,3 @@ $variant: 'light';
|
||||
/* keep in sync with -panel-corner-radius */
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
@each $v in us, intl {
|
||||
.toggle-switch-#{$v} {
|
||||
background-image: url("classic-toggle-off-#{$v}.svg");
|
||||
&:checked { background-image: url("classic-toggle-on-#{$v}.svg"); }
|
||||
}
|
||||
}
|
||||
|
||||
.calendar-day-with-events {
|
||||
background-image: url("calendar-today.svg");
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
[GNOME Session]
|
||||
Name=GNOME Classic
|
||||
RequiredComponents=org.gnome.Shell;org.gnome.SettingsDaemon.A11yKeyboard;org.gnome.SettingsDaemon.A11ySettings;org.gnome.SettingsDaemon.Clipboard;org.gnome.SettingsDaemon.Color;org.gnome.SettingsDaemon.Datetime;org.gnome.SettingsDaemon.Housekeeping;org.gnome.SettingsDaemon.Keyboard;org.gnome.SettingsDaemon.MediaKeys;org.gnome.SettingsDaemon.Mouse;org.gnome.SettingsDaemon.Orientation;org.gnome.SettingsDaemon.Power;org.gnome.SettingsDaemon.PrintNotifications;org.gnome.SettingsDaemon.Rfkill;org.gnome.SettingsDaemon.ScreensaverProxy;org.gnome.SettingsDaemon.Sharing;org.gnome.SettingsDaemon.Smartcard;org.gnome.SettingsDaemon.Sound;org.gnome.SettingsDaemon.Wacom;org.gnome.SettingsDaemon.XRANDR;org.gnome.SettingsDaemon.XSettings;nautilus-classic;
|
||||
Submodule data/gnome-shell-sass updated: e94bce1fcf...4697a3f0c6
113
data/meson.build
Normal file
113
data/meson.build
Normal file
@@ -0,0 +1,113 @@
|
||||
session_desktop_base = 'gnome-classic'
|
||||
|
||||
session_desktops = [
|
||||
session_desktop_base,
|
||||
session_desktop_base + '-xorg',
|
||||
session_desktop_base + '-wayland',
|
||||
]
|
||||
|
||||
foreach name: session_desktops
|
||||
session_desktop = name + '.desktop'
|
||||
if name.endswith('-xorg')
|
||||
session_instdir = xsessiondir
|
||||
elif name.endswith('-wayland')
|
||||
session_instdir = wlsessiondir
|
||||
else
|
||||
# FIXME: The same target can not be copied into two directories.
|
||||
# There is a workaround in meson/session-post-install.py until proper
|
||||
# solution arises:
|
||||
# https://github.com/mesonbuild/meson/issues/2416
|
||||
session_instdir = xsessiondir
|
||||
#session_instdir = [ xesssiondir, wlsessiondir ]
|
||||
endif
|
||||
i18n.merge_file(
|
||||
input: session_desktop + '.in',
|
||||
output: session_desktop,
|
||||
po_dir: '../po',
|
||||
install: true,
|
||||
install_dir: session_instdir,
|
||||
type: 'desktop'
|
||||
)
|
||||
endforeach
|
||||
|
||||
classic_uuids = []
|
||||
foreach e : classic_extensions
|
||||
classic_uuids += e + uuid_suffix
|
||||
endforeach
|
||||
|
||||
mode_conf = configuration_data()
|
||||
mode_conf.set('CLASSIC_EXTENSIONS', '"' + '", "'.join(classic_uuids) + '"')
|
||||
|
||||
mode_file = 'classic.json'
|
||||
configure_file(
|
||||
input: mode_file + '.in',
|
||||
output: mode_file,
|
||||
configuration: mode_conf,
|
||||
install_dir: modedir
|
||||
)
|
||||
|
||||
theme_sources = files(
|
||||
'gnome-shell-sass/_colors.scss',
|
||||
'gnome-shell-sass/_common.scss',
|
||||
'gnome-shell-sass/_drawing.scss',
|
||||
'gnome-shell-sass/_high-contrast-colors.scss',
|
||||
'gnome-shell-sass/_widgets.scss',
|
||||
'gnome-shell-sass/widgets/_a11y.scss',
|
||||
'gnome-shell-sass/widgets/_app-grid.scss',
|
||||
'gnome-shell-sass/widgets/_base.scss',
|
||||
'gnome-shell-sass/widgets/_buttons.scss',
|
||||
'gnome-shell-sass/widgets/_calendar.scss',
|
||||
'gnome-shell-sass/widgets/_check-box.scss',
|
||||
'gnome-shell-sass/widgets/_corner-ripple.scss',
|
||||
'gnome-shell-sass/widgets/_dash.scss',
|
||||
'gnome-shell-sass/widgets/_dialogs.scss',
|
||||
'gnome-shell-sass/widgets/_entries.scss',
|
||||
'gnome-shell-sass/widgets/_hotplug.scss',
|
||||
'gnome-shell-sass/widgets/_ibus-popup.scss',
|
||||
'gnome-shell-sass/widgets/_keyboard.scss',
|
||||
'gnome-shell-sass/widgets/_login-dialog.scss',
|
||||
'gnome-shell-sass/widgets/_looking-glass.scss',
|
||||
'gnome-shell-sass/widgets/_message-list.scss',
|
||||
'gnome-shell-sass/widgets/_misc.scss',
|
||||
'gnome-shell-sass/widgets/_network-dialog.scss',
|
||||
'gnome-shell-sass/widgets/_notifications.scss',
|
||||
'gnome-shell-sass/widgets/_osd.scss',
|
||||
'gnome-shell-sass/widgets/_overview.scss',
|
||||
'gnome-shell-sass/widgets/_panel.scss',
|
||||
'gnome-shell-sass/widgets/_popovers.scss',
|
||||
'gnome-shell-sass/widgets/_screen-shield.scss',
|
||||
'gnome-shell-sass/widgets/_scrollbars.scss',
|
||||
'gnome-shell-sass/widgets/_search-entry.scss',
|
||||
'gnome-shell-sass/widgets/_search-results.scss',
|
||||
'gnome-shell-sass/widgets/_slider.scss',
|
||||
'gnome-shell-sass/widgets/_switcher-popup.scss',
|
||||
'gnome-shell-sass/widgets/_switches.scss',
|
||||
'gnome-shell-sass/widgets/_window-picker.scss',
|
||||
'gnome-shell-sass/widgets/_workspace-switcher.scss',
|
||||
'gnome-shell-sass/widgets/_workspace-thumbnails.scss'
|
||||
)
|
||||
|
||||
theme_data = [
|
||||
'classic-process-working.svg',
|
||||
'gnome-classic-high-contrast.css'
|
||||
]
|
||||
|
||||
stylesheet = 'gnome-classic.css'
|
||||
if fs.exists(stylesheet)
|
||||
install_data(stylesheet, install_dir: themedir)
|
||||
else
|
||||
sassc = find_program('sassc', required: true)
|
||||
custom_target(stylesheet,
|
||||
input: fs.replace_suffix(stylesheet, '.scss'),
|
||||
output: stylesheet,
|
||||
depend_files: theme_sources,
|
||||
command: [sassc, '-a', '@INPUT@', '@OUTPUT@'],
|
||||
install: true,
|
||||
install_dir: themedir
|
||||
)
|
||||
endif
|
||||
|
||||
install_data(theme_data, install_dir: themedir)
|
||||
|
||||
classic_override = '00_org.gnome.shell.extensions.classic.gschema.override'
|
||||
install_data(classic_override, install_dir: schemadir)
|
||||
@@ -1,46 +0,0 @@
|
||||
<schemalist>
|
||||
<schema id="org.gnome.shell.extensions.classic-overrides"
|
||||
path="/org/gnome/shell/extensions/classic-overrides/"
|
||||
gettext-domain="gnome-shell-extensions">
|
||||
<key name="attach-modal-dialogs" type="b">
|
||||
<default>true</default>
|
||||
<summary>Attach modal dialog to the parent window</summary>
|
||||
<description>
|
||||
This key overrides the key in org.gnome.mutter when running
|
||||
GNOME Shell.
|
||||
</description>
|
||||
</key>
|
||||
|
||||
<key name="button-layout" type="s">
|
||||
<default>"appmenu:minimize,maximize,close"</default>
|
||||
<summary>Arrangement of buttons on the titlebar</summary>
|
||||
<description>
|
||||
This key overrides the key in org.gnome.desktop.wm.preferences when running GNOME Shell.
|
||||
</description>
|
||||
</key>
|
||||
|
||||
<key name="edge-tiling" type="b">
|
||||
<default>true</default>
|
||||
<summary>Enable edge tiling when dropping windows on screen edges</summary>
|
||||
<description>
|
||||
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||
</description>
|
||||
</key>
|
||||
|
||||
<key name="workspaces-only-on-primary" type="b">
|
||||
<default>true</default>
|
||||
<summary>Workspaces only on primary monitor</summary>
|
||||
<description>
|
||||
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||
</description>
|
||||
</key>
|
||||
|
||||
<key name="focus-change-on-pointer-rest" type="b">
|
||||
<default>true</default>
|
||||
<summary>Delay focus changes in mouse mode until the pointer stops moving</summary>
|
||||
<description>
|
||||
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||
</description>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|
||||
47
export-zips.sh
Executable file
47
export-zips.sh
Executable file
@@ -0,0 +1,47 @@
|
||||
#!/bin/sh
|
||||
|
||||
srcdir=`dirname $0`
|
||||
srcdir=`(cd $srcdir && pwd)`
|
||||
|
||||
builddir=`mktemp -p $srcdir -d _build.XXXXXX` || exit 1
|
||||
installdir=`mktemp -p $srcdir -d _install.XXXXXX` || exit 1
|
||||
|
||||
meson setup --prefix=$installdir -Dextension_set=all $srcdir $builddir
|
||||
meson install -C $builddir
|
||||
|
||||
rm -rf $srcdir/zip-files
|
||||
mkdir $srcdir/zip-files
|
||||
|
||||
extensiondir=$installdir/share/gnome-shell/extensions
|
||||
schemadir=$installdir/share/glib-2.0/schemas
|
||||
|
||||
for f in $extensiondir/*; do
|
||||
name=`basename ${f%%@*}`
|
||||
uuid=$name@gnome-shell-extensions.gcampax.github.com
|
||||
schema=$schemadir/org.gnome.shell.extensions.$name.gschema.xml
|
||||
|
||||
olddomain=gnome-shell-extensions
|
||||
newdomain=gnome-shell-extension-$name
|
||||
sed -i "/gettext-domain/ s:$olddomain:$newdomain:" $f/metadata.json
|
||||
|
||||
xgettext --from-code=UTF-8 --output-dir=$builddir --output=$name.pot $f/*.js
|
||||
|
||||
if [ -f $builddir/$name.pot ]; then
|
||||
mkdir $f/po
|
||||
for l in $(<$srcdir/po/LINGUAS); do
|
||||
msgmerge --quiet --output-file=$f/po/$l.po \
|
||||
$srcdir/po/$l.po $builddir/$name.pot
|
||||
done
|
||||
fi
|
||||
|
||||
cp $srcdir/NEWS $srcdir/COPYING $f
|
||||
sources=(NEWS COPYING $(cd $f; ls *.js))
|
||||
|
||||
[ -f $schema ] || unset schema
|
||||
|
||||
gnome-extensions pack ${sources[@]/#/--extra-source=} \
|
||||
${schema:+--schema=$schema} --out-dir=$srcdir/zip-files $f
|
||||
done
|
||||
|
||||
rm -rf $builddir
|
||||
rm -rf $installdir
|
||||
18
extension.mk
18
extension.mk
@@ -1,18 +0,0 @@
|
||||
include $(top_srcdir)/include.mk
|
||||
|
||||
dist_extension_DATA = extension.js stylesheet.css $(EXTRA_MODULES)
|
||||
nodist_extension_DATA = metadata.json $(top_srcdir)/lib/convenience.js $(EXTRA_EXTENSION)
|
||||
|
||||
EXTRA_DIST = metadata.json.in
|
||||
|
||||
metadata.json: metadata.json.in $(top_builddir)/config.status
|
||||
$(AM_V_GEN) sed \
|
||||
-e "s|[@]extension_id@|$(EXTENSION_ID)|" \
|
||||
-e "s|[@]uuid@|$(uuid)|" \
|
||||
-e "s|[@]gschemaname@|$(gschemaname)|" \
|
||||
-e "s|[@]gettext_domain@|$(GETTEXT_PACKAGE)|" \
|
||||
-e "s|[@]shell_current@|$(SHELL_VERSION)|" \
|
||||
-e "s|[@]url@|$(extensionurl)|" \
|
||||
$< > $@
|
||||
|
||||
CLEANFILES = metadata.json
|
||||
0
extensions/.lock
Normal file
0
extensions/.lock
Normal file
@@ -1,3 +0,0 @@
|
||||
DIST_SUBDIRS = $(ALL_EXTENSIONS)
|
||||
|
||||
SUBDIRS = $(ENABLED_EXTENSIONS)
|
||||
@@ -1,5 +0,0 @@
|
||||
EXTENSION_ID = alternate-tab
|
||||
|
||||
EXTRA_MODULES = prefs.js
|
||||
|
||||
include ../../extension.mk
|
||||
@@ -1,59 +0,0 @@
|
||||
/* -*- mode: js; js-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const AltTab = imports.ui.altTab;
|
||||
const Main = imports.ui.main;
|
||||
const WindowManager = imports.ui.windowManager;
|
||||
|
||||
let injections = {};
|
||||
|
||||
function init(metadata) {
|
||||
}
|
||||
|
||||
function setKeybinding(name, func) {
|
||||
Main.wm.setCustomKeybindingHandler(name, Shell.ActionMode.NORMAL, func);
|
||||
}
|
||||
|
||||
function enable() {
|
||||
injections['_keyPressHandler'] = AltTab.WindowSwitcherPopup.prototype._keyPressHandler;
|
||||
AltTab.WindowSwitcherPopup.prototype._keyPressHandler = function(keysym, action) {
|
||||
switch(action) {
|
||||
case Meta.KeyBindingAction.SWITCH_APPLICATIONS:
|
||||
action = Meta.KeyBindingAction.SWITCH_WINDOWS;
|
||||
break;
|
||||
case Meta.KeyBindingAction.SWITCH_APPLICATIONS_BACKWARD:
|
||||
action = Meta.KeyBindingAction.SWITCH_WINDOWS_BACKWARD;
|
||||
break;
|
||||
}
|
||||
return injections['_keyPressHandler'].call(this, keysym, action);
|
||||
};
|
||||
|
||||
Main.wm._forcedWindowSwitcher = function(display, screen, window, binding) {
|
||||
/* prevent a corner case where both popups show up at once */
|
||||
if (this._workspaceSwitcherPopup != null)
|
||||
this._workspaceSwitcherPopup.destroy();
|
||||
|
||||
let tabPopup = new AltTab.WindowSwitcherPopup();
|
||||
|
||||
if (!tabPopup.show(binding.is_reversed(), binding.get_name(), binding.get_mask()))
|
||||
tabPopup.destroy();
|
||||
};
|
||||
|
||||
setKeybinding('switch-applications', Lang.bind(Main.wm, Main.wm._forcedWindowSwitcher));
|
||||
setKeybinding('switch-applications-backward', Lang.bind(Main.wm, Main.wm._forcedWindowSwitcher));
|
||||
}
|
||||
|
||||
function disable() {
|
||||
var prop;
|
||||
|
||||
setKeybinding('switch-applications', Lang.bind(Main.wm, Main.wm._startSwitcher));
|
||||
setKeybinding('switch-applications-backward', Lang.bind(Main.wm, Main.wm._startSwitcher));
|
||||
|
||||
for (prop in injections)
|
||||
AltTab.WindowSwitcherPopup.prototype[prop] = injections[prop];
|
||||
delete Main.wm._forcedWindowSwitcher;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"extension-id": "@extension_id@",
|
||||
"uuid": "@uuid@",
|
||||
"settings-schema": "@gschemaname@",
|
||||
"gettext-domain": "@gettext_domain@",
|
||||
"name": "AlternateTab",
|
||||
"description": "Substitute Alt-Tab with a window based switcher that does not group by application.\nThis extension is part of Classic Mode and is officially supported by GNOME. Please do not report bugs using the form below, use GNOME Bugzilla instead.",
|
||||
"original-authors": [ "jw@bargsten.org", "thomas.bouffon@gmail.com" ],
|
||||
"shell-version": [ "@shell_current@" ],
|
||||
"url": "@url@"
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Lang = imports.lang;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell-extensions');
|
||||
const _ = Gettext.gettext;
|
||||
const N_ = function(e) { return e };
|
||||
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Me = ExtensionUtils.getCurrentExtension();
|
||||
const Convenience = Me.imports.convenience;
|
||||
|
||||
const SETTINGS_APP_ICON_MODE = 'app-icon-mode';
|
||||
const SETTINGS_CURRENT_WORKSPACE_ONLY = 'current-workspace-only';
|
||||
|
||||
const MODES = {
|
||||
'thumbnail-only': N_("Thumbnail only"),
|
||||
'app-icon-only': N_("Application icon only"),
|
||||
'both': N_("Thumbnail and application icon"),
|
||||
};
|
||||
|
||||
const AltTabSettingsWidget = new GObject.Class({
|
||||
Name: 'AlternateTab.Prefs.AltTabSettingsWidget',
|
||||
GTypeName: 'AltTabSettingsWidget',
|
||||
Extends: Gtk.Grid,
|
||||
|
||||
_init : function(params) {
|
||||
this.parent(params);
|
||||
this.margin = 24;
|
||||
this.row_spacing = 6;
|
||||
this.orientation = Gtk.Orientation.VERTICAL;
|
||||
|
||||
this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell.window-switcher' });
|
||||
|
||||
let presentLabel = '<b>' + _("Present windows as") + '</b>';
|
||||
this.add(new Gtk.Label({ label: presentLabel, use_markup: true,
|
||||
halign: Gtk.Align.START }));
|
||||
|
||||
let align = new Gtk.Alignment({ left_padding: 12 });
|
||||
this.add(align);
|
||||
|
||||
let grid = new Gtk.Grid({ orientation: Gtk.Orientation.VERTICAL,
|
||||
row_spacing: 6,
|
||||
column_spacing: 6 });
|
||||
align.add(grid);
|
||||
|
||||
let radio = null;
|
||||
let currentMode = this._settings.get_string(SETTINGS_APP_ICON_MODE);
|
||||
for (let mode in MODES) {
|
||||
// copy the mode variable because it has function scope, not block scope
|
||||
// so cannot be used in a closure
|
||||
let modeCapture = mode;
|
||||
let name = Gettext.gettext(MODES[mode]);
|
||||
|
||||
radio = new Gtk.RadioButton({ group: radio, label: name, valign: Gtk.Align.START });
|
||||
radio.connect('toggled', Lang.bind(this, function(widget) {
|
||||
if (widget.active)
|
||||
this._settings.set_string(SETTINGS_APP_ICON_MODE, modeCapture);
|
||||
}));
|
||||
grid.add(radio);
|
||||
|
||||
if (mode == currentMode)
|
||||
radio.active = true;
|
||||
}
|
||||
|
||||
let check = new Gtk.CheckButton({ label: _("Show only windows in the current workspace"),
|
||||
margin_top: 6 });
|
||||
this._settings.bind(SETTINGS_CURRENT_WORKSPACE_ONLY, check, 'active', Gio.SettingsBindFlags.DEFAULT);
|
||||
this.add(check);
|
||||
},
|
||||
});
|
||||
|
||||
function init() {
|
||||
Convenience.initTranslations();
|
||||
}
|
||||
|
||||
function buildPrefsWidget() {
|
||||
let widget = new AltTabSettingsWidget();
|
||||
widget.show_all();
|
||||
|
||||
return widget;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
/* This extensions requires no special styling */
|
||||
@@ -1,3 +0,0 @@
|
||||
EXTENSION_ID = apps-menu
|
||||
|
||||
include ../../extension.mk
|
||||
File diff suppressed because it is too large
Load Diff
7
extensions/apps-menu/meson.build
Normal file
7
extensions/apps-menu/meson.build
Normal file
@@ -0,0 +1,7 @@
|
||||
extension_data += configure_file(
|
||||
input: metadata_name + '.in',
|
||||
output: metadata_name,
|
||||
configuration: metadata_conf
|
||||
)
|
||||
extension_data += files('stylesheet.css')
|
||||
extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"extension-id": "@extension_id@",
|
||||
"uuid": "@uuid@",
|
||||
"settings-schema": "@gschemaname@",
|
||||
"gettext-domain": "@gettext_domain@",
|
||||
"name": "Applications Menu",
|
||||
"description": "Add a category-based menu for applications.\nThis extension is part of Classic Mode and is officially supported by GNOME. Please do not report bugs using the form below, use GNOME Bugzilla instead.",
|
||||
"description": "Add a category-based menu for applications.\nThis extension is part of Classic Mode and is officially supported by GNOME. Please do not report bugs using the form below, use GNOME's GitLab instance instead.",
|
||||
"original-authors": [ "e2002@bk.ru", "debarshir@gnome.org" ],
|
||||
"shell-version": [ "@shell_current@" ],
|
||||
"url": "@url@"
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<schemalist gettext-domain="gnome-shell-extensions">
|
||||
<schema id="org.gnome.shell.extensions.apps-menu"
|
||||
path="/org/gnome/shell/extensions/apps-menu/">
|
||||
<key name="apps-menu-toggle-menu" type="as">
|
||||
<default>["<Alt>F1"]</default>
|
||||
<summary>Keybinding to open the applications menu</summary>
|
||||
<description>
|
||||
Keybinding to open the applications menu.
|
||||
</description>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|
||||
@@ -1,7 +0,0 @@
|
||||
EXTENSION_ID = auto-move-windows
|
||||
|
||||
EXTRA_MODULES = prefs.js
|
||||
|
||||
include ../../extension.mk
|
||||
include ../../settings.mk
|
||||
|
||||
@@ -1,152 +1,142 @@
|
||||
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
|
||||
// Start apps on custom workspaces
|
||||
/* exported init enable disable */
|
||||
|
||||
const Glib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const { Shell } = imports.gi;
|
||||
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Me = ExtensionUtils.getCurrentExtension();
|
||||
const Convenience = Me.imports.convenience;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const SETTINGS_KEY = 'application-list';
|
||||
class WindowMover {
|
||||
constructor() {
|
||||
this._settings = ExtensionUtils.getSettings();
|
||||
this._appSystem = Shell.AppSystem.get_default();
|
||||
this._appConfigs = new Map();
|
||||
this._appData = new Map();
|
||||
|
||||
let settings;
|
||||
this._appsChangedId =
|
||||
this._appSystem.connect('installed-changed',
|
||||
this._updateAppData.bind(this));
|
||||
|
||||
const WindowMover = new Lang.Class({
|
||||
Name: 'AutoMoveWindows.WindowMover',
|
||||
|
||||
_init: function() {
|
||||
this._settings = settings;
|
||||
this._windowTracker = Shell.WindowTracker.get_default();
|
||||
|
||||
let display = global.screen.get_display();
|
||||
// Connect after so the handler from ShellWindowTracker has already run
|
||||
this._windowCreatedId = display.connect_after('window-created', Lang.bind(this, this._findAndMove));
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this._windowCreatedId) {
|
||||
global.screen.get_display().disconnect(this._windowCreatedId);
|
||||
this._windowCreatedId = 0;
|
||||
}
|
||||
},
|
||||
|
||||
_ensureAtLeastWorkspaces: function(num, window) {
|
||||
for (let j = global.screen.n_workspaces; j <= num; j++) {
|
||||
window.change_workspace_by_index(j-1, false);
|
||||
global.screen.append_new_workspace(false, 0);
|
||||
}
|
||||
},
|
||||
|
||||
_findAndMove: function(display, window, noRecurse) {
|
||||
if (window.skip_taskbar)
|
||||
return;
|
||||
|
||||
let spaces = this._settings.get_strv(SETTINGS_KEY);
|
||||
|
||||
let app = this._windowTracker.get_window_app(window);
|
||||
if (!app) {
|
||||
if (!noRecurse) {
|
||||
// window is not tracked yet
|
||||
Mainloop.idle_add(Lang.bind(this, function() {
|
||||
this._findAndMove(display, window, true);
|
||||
return false;
|
||||
}));
|
||||
} else
|
||||
log ('Cannot find application for window');
|
||||
return;
|
||||
}
|
||||
let app_id = app.get_id();
|
||||
for ( let j = 0 ; j < spaces.length; j++ ) {
|
||||
let apps_to_space = spaces[j].split(":");
|
||||
// Match application id
|
||||
if (apps_to_space[0] == app_id) {
|
||||
let workspace_num = parseInt(apps_to_space[1]) - 1;
|
||||
|
||||
if (workspace_num >= global.screen.n_workspaces)
|
||||
this._ensureAtLeastWorkspaces(workspace_num, window);
|
||||
|
||||
window.change_workspace_by_index(workspace_num, false);
|
||||
}
|
||||
}
|
||||
this._settings.connect('changed', this._updateAppConfigs.bind(this));
|
||||
this._updateAppConfigs();
|
||||
}
|
||||
});
|
||||
|
||||
_updateAppConfigs() {
|
||||
this._appConfigs.clear();
|
||||
|
||||
this._settings.get_strv('application-list').forEach(v => {
|
||||
let [appId, num] = v.split(':');
|
||||
this._appConfigs.set(appId, parseInt(num) - 1);
|
||||
});
|
||||
|
||||
this._updateAppData();
|
||||
}
|
||||
|
||||
_updateAppData() {
|
||||
let ids = [...this._appConfigs.keys()];
|
||||
let removedApps = [...this._appData.keys()]
|
||||
.filter(a => !ids.includes(a.id));
|
||||
removedApps.forEach(app => {
|
||||
app.disconnect(this._appData.get(app).windowsChangedId);
|
||||
this._appData.delete(app);
|
||||
});
|
||||
|
||||
let addedApps = ids
|
||||
.map(id => this._appSystem.lookup_app(id))
|
||||
.filter(app => app && !this._appData.has(app));
|
||||
addedApps.forEach(app => {
|
||||
let data = {
|
||||
windowsChangedId: app.connect('windows-changed',
|
||||
this._appWindowsChanged.bind(this)),
|
||||
moveWindowsId: 0,
|
||||
windows: app.get_windows(),
|
||||
};
|
||||
this._appData.set(app, data);
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this._appsChangedId) {
|
||||
this._appSystem.disconnect(this._appsChangedId);
|
||||
this._appsChangedId = 0;
|
||||
}
|
||||
|
||||
if (this._settings) {
|
||||
this._settings.run_dispose();
|
||||
this._settings = null;
|
||||
}
|
||||
|
||||
this._appConfigs.clear();
|
||||
this._updateAppData();
|
||||
}
|
||||
|
||||
_moveWindow(window, workspaceNum) {
|
||||
if (window.skip_taskbar || window.is_on_all_workspaces())
|
||||
return;
|
||||
|
||||
// ensure we have the required number of workspaces
|
||||
let workspaceManager = global.workspace_manager;
|
||||
for (let i = workspaceManager.n_workspaces; i <= workspaceNum; i++) {
|
||||
window.change_workspace_by_index(i - 1, false);
|
||||
workspaceManager.append_new_workspace(false, 0);
|
||||
}
|
||||
|
||||
window.change_workspace_by_index(workspaceNum, false);
|
||||
}
|
||||
|
||||
_appWindowsChanged(app) {
|
||||
let data = this._appData.get(app);
|
||||
let windows = app.get_windows();
|
||||
|
||||
// If get_compositor_private() returns non-NULL on a removed windows,
|
||||
// the window still exists and is just moved to a different workspace
|
||||
// or something; assume it'll be added back immediately, so keep it
|
||||
// to avoid moving it again
|
||||
windows.push(...data.windows.filter(w => {
|
||||
return !windows.includes(w) && w.get_compositor_private() !== null;
|
||||
}));
|
||||
|
||||
let workspaceNum = this._appConfigs.get(app.id);
|
||||
windows.filter(w => !data.windows.includes(w)).forEach(window => {
|
||||
this._moveWindow(window, workspaceNum);
|
||||
});
|
||||
data.windows = windows;
|
||||
}
|
||||
}
|
||||
|
||||
let prevCheckWorkspaces;
|
||||
let winMover;
|
||||
|
||||
/** */
|
||||
function init() {
|
||||
Convenience.initTranslations();
|
||||
settings = Convenience.getSettings();
|
||||
ExtensionUtils.initTranslations();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {bool} - false (used as MetaLater handler)
|
||||
*/
|
||||
function myCheckWorkspaces() {
|
||||
let i;
|
||||
let emptyWorkspaces = new Array(this._workspaces.length);
|
||||
|
||||
if (!Meta.prefs_get_dynamic_workspaces()) {
|
||||
this._checkWorkspacesId = 0;
|
||||
return false;
|
||||
let keepAliveWorkspaces = [];
|
||||
let foundNonEmpty = false;
|
||||
for (let i = this._workspaces.length - 1; i >= 0; i--) {
|
||||
if (!foundNonEmpty) {
|
||||
foundNonEmpty = this._workspaces[i].list_windows().some(
|
||||
w => !w.is_on_all_workspaces());
|
||||
} else if (!this._workspaces[i]._keepAliveId) {
|
||||
keepAliveWorkspaces.push(this._workspaces[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < this._workspaces.length; i++) {
|
||||
let lastRemoved = this._workspaces[i]._lastRemovedWindow;
|
||||
if ((lastRemoved &&
|
||||
(lastRemoved.get_window_type() == Meta.WindowType.SPLASHSCREEN ||
|
||||
lastRemoved.get_window_type() == Meta.WindowType.DIALOG ||
|
||||
lastRemoved.get_window_type() == Meta.WindowType.MODAL_DIALOG)) ||
|
||||
this._workspaces[i]._keepAliveId)
|
||||
emptyWorkspaces[i] = false;
|
||||
else
|
||||
emptyWorkspaces[i] = true;
|
||||
}
|
||||
// make sure the original method only removes empty workspaces at the end
|
||||
keepAliveWorkspaces.forEach(ws => (ws._keepAliveId = 1));
|
||||
prevCheckWorkspaces.call(this);
|
||||
keepAliveWorkspaces.forEach(ws => delete ws._keepAliveId);
|
||||
|
||||
let sequences = Shell.WindowTracker.get_default().get_startup_sequences();
|
||||
for (i = 0; i < sequences.length; i++) {
|
||||
let index = sequences[i].get_workspace();
|
||||
if (index >= 0 && index <= global.screen.n_workspaces)
|
||||
emptyWorkspaces[index] = false;
|
||||
}
|
||||
|
||||
let windows = global.get_window_actors();
|
||||
for (i = 0; i < windows.length; i++) {
|
||||
let winActor = windows[i];
|
||||
let win = winActor.meta_window;
|
||||
if (win.is_on_all_workspaces())
|
||||
continue;
|
||||
|
||||
let workspaceIndex = win.get_workspace().index();
|
||||
emptyWorkspaces[workspaceIndex] = false;
|
||||
}
|
||||
|
||||
// If we don't have an empty workspace at the end, add one
|
||||
if (!emptyWorkspaces[emptyWorkspaces.length -1]) {
|
||||
global.screen.append_new_workspace(false, global.get_current_time());
|
||||
emptyWorkspaces.push(false);
|
||||
}
|
||||
|
||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||
emptyWorkspaces[activeWorkspaceIndex] = false;
|
||||
|
||||
// Delete other empty workspaces; do it from the end to avoid index changes
|
||||
for (i = emptyWorkspaces.length - 2; i >= 0; i--) {
|
||||
if (emptyWorkspaces[i])
|
||||
global.screen.remove_workspace(this._workspaces[i], global.get_current_time());
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
this._checkWorkspacesId = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/** */
|
||||
function enable() {
|
||||
prevCheckWorkspaces = Main.wm._workspaceTracker._checkWorkspaces;
|
||||
Main.wm._workspaceTracker._checkWorkspaces = myCheckWorkspaces;
|
||||
@@ -154,6 +144,7 @@ function enable() {
|
||||
winMover = new WindowMover();
|
||||
}
|
||||
|
||||
/** */
|
||||
function disable() {
|
||||
Main.wm._workspaceTracker._checkWorkspaces = prevCheckWorkspaces;
|
||||
winMover.destroy();
|
||||
|
||||
8
extensions/auto-move-windows/meson.build
Normal file
8
extensions/auto-move-windows/meson.build
Normal file
@@ -0,0 +1,8 @@
|
||||
extension_data += configure_file(
|
||||
input: metadata_name + '.in',
|
||||
output: metadata_name,
|
||||
configuration: metadata_conf
|
||||
)
|
||||
|
||||
extension_sources += files('prefs.js')
|
||||
extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
|
||||
@@ -1,274 +1,351 @@
|
||||
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
|
||||
// Start apps on custom workspaces
|
||||
/* exported init buildPrefsWidget */
|
||||
|
||||
const GdkPixbuf = imports.gi.GdkPixbuf;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const GObject = imports.gi.GObject;
|
||||
const GMenu = imports.gi.GMenu;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell-extensions');
|
||||
const _ = Gettext.gettext;
|
||||
const N_ = function(e) { return e };
|
||||
const { Adw, Gio, GLib, GObject, Gtk } = imports.gi;
|
||||
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Me = ExtensionUtils.getCurrentExtension();
|
||||
const Convenience = Me.imports.convenience;
|
||||
|
||||
const _ = ExtensionUtils.gettext;
|
||||
|
||||
const SETTINGS_KEY = 'application-list';
|
||||
|
||||
const WORKSPACE_MAX = 36; // compiled in limit of mutter
|
||||
|
||||
const Columns = {
|
||||
APPINFO: 0,
|
||||
DISPLAY_NAME: 1,
|
||||
ICON: 2,
|
||||
WORKSPACE: 3,
|
||||
ADJUSTMENT: 4
|
||||
};
|
||||
class NewItem extends GObject.Object {}
|
||||
GObject.registerClass(NewItem);
|
||||
|
||||
const Widget = new GObject.Class({
|
||||
Name: 'AutoMoveWindows.Prefs.Widget',
|
||||
GTypeName: 'AutoMoveWindowsPrefsWidget',
|
||||
Extends: Gtk.Grid,
|
||||
|
||||
_init: function(params) {
|
||||
this.parent(params);
|
||||
this.set_orientation(Gtk.Orientation.VERTICAL);
|
||||
|
||||
this._settings = Convenience.getSettings();
|
||||
this._settings.connect('changed', Lang.bind(this, this._refresh));
|
||||
this._changedPermitted = false;
|
||||
|
||||
this._store = new Gtk.ListStore();
|
||||
this._store.set_column_types([Gio.AppInfo, GObject.TYPE_STRING, Gio.Icon, GObject.TYPE_INT,
|
||||
Gtk.Adjustment]);
|
||||
|
||||
let scrolled = new Gtk.ScrolledWindow({ shadow_type: Gtk.ShadowType.IN});
|
||||
scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
this.add(scrolled);
|
||||
|
||||
|
||||
this._treeView = new Gtk.TreeView({ model: this._store,
|
||||
hexpand: true, vexpand: true });
|
||||
this._treeView.get_selection().set_mode(Gtk.SelectionMode.SINGLE);
|
||||
|
||||
let appColumn = new Gtk.TreeViewColumn({ expand: true, sort_column_id: Columns.DISPLAY_NAME,
|
||||
title: _("Application") });
|
||||
let iconRenderer = new Gtk.CellRendererPixbuf;
|
||||
appColumn.pack_start(iconRenderer, false);
|
||||
appColumn.add_attribute(iconRenderer, "gicon", Columns.ICON);
|
||||
let nameRenderer = new Gtk.CellRendererText;
|
||||
appColumn.pack_start(nameRenderer, true);
|
||||
appColumn.add_attribute(nameRenderer, "text", Columns.DISPLAY_NAME);
|
||||
this._treeView.append_column(appColumn);
|
||||
|
||||
let workspaceColumn = new Gtk.TreeViewColumn({ title: _("Workspace"),
|
||||
sort_column_id: Columns.WORKSPACE });
|
||||
let workspaceRenderer = new Gtk.CellRendererSpin({ editable: true });
|
||||
workspaceRenderer.connect('edited', Lang.bind(this, this._workspaceEdited));
|
||||
workspaceColumn.pack_start(workspaceRenderer, true);
|
||||
workspaceColumn.add_attribute(workspaceRenderer, "adjustment", Columns.ADJUSTMENT);
|
||||
workspaceColumn.add_attribute(workspaceRenderer, "text", Columns.WORKSPACE);
|
||||
this._treeView.append_column(workspaceColumn);
|
||||
|
||||
scrolled.add(this._treeView);
|
||||
|
||||
let toolbar = new Gtk.Toolbar({ icon_size: Gtk.IconSize.SMALL_TOOLBAR });
|
||||
toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_INLINE_TOOLBAR);
|
||||
this.add(toolbar);
|
||||
|
||||
let newButton = new Gtk.ToolButton({ icon_name: 'bookmark-new-symbolic',
|
||||
label: _("Add Rule"),
|
||||
is_important: true });
|
||||
newButton.connect('clicked', Lang.bind(this, this._createNew));
|
||||
toolbar.add(newButton);
|
||||
|
||||
let delButton = new Gtk.ToolButton({ icon_name: 'edit-delete-symbolic' });
|
||||
delButton.connect('clicked', Lang.bind(this, this._deleteSelected));
|
||||
toolbar.add(delButton);
|
||||
|
||||
let selection = this._treeView.get_selection();
|
||||
selection.connect('changed',
|
||||
function() {
|
||||
delButton.sensitive = selection.count_selected_rows() > 0;
|
||||
});
|
||||
delButton.sensitive = selection.count_selected_rows() > 0;
|
||||
|
||||
this._changedPermitted = true;
|
||||
this._refresh();
|
||||
},
|
||||
|
||||
_createNew: function() {
|
||||
let dialog = new Gtk.Dialog({ title: _("Create new matching rule"),
|
||||
transient_for: this.get_toplevel(),
|
||||
use_header_bar: true,
|
||||
modal: true });
|
||||
dialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL);
|
||||
let addButton = dialog.add_button(_("Add"), Gtk.ResponseType.OK);
|
||||
dialog.set_default_response(Gtk.ResponseType.OK);
|
||||
|
||||
let grid = new Gtk.Grid({ column_spacing: 10,
|
||||
row_spacing: 15,
|
||||
margin: 10 });
|
||||
dialog._appChooser = new Gtk.AppChooserWidget({ show_all: true });
|
||||
dialog._appChooser.connect('application-selected', Lang.bind(this,
|
||||
function(w, appInfo) {
|
||||
addButton.sensitive = appInfo &&
|
||||
this._checkId(appInfo.get_id());
|
||||
}));
|
||||
let appInfo = dialog._appChooser.get_app_info();
|
||||
addButton.sensitive = appInfo && this._checkId(appInfo.get_id());
|
||||
|
||||
grid.attach(dialog._appChooser, 0, 0, 2, 1);
|
||||
grid.attach(new Gtk.Label({ label: _("Workspace"),
|
||||
halign: Gtk.Align.END }), 0, 1, 1, 1);
|
||||
let adjustment = new Gtk.Adjustment({ lower: 1,
|
||||
upper: WORKSPACE_MAX,
|
||||
step_increment: 1
|
||||
});
|
||||
dialog._spin = new Gtk.SpinButton({ adjustment: adjustment,
|
||||
snap_to_ticks: true });
|
||||
dialog._spin.set_value(1);
|
||||
grid.attach(dialog._spin, 1, 1, 1, 1);
|
||||
dialog.get_content_area().add(grid);
|
||||
|
||||
dialog.connect('response', Lang.bind(this, function(dialog, id) {
|
||||
if (id != Gtk.ResponseType.OK) {
|
||||
dialog.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
let appInfo = dialog._appChooser.get_app_info();
|
||||
if (!appInfo)
|
||||
return;
|
||||
let index = Math.floor(dialog._spin.value);
|
||||
if (isNaN(index) || index < 0)
|
||||
index = 1;
|
||||
|
||||
this._changedPermitted = false;
|
||||
this._appendItem(appInfo.get_id(), index);
|
||||
this._changedPermitted = true;
|
||||
|
||||
let iter = this._store.append();
|
||||
let adj = new Gtk.Adjustment({ lower: 1,
|
||||
upper: WORKSPACE_MAX,
|
||||
step_increment: 1,
|
||||
value: index });
|
||||
this._store.set(iter,
|
||||
[Columns.APPINFO, Columns.ICON, Columns.DISPLAY_NAME, Columns.WORKSPACE, Columns.ADJUSTMENT],
|
||||
[appInfo, appInfo.get_icon(), appInfo.get_display_name(), index, adj]);
|
||||
|
||||
dialog.destroy();
|
||||
}));
|
||||
dialog.show_all();
|
||||
},
|
||||
|
||||
_deleteSelected: function() {
|
||||
let [any, model, iter] = this._treeView.get_selection().get_selected();
|
||||
|
||||
if (any) {
|
||||
let appInfo = this._store.get_value(iter, Columns.APPINFO);
|
||||
|
||||
this._changedPermitted = false;
|
||||
this._removeItem(appInfo.get_id());
|
||||
this._changedPermitted = true;
|
||||
this._store.remove(iter);
|
||||
}
|
||||
},
|
||||
|
||||
_workspaceEdited: function(renderer, pathString, text) {
|
||||
let index = parseInt(text);
|
||||
if (isNaN(index) || index < 0)
|
||||
index = 1;
|
||||
let path = Gtk.TreePath.new_from_string(pathString);
|
||||
let [model, iter] = this._store.get_iter(path);
|
||||
let appInfo = this._store.get_value(iter, Columns.APPINFO);
|
||||
|
||||
this._changedPermitted = false;
|
||||
this._changeItem(appInfo.get_id(), index);
|
||||
this._store.set_value(iter, Columns.WORKSPACE, index);
|
||||
this._changedPermitted = true;
|
||||
},
|
||||
|
||||
_refresh: function() {
|
||||
if (!this._changedPermitted)
|
||||
// Ignore this notification, model is being modified outside
|
||||
return;
|
||||
|
||||
this._store.clear();
|
||||
|
||||
let currentItems = this._settings.get_strv(SETTINGS_KEY);
|
||||
let validItems = [ ];
|
||||
for (let i = 0; i < currentItems.length; i++) {
|
||||
let [id, index] = currentItems[i].split(':');
|
||||
let appInfo = Gio.DesktopAppInfo.new(id);
|
||||
if (!appInfo)
|
||||
continue;
|
||||
validItems.push(currentItems[i]);
|
||||
|
||||
let iter = this._store.append();
|
||||
let adj = new Gtk.Adjustment({ lower: 1,
|
||||
upper: WORKSPACE_MAX,
|
||||
step_increment: 1,
|
||||
value: index });
|
||||
this._store.set(iter,
|
||||
[Columns.APPINFO, Columns.ICON, Columns.DISPLAY_NAME, Columns.WORKSPACE, Columns.ADJUSTMENT],
|
||||
[appInfo, appInfo.get_icon(), appInfo.get_display_name(), parseInt(index), adj]);
|
||||
}
|
||||
|
||||
if (validItems.length != currentItems.length) // some items were filtered out
|
||||
this._settings.set_strv(SETTINGS_KEY, validItems);
|
||||
},
|
||||
|
||||
_checkId: function(id) {
|
||||
let items = this._settings.get_strv(SETTINGS_KEY);
|
||||
return !items.some(function(i) { return i.startsWith(id + ':'); });
|
||||
},
|
||||
|
||||
_appendItem: function(id, workspace) {
|
||||
let currentItems = this._settings.get_strv(SETTINGS_KEY);
|
||||
currentItems.push(id + ':' + workspace);
|
||||
this._settings.set_strv(SETTINGS_KEY, currentItems);
|
||||
},
|
||||
|
||||
_removeItem: function(id) {
|
||||
let currentItems = this._settings.get_strv(SETTINGS_KEY);
|
||||
let index = currentItems.map(function(el) {
|
||||
return el.split(':')[0];
|
||||
}).indexOf(id);
|
||||
|
||||
if (index < 0)
|
||||
return;
|
||||
currentItems.splice(index, 1);
|
||||
this._settings.set_strv(SETTINGS_KEY, currentItems);
|
||||
},
|
||||
|
||||
_changeItem: function(id, workspace) {
|
||||
let currentItems = this._settings.get_strv(SETTINGS_KEY);
|
||||
let index = currentItems.map(function(el) {
|
||||
return el.split(':')[0];
|
||||
}).indexOf(id);
|
||||
|
||||
if (index < 0)
|
||||
currentItems.push(id + ':' + workspace);
|
||||
else
|
||||
currentItems[index] = id + ':' + workspace;
|
||||
this._settings.set_strv(SETTINGS_KEY, currentItems);
|
||||
class NewItemModel extends GObject.Object {
|
||||
static [GObject.interfaces] = [Gio.ListModel];
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
});
|
||||
|
||||
#item = new NewItem();
|
||||
|
||||
vfunc_get_item_type() {
|
||||
return NewItem;
|
||||
}
|
||||
|
||||
vfunc_get_n_items() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
vfunc_get_item(_pos) {
|
||||
return this.#item;
|
||||
}
|
||||
}
|
||||
|
||||
class Rule extends GObject.Object {
|
||||
static [GObject.properties] = {
|
||||
'app-info': GObject.ParamSpec.object(
|
||||
'app-info', 'app-info', 'app-info',
|
||||
GObject.ParamFlags.READWRITE,
|
||||
Gio.DesktopAppInfo),
|
||||
'workspace': GObject.ParamSpec.uint(
|
||||
'workspace', 'workspace', 'workspace',
|
||||
GObject.ParamFlags.READWRITE,
|
||||
1, WORKSPACE_MAX, 1),
|
||||
};
|
||||
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
}
|
||||
|
||||
class RulesList extends GObject.Object {
|
||||
static [GObject.interfaces] = [Gio.ListModel];
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
|
||||
#settings = ExtensionUtils.getSettings();
|
||||
#rules = [];
|
||||
#changedId;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.#changedId =
|
||||
this.#settings.connect(`changed::${SETTINGS_KEY}`,
|
||||
() => this.#sync());
|
||||
this.#sync();
|
||||
}
|
||||
|
||||
append(appInfo) {
|
||||
const pos = this.#rules.length;
|
||||
|
||||
this.#rules.push(new Rule({ appInfo }));
|
||||
this.#saveRules();
|
||||
|
||||
this.items_changed(pos, 0, 1);
|
||||
}
|
||||
|
||||
remove(id) {
|
||||
const pos = this.#rules.findIndex(r => r.appInfo.get_id() === id);
|
||||
if (pos < 0)
|
||||
return;
|
||||
|
||||
this.#rules.splice(pos, 1);
|
||||
this.#saveRules();
|
||||
|
||||
this.items_changed(pos, 1, 0);
|
||||
}
|
||||
|
||||
changeWorkspace(id, workspace) {
|
||||
const pos = this.#rules.findIndex(r => r.appInfo.get_id() === id);
|
||||
if (pos < 0)
|
||||
return;
|
||||
|
||||
this.#rules[pos].set({ workspace });
|
||||
this.#saveRules();
|
||||
}
|
||||
|
||||
#saveRules() {
|
||||
this.#settings.block_signal_handler(this.#changedId);
|
||||
this.#settings.set_strv(SETTINGS_KEY,
|
||||
this.#rules.map(r => `${r.app_info.get_id()}:${r.workspace}`));
|
||||
this.#settings.unblock_signal_handler(this.#changedId);
|
||||
}
|
||||
|
||||
#sync() {
|
||||
const removed = this.#rules.length;
|
||||
|
||||
this.#rules = [];
|
||||
for (const stringRule of this.#settings.get_strv(SETTINGS_KEY)) {
|
||||
const [id, workspace] = stringRule.split(':');
|
||||
const appInfo = Gio.DesktopAppInfo.new(id);
|
||||
if (appInfo)
|
||||
this.#rules.push(new Rule({ appInfo, workspace }));
|
||||
else
|
||||
log(`Invalid ID ${id}`);
|
||||
}
|
||||
this.items_changed(0, removed, this.#rules.length);
|
||||
}
|
||||
|
||||
vfunc_get_item_type() {
|
||||
return Rule;
|
||||
}
|
||||
|
||||
vfunc_get_n_items() {
|
||||
return this.#rules.length;
|
||||
}
|
||||
|
||||
vfunc_get_item(pos) {
|
||||
return this.#rules[pos] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
class AutoMoveSettingsWidget extends Adw.PreferencesGroup {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
|
||||
this.install_action('rules.add', null, self => self._addNewRule());
|
||||
this.install_action('rules.remove', 's',
|
||||
(self, name, param) => self._rules.remove(param.unpack()));
|
||||
this.install_action('rules.change-workspace', '(si)',
|
||||
(self, name, param) => self._rules.changeWorkspace(...param.deepUnpack()));
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
title: _('Workspace Rules'),
|
||||
});
|
||||
|
||||
this._rules = new RulesList();
|
||||
|
||||
const store = new Gio.ListStore({ item_type: Gio.ListModel });
|
||||
const listModel = new Gtk.FlattenListModel({ model: store });
|
||||
store.append(this._rules);
|
||||
store.append(new NewItemModel());
|
||||
|
||||
this._list = new Gtk.ListBox({
|
||||
selection_mode: Gtk.SelectionMode.NONE,
|
||||
css_classes: ['boxed-list'],
|
||||
});
|
||||
this.add(this._list);
|
||||
|
||||
this._list.bind_model(listModel, item => {
|
||||
return item instanceof NewItem
|
||||
? new NewRuleRow()
|
||||
: new RuleRow(item);
|
||||
});
|
||||
}
|
||||
|
||||
_addNewRule() {
|
||||
const dialog = new NewRuleDialog(this.get_root());
|
||||
dialog.connect('response', (dlg, id) => {
|
||||
const appInfo = id === Gtk.ResponseType.OK
|
||||
? dialog.get_widget().get_app_info() : null;
|
||||
if (appInfo)
|
||||
this._rules.append(appInfo);
|
||||
dialog.destroy();
|
||||
});
|
||||
dialog.show();
|
||||
}
|
||||
}
|
||||
|
||||
class WorkspaceSelector extends Gtk.Widget {
|
||||
static [GObject.properties] = {
|
||||
'number': GObject.ParamSpec.uint(
|
||||
'number', 'number', 'number',
|
||||
GObject.ParamFlags.READWRITE,
|
||||
1, WORKSPACE_MAX, 1),
|
||||
};
|
||||
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
|
||||
this.set_layout_manager_type(Gtk.BoxLayout);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.layout_manager.spacing = 6;
|
||||
|
||||
const label = new Gtk.Label({
|
||||
xalign: 1,
|
||||
margin_end: 6,
|
||||
});
|
||||
this.bind_property('number',
|
||||
label, 'label',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
label.set_parent(this);
|
||||
|
||||
const buttonProps = {
|
||||
css_classes: ['circular'],
|
||||
valign: Gtk.Align.CENTER,
|
||||
};
|
||||
|
||||
this._decButton = new Gtk.Button({
|
||||
icon_name: 'list-remove-symbolic',
|
||||
...buttonProps,
|
||||
});
|
||||
this._decButton.set_parent(this);
|
||||
this._decButton.connect('clicked', () => this.number--);
|
||||
|
||||
this._incButton = new Gtk.Button({
|
||||
icon_name: 'list-add-symbolic',
|
||||
...buttonProps,
|
||||
});
|
||||
this._incButton.set_parent(this);
|
||||
this._incButton.connect('clicked', () => this.number++);
|
||||
|
||||
this.connect('notify::number', () => this._syncButtons());
|
||||
this._syncButtons();
|
||||
}
|
||||
|
||||
_syncButtons() {
|
||||
this._decButton.sensitive = this.number > 1;
|
||||
this._incButton.sensitive = this.number < WORKSPACE_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
class RuleRow extends Adw.ActionRow {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
|
||||
constructor(rule) {
|
||||
const { appInfo } = rule;
|
||||
const id = appInfo.get_id();
|
||||
|
||||
super({
|
||||
activatable: false,
|
||||
title: rule.appInfo.get_display_name(),
|
||||
});
|
||||
|
||||
const icon = new Gtk.Image({
|
||||
css_classes: ['icon-dropshadow'],
|
||||
gicon: appInfo.get_icon(),
|
||||
pixel_size: 32,
|
||||
});
|
||||
this.add_prefix(icon);
|
||||
|
||||
const wsButton = new WorkspaceSelector();
|
||||
rule.bind_property('workspace',
|
||||
wsButton, 'number',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
this.add_suffix(wsButton);
|
||||
|
||||
wsButton.connect('notify::number', () => {
|
||||
this.activate_action('rules.change-workspace',
|
||||
new GLib.Variant('(si)', [id, wsButton.number]));
|
||||
});
|
||||
|
||||
const button = new Gtk.Button({
|
||||
action_name: 'rules.remove',
|
||||
action_target: new GLib.Variant('s', id),
|
||||
icon_name: 'edit-delete-symbolic',
|
||||
has_frame: false,
|
||||
valign: Gtk.Align.CENTER,
|
||||
});
|
||||
this.add_suffix(button);
|
||||
}
|
||||
}
|
||||
|
||||
class NewRuleRow extends Gtk.ListBoxRow {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
action_name: 'rules.add',
|
||||
child: new Gtk.Image({
|
||||
icon_name: 'list-add-symbolic',
|
||||
pixel_size: 16,
|
||||
margin_top: 12,
|
||||
margin_bottom: 12,
|
||||
margin_start: 12,
|
||||
margin_end: 12,
|
||||
}),
|
||||
});
|
||||
this.update_property(
|
||||
[Gtk.AccessibleProperty.LABEL], [_('Add Rule')]);
|
||||
}
|
||||
}
|
||||
|
||||
class NewRuleDialog extends Gtk.AppChooserDialog {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
|
||||
constructor(parent) {
|
||||
super({
|
||||
transient_for: parent,
|
||||
modal: true,
|
||||
});
|
||||
|
||||
this._settings = ExtensionUtils.getSettings();
|
||||
|
||||
this.get_widget().set({
|
||||
show_all: true,
|
||||
show_other: true, // hide more button
|
||||
});
|
||||
|
||||
this.get_widget().connect('application-selected',
|
||||
this._updateSensitivity.bind(this));
|
||||
this._updateSensitivity();
|
||||
}
|
||||
|
||||
_updateSensitivity() {
|
||||
const rules = this._settings.get_strv(SETTINGS_KEY);
|
||||
const appInfo = this.get_widget().get_app_info();
|
||||
this.set_response_sensitive(Gtk.ResponseType.OK,
|
||||
appInfo && !rules.some(i => i.startsWith(appInfo.get_id())));
|
||||
}
|
||||
}
|
||||
|
||||
/** */
|
||||
function init() {
|
||||
Convenience.initTranslations();
|
||||
ExtensionUtils.initTranslations();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Gtk.Widget} - the prefs widget
|
||||
*/
|
||||
function buildPrefsWidget() {
|
||||
let widget = new Widget({ margin: 12 });
|
||||
widget.show_all();
|
||||
|
||||
return widget;
|
||||
return new AutoMoveSettingsWidget();
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
/* This extensions requires no special styling */
|
||||
@@ -1,3 +0,0 @@
|
||||
EXTENSION_ID = drive-menu
|
||||
|
||||
include ../../extension.mk
|
||||
@@ -1,56 +1,66 @@
|
||||
/* exported init enable disable */
|
||||
// Drive menu extension
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell-extensions');
|
||||
const _ = Gettext.gettext;
|
||||
const { Clutter, Gio, GObject, Shell, St } = imports.gi;
|
||||
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Main = imports.ui.main;
|
||||
const Panel = imports.ui.panel;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Me = ExtensionUtils.getCurrentExtension();
|
||||
const Convenience = Me.imports.convenience;
|
||||
const _ = ExtensionUtils.gettext;
|
||||
|
||||
const MountMenuItem = new Lang.Class({
|
||||
Name: 'DriveMenu.MountMenuItem',
|
||||
Extends: PopupMenu.PopupBaseMenuItem,
|
||||
Gio._promisify(Gio.File.prototype, 'query_filesystem_info_async');
|
||||
|
||||
_init: function(mount) {
|
||||
this.parent();
|
||||
class MountMenuItem extends PopupMenu.PopupBaseMenuItem {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
|
||||
this.label = new St.Label({ text: mount.get_name() });
|
||||
this.actor.add(this.label, { expand: true });
|
||||
this.actor.label_actor = this.label;
|
||||
constructor(mount) {
|
||||
super({
|
||||
style_class: 'drive-menu-item',
|
||||
});
|
||||
|
||||
this.mount = mount;
|
||||
this.label = new St.Label({
|
||||
text: mount.get_name(),
|
||||
x_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
});
|
||||
this.add_child(this.label);
|
||||
this.label_actor = this.label;
|
||||
|
||||
let ejectIcon = new St.Icon({ icon_name: 'media-eject-symbolic',
|
||||
style_class: 'popup-menu-icon ' });
|
||||
let ejectButton = new St.Button({ child: ejectIcon });
|
||||
ejectButton.connect('clicked', Lang.bind(this, this._eject));
|
||||
this.actor.add(ejectButton);
|
||||
this.mount = mount;
|
||||
|
||||
this._changedId = mount.connect('changed', Lang.bind(this, this._syncVisibility));
|
||||
this.connect('destroy', this._onDestroy.bind(this));
|
||||
|
||||
let ejectIcon = new St.Icon({
|
||||
icon_name: 'media-eject-symbolic',
|
||||
style_class: 'popup-menu-icon',
|
||||
});
|
||||
let ejectButton = new St.Button({
|
||||
child: ejectIcon,
|
||||
style_class: 'button',
|
||||
});
|
||||
ejectButton.connect('clicked', this._eject.bind(this));
|
||||
this.add(ejectButton);
|
||||
|
||||
this.hide();
|
||||
|
||||
this._changedId = mount.connect('changed', this._syncVisibility.bind(this));
|
||||
this._syncVisibility();
|
||||
},
|
||||
}
|
||||
|
||||
destroy: function() {
|
||||
_onDestroy() {
|
||||
if (this._changedId) {
|
||||
this.mount.disconnect(this._changedId);
|
||||
this._changedId = 0;
|
||||
}
|
||||
|
||||
this.parent();
|
||||
},
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
_isInteresting: function() {
|
||||
async _isInteresting() {
|
||||
if (!this.mount.can_eject() && !this.mount.can_unmount())
|
||||
return false;
|
||||
if (this.mount.is_shadowed())
|
||||
@@ -58,151 +68,164 @@ const MountMenuItem = new Lang.Class({
|
||||
|
||||
let volume = this.mount.get_volume();
|
||||
|
||||
if (volume == null) {
|
||||
// probably a GDaemonMount, could be network or
|
||||
// local, but we can't tell; assume it's local for now
|
||||
return true;
|
||||
if (volume)
|
||||
return volume.get_identifier('class') !== 'network';
|
||||
|
||||
const root = this.mount.get_root();
|
||||
|
||||
try {
|
||||
const attr = Gio.FILE_ATTRIBUTE_FILESYSTEM_REMOTE;
|
||||
const info = await root.query_filesystem_info_async(attr, null);
|
||||
return !info.get_attribute_boolean(attr);
|
||||
} catch (e) {
|
||||
log(`Failed to query filesystem: ${e.message}`);
|
||||
}
|
||||
|
||||
return volume.get_identifier('class') != 'network';
|
||||
},
|
||||
|
||||
_syncVisibility: function() {
|
||||
this.actor.visible = this._isInteresting();
|
||||
},
|
||||
|
||||
_eject: function() {
|
||||
let mountOp = new ShellMountOperation.ShellMountOperation(this.mount);
|
||||
|
||||
if (this.mount.can_eject())
|
||||
this.mount.eject_with_operation(Gio.MountUnmountFlags.NONE,
|
||||
mountOp.mountOp,
|
||||
null, // Gio.Cancellable
|
||||
Lang.bind(this, this._ejectFinish));
|
||||
else
|
||||
this.mount.unmount_with_operation(Gio.MountUnmountFlags.NONE,
|
||||
mountOp.mountOp,
|
||||
null, // Gio.Cancellable
|
||||
Lang.bind(this, this._unmountFinish));
|
||||
},
|
||||
|
||||
_unmountFinish: function(mount, result) {
|
||||
try {
|
||||
mount.unmount_with_operation_finish(result);
|
||||
} catch(e) {
|
||||
this._reportFailure(e);
|
||||
}
|
||||
},
|
||||
|
||||
_ejectFinish: function(mount, result) {
|
||||
try {
|
||||
mount.eject_with_operation_finish(result);
|
||||
} catch(e) {
|
||||
this._reportFailure(e);
|
||||
}
|
||||
},
|
||||
|
||||
_reportFailure: function(exception) {
|
||||
let msg = _("Ejecting drive “%s” failed:").format(this.mount.get_name());
|
||||
Main.notifyError(msg, exception.message);
|
||||
},
|
||||
|
||||
activate: function(event) {
|
||||
let context = global.create_app_launch_context(event.get_time(), -1);
|
||||
Gio.AppInfo.launch_default_for_uri(this.mount.get_root().get_uri(),
|
||||
context);
|
||||
|
||||
this.parent(event);
|
||||
// Hack, fall back to looking at GType
|
||||
return Gio._LocalFilePrototype.isPrototypeOf(root);
|
||||
}
|
||||
});
|
||||
|
||||
const DriveMenu = new Lang.Class({
|
||||
Name: 'DriveMenu.DriveMenu',
|
||||
Extends: PanelMenu.Button,
|
||||
async _syncVisibility() {
|
||||
this.visible = await this._isInteresting();
|
||||
}
|
||||
|
||||
_init: function() {
|
||||
this.parent(0.0, _("Removable devices"));
|
||||
_eject() {
|
||||
let unmountArgs = [
|
||||
Gio.MountUnmountFlags.NONE,
|
||||
new ShellMountOperation.ShellMountOperation(this.mount).mountOp,
|
||||
null, // Gio.Cancellable
|
||||
];
|
||||
|
||||
let hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
|
||||
let icon = new St.Icon({ icon_name: 'media-eject-symbolic',
|
||||
style_class: 'system-status-icon' });
|
||||
if (this.mount.can_eject()) {
|
||||
this.mount.eject_with_operation(...unmountArgs,
|
||||
this._ejectFinish.bind(this));
|
||||
} else {
|
||||
this.mount.unmount_with_operation(...unmountArgs,
|
||||
this._unmountFinish.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
hbox.add_child(icon);
|
||||
hbox.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM));
|
||||
this.actor.add_child(hbox);
|
||||
_unmountFinish(mount, result) {
|
||||
try {
|
||||
mount.unmount_with_operation_finish(result);
|
||||
} catch (e) {
|
||||
this._reportFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
this._monitor = Gio.VolumeMonitor.get();
|
||||
this._addedId = this._monitor.connect('mount-added', Lang.bind(this, function(monitor, mount) {
|
||||
this._addMount(mount);
|
||||
this._updateMenuVisibility();
|
||||
}));
|
||||
this._removedId = this._monitor.connect('mount-removed', Lang.bind(this, function(monitor, mount) {
|
||||
this._removeMount(mount);
|
||||
this._updateMenuVisibility();
|
||||
}));
|
||||
_ejectFinish(mount, result) {
|
||||
try {
|
||||
mount.eject_with_operation_finish(result);
|
||||
} catch (e) {
|
||||
this._reportFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
this._mounts = [ ];
|
||||
_reportFailure(exception) {
|
||||
// TRANSLATORS: %s is the filesystem name
|
||||
let msg = _('Ejecting drive “%s” failed:').format(this.mount.get_name());
|
||||
Main.notifyError(msg, exception.message);
|
||||
}
|
||||
|
||||
this._monitor.get_mounts().forEach(Lang.bind(this, this._addMount));
|
||||
activate(event) {
|
||||
let uri = this.mount.get_root().get_uri();
|
||||
let context = global.create_app_launch_context(event.get_time(), -1);
|
||||
Gio.AppInfo.launch_default_for_uri(uri, context);
|
||||
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addAction(_("Open Files"), function(event) {
|
||||
let appSystem = Shell.AppSystem.get_default();
|
||||
let app = appSystem.lookup_app('org.gnome.Nautilus.desktop');
|
||||
app.activate_full(-1, event.get_time());
|
||||
});
|
||||
super.activate(event);
|
||||
}
|
||||
}
|
||||
|
||||
this._updateMenuVisibility();
|
||||
},
|
||||
class DriveMenu extends PanelMenu.Button {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
|
||||
_updateMenuVisibility: function() {
|
||||
if (this._mounts.filter(function(i) i.actor.visible).length > 0)
|
||||
this.actor.show();
|
||||
else
|
||||
this.actor.hide();
|
||||
},
|
||||
constructor() {
|
||||
super(0.0, _('Removable devices'));
|
||||
|
||||
_addMount: function(mount) {
|
||||
let item = new MountMenuItem(mount);
|
||||
this._mounts.unshift(item);
|
||||
this.menu.addMenuItem(item, 0);
|
||||
},
|
||||
let icon = new St.Icon({
|
||||
icon_name: 'media-eject-symbolic',
|
||||
style_class: 'system-status-icon',
|
||||
});
|
||||
|
||||
_removeMount: function(mount) {
|
||||
for (let i = 0; i < this._mounts.length; i++) {
|
||||
let item = this._mounts[i];
|
||||
if (item.mount == mount) {
|
||||
item.destroy();
|
||||
this._mounts.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
log ('Removing a mount that was never added to the menu');
|
||||
},
|
||||
this.add_child(icon);
|
||||
|
||||
destroy: function() {
|
||||
if (this._connectedId) {
|
||||
this._monitor.disconnect(this._connectedId);
|
||||
this._monitor.disconnect(this._disconnectedId);
|
||||
this._connectedId = 0;
|
||||
this._disconnectedId = 0;
|
||||
this._monitor = Gio.VolumeMonitor.get();
|
||||
this._addedId = this._monitor.connect('mount-added',
|
||||
(monitor, mount) => this._addMount(mount));
|
||||
this._removedId = this._monitor.connect('mount-removed', (monitor, mount) => {
|
||||
this._removeMount(mount);
|
||||
this._updateMenuVisibility();
|
||||
});
|
||||
|
||||
this._mounts = [];
|
||||
|
||||
this._monitor.get_mounts().forEach(this._addMount.bind(this));
|
||||
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addAction(_('Open Files'), event => {
|
||||
let appSystem = Shell.AppSystem.get_default();
|
||||
let app = appSystem.lookup_app('org.gnome.Nautilus.desktop');
|
||||
app.activate_full(-1, event.get_time());
|
||||
});
|
||||
|
||||
this._updateMenuVisibility();
|
||||
}
|
||||
|
||||
_updateMenuVisibility() {
|
||||
if (this._mounts.filter(i => i.visible).length > 0)
|
||||
this.show();
|
||||
else
|
||||
this.hide();
|
||||
}
|
||||
|
||||
_addMount(mount) {
|
||||
let item = new MountMenuItem(mount);
|
||||
this._mounts.unshift(item);
|
||||
this.menu.addMenuItem(item, 0);
|
||||
|
||||
item.connect('notify::visible', () => this._updateMenuVisibility());
|
||||
}
|
||||
|
||||
_removeMount(mount) {
|
||||
for (let i = 0; i < this._mounts.length; i++) {
|
||||
let item = this._mounts[i];
|
||||
if (item.mount === mount) {
|
||||
item.destroy();
|
||||
this._mounts.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
log('Removing a mount that was never added to the menu');
|
||||
}
|
||||
|
||||
_onDestroy() {
|
||||
if (this._addedId) {
|
||||
this._monitor.disconnect(this._addedId);
|
||||
this._monitor.disconnect(this._removedId);
|
||||
this._addedId = 0;
|
||||
this._removedId = 0;
|
||||
}
|
||||
|
||||
this.parent();
|
||||
},
|
||||
});
|
||||
super._onDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
/** */
|
||||
function init() {
|
||||
Convenience.initTranslations();
|
||||
ExtensionUtils.initTranslations();
|
||||
}
|
||||
|
||||
let _indicator;
|
||||
|
||||
/** */
|
||||
function enable() {
|
||||
_indicator = new DriveMenu;
|
||||
_indicator = new DriveMenu();
|
||||
Main.panel.addToStatusArea('drive-menu', _indicator);
|
||||
}
|
||||
|
||||
/** */
|
||||
function disable() {
|
||||
_indicator.destroy();
|
||||
}
|
||||
|
||||
6
extensions/drive-menu/meson.build
Normal file
6
extensions/drive-menu/meson.build
Normal file
@@ -0,0 +1,6 @@
|
||||
extension_data += configure_file(
|
||||
input: metadata_name + '.in',
|
||||
output: metadata_name,
|
||||
configuration: metadata_conf
|
||||
)
|
||||
extension_data += files('stylesheet.css')
|
||||
@@ -1 +1,7 @@
|
||||
/* This extensions requires no custom styling */
|
||||
.drive-menu-item { spacing: 12px; }
|
||||
|
||||
.drive-menu-item .button {
|
||||
border-radius: 99px;
|
||||
padding: 3px;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
EXTENSION_ID = example
|
||||
|
||||
EXTRA_MODULES = prefs.js
|
||||
|
||||
include ../../extension.mk
|
||||
include ../../settings.mk
|
||||
@@ -1,49 +0,0 @@
|
||||
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
|
||||
// Sample extension code, makes clicking on the panel show a message
|
||||
const St = imports.gi.St;
|
||||
const Mainloop = imports.mainloop;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell-extensions');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Me = ExtensionUtils.getCurrentExtension();
|
||||
const Convenience = Me.imports.convenience;
|
||||
|
||||
function _showHello() {
|
||||
let settings = Convenience.getSettings();
|
||||
let text = settings.get_string('hello-text') || _("Hello, world!");
|
||||
|
||||
let label = new St.Label({ style_class: 'helloworld-label', text: text });
|
||||
let monitor = Main.layoutManager.primaryMonitor;
|
||||
global.stage.add_actor(label);
|
||||
label.set_position(Math.floor (monitor.width / 2 - label.width / 2), Math.floor(monitor.height / 2 - label.height / 2));
|
||||
Mainloop.timeout_add(3000, function () { label.destroy(); });
|
||||
}
|
||||
|
||||
// Put your extension initialization code here
|
||||
function init(metadata) {
|
||||
log ('Example extension initalized');
|
||||
|
||||
Convenience.initTranslations();
|
||||
}
|
||||
|
||||
let signalId;
|
||||
|
||||
function enable() {
|
||||
log ('Example extension enabled');
|
||||
|
||||
Main.panel.actor.reactive = true;
|
||||
signalId = Main.panel.actor.connect('button-release-event', _showHello);
|
||||
}
|
||||
|
||||
function disable() {
|
||||
log ('Example extension disabled');
|
||||
|
||||
if (signalId) {
|
||||
Main.panel.actor.disconnect(signalId);
|
||||
signalId = 0;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"extension-id": "@extension_id@",
|
||||
"uuid": "@uuid@",
|
||||
"settings-schema": "@gschemaname@",
|
||||
"gettext-domain": "@gettext_domain@",
|
||||
"name": "Hello, World!",
|
||||
"description": "An example extension to show how it works. Shows Hello, world when clicking on the top panel.",
|
||||
"shell-version": [ "@shell_current@" ],
|
||||
"url": "@url@"
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
<schemalist gettext-domain="gnome-shell-extensions">
|
||||
<schema id="org.gnome.shell.extensions.example" path="/org/gnome/shell/extensions/example/">
|
||||
<key name="hello-text" type="s">
|
||||
<default>''</default>
|
||||
<summary>Alternative greeting text.</summary>
|
||||
<description>If not empty, it contains the text that will be shown when clicking on the panel.</description>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|
||||
@@ -1,57 +0,0 @@
|
||||
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell-extensions');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Me = ExtensionUtils.getCurrentExtension();
|
||||
const Convenience = Me.imports.convenience;
|
||||
|
||||
function init() {
|
||||
Convenience.initTranslations();
|
||||
}
|
||||
|
||||
const ExamplePrefsWidget = new GObject.Class({
|
||||
Name: 'Example.Prefs.Widget',
|
||||
GTypeName: 'ExamplePrefsWidget',
|
||||
Extends: Gtk.Grid,
|
||||
|
||||
_init: function(params) {
|
||||
this.parent(params);
|
||||
this.margin = 12;
|
||||
this.row_spacing = this.column_spacing = 6;
|
||||
this.set_orientation(Gtk.Orientation.VERTICAL);
|
||||
|
||||
this.add(new Gtk.Label({ label: '<b>' + _("Message") + '</b>',
|
||||
use_markup: true,
|
||||
halign: Gtk.Align.START }));
|
||||
|
||||
let entry = new Gtk.Entry({ hexpand: true,
|
||||
margin_bottom: 12 });
|
||||
this.add(entry);
|
||||
|
||||
this._settings = Convenience.getSettings();
|
||||
this._settings.bind('hello-text', entry, 'text', Gio.SettingsBindFlags.DEFAULT);
|
||||
|
||||
// TRANSLATORS: Example is the name of the extension, should not be
|
||||
// translated
|
||||
let primaryText = _("Example aims to show how to build well behaved \
|
||||
extensions for the Shell and as such it has little functionality on its own.\n\
|
||||
Nevertheless it’s possible to customize the greeting message.");
|
||||
|
||||
this.add(new Gtk.Label({ label: primaryText,
|
||||
wrap: true, xalign: 0 }));
|
||||
}
|
||||
});
|
||||
|
||||
function buildPrefsWidget() {
|
||||
let widget = new ExamplePrefsWidget();
|
||||
widget.show_all();
|
||||
|
||||
return widget;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/* Example stylesheet */
|
||||
.helloworld-label {
|
||||
font-size: 36px;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
background-color: rgba(10,10,10,0.7);
|
||||
border-radius: 5px;
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
EXTENSION_ID = launch-new-instance
|
||||
|
||||
include ../../extension.mk
|
||||
@@ -1,17 +1,17 @@
|
||||
/* exported enable disable */
|
||||
const AppDisplay = imports.ui.appDisplay;
|
||||
|
||||
let _activateOriginal = null;
|
||||
|
||||
function init() {
|
||||
}
|
||||
|
||||
/** */
|
||||
function enable() {
|
||||
_activateOriginal = AppDisplay.AppIcon.prototype.activate;
|
||||
AppDisplay.AppIcon.prototype.activate = function() {
|
||||
_activateOriginal.call(this, 2);
|
||||
};
|
||||
_activateOriginal = AppDisplay.AppIcon.prototype.activate;
|
||||
AppDisplay.AppIcon.prototype.activate = function () {
|
||||
_activateOriginal.call(this, 2);
|
||||
};
|
||||
}
|
||||
|
||||
/** */
|
||||
function disable() {
|
||||
AppDisplay.AppIcon.prototype.activate = _activateOriginal;
|
||||
AppDisplay.AppIcon.prototype.activate = _activateOriginal;
|
||||
}
|
||||
|
||||
5
extensions/launch-new-instance/meson.build
Normal file
5
extensions/launch-new-instance/meson.build
Normal file
@@ -0,0 +1,5 @@
|
||||
extension_data += configure_file(
|
||||
input: metadata_name + '.in',
|
||||
output: metadata_name,
|
||||
configuration: metadata_conf
|
||||
)
|
||||
@@ -4,7 +4,7 @@
|
||||
"settings-schema": "@gschemaname@",
|
||||
"gettext-domain": "@gettext_domain@",
|
||||
"name": "Launch new instance",
|
||||
"description": "Always launch a new instance when clicking in the dash or the application view.\nThis extension is part of Classic Mode and is officially supported by GNOME. Please do not report bugs using the form below, use GNOME Bugzilla instead.",
|
||||
"description": "Always launch a new instance when clicking in the dash or the application view.\nThis extension is part of Classic Mode and is officially supported by GNOME. Please do not report bugs using the form below, use GNOME's GitLab instance instead.",
|
||||
"shell-version": [ "@shell_current@" ],
|
||||
"url": "@url@"
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
/* This extensions requires no special styling */
|
||||
29
extensions/meson.build
Normal file
29
extensions/meson.build
Normal file
@@ -0,0 +1,29 @@
|
||||
extension_schemas = []
|
||||
js_sources = []
|
||||
|
||||
metadata_name = 'metadata.json'
|
||||
|
||||
foreach e : enabled_extensions
|
||||
uuid = e + uuid_suffix
|
||||
|
||||
metadata_conf = configuration_data()
|
||||
metadata_conf.set('extension_id', e)
|
||||
metadata_conf.set('uuid', uuid)
|
||||
metadata_conf.set('gschemaname', 'org.gnome.shell.extensions.' + e)
|
||||
metadata_conf.set('gettext_domain', gettext_domain)
|
||||
metadata_conf.set('shell_current', shell_version)
|
||||
metadata_conf.set('url', 'https://gitlab.gnome.org/GNOME/gnome-shell-extensions')
|
||||
|
||||
extension_sources = files(e + '/extension.js')
|
||||
extension_data = []
|
||||
|
||||
subdir(e)
|
||||
|
||||
install_data (extension_sources + extension_data,
|
||||
install_dir: join_paths(extensiondir, uuid)
|
||||
)
|
||||
endforeach
|
||||
|
||||
install_data (extension_schemas,
|
||||
install_dir: schemadir
|
||||
)
|
||||
9
extensions/meson.build.template
Normal file
9
extensions/meson.build.template
Normal file
@@ -0,0 +1,9 @@
|
||||
extension_data += configure_file(
|
||||
input: metadata_name + '.in',
|
||||
output: metadata_name,
|
||||
configuration: metadata_conf
|
||||
)
|
||||
|
||||
# extension_data += files('stylesheet.css')
|
||||
# extension_sources += files('prefs.js')
|
||||
# extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
|
||||
@@ -1,5 +0,0 @@
|
||||
EXTENSION_ID = native-window-placement
|
||||
|
||||
include ../../extension.mk
|
||||
include ../../settings.mk
|
||||
|
||||
@@ -1,192 +1,139 @@
|
||||
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
|
||||
// import just everything from workspace.js:
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const DND = imports.ui.dnd;
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const Overview = imports.ui.overview;
|
||||
const Panel = imports.ui.panel;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const Workspace = imports.ui.workspace;
|
||||
const WindowPositionFlags = Workspace.WindowPositionFlags;
|
||||
/* exported enable disable */
|
||||
const { Clutter } = imports.gi;
|
||||
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Me = ExtensionUtils.getCurrentExtension();
|
||||
const Convenience = Me.imports.convenience;
|
||||
const Main = imports.ui.main;
|
||||
const { WindowPreview } = imports.ui.windowPreview;
|
||||
const Workspace = imports.ui.workspace;
|
||||
|
||||
// testing settings for natural window placement strategy:
|
||||
const WINDOW_PLACEMENT_NATURAL_FILLGAPS = true; // enlarge windows at the end to fill gaps // not implemented yet
|
||||
const WINDOW_PLACEMENT_NATURAL_GRID_FALLBACK = true; // fallback to grid mode if all windows have the same size and positions. // not implemented yet
|
||||
const WINDOW_PLACEMENT_NATURAL_ACCURACY = 20; // accuracy of window translate moves (KDE-default: 20)
|
||||
const WINDOW_PLACEMENT_NATURAL_GAPS = 5; // half of the minimum gap between windows
|
||||
const WINDOW_PLACEMENT_NATURAL_MAX_TRANSLATIONS = 5000; // safety limit for preventing endless loop if something is wrong in the algorithm
|
||||
|
||||
const PLACE_WINDOW_CAPTIONS_ON_TOP = true; // place window titles in overview on top of windows with overlap parameter
|
||||
|
||||
const WORKSPACE_BORDER_GAP = 10; // minimum gap between the workspace area and the workspace selector
|
||||
const WINDOW_AREA_TOP_GAP = 20; // minimum gap between the workspace area and the top border. This keeps window captions and close buttons visible. 13px (26/2) should currently be enough.
|
||||
|
||||
const BUTTON_LAYOUT_SCHEMA = 'org.gnome.desktop.wm.preferences';
|
||||
const BUTTON_LAYOUT_KEY = 'button-layout';
|
||||
|
||||
function injectToFunction(parent, name, func) {
|
||||
let origin = parent[name];
|
||||
parent[name] = function() {
|
||||
let ret;
|
||||
ret = origin.apply(this, arguments);
|
||||
if (ret === undefined)
|
||||
ret = func.apply(this, arguments);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
const Rect = new Lang.Class({
|
||||
Name: 'NativeWindowPlacement.Rect',
|
||||
|
||||
_init: function(x, y, width, height) {
|
||||
class Rect {
|
||||
constructor(x, y, width, height) {
|
||||
[this.x, this.y, this.width, this.height] = [x, y, width, height];
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* used in _calculateWindowTransformationsNatural to replace Meta.Rectangle that is too slow.
|
||||
*/
|
||||
copy: function() {
|
||||
// used in _calculateWindowTransformationsNatural to replace Meta.Rectangle that is too slow.
|
||||
copy() {
|
||||
return new Rect(this.x, this.y, this.width, this.height);
|
||||
},
|
||||
}
|
||||
|
||||
union: function(rect2) {
|
||||
union(rect2) {
|
||||
let dest = this.copy();
|
||||
if (rect2.x < dest.x)
|
||||
{
|
||||
if (rect2.x < dest.x) {
|
||||
dest.width += dest.x - rect2.x;
|
||||
dest.x = rect2.x;
|
||||
}
|
||||
if (rect2.y < dest.y)
|
||||
{
|
||||
}
|
||||
if (rect2.y < dest.y) {
|
||||
dest.height += dest.y - rect2.y;
|
||||
dest.y = rect2.y;
|
||||
}
|
||||
}
|
||||
if (rect2.x + rect2.width > dest.x + dest.width)
|
||||
dest.width = rect2.x + rect2.width - dest.x;
|
||||
dest.width = rect2.x + rect2.width - dest.x;
|
||||
if (rect2.y + rect2.height > dest.y + dest.height)
|
||||
dest.height = rect2.y + rect2.height - dest.y;
|
||||
dest.height = rect2.y + rect2.height - dest.y;
|
||||
|
||||
return dest;
|
||||
},
|
||||
}
|
||||
|
||||
adjusted: function(dx, dy, dx2, dy2) {
|
||||
adjusted(dx, dy, dx2, dy2) {
|
||||
let dest = this.copy();
|
||||
dest.x += dx;
|
||||
dest.y += dy;
|
||||
dest.width += -dx + dx2;
|
||||
dest.height += -dy + dy2;
|
||||
return dest;
|
||||
},
|
||||
}
|
||||
|
||||
overlap: function(rect2) {
|
||||
return !((this.x + this.width <= rect2.x) ||
|
||||
(rect2.x + rect2.width <= this.x) ||
|
||||
(this.y + this.height <= rect2.y) ||
|
||||
(rect2.y + rect2.height <= this.y));
|
||||
},
|
||||
overlap(rect2) {
|
||||
return !(this.x + this.width <= rect2.x ||
|
||||
rect2.x + rect2.width <= this.x ||
|
||||
this.y + this.height <= rect2.y ||
|
||||
rect2.y + rect2.height <= this.y);
|
||||
}
|
||||
|
||||
center: function() {
|
||||
center() {
|
||||
return [this.x + this.width / 2, this.y + this.height / 2];
|
||||
},
|
||||
}
|
||||
|
||||
translate: function(dx, dy) {
|
||||
translate(dx, dy) {
|
||||
this.x += dx;
|
||||
this.y += dy;
|
||||
}
|
||||
});
|
||||
|
||||
let winInjections, workspaceInjections, connectedSignals;
|
||||
|
||||
function resetState() {
|
||||
winInjections = { };
|
||||
workspaceInjections = { };
|
||||
connectedSignals = [ ];
|
||||
}
|
||||
|
||||
function enable() {
|
||||
resetState();
|
||||
class NaturalLayoutStrategy extends Workspace.LayoutStrategy {
|
||||
constructor(params, settings) {
|
||||
super(params);
|
||||
this._settings = settings;
|
||||
}
|
||||
|
||||
let settings = Convenience.getSettings();
|
||||
let useMoreScreen = settings.get_boolean('use-more-screen');
|
||||
let windowCaptionsOnTop = settings.get_boolean('window-captions-on-top');
|
||||
let signalId = settings.connect('changed::use-more-screen', function() {
|
||||
useMoreScreen = settings.get_boolean('use-more-screen');
|
||||
});
|
||||
connectedSignals.push({ obj: settings, id: signalId });
|
||||
computeLayout(windows, _params) {
|
||||
return {
|
||||
windows,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* _calculateWindowTransformationsNatural:
|
||||
* @clones: Array of #MetaWindow
|
||||
*
|
||||
/*
|
||||
* Returns clones with matching target coordinates and scales to arrange windows in a natural way that no overlap exists and relative window size is preserved.
|
||||
* This function is almost a 1:1 copy of the function
|
||||
* PresentWindowsEffect::calculateWindowTransformationsNatural() from KDE, see:
|
||||
* https://projects.kde.org/projects/kde/kdebase/kde-workspace/repository/revisions/master/entry/kwin/effects/presentwindows/presentwindows.cpp
|
||||
*/
|
||||
Workspace.Workspace.prototype._calculateWindowTransformationsNatural = function(clones, area) {
|
||||
computeWindowSlots(layout, area) {
|
||||
// As we are using pseudo-random movement (See "slot") we need to make sure the list
|
||||
// is always sorted the same way no matter which window is currently active.
|
||||
|
||||
let area_rect = new Rect(area.x, area.y, area.width, area.height);
|
||||
let bounds = area_rect.copy();
|
||||
let areaRect = new Rect(area.x, area.y, area.width, area.height);
|
||||
let bounds = areaRect.copy();
|
||||
let clones = layout.windows;
|
||||
|
||||
let direction = 0;
|
||||
let directions = [];
|
||||
let rects = [];
|
||||
for (let i = 0; i < clones.length; i++) {
|
||||
// save rectangles into 4-dimensional arrays representing two corners of the rectangular: [left_x, top_y, right_x, bottom_y]
|
||||
let rect = clones[i].metaWindow.get_frame_rect();
|
||||
let rect = clones[i].boundingBox;
|
||||
rects[i] = new Rect(rect.x, rect.y, rect.width, rect.height);
|
||||
bounds = bounds.union(rects[i]);
|
||||
|
||||
// This is used when the window is on the edge of the screen to try to use as much screen real estate as possible.
|
||||
directions[i] = direction;
|
||||
direction++;
|
||||
if (direction == 4) {
|
||||
if (direction === 4)
|
||||
direction = 0;
|
||||
}
|
||||
}
|
||||
|
||||
let loop_counter = 0;
|
||||
let loopCounter = 0;
|
||||
let overlap;
|
||||
do {
|
||||
overlap = false;
|
||||
for (let i = 0; i < rects.length; i++) {
|
||||
for (let j = 0; j < rects.length; j++) {
|
||||
if (i != j && rects[i].adjusted(-WINDOW_PLACEMENT_NATURAL_GAPS, -WINDOW_PLACEMENT_NATURAL_GAPS,
|
||||
WINDOW_PLACEMENT_NATURAL_GAPS, WINDOW_PLACEMENT_NATURAL_GAPS).overlap(
|
||||
rects[j].adjusted(-WINDOW_PLACEMENT_NATURAL_GAPS, -WINDOW_PLACEMENT_NATURAL_GAPS,
|
||||
WINDOW_PLACEMENT_NATURAL_GAPS, WINDOW_PLACEMENT_NATURAL_GAPS))) {
|
||||
loop_counter++;
|
||||
let adjustments = [-1, -1, 1, 1]
|
||||
.map(v => (v *= WINDOW_PLACEMENT_NATURAL_GAPS));
|
||||
let iAdjusted = rects[i].adjusted(...adjustments);
|
||||
let jAdjusted = rects[j].adjusted(...adjustments);
|
||||
if (i !== j && iAdjusted.overlap(jAdjusted)) {
|
||||
loopCounter++;
|
||||
overlap = true;
|
||||
|
||||
// TODO: something like a Point2D would be nicer here:
|
||||
|
||||
// Determine pushing direction
|
||||
let i_center = rects[i].center();
|
||||
let j_center = rects[j].center();
|
||||
let diff = [j_center[0] - i_center[0], j_center[1] - i_center[1]];
|
||||
let iCenter = rects[i].center();
|
||||
let jCenter = rects[j].center();
|
||||
let diff = [jCenter[0] - iCenter[0], jCenter[1] - iCenter[1]];
|
||||
|
||||
// Prevent dividing by zero and non-movement
|
||||
if (diff[0] == 0 && diff[1] == 0)
|
||||
if (diff[0] === 0 && diff[1] === 0)
|
||||
diff[0] = 1;
|
||||
// Try to keep screen/workspace aspect ratio
|
||||
if ( bounds.height / bounds.width > area_rect.height / area_rect.width )
|
||||
if (bounds.height / bounds.width > areaRect.height / areaRect.width)
|
||||
diff[0] *= 2;
|
||||
else
|
||||
diff[1] *= 2;
|
||||
@@ -201,7 +148,7 @@ function enable() {
|
||||
rects[j].translate(diff[0], diff[1]);
|
||||
|
||||
|
||||
if (useMoreScreen) {
|
||||
if (this._settings.get_boolean('use-more-screen')) {
|
||||
// Try to keep the bounding rect the same aspect as the screen so that more
|
||||
// screen real estate is utilised. We do this by splitting the screen into nine
|
||||
// equal sections, if the window center is in any of the corner sections pull the
|
||||
@@ -215,33 +162,33 @@ function enable() {
|
||||
let xSection = Math.round((rects[i].x - bounds.x) / (bounds.width / 3));
|
||||
let ySection = Math.round((rects[i].y - bounds.y) / (bounds.height / 3));
|
||||
|
||||
let i_center = rects[i].center();
|
||||
iCenter = rects[i].center();
|
||||
diff[0] = 0;
|
||||
diff[1] = 0;
|
||||
if (xSection != 1 || ySection != 1) { // Remove this if you want the center to pull as well
|
||||
if (xSection == 1)
|
||||
xSection = (directions[i] / 2 ? 2 : 0);
|
||||
if (ySection == 1)
|
||||
ySection = (directions[i] % 2 ? 2 : 0);
|
||||
if (xSection !== 1 || ySection !== 1) { // Remove this if you want the center to pull as well
|
||||
if (xSection === 1)
|
||||
xSection = directions[i] / 2 ? 2 : 0;
|
||||
if (ySection === 1)
|
||||
ySection = directions[i] % 2 ? 2 : 0;
|
||||
}
|
||||
if (xSection == 0 && ySection == 0) {
|
||||
diff[0] = bounds.x - i_center[0];
|
||||
diff[1] = bounds.y - i_center[1];
|
||||
if (xSection === 0 && ySection === 0) {
|
||||
diff[0] = bounds.x - iCenter[0];
|
||||
diff[1] = bounds.y - iCenter[1];
|
||||
}
|
||||
if (xSection == 2 && ySection == 0) {
|
||||
diff[0] = bounds.x + bounds.width - i_center[0];
|
||||
diff[1] = bounds.y - i_center[1];
|
||||
if (xSection === 2 && ySection === 0) {
|
||||
diff[0] = bounds.x + bounds.width - iCenter[0];
|
||||
diff[1] = bounds.y - iCenter[1];
|
||||
}
|
||||
if (xSection == 2 && ySection == 2) {
|
||||
diff[0] = bounds.x + bounds.width - i_center[0];
|
||||
diff[1] = bounds.y + bounds.height - i_center[1];
|
||||
if (xSection === 2 && ySection === 2) {
|
||||
diff[0] = bounds.x + bounds.width - iCenter[0];
|
||||
diff[1] = bounds.y + bounds.height - iCenter[1];
|
||||
}
|
||||
if (xSection == 0 && ySection == 2) {
|
||||
diff[0] = bounds.x - i_center[0];
|
||||
diff[1] = bounds.y + bounds.height - i_center[1];
|
||||
if (xSection === 0 && ySection === 2) {
|
||||
diff[0] = bounds.x - iCenter[0];
|
||||
diff[1] = bounds.y + bounds.height - iCenter[1];
|
||||
}
|
||||
if (diff[0] != 0 || diff[1] != 0) {
|
||||
let length = Math.sqrt(diff[0]*diff[0] + diff[1]*diff[1]);
|
||||
if (diff[0] !== 0 || diff[1] !== 0) {
|
||||
length = Math.sqrt(diff[0] * diff[0] + diff[1] * diff[1]);
|
||||
diff[0] *= WINDOW_PLACEMENT_NATURAL_ACCURACY / length / 2; // /2 to make it less influencing than the normal center-move above
|
||||
diff[1] *= WINDOW_PLACEMENT_NATURAL_ACCURACY / length / 2;
|
||||
rects[i].translate(diff[0], diff[1]);
|
||||
@@ -254,196 +201,94 @@ function enable() {
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (overlap && loop_counter < WINDOW_PLACEMENT_NATURAL_MAX_TRANSLATIONS);
|
||||
} while (overlap && loopCounter < WINDOW_PLACEMENT_NATURAL_MAX_TRANSLATIONS);
|
||||
|
||||
// Work out scaling by getting the most top-left and most bottom-right window coords.
|
||||
let scale;
|
||||
scale = Math.min(area_rect.width / bounds.width,
|
||||
area_rect.height / bounds.height,
|
||||
1.0);
|
||||
scale = Math.min(
|
||||
areaRect.width / bounds.width,
|
||||
areaRect.height / bounds.height,
|
||||
1.0);
|
||||
|
||||
// Make bounding rect fill the screen size for later steps
|
||||
bounds.x = bounds.x - (area_rect.width - bounds.width * scale) / 2;
|
||||
bounds.y = bounds.y - (area_rect.height - bounds.height * scale) / 2;
|
||||
bounds.width = area_rect.width / scale;
|
||||
bounds.height = area_rect.height / scale;
|
||||
bounds.x -= (areaRect.width - bounds.width * scale) / 2;
|
||||
bounds.y -= (areaRect.height - bounds.height * scale) / 2;
|
||||
bounds.width = areaRect.width / scale;
|
||||
bounds.height = areaRect.height / scale;
|
||||
|
||||
// Move all windows back onto the screen and set their scale
|
||||
for (let i = 0; i < rects.length; i++) {
|
||||
for (let i = 0; i < rects.length; i++)
|
||||
rects[i].translate(-bounds.x, -bounds.y);
|
||||
}
|
||||
|
||||
// TODO: Implement the KDE part "Try to fill the gaps by enlarging windows if they have the space" here. (If this is wanted)
|
||||
|
||||
// rescale to workspace
|
||||
let scales = [];
|
||||
|
||||
let buttonOuterHeight, captionHeight;
|
||||
let buttonOuterWidth = 0;
|
||||
|
||||
let slots = [];
|
||||
for (let i = 0; i < rects.length; i++) {
|
||||
rects[i].x = rects[i].x * scale + area_rect.x;
|
||||
rects[i].y = rects[i].y * scale + area_rect.y;
|
||||
rects[i].x = rects[i].x * scale + areaRect.x;
|
||||
rects[i].y = rects[i].y * scale + areaRect.y;
|
||||
rects[i].width *= scale;
|
||||
rects[i].height *= scale;
|
||||
|
||||
slots.push([rects[i].x, rects[i].y, scale, clones[i]]);
|
||||
slots.push([rects[i].x, rects[i].y, rects[i].width, rects[i].height, clones[i]]);
|
||||
}
|
||||
|
||||
return slots;
|
||||
}
|
||||
workspaceInjections['_calculateWindowTransformationsNatural'] = undefined;
|
||||
|
||||
/**
|
||||
* _updateWindowPositions:
|
||||
* @flags:
|
||||
* INITIAL - this is the initial positioning of the windows.
|
||||
* ANIMATE - Indicates that we need animate changing position.
|
||||
*/
|
||||
workspaceInjections['_updateWindowPositions'] = Workspace.Workspace.prototype._updateWindowPositions;
|
||||
Workspace.Workspace.prototype._updateWindowPositions = function(flags) {
|
||||
if (this._currentLayout == null) {
|
||||
this._recalculateWindowPositions(flags);
|
||||
return;
|
||||
}
|
||||
|
||||
let initialPositioning = flags & WindowPositionFlags.INITIAL;
|
||||
let animate = flags & WindowPositionFlags.ANIMATE;
|
||||
|
||||
let layout = this._currentLayout;
|
||||
let strategy = layout.strategy;
|
||||
|
||||
let [, , padding] = this._getSpacingAndPadding();
|
||||
let area = Workspace.padArea(this._actualGeometry, padding);
|
||||
|
||||
/// EDIT replace this version by our own:
|
||||
//let slots = strategy.computeWindowSlots(layout, area);
|
||||
|
||||
|
||||
/// EDIT copied from _realRecalculateWindowPositions:
|
||||
let clones = this._windows.slice();
|
||||
if (clones.length == 0)
|
||||
return;
|
||||
|
||||
clones.sort(function(a, b) {
|
||||
return a.metaWindow.get_stable_sequence() - b.metaWindow.get_stable_sequence();
|
||||
});
|
||||
|
||||
if (this._reservedSlot)
|
||||
clones.push(this._reservedSlot);
|
||||
|
||||
/// EDIT our own window placement function:
|
||||
let slots = this._calculateWindowTransformationsNatural(clones, area);
|
||||
|
||||
|
||||
let currentWorkspace = global.screen.get_active_workspace();
|
||||
let isOnCurrentWorkspace = this.metaWorkspace == null || this.metaWorkspace == currentWorkspace;
|
||||
|
||||
for (let i = 0; i < slots.length; i++) {
|
||||
let slot = slots[i];
|
||||
let [x, y, scale, clone] = slot;
|
||||
let metaWindow = clone.metaWindow;
|
||||
let overlay = clone.overlay;
|
||||
clone.slotId = i;
|
||||
|
||||
// Positioning a window currently being dragged must be avoided;
|
||||
// we'll just leave a blank spot in the layout for it.
|
||||
if (clone.inDrag)
|
||||
continue;
|
||||
|
||||
let cloneWidth = clone.actor.width * scale;
|
||||
let cloneHeight = clone.actor.height * scale;
|
||||
clone.slot = [x, y, cloneWidth, cloneHeight];
|
||||
|
||||
if (overlay && (initialPositioning || !clone.positioned))
|
||||
overlay.hide();
|
||||
|
||||
if (!clone.positioned) {
|
||||
// This window appeared after the overview was already up
|
||||
// Grow the clone from the center of the slot
|
||||
clone.actor.x = x + cloneWidth / 2;
|
||||
clone.actor.y = y + cloneHeight / 2;
|
||||
clone.actor.scale_x = 0;
|
||||
clone.actor.scale_y = 0;
|
||||
clone.positioned = true;
|
||||
}
|
||||
|
||||
if (animate && isOnCurrentWorkspace) {
|
||||
if (!metaWindow.showing_on_its_workspace()) {
|
||||
/* Hidden windows should fade in and grow
|
||||
* therefore we need to resize them now so they
|
||||
* can be scaled up later */
|
||||
if (initialPositioning) {
|
||||
clone.actor.opacity = 0;
|
||||
clone.actor.scale_x = 0;
|
||||
clone.actor.scale_y = 0;
|
||||
clone.actor.x = x;
|
||||
clone.actor.y = y;
|
||||
}
|
||||
|
||||
Tweener.addTween(clone.actor,
|
||||
{ opacity: 255,
|
||||
time: Overview.ANIMATION_TIME,
|
||||
transition: 'easeInQuad'
|
||||
});
|
||||
}
|
||||
|
||||
this._animateClone(clone, overlay, x, y, scale, initialPositioning);
|
||||
} else {
|
||||
// cancel any active tweens (otherwise they might override our changes)
|
||||
Tweener.removeTweens(clone.actor);
|
||||
clone.actor.set_position(x, y);
|
||||
clone.actor.set_scale(scale, scale);
|
||||
clone.overlay.relayout(false);
|
||||
this._showWindowOverlay(clone, overlay, isOnCurrentWorkspace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// position window titles on top of windows in overlay ////
|
||||
if (windowCaptionsOnTop) {
|
||||
|
||||
/// This is almost a direct copy of the original relayout function. Differences are marked.
|
||||
winInjections['relayout'] = Workspace.WindowOverlay.prototype.relayout;
|
||||
Workspace.WindowOverlay.prototype.relayout = function(animate) {
|
||||
winInjections['relayout'].call(this, animate);
|
||||
let title = this.title;
|
||||
let border = this.border;
|
||||
|
||||
this._parentActor.set_child_above_sibling(title, border);
|
||||
|
||||
Tweener.removeTweens(title);
|
||||
|
||||
let [cloneX, cloneY, cloneWidth, cloneHeight] = this._windowClone.slot;
|
||||
|
||||
// Clutter.Actor.get_preferred_width() will return the fixed width if one
|
||||
// is set, so we need to reset the width by calling set_width(-1), to forward
|
||||
// the call down to StLabel.
|
||||
// We also need to save and restore the current width, otherwise the animation
|
||||
// starts from the wrong point.
|
||||
let prevTitleWidth = title.width;
|
||||
title.set_width(-1);
|
||||
let [titleMinWidth, titleNatWidth] = title.get_preferred_width(-1);
|
||||
let titleWidth = Math.max(titleMinWidth, Math.min(titleNatWidth, cloneWidth));
|
||||
title.width = prevTitleWidth;
|
||||
|
||||
let titleX = cloneX + (cloneWidth - titleWidth) / 2;
|
||||
|
||||
/// this is the actual difference to original gnome-shell:
|
||||
//let titleY = cloneY + cloneHeight + title._spacing;
|
||||
let titleY = cloneY - title.height + title._spacing;
|
||||
|
||||
if (animate)
|
||||
this._animateOverlayActor(title, Math.floor(titleX), Math.floor(titleY), titleWidth);
|
||||
else {
|
||||
title.width = titleWidth;
|
||||
title.set_position(Math.floor(titleX), Math.floor(titleY));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let winInjections, workspaceInjections;
|
||||
|
||||
/** */
|
||||
function resetState() {
|
||||
winInjections = { };
|
||||
workspaceInjections = { };
|
||||
}
|
||||
|
||||
/** */
|
||||
function enable() {
|
||||
resetState();
|
||||
|
||||
let settings = ExtensionUtils.getSettings();
|
||||
|
||||
workspaceInjections['_createBestLayout'] = Workspace.WorkspaceLayout.prototype._createBestLayout;
|
||||
Workspace.WorkspaceLayout.prototype._createBestLayout = function (_area) {
|
||||
this._layoutStrategy = new NaturalLayoutStrategy({
|
||||
monitor: Main.layoutManager.monitors[this._monitorIndex],
|
||||
}, settings);
|
||||
return this._layoutStrategy.computeLayout(this._sortedWindows);
|
||||
};
|
||||
|
||||
// position window titles on top of windows in overlay
|
||||
winInjections['_init'] = WindowPreview.prototype._init;
|
||||
WindowPreview.prototype._init = function (...args) {
|
||||
winInjections['_init'].call(this, ...args);
|
||||
|
||||
if (!settings.get_boolean('window-captions-on-top'))
|
||||
return;
|
||||
|
||||
const alignConstraint = this._title.get_constraints().find(
|
||||
c => c.align_axis && c.align_axis === Clutter.AlignAxis.Y_AXIS);
|
||||
alignConstraint.factor = 0;
|
||||
|
||||
const bindConstraint = this._title.get_constraints().find(
|
||||
c => c.coordinate && c.coordinate === Clutter.BindCoordinate.Y);
|
||||
bindConstraint.offset = 0;
|
||||
};
|
||||
winInjections['_adjustOverlayOffsets'] =
|
||||
WindowPreview.prototype._adjustOverlayOffsets;
|
||||
WindowPreview.prototype._adjustOverlayOffsets = function (...args) {
|
||||
winInjections['_adjustOverlayOffsets'].call(this, ...args);
|
||||
|
||||
if (settings.get_boolean('window-captions-on-top'))
|
||||
this._title.translation_y = -this._title.translation_y;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} object - object that was modified
|
||||
* @param {object} injection - the map of previous injections
|
||||
* @param {string} name - the @injection key that should be removed
|
||||
*/
|
||||
function removeInjection(object, injection, name) {
|
||||
if (injection[name] === undefined)
|
||||
delete object[name];
|
||||
@@ -451,21 +296,15 @@ function removeInjection(object, injection, name) {
|
||||
object[name] = injection[name];
|
||||
}
|
||||
|
||||
/** */
|
||||
function disable() {
|
||||
var i;
|
||||
|
||||
for (i in workspaceInjections)
|
||||
removeInjection(Workspace.Workspace.prototype, workspaceInjections, i);
|
||||
removeInjection(Workspace.WorkspaceLayout.prototype, workspaceInjections, i);
|
||||
for (i in winInjections)
|
||||
removeInjection(Workspace.WindowOverlay.prototype, winInjections, i);
|
||||
|
||||
for each (i in connectedSignals)
|
||||
i.obj.disconnect(i.id);
|
||||
removeInjection(WindowPreview.prototype, winInjections, i);
|
||||
|
||||
global.stage.queue_relayout();
|
||||
resetState();
|
||||
}
|
||||
|
||||
function init() {
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
7
extensions/native-window-placement/meson.build
Normal file
7
extensions/native-window-placement/meson.build
Normal file
@@ -0,0 +1,7 @@
|
||||
extension_data += configure_file(
|
||||
input: metadata_name + '.in',
|
||||
output: metadata_name,
|
||||
configuration: metadata_conf
|
||||
)
|
||||
|
||||
extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
|
||||
@@ -1,9 +0,0 @@
|
||||
.window-caption {
|
||||
-shell-caption-spacing: 13px; /* current caption height is 26px => set it to half of it. TODO: better solution needed */
|
||||
}
|
||||
|
||||
.window-picker {
|
||||
-horizontal-spacing: 32px;
|
||||
-vertical-spacing: 32px;
|
||||
padding: 64px 32px;
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
EXTENSION_ID = places-menu
|
||||
|
||||
EXTRA_MODULES = placeDisplay.js
|
||||
|
||||
include ../../extension.mk
|
||||
@@ -1,143 +1,161 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/* exported init enable disable */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const { Clutter, GObject, St } = imports.gi;
|
||||
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Panel = imports.ui.panel;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell-extensions');
|
||||
const _ = Gettext.gettext;
|
||||
const N_ = function(x) { return x; }
|
||||
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Me = ExtensionUtils.getCurrentExtension();
|
||||
const Convenience = Me.imports.convenience;
|
||||
const PlaceDisplay = Me.imports.placeDisplay;
|
||||
|
||||
const _ = ExtensionUtils.gettext;
|
||||
const N_ = x => x;
|
||||
|
||||
const PLACE_ICON_SIZE = 16;
|
||||
|
||||
const PlaceMenuItem = new Lang.Class({
|
||||
Name: 'PlaceMenuItem',
|
||||
Extends: PopupMenu.PopupBaseMenuItem,
|
||||
class PlaceMenuItem extends PopupMenu.PopupBaseMenuItem {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
|
||||
_init: function(info) {
|
||||
this.parent();
|
||||
this._info = info;
|
||||
constructor(info) {
|
||||
super({
|
||||
style_class: 'place-menu-item',
|
||||
});
|
||||
this._info = info;
|
||||
|
||||
this._icon = new St.Icon({ gicon: info.icon,
|
||||
icon_size: PLACE_ICON_SIZE });
|
||||
this.actor.add_child(this._icon);
|
||||
this._icon = new St.Icon({
|
||||
gicon: info.icon,
|
||||
icon_size: PLACE_ICON_SIZE,
|
||||
});
|
||||
this.add_child(this._icon);
|
||||
|
||||
this._label = new St.Label({ text: info.name });
|
||||
this.actor.add_child(this._label);
|
||||
this._label = new St.Label({
|
||||
text: info.name,
|
||||
x_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
});
|
||||
this.add_child(this._label);
|
||||
|
||||
if (info.isRemovable()) {
|
||||
this._ejectIcon = new St.Icon({
|
||||
icon_name: 'media-eject-symbolic',
|
||||
style_class: 'popup-menu-icon',
|
||||
});
|
||||
this._ejectButton = new St.Button({
|
||||
child: this._ejectIcon,
|
||||
style_class: 'button',
|
||||
});
|
||||
this._ejectButton.connect('clicked', info.eject.bind(info));
|
||||
this.add_child(this._ejectButton);
|
||||
}
|
||||
|
||||
this._changedId = info.connect('changed',
|
||||
Lang.bind(this, this._propertiesChanged));
|
||||
},
|
||||
this._propertiesChanged.bind(this));
|
||||
}
|
||||
|
||||
destroy: function() {
|
||||
destroy() {
|
||||
if (this._changedId) {
|
||||
this._info.disconnect(this._changedId);
|
||||
this._changedId = 0;
|
||||
}
|
||||
|
||||
this.parent();
|
||||
},
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
activate: function(event) {
|
||||
this._info.launch(event.get_time());
|
||||
activate(event) {
|
||||
this._info.launch(event.get_time());
|
||||
|
||||
this.parent(event);
|
||||
},
|
||||
super.activate(event);
|
||||
}
|
||||
|
||||
_propertiesChanged: function(info) {
|
||||
_propertiesChanged(info) {
|
||||
this._icon.gicon = info.icon;
|
||||
this._label.text = info.name;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const SECTIONS = [
|
||||
'special',
|
||||
'devices',
|
||||
'bookmarks',
|
||||
'network'
|
||||
]
|
||||
'network',
|
||||
];
|
||||
|
||||
const PlacesMenu = new Lang.Class({
|
||||
Name: 'PlacesMenu.PlacesMenu',
|
||||
Extends: PanelMenu.Button,
|
||||
class PlacesMenu extends PanelMenu.Button {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
|
||||
_init: function() {
|
||||
this.parent(0.0, _("Places"));
|
||||
constructor() {
|
||||
super(0.0, _('Places'));
|
||||
|
||||
let hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
|
||||
let label = new St.Label({ text: _("Places"),
|
||||
y_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER });
|
||||
hbox.add_child(label);
|
||||
hbox.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM));
|
||||
this.actor.add_actor(hbox);
|
||||
let label = new St.Label({
|
||||
text: _('Places'),
|
||||
y_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
});
|
||||
this.add_actor(label);
|
||||
|
||||
this.placesManager = new PlaceDisplay.PlacesManager();
|
||||
|
||||
this._sections = { };
|
||||
this._sections = { };
|
||||
|
||||
for (let i=0; i < SECTIONS.length; i++) {
|
||||
let id = SECTIONS[i];
|
||||
this._sections[id] = new PopupMenu.PopupMenuSection();
|
||||
this.placesManager.connect(id + '-updated', Lang.bind(this, function() {
|
||||
this._redisplay(id);
|
||||
}));
|
||||
for (let i = 0; i < SECTIONS.length; i++) {
|
||||
let id = SECTIONS[i];
|
||||
this._sections[id] = new PopupMenu.PopupMenuSection();
|
||||
this.placesManager.connect(`${id}-updated`, () => {
|
||||
this._redisplay(id);
|
||||
});
|
||||
|
||||
this._create(id);
|
||||
this.menu.addMenuItem(this._sections[id]);
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
}
|
||||
},
|
||||
this._create(id);
|
||||
this.menu.addMenuItem(this._sections[id]);
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
}
|
||||
}
|
||||
|
||||
destroy: function() {
|
||||
this.placesManager.destroy();
|
||||
_onDestroy() {
|
||||
this.placesManager.destroy();
|
||||
|
||||
this.parent();
|
||||
},
|
||||
super._onDestroy();
|
||||
}
|
||||
|
||||
_redisplay: function(id) {
|
||||
this._sections[id].removeAll();
|
||||
_redisplay(id) {
|
||||
this._sections[id].removeAll();
|
||||
this._create(id);
|
||||
},
|
||||
}
|
||||
|
||||
_create: function(id) {
|
||||
_create(id) {
|
||||
let places = this.placesManager.get(id);
|
||||
|
||||
for (let i = 0; i < places.length; i++)
|
||||
this._sections[id].addMenuItem(new PlaceMenuItem(places[i]));
|
||||
|
||||
this._sections[id].actor.visible = places.length > 0;
|
||||
this._sections[id].actor.visible = places.length > 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** */
|
||||
function init() {
|
||||
Convenience.initTranslations();
|
||||
ExtensionUtils.initTranslations();
|
||||
}
|
||||
|
||||
let _indicator;
|
||||
|
||||
/** */
|
||||
function enable() {
|
||||
_indicator = new PlacesMenu;
|
||||
_indicator = new PlacesMenu();
|
||||
|
||||
let pos = 1;
|
||||
let pos = Main.sessionMode.panel.left.indexOf('appMenu');
|
||||
if ('apps-menu' in Main.panel.statusArea)
|
||||
pos = 2;
|
||||
pos++;
|
||||
Main.panel.addToStatusArea('places-menu', _indicator, pos, 'left');
|
||||
}
|
||||
|
||||
/** */
|
||||
function disable() {
|
||||
_indicator.destroy();
|
||||
}
|
||||
|
||||
8
extensions/places-menu/meson.build
Normal file
8
extensions/places-menu/meson.build
Normal file
@@ -0,0 +1,8 @@
|
||||
extension_data += configure_file(
|
||||
input: metadata_name + '.in',
|
||||
output: metadata_name,
|
||||
configuration: metadata_conf
|
||||
)
|
||||
extension_data += files('stylesheet.css')
|
||||
|
||||
extension_sources += files('placeDisplay.js')
|
||||
@@ -4,7 +4,7 @@
|
||||
"settings-schema": "@gschemaname@",
|
||||
"gettext-domain": "@gettext_domain@",
|
||||
"name": "Places Status Indicator",
|
||||
"description": "Add a menu for quickly navigating places in the system.\nThis extension is part of Classic Mode and is officially supported by GNOME. Please do not report bugs using the form below, use GNOME Bugzilla instead.",
|
||||
"description": "Add a menu for quickly navigating places in the system.\nThis extension is part of Classic Mode and is officially supported by GNOME. Please do not report bugs using the form below, use GNOME's GitLab instance instead.",
|
||||
"shell-version": [ "@shell_current@" ],
|
||||
"url": "@url@"
|
||||
}
|
||||
|
||||
@@ -1,23 +1,17 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const { Gio, GLib, Shell } = imports.gi;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const DND = imports.ui.dnd;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const Search = imports.ui.search;
|
||||
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell-extensions');
|
||||
const _ = Gettext.gettext;
|
||||
const N_ = function(x) { return x; }
|
||||
const _ = ExtensionUtils.gettext;
|
||||
const N_ = x => x;
|
||||
|
||||
Gio._promisify(Gio.AppInfo, 'launch_default_for_uri_async');
|
||||
Gio._promisify(Gio.File.prototype, 'mount_enclosing_volume');
|
||||
|
||||
const BACKGROUND_SCHEMA = 'org.gnome.desktop.background';
|
||||
|
||||
@@ -28,78 +22,73 @@ const Hostname1Iface = '<node> \
|
||||
</node>';
|
||||
const Hostname1 = Gio.DBusProxy.makeProxyWrapper(Hostname1Iface);
|
||||
|
||||
const PlaceInfo = new Lang.Class({
|
||||
Name: 'PlaceInfo',
|
||||
class PlaceInfo {
|
||||
constructor(...params) {
|
||||
this._init(...params);
|
||||
}
|
||||
|
||||
_init: function(kind, file, name, icon) {
|
||||
_init(kind, file, name, icon) {
|
||||
this.kind = kind;
|
||||
this.file = file;
|
||||
this.name = name || this._getFileName();
|
||||
this.icon = icon ? new Gio.ThemedIcon({ name: icon }) : this.getIcon();
|
||||
},
|
||||
}
|
||||
|
||||
destroy: function() {
|
||||
},
|
||||
destroy() {
|
||||
}
|
||||
|
||||
isRemovable: function() {
|
||||
isRemovable() {
|
||||
return false;
|
||||
},
|
||||
}
|
||||
|
||||
_createLaunchCallback: function(launchContext, tryMount) {
|
||||
return (_ignored, result) => {
|
||||
async _ensureMountAndLaunch(context, tryMount) {
|
||||
try {
|
||||
await Gio.AppInfo.launch_default_for_uri_async(this.file.get_uri(), context, null);
|
||||
} catch (err) {
|
||||
if (!err.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_MOUNTED)) {
|
||||
Main.notifyError(_('Failed to launch “%s”').format(this.name), err.message);
|
||||
return;
|
||||
}
|
||||
|
||||
let source = {
|
||||
get_icon: () => this.icon,
|
||||
};
|
||||
let op = new ShellMountOperation.ShellMountOperation(source);
|
||||
try {
|
||||
Gio.AppInfo.launch_default_for_uri_finish(result);
|
||||
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_MOUNTED)) {
|
||||
let source = {
|
||||
get_icon: () => { return this.icon; }
|
||||
};
|
||||
let op = new ShellMountOperation.ShellMountOperation(source);
|
||||
this.file.mount_enclosing_volume(0, op.mountOp, null, (file, result) => {
|
||||
try {
|
||||
op.close();
|
||||
file.mount_enclosing_volume_finish(result);
|
||||
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED)) {
|
||||
// e.g. user canceled the password dialog
|
||||
return;
|
||||
} catch(e) {
|
||||
Main.notifyError(_("Failed to mount volume for “%s”").format(this.name), e.message);
|
||||
return;
|
||||
}
|
||||
await this.file.mount_enclosing_volume(0, op.mountOp, null);
|
||||
|
||||
if (tryMount) {
|
||||
let callback = this._createLaunchCallback(launchContext, false);
|
||||
Gio.AppInfo.launch_default_for_uri_async(file.get_uri(),
|
||||
launchContext,
|
||||
null,
|
||||
callback);
|
||||
}
|
||||
});
|
||||
} catch(e) {
|
||||
Main.notifyError(_("Failed to launch “%s”").format(this.name), e.message);
|
||||
if (tryMount)
|
||||
this._ensureMountAndLaunch(context, false);
|
||||
} catch (e) {
|
||||
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
|
||||
Main.notifyError(_('Failed to mount volume for “%s”').format(this.name), e.message);
|
||||
} finally {
|
||||
op.close();
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
launch: function(timestamp) {
|
||||
launch(timestamp) {
|
||||
let launchContext = global.create_app_launch_context(timestamp, -1);
|
||||
let callback = this._createLaunchCallback(launchContext, true);
|
||||
Gio.AppInfo.launch_default_for_uri_async(this.file.get_uri(),
|
||||
launchContext,
|
||||
null,
|
||||
callback);
|
||||
},
|
||||
this._ensureMountAndLaunch(launchContext, true);
|
||||
}
|
||||
|
||||
getIcon: function() {
|
||||
this.file.query_info_async('standard::symbolic-icon', 0, 0, null,
|
||||
Lang.bind(this, function(file, result) {
|
||||
try {
|
||||
let info = file.query_info_finish(result);
|
||||
this.icon = info.get_symbolic_icon();
|
||||
this.emit('changed');
|
||||
} catch(e if e instanceof Gio.IOErrorEnum) {
|
||||
return;
|
||||
}
|
||||
}));
|
||||
getIcon() {
|
||||
this.file.query_info_async('standard::symbolic-icon',
|
||||
Gio.FileQueryInfoFlags.NONE,
|
||||
0,
|
||||
null,
|
||||
(file, result) => {
|
||||
try {
|
||||
let info = file.query_info_finish(result);
|
||||
this.icon = info.get_symbolic_icon();
|
||||
this.emit('changed');
|
||||
} catch (e) {
|
||||
if (e instanceof Gio.IOErrorEnum)
|
||||
return;
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
// return a generic icon for this kind for now, until we have the
|
||||
// icon from the query info above
|
||||
@@ -116,101 +105,138 @@ const PlaceInfo = new Lang.Class({
|
||||
else
|
||||
return new Gio.ThemedIcon({ name: 'folder-symbolic' });
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_getFileName: function() {
|
||||
_getFileName() {
|
||||
try {
|
||||
let info = this.file.query_info('standard::display-name', 0, null);
|
||||
return info.get_display_name();
|
||||
} catch(e if e instanceof Gio.IOErrorEnum) {
|
||||
return this.file.get_basename();
|
||||
} catch (e) {
|
||||
if (e instanceof Gio.IOErrorEnum)
|
||||
return this.file.get_basename();
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
Signals.addSignalMethods(PlaceInfo.prototype);
|
||||
|
||||
const RootInfo = new Lang.Class({
|
||||
Name: 'RootInfo',
|
||||
Extends: PlaceInfo,
|
||||
class RootInfo extends PlaceInfo {
|
||||
_init() {
|
||||
super._init('devices', Gio.File.new_for_path('/'), _('Computer'));
|
||||
|
||||
_init: function() {
|
||||
this.parent('devices', Gio.File.new_for_path('/'), _("Computer"));
|
||||
let busName = 'org.freedesktop.hostname1';
|
||||
let objPath = '/org/freedesktop/hostname1';
|
||||
new Hostname1(Gio.DBus.system, busName, objPath, (obj, error) => {
|
||||
if (error)
|
||||
return;
|
||||
|
||||
this._proxy = new Hostname1(Gio.DBus.system,
|
||||
'org.freedesktop.hostname1',
|
||||
'/org/freedesktop/hostname1',
|
||||
Lang.bind(this, function(obj, error) {
|
||||
if (error)
|
||||
return;
|
||||
this._proxy = obj;
|
||||
this._proxy.connect('g-properties-changed',
|
||||
this._propertiesChanged.bind(this));
|
||||
this._propertiesChanged(obj);
|
||||
});
|
||||
}
|
||||
|
||||
this._proxy.connect('g-properties-changed',
|
||||
Lang.bind(this, this._propertiesChanged));
|
||||
this._propertiesChanged(obj);
|
||||
}));
|
||||
},
|
||||
|
||||
getIcon: function() {
|
||||
getIcon() {
|
||||
return new Gio.ThemedIcon({ name: 'drive-harddisk-symbolic' });
|
||||
},
|
||||
}
|
||||
|
||||
_propertiesChanged: function(proxy) {
|
||||
_propertiesChanged(proxy) {
|
||||
// GDBusProxy will emit a g-properties-changed when hostname1 goes down
|
||||
// ignore it
|
||||
if (proxy.g_name_owner) {
|
||||
this.name = proxy.PrettyHostname || _("Computer");
|
||||
this.name = proxy.PrettyHostname || _('Computer');
|
||||
this.emit('changed');
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this._proxy.run_dispose();
|
||||
this.parent();
|
||||
}
|
||||
});
|
||||
|
||||
destroy() {
|
||||
if (this._proxy) {
|
||||
this._proxy.run_dispose();
|
||||
this._proxy = null;
|
||||
}
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const PlaceDeviceInfo = new Lang.Class({
|
||||
Name: 'PlaceDeviceInfo',
|
||||
Extends: PlaceInfo,
|
||||
|
||||
_init: function(kind, mount) {
|
||||
class PlaceDeviceInfo extends PlaceInfo {
|
||||
_init(kind, mount) {
|
||||
this._mount = mount;
|
||||
this.parent(kind, mount.get_root(), mount.get_name());
|
||||
},
|
||||
super._init(kind, mount.get_root(), mount.get_name());
|
||||
}
|
||||
|
||||
getIcon: function() {
|
||||
getIcon() {
|
||||
return this._mount.get_symbolic_icon();
|
||||
}
|
||||
});
|
||||
|
||||
const PlaceVolumeInfo = new Lang.Class({
|
||||
Name: 'PlaceVolumeInfo',
|
||||
Extends: PlaceInfo,
|
||||
isRemovable() {
|
||||
return this._mount.can_eject() || this._mount.can_unmount();
|
||||
}
|
||||
|
||||
_init: function(kind, volume) {
|
||||
eject() {
|
||||
let unmountArgs = [
|
||||
Gio.MountUnmountFlags.NONE,
|
||||
new ShellMountOperation.ShellMountOperation(this._mount).mountOp,
|
||||
null, // Gio.Cancellable
|
||||
];
|
||||
|
||||
if (this._mount.can_eject()) {
|
||||
this._mount.eject_with_operation(...unmountArgs,
|
||||
this._ejectFinish.bind(this));
|
||||
} else {
|
||||
this._mount.unmount_with_operation(...unmountArgs,
|
||||
this._unmountFinish.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
_ejectFinish(mount, result) {
|
||||
try {
|
||||
mount.eject_with_operation_finish(result);
|
||||
} catch (e) {
|
||||
this._reportFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
_unmountFinish(mount, result) {
|
||||
try {
|
||||
mount.unmount_with_operation_finish(result);
|
||||
} catch (e) {
|
||||
this._reportFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
_reportFailure(exception) {
|
||||
let msg = _('Ejecting drive “%s” failed:').format(this._mount.get_name());
|
||||
Main.notifyError(msg, exception.message);
|
||||
}
|
||||
}
|
||||
|
||||
class PlaceVolumeInfo extends PlaceInfo {
|
||||
_init(kind, volume) {
|
||||
this._volume = volume;
|
||||
this.parent(kind, volume.get_activation_root(), volume.get_name());
|
||||
},
|
||||
super._init(kind, volume.get_activation_root(), volume.get_name());
|
||||
}
|
||||
|
||||
launch: function(timestamp) {
|
||||
launch(timestamp) {
|
||||
if (this.file) {
|
||||
this.parent(timestamp);
|
||||
super.launch(timestamp);
|
||||
return;
|
||||
}
|
||||
|
||||
this._volume.mount(0, null, null, Lang.bind(this, function(volume, result) {
|
||||
this._volume.mount(0, null, null, (volume, result) => {
|
||||
volume.mount_finish(result);
|
||||
|
||||
let mount = volume.get_mount();
|
||||
this.file = mount.get_root();
|
||||
this.parent(timestamp);
|
||||
}));
|
||||
},
|
||||
super.launch(timestamp);
|
||||
});
|
||||
}
|
||||
|
||||
getIcon: function() {
|
||||
getIcon() {
|
||||
return this._volume.get_symbolic_icon();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const DEFAULT_DIRECTORIES = [
|
||||
GLib.UserDirectory.DIRECTORY_DOCUMENTS,
|
||||
@@ -220,10 +246,8 @@ const DEFAULT_DIRECTORIES = [
|
||||
GLib.UserDirectory.DIRECTORY_VIDEOS,
|
||||
];
|
||||
|
||||
const PlacesManager = new Lang.Class({
|
||||
Name: 'PlacesManager',
|
||||
|
||||
_init: function() {
|
||||
var PlacesManager = class {
|
||||
constructor() {
|
||||
this._places = {
|
||||
special: [],
|
||||
devices: [],
|
||||
@@ -232,9 +256,8 @@ const PlacesManager = new Lang.Class({
|
||||
};
|
||||
|
||||
this._settings = new Gio.Settings({ schema_id: BACKGROUND_SCHEMA });
|
||||
this._showDesktopIconsChangedId =
|
||||
this._settings.connect('changed::show-desktop-icons',
|
||||
Lang.bind(this, this._updateSpecials));
|
||||
this._showDesktopIconsChangedId = this._settings.connect(
|
||||
'changed::show-desktop-icons', this._updateSpecials.bind(this));
|
||||
this._updateSpecials();
|
||||
|
||||
/*
|
||||
@@ -244,41 +267,50 @@ const PlacesManager = new Lang.Class({
|
||||
this._connectVolumeMonitorSignals();
|
||||
this._updateMounts();
|
||||
|
||||
this._bookmarksFile = this._findBookmarksFile()
|
||||
this._bookmarksFile = this._findBookmarksFile();
|
||||
this._bookmarkTimeoutId = 0;
|
||||
this._monitor = null;
|
||||
|
||||
if (this._bookmarksFile) {
|
||||
this._monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null);
|
||||
this._monitor.connect('changed', Lang.bind(this, function () {
|
||||
this._monitor.connect('changed', () => {
|
||||
if (this._bookmarkTimeoutId > 0)
|
||||
return;
|
||||
/* Defensive event compression */
|
||||
this._bookmarkTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, function () {
|
||||
this._bookmarkTimeoutId = 0;
|
||||
this._reloadBookmarks();
|
||||
return false;
|
||||
}));
|
||||
}));
|
||||
this._bookmarkTimeoutId = GLib.timeout_add(
|
||||
GLib.PRIORITY_DEFAULT, 100, () => {
|
||||
this._bookmarkTimeoutId = 0;
|
||||
this._reloadBookmarks();
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
this._reloadBookmarks();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_connectVolumeMonitorSignals: function() {
|
||||
const signals = ['volume-added', 'volume-removed', 'volume-changed',
|
||||
'mount-added', 'mount-removed', 'mount-changed',
|
||||
'drive-connected', 'drive-disconnected', 'drive-changed'];
|
||||
_connectVolumeMonitorSignals() {
|
||||
const signals = [
|
||||
'volume-added',
|
||||
'volume-removed',
|
||||
'volume-changed',
|
||||
'mount-added',
|
||||
'mount-removed',
|
||||
'mount-changed',
|
||||
'drive-connected',
|
||||
'drive-disconnected',
|
||||
'drive-changed',
|
||||
];
|
||||
|
||||
this._volumeMonitorSignals = [];
|
||||
let func = Lang.bind(this, this._updateMounts);
|
||||
let func = this._updateMounts.bind(this);
|
||||
for (let i = 0; i < signals.length; i++) {
|
||||
let id = this._volumeMonitor.connect(signals[i], func);
|
||||
this._volumeMonitorSignals.push(id);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
destroy: function() {
|
||||
destroy() {
|
||||
if (this._settings)
|
||||
this._settings.disconnect(this._showDesktopIconsChangedId);
|
||||
this._settings = null;
|
||||
@@ -289,18 +321,19 @@ const PlacesManager = new Lang.Class({
|
||||
if (this._monitor)
|
||||
this._monitor.cancel();
|
||||
if (this._bookmarkTimeoutId)
|
||||
Mainloop.source_remove(this._bookmarkTimeoutId);
|
||||
},
|
||||
GLib.source_remove(this._bookmarkTimeoutId);
|
||||
}
|
||||
|
||||
_updateSpecials: function() {
|
||||
this._places.special.forEach(function (p) { p.destroy(); });
|
||||
_updateSpecials() {
|
||||
this._places.special.forEach(p => p.destroy());
|
||||
this._places.special = [];
|
||||
|
||||
let homePath = GLib.get_home_dir();
|
||||
|
||||
this._places.special.push(new PlaceInfo('special',
|
||||
Gio.File.new_for_path(homePath),
|
||||
_("Home")));
|
||||
this._places.special.push(new PlaceInfo(
|
||||
'special',
|
||||
Gio.File.new_for_path(homePath),
|
||||
_('Home')));
|
||||
|
||||
let specials = [];
|
||||
let dirs = DEFAULT_DIRECTORIES.slice();
|
||||
@@ -310,55 +343,56 @@ const PlacesManager = new Lang.Class({
|
||||
|
||||
for (let i = 0; i < dirs.length; i++) {
|
||||
let specialPath = GLib.get_user_special_dir(dirs[i]);
|
||||
if (specialPath == null || specialPath == homePath)
|
||||
if (!specialPath || specialPath === homePath)
|
||||
continue;
|
||||
|
||||
let file = Gio.File.new_for_path(specialPath), info;
|
||||
try {
|
||||
info = new PlaceInfo('special', file);
|
||||
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND)) {
|
||||
continue;
|
||||
} catch (e) {
|
||||
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
|
||||
continue;
|
||||
throw e;
|
||||
}
|
||||
|
||||
specials.push(info);
|
||||
}
|
||||
|
||||
specials.sort(function(a, b) {
|
||||
return GLib.utf8_collate(a.name, b.name);
|
||||
});
|
||||
specials.sort((a, b) => GLib.utf8_collate(a.name, b.name));
|
||||
this._places.special = this._places.special.concat(specials);
|
||||
|
||||
this.emit('special-updated');
|
||||
},
|
||||
}
|
||||
|
||||
_updateMounts: function() {
|
||||
_updateMounts() {
|
||||
let networkMounts = [];
|
||||
let networkVolumes = [];
|
||||
|
||||
this._places.devices.forEach(function (p) { p.destroy(); });
|
||||
this._places.devices.forEach(p => p.destroy());
|
||||
this._places.devices = [];
|
||||
this._places.network.forEach(function (p) { p.destroy(); });
|
||||
this._places.network.forEach(p => p.destroy());
|
||||
this._places.network = [];
|
||||
|
||||
/* Add standard places */
|
||||
this._places.devices.push(new RootInfo());
|
||||
this._places.network.push(new PlaceInfo('network',
|
||||
Gio.File.new_for_uri('network:///'),
|
||||
_("Browse Network"),
|
||||
'network-workgroup-symbolic'));
|
||||
this._places.network.push(new PlaceInfo(
|
||||
'network',
|
||||
Gio.File.new_for_uri('network:///'),
|
||||
_('Browse Network'),
|
||||
'network-workgroup-symbolic'));
|
||||
|
||||
/* first go through all connected drives */
|
||||
let drives = this._volumeMonitor.get_connected_drives();
|
||||
for (let i = 0; i < drives.length; i++) {
|
||||
let volumes = drives[i].get_volumes();
|
||||
|
||||
for(let j = 0; j < volumes.length; j++) {
|
||||
for (let j = 0; j < volumes.length; j++) {
|
||||
let identifier = volumes[j].get_identifier('class');
|
||||
if (identifier && identifier.indexOf('network') >= 0) {
|
||||
if (identifier && identifier.includes('network')) {
|
||||
networkVolumes.push(volumes[j]);
|
||||
} else {
|
||||
let mount = volumes[j].get_mount();
|
||||
if(mount != null)
|
||||
if (mount)
|
||||
this._addMount('devices', mount);
|
||||
}
|
||||
}
|
||||
@@ -366,27 +400,27 @@ const PlacesManager = new Lang.Class({
|
||||
|
||||
/* add all volumes that is not associated with a drive */
|
||||
let volumes = this._volumeMonitor.get_volumes();
|
||||
for(let i = 0; i < volumes.length; i++) {
|
||||
if(volumes[i].get_drive() != null)
|
||||
for (let i = 0; i < volumes.length; i++) {
|
||||
if (volumes[i].get_drive())
|
||||
continue;
|
||||
|
||||
let identifier = volumes[i].get_identifier('class');
|
||||
if (identifier && identifier.indexOf('network') >= 0) {
|
||||
if (identifier && identifier.includes('network')) {
|
||||
networkVolumes.push(volumes[i]);
|
||||
} else {
|
||||
let mount = volumes[i].get_mount();
|
||||
if(mount != null)
|
||||
if (mount)
|
||||
this._addMount('devices', mount);
|
||||
}
|
||||
}
|
||||
|
||||
/* add mounts that have no volume (/etc/mtab mounts, ftp, sftp,...) */
|
||||
let mounts = this._volumeMonitor.get_mounts();
|
||||
for(let i = 0; i < mounts.length; i++) {
|
||||
if(mounts[i].is_shadowed())
|
||||
for (let i = 0; i < mounts.length; i++) {
|
||||
if (mounts[i].is_shadowed())
|
||||
continue;
|
||||
|
||||
if(mounts[i].get_volume())
|
||||
if (mounts[i].get_volume())
|
||||
continue;
|
||||
|
||||
let root = mounts[i].get_default_location();
|
||||
@@ -406,15 +440,15 @@ const PlacesManager = new Lang.Class({
|
||||
this._addVolume('network', networkVolumes[i]);
|
||||
}
|
||||
|
||||
for (let i = 0; i < networkMounts.length; i++) {
|
||||
for (let i = 0; i < networkMounts.length; i++)
|
||||
this._addMount('network', networkMounts[i]);
|
||||
}
|
||||
|
||||
|
||||
this.emit('devices-updated');
|
||||
this.emit('network-updated');
|
||||
},
|
||||
}
|
||||
|
||||
_findBookmarksFile: function() {
|
||||
_findBookmarksFile() {
|
||||
let paths = [
|
||||
GLib.build_filenamev([GLib.get_user_config_dir(), 'gtk-3.0', 'bookmarks']),
|
||||
GLib.build_filenamev([GLib.get_home_dir(), '.gtk-bookmarks']),
|
||||
@@ -426,10 +460,9 @@ const PlacesManager = new Lang.Class({
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
_reloadBookmarks: function() {
|
||||
}
|
||||
|
||||
_reloadBookmarks() {
|
||||
this._bookmarks = [];
|
||||
|
||||
let content = Shell.get_file_contents_utf8_sync(this._bookmarksFile.get_path());
|
||||
@@ -439,7 +472,7 @@ const PlacesManager = new Lang.Class({
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
let line = lines[i];
|
||||
let components = line.split(' ');
|
||||
let bookmark = components[0];
|
||||
let [bookmark] = components;
|
||||
|
||||
if (!bookmark)
|
||||
continue;
|
||||
@@ -449,16 +482,16 @@ const PlacesManager = new Lang.Class({
|
||||
continue;
|
||||
|
||||
let duplicate = false;
|
||||
for (let i = 0; i < this._places.special.length; i++) {
|
||||
if (file.equal(this._places.special[i].file)) {
|
||||
for (let j = 0; j < this._places.special.length; j++) {
|
||||
if (file.equal(this._places.special[j].file)) {
|
||||
duplicate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (duplicate)
|
||||
continue;
|
||||
for (let i = 0; i < bookmarks.length; i++) {
|
||||
if (file.equal(bookmarks[i].file)) {
|
||||
for (let j = 0; j < bookmarks.length; j++) {
|
||||
if (file.equal(bookmarks[j].file)) {
|
||||
duplicate = true;
|
||||
break;
|
||||
}
|
||||
@@ -476,34 +509,38 @@ const PlacesManager = new Lang.Class({
|
||||
this._places.bookmarks = bookmarks;
|
||||
|
||||
this.emit('bookmarks-updated');
|
||||
},
|
||||
}
|
||||
|
||||
_addMount: function(kind, mount) {
|
||||
_addMount(kind, mount) {
|
||||
let devItem;
|
||||
|
||||
try {
|
||||
devItem = new PlaceDeviceInfo(kind, mount);
|
||||
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND)) {
|
||||
return;
|
||||
} catch (e) {
|
||||
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
|
||||
return;
|
||||
throw e;
|
||||
}
|
||||
|
||||
this._places[kind].push(devItem);
|
||||
},
|
||||
}
|
||||
|
||||
_addVolume: function(kind, volume) {
|
||||
_addVolume(kind, volume) {
|
||||
let volItem;
|
||||
|
||||
try {
|
||||
volItem = new PlaceVolumeInfo(kind, volume);
|
||||
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND)) {
|
||||
return;
|
||||
} catch (e) {
|
||||
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
|
||||
return;
|
||||
throw e;
|
||||
}
|
||||
|
||||
this._places[kind].push(volItem);
|
||||
},
|
||||
}
|
||||
|
||||
get: function (kind) {
|
||||
get(kind) {
|
||||
return this._places[kind];
|
||||
}
|
||||
});
|
||||
};
|
||||
Signals.addSignalMethods(PlacesManager.prototype);
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
/* none used*/
|
||||
.place-menu-item .button {
|
||||
border-radius: 99px;
|
||||
padding: 3px;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.place-menu-item .button:ltr { margin-left: 6px; }
|
||||
.place-menu-item .button:rtl { margin-right: 6px; }
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
EXTENSION_ID = screenshot-window-sizer
|
||||
|
||||
include ../../extension.mk
|
||||
include ../../settings.mk
|
||||
@@ -1,3 +1,4 @@
|
||||
/* exported enable disable */
|
||||
/* Screenshot Window Sizer for Gnome Shell
|
||||
*
|
||||
* Copyright (c) 2013 Owen Taylor <otaylor@redhat.com>
|
||||
@@ -18,47 +19,46 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const { Clutter, Meta, Shell, St } = imports.gi;
|
||||
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Me = ExtensionUtils.getCurrentExtension();
|
||||
const Convenience = Me.imports.convenience;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const MESSAGE_FADE_TIME = 2;
|
||||
const MESSAGE_FADE_TIME = 2000;
|
||||
|
||||
let text, button;
|
||||
let text;
|
||||
|
||||
/** */
|
||||
function hideMessage() {
|
||||
text.destroy();
|
||||
text = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} message - the message to flash
|
||||
*/
|
||||
function flashMessage(message) {
|
||||
if (!text) {
|
||||
text = new St.Label({ style_class: 'screenshot-sizer-message' });
|
||||
Main.uiGroup.add_actor(text);
|
||||
}
|
||||
|
||||
Tweener.removeTweens(text);
|
||||
text.remove_all_transitions();
|
||||
text.text = message;
|
||||
|
||||
text.opacity = 255;
|
||||
|
||||
let monitor = Main.layoutManager.primaryMonitor;
|
||||
text.set_position(monitor.x + Math.floor(monitor.width / 2 - text.width / 2),
|
||||
monitor.y + Math.floor(monitor.height / 2 - text.height / 2));
|
||||
text.set_position(
|
||||
monitor.x + Math.floor(monitor.width / 2 - text.width / 2),
|
||||
monitor.y + Math.floor(monitor.height / 2 - text.height / 2));
|
||||
|
||||
Tweener.addTween(text,
|
||||
{ opacity: 0,
|
||||
time: MESSAGE_FADE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: hideMessage });
|
||||
text.ease({
|
||||
opacity: 0,
|
||||
duration: MESSAGE_FADE_TIME,
|
||||
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||||
onComplete: hideMessage,
|
||||
});
|
||||
}
|
||||
|
||||
let SIZES = [
|
||||
@@ -66,29 +66,33 @@ let SIZES = [
|
||||
[800, 450],
|
||||
[1024, 576],
|
||||
[1200, 675],
|
||||
[1600, 900]
|
||||
[1600, 900],
|
||||
[360, 654], // Phone portrait maximized
|
||||
[720, 360], // Phone landscape fullscreen
|
||||
];
|
||||
|
||||
function cycleScreenshotSizes(display, screen, window, binding) {
|
||||
/**
|
||||
* @param {Meta.Display} display - the display
|
||||
* @param {Meta.Window=} window - for per-window bindings, the window
|
||||
* @param {Meta.KeyBinding} binding - the key binding
|
||||
*/
|
||||
function cycleScreenshotSizes(display, window, binding) {
|
||||
// Probably this isn't useful with 5 sizes, but you can decrease instead
|
||||
// of increase by holding down shift.
|
||||
let modifiers = binding.get_modifiers();
|
||||
let backwards = (modifiers & Meta.VirtualModifier.SHIFT_MASK) != 0;
|
||||
let backwards = (modifiers & Meta.VirtualModifier.SHIFT_MASK) !== 0;
|
||||
|
||||
// Unmaximize first
|
||||
if (window.maximized_horizontally || window.maximizedVertically)
|
||||
window.unmaximize(Meta.MaximizeFlags.HORIZONTAL | Meta.MaximizeFlags.VERTICAL);
|
||||
if (window.get_maximized() !== 0)
|
||||
window.unmaximize(Meta.MaximizeFlags.BOTH);
|
||||
|
||||
let workArea = window.get_work_area_current_monitor();
|
||||
let outerRect = window.get_frame_rect();
|
||||
|
||||
// Double both axes if on a hidpi display
|
||||
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
let scaledSizes = SIZES.map(function(size) {
|
||||
return size.map(function(wh) {
|
||||
return wh * scaleFactor;
|
||||
});
|
||||
});
|
||||
let scaledSizes = SIZES.map(size => size.map(wh => wh * scaleFactor))
|
||||
.filter(([w, h]) => w <= workArea.width && h <= workArea.height);
|
||||
|
||||
// Find the nearest 16:9 size for the current window size
|
||||
let nearestIndex;
|
||||
@@ -97,13 +101,9 @@ function cycleScreenshotSizes(display, screen, window, binding) {
|
||||
for (let i = 0; i < scaledSizes.length; i++) {
|
||||
let [width, height] = scaledSizes[i];
|
||||
|
||||
// ignore sizes bigger than the workArea
|
||||
if (width > workArea.width || height > workArea.height)
|
||||
continue;
|
||||
|
||||
// get the best initial window size
|
||||
let error = Math.abs(width - outerRect.width) + Math.abs(height - outerRect.height);
|
||||
if (nearestIndex == null || error < nearestError) {
|
||||
if (nearestIndex === undefined || error < nearestError) {
|
||||
nearestIndex = i;
|
||||
nearestError = error;
|
||||
}
|
||||
@@ -111,10 +111,7 @@ function cycleScreenshotSizes(display, screen, window, binding) {
|
||||
|
||||
// get the next size up or down from ideal
|
||||
let newIndex = (nearestIndex + (backwards ? -1 : 1)) % scaledSizes.length;
|
||||
let newWidth, newHeight;
|
||||
[newWidth, newHeight] = scaledSizes[newIndex];
|
||||
if (newWidth > workArea.width || newHeight > workArea.height)
|
||||
[newWidth, newHeight] = scaledSizes[0];
|
||||
let [newWidth, newHeight] = scaledSizes[newIndex];
|
||||
|
||||
// Push the window onscreen if it would be resized offscreen
|
||||
let newX = outerRect.x;
|
||||
@@ -124,34 +121,51 @@ function cycleScreenshotSizes(display, screen, window, binding) {
|
||||
if (newY + newHeight > workArea.y + workArea.height)
|
||||
newY = Math.max(workArea.y + workArea.height - newHeight);
|
||||
|
||||
const id = window.connect('size-changed', () => {
|
||||
window.disconnect(id);
|
||||
_notifySizeChange(window);
|
||||
});
|
||||
window.move_resize_frame(true, newX, newY, newWidth, newHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Meta.Window} window - the window whose size changed
|
||||
*/
|
||||
function _notifySizeChange(window) {
|
||||
const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage);
|
||||
let newOuterRect = window.get_frame_rect();
|
||||
let message = '%d×%d'.format(
|
||||
(newOuterRect.width / scaleFactor),
|
||||
(newOuterRect.height / scaleFactor));
|
||||
newOuterRect.width / scaleFactor,
|
||||
newOuterRect.height / scaleFactor);
|
||||
|
||||
// The new size might have been constrained by geometry hints (e.g. for
|
||||
// a terminal) - in that case, include the actual ratio to the message
|
||||
// we flash
|
||||
let actualNumerator = (newOuterRect.width / newOuterRect.height) * 9;
|
||||
let actualNumerator = 9 * newOuterRect.width / newOuterRect.height;
|
||||
if (Math.abs(actualNumerator - 16) > 0.01)
|
||||
message += ' (%.2f:9)'.format(actualNumerator);
|
||||
|
||||
flashMessage(message);
|
||||
}
|
||||
|
||||
function init() {
|
||||
}
|
||||
|
||||
/** */
|
||||
function enable() {
|
||||
Main.wm.addKeybinding('cycle-screenshot-sizes',
|
||||
Convenience.getSettings(),
|
||||
Meta.KeyBindingFlags.PER_WINDOW | Meta.KeyBindingFlags.REVERSES,
|
||||
Shell.ActionMode.NORMAL,
|
||||
cycleScreenshotSizes);
|
||||
Main.wm.addKeybinding(
|
||||
'cycle-screenshot-sizes',
|
||||
ExtensionUtils.getSettings(),
|
||||
Meta.KeyBindingFlags.PER_WINDOW,
|
||||
Shell.ActionMode.NORMAL,
|
||||
cycleScreenshotSizes);
|
||||
Main.wm.addKeybinding(
|
||||
'cycle-screenshot-sizes-backward',
|
||||
ExtensionUtils.getSettings(),
|
||||
Meta.KeyBindingFlags.PER_WINDOW | Meta.KeyBindingFlags.IS_REVERSED,
|
||||
Shell.ActionMode.NORMAL,
|
||||
cycleScreenshotSizes);
|
||||
}
|
||||
|
||||
/** */
|
||||
function disable() {
|
||||
Main.wm.removeKeybinding('cycle-screenshot-sizes');
|
||||
Main.wm.removeKeybinding('cycle-screenshot-sizes-backward');
|
||||
}
|
||||
|
||||
8
extensions/screenshot-window-sizer/meson.build
Normal file
8
extensions/screenshot-window-sizer/meson.build
Normal file
@@ -0,0 +1,8 @@
|
||||
extension_data += configure_file(
|
||||
input: metadata_name + '.in',
|
||||
output: metadata_name,
|
||||
configuration: metadata_conf
|
||||
)
|
||||
extension_data += files('stylesheet.css')
|
||||
|
||||
extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
|
||||
@@ -1,10 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<schemalist>
|
||||
<schemalist gettext-domain="gnome-shell-extensions">
|
||||
<schema id="org.gnome.shell.extensions.screenshot-window-sizer"
|
||||
path="/org/gnome/shell/extensions/screenshot-window-sizer/">
|
||||
<key type="as" name="cycle-screenshot-sizes">
|
||||
<default><![CDATA[['<Alt><Control>s']]]></default>
|
||||
<summary>Cycle Screenshot Sizes</summary>
|
||||
</key>
|
||||
<key type="as" name="cycle-screenshot-sizes-backward">
|
||||
<default><![CDATA[['<Shift><Alt><Control>s']]]></default>
|
||||
<summary>Cycle Screenshot Sizes Backward</summary>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
EXTENSION_ID = user-theme
|
||||
|
||||
include ../../extension.mk
|
||||
include ../../settings.mk
|
||||
|
||||
@@ -1,71 +1,61 @@
|
||||
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
|
||||
// Load shell theme from ~/.themes/name/gnome-shell
|
||||
// Load shell theme from ~/.local/share/themes/name/gnome-shell
|
||||
/* exported init */
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const { Gio } = imports.gi;
|
||||
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const Me = ExtensionUtils.getCurrentExtension();
|
||||
const Util = Me.imports.util;
|
||||
|
||||
const SETTINGS_KEY = 'name';
|
||||
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Me = ExtensionUtils.getCurrentExtension();
|
||||
const Convenience = Me.imports.convenience;
|
||||
|
||||
const ThemeManager = new Lang.Class({
|
||||
Name: 'UserTheme.ThemeManager',
|
||||
|
||||
_init: function() {
|
||||
this._settings = Convenience.getSettings();
|
||||
},
|
||||
|
||||
enable: function() {
|
||||
this._changedId = this._settings.connect('changed::'+SETTINGS_KEY, Lang.bind(this, this._changeTheme));
|
||||
class ThemeManager {
|
||||
enable() {
|
||||
this._settings = ExtensionUtils.getSettings();
|
||||
this._settings.connect(`changed::${SETTINGS_KEY}`, this._changeTheme.bind(this));
|
||||
this._changeTheme();
|
||||
},
|
||||
}
|
||||
|
||||
disable: function() {
|
||||
if (this._changedId) {
|
||||
this._settings.disconnect(this._changedId);
|
||||
this._changedId = 0;
|
||||
}
|
||||
disable() {
|
||||
this._settings?.run_dispose();
|
||||
this._settings = null;
|
||||
|
||||
Main.setThemeStylesheet(null);
|
||||
Main.loadTheme();
|
||||
},
|
||||
}
|
||||
|
||||
_changeTheme: function() {
|
||||
let _stylesheet = null;
|
||||
let _themeName = this._settings.get_string(SETTINGS_KEY);
|
||||
_changeTheme() {
|
||||
let stylesheet = null;
|
||||
let themeName = this._settings.get_string(SETTINGS_KEY);
|
||||
|
||||
if (_themeName) {
|
||||
let _userCssStylesheet = GLib.get_home_dir() + '/.themes/' + _themeName + '/gnome-shell/gnome-shell.css';
|
||||
let file = Gio.file_new_for_path(_userCssStylesheet);
|
||||
if (file.query_exists(null))
|
||||
_stylesheet = _userCssStylesheet;
|
||||
else {
|
||||
let sysdirs = GLib.get_system_data_dirs();
|
||||
sysdirs.unshift(GLib.get_user_data_dir());
|
||||
for (let i = 0; i < sysdirs.length; i++) {
|
||||
_userCssStylesheet = sysdirs[i] + '/themes/' + _themeName + '/gnome-shell/gnome-shell.css';
|
||||
let file = Gio.file_new_for_path(_userCssStylesheet);
|
||||
if (file.query_exists(null)) {
|
||||
_stylesheet = _userCssStylesheet;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (themeName) {
|
||||
const stylesheetPaths = Util.getThemeDirs()
|
||||
.map(dir => `${dir}/${themeName}/gnome-shell/gnome-shell.css`);
|
||||
|
||||
stylesheetPaths.push(...Util.getModeThemeDirs()
|
||||
.map(dir => `${dir}/${themeName}.css`));
|
||||
|
||||
stylesheet = stylesheetPaths.find(path => {
|
||||
let file = Gio.file_new_for_path(path);
|
||||
return file.query_exists(null);
|
||||
});
|
||||
}
|
||||
|
||||
if (_stylesheet)
|
||||
global.log('loading user theme: ' + _stylesheet);
|
||||
if (stylesheet)
|
||||
global.log(`loading user theme: ${stylesheet}`);
|
||||
else
|
||||
global.log('loading default theme (Adwaita)');
|
||||
Main.setThemeStylesheet(_stylesheet);
|
||||
Main.setThemeStylesheet(stylesheet);
|
||||
Main.loadTheme();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {ThemeManager} - the extension state object
|
||||
*/
|
||||
function init() {
|
||||
return new ThemeManager();
|
||||
}
|
||||
|
||||
8
extensions/user-theme/meson.build
Normal file
8
extensions/user-theme/meson.build
Normal file
@@ -0,0 +1,8 @@
|
||||
extension_data += configure_file(
|
||||
input: metadata_name + '.in',
|
||||
output: metadata_name,
|
||||
configuration: metadata_conf
|
||||
)
|
||||
|
||||
extension_sources += files('prefs.js', 'util.js')
|
||||
extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
|
||||
137
extensions/user-theme/prefs.js
Normal file
137
extensions/user-theme/prefs.js
Normal file
@@ -0,0 +1,137 @@
|
||||
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
|
||||
/* exported init buildPrefsWidget */
|
||||
|
||||
// we use async/await here to not block the mainloop, not to parallelize
|
||||
/* eslint-disable no-await-in-loop */
|
||||
|
||||
const { Adw, Gio, GLib, GObject, Gtk } = imports.gi;
|
||||
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
|
||||
const Me = ExtensionUtils.getCurrentExtension();
|
||||
const Util = Me.imports.util;
|
||||
|
||||
Gio._promisify(Gio.File.prototype, 'enumerate_children_async');
|
||||
Gio._promisify(Gio.File.prototype, 'query_info_async');
|
||||
Gio._promisify(Gio.FileEnumerator.prototype, 'next_files_async');
|
||||
|
||||
class UserThemePrefsWidget extends Adw.PreferencesGroup {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super({ title: 'Themes' });
|
||||
|
||||
this._actionGroup = new Gio.SimpleActionGroup();
|
||||
this.insert_action_group('theme', this._actionGroup);
|
||||
|
||||
this._settings = ExtensionUtils.getSettings();
|
||||
this._actionGroup.add_action(
|
||||
this._settings.create_action('name'));
|
||||
|
||||
this.connect('destroy', () => this._settings.run_dispose());
|
||||
|
||||
this._rows = new Map();
|
||||
this._addTheme(''); // default
|
||||
|
||||
this._collectThemes();
|
||||
}
|
||||
|
||||
async _collectThemes() {
|
||||
for (const dirName of Util.getThemeDirs()) {
|
||||
const dir = Gio.File.new_for_path(dirName);
|
||||
for (const name of await this._enumerateDir(dir)) {
|
||||
if (this._rows.has(name))
|
||||
continue;
|
||||
|
||||
const file = dir.resolve_relative_path(
|
||||
`${name}/gnome-shell/gnome-shell.css`);
|
||||
try {
|
||||
await file.query_info_async(
|
||||
Gio.FILE_ATTRIBUTE_STANDARD_NAME,
|
||||
Gio.FileQueryInfoFlags.NONE,
|
||||
GLib.PRIORITY_DEFAULT, null);
|
||||
this._addTheme(name);
|
||||
} catch (e) {
|
||||
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
|
||||
logError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const dirName of Util.getModeThemeDirs()) {
|
||||
const dir = Gio.File.new_for_path(dirName);
|
||||
for (const filename of await this._enumerateDir(dir)) {
|
||||
if (!filename.endsWith('.css'))
|
||||
continue;
|
||||
|
||||
const name = filename.slice(0, -4);
|
||||
if (!this._rows.has(name))
|
||||
this._addTheme(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_addTheme(name) {
|
||||
const row = new ThemeRow(name);
|
||||
this._rows.set(name, row);
|
||||
|
||||
this.add(row);
|
||||
}
|
||||
|
||||
async _enumerateDir(dir) {
|
||||
const fileInfos = [];
|
||||
let fileEnum;
|
||||
|
||||
try {
|
||||
fileEnum = await dir.enumerate_children_async(
|
||||
Gio.FILE_ATTRIBUTE_STANDARD_NAME,
|
||||
Gio.FileQueryInfoFlags.NONE,
|
||||
GLib.PRIORITY_DEFAULT, null);
|
||||
} catch (e) {
|
||||
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
|
||||
logError(e);
|
||||
return [];
|
||||
}
|
||||
|
||||
let infos;
|
||||
do {
|
||||
infos = await fileEnum.next_files_async(100,
|
||||
GLib.PRIORITY_DEFAULT, null);
|
||||
fileInfos.push(...infos);
|
||||
} while (infos.length > 0);
|
||||
|
||||
return fileInfos.map(info => info.get_name());
|
||||
}
|
||||
}
|
||||
|
||||
class ThemeRow extends Adw.ActionRow {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
|
||||
constructor(name) {
|
||||
const check = new Gtk.CheckButton({
|
||||
action_name: 'theme.name',
|
||||
action_target: new GLib.Variant('s', name),
|
||||
});
|
||||
|
||||
super({
|
||||
title: name || 'Default',
|
||||
activatable_widget: check,
|
||||
});
|
||||
this.add_prefix(check);
|
||||
}
|
||||
}
|
||||
|
||||
/** */
|
||||
function init() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Gtk.Widget} - the prefs widget
|
||||
*/
|
||||
function buildPrefsWidget() {
|
||||
return new UserThemePrefsWidget();
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
/* none used */
|
||||
23
extensions/user-theme/util.js
Normal file
23
extensions/user-theme/util.js
Normal file
@@ -0,0 +1,23 @@
|
||||
/* exported getThemeDirs getModeThemeDirs */
|
||||
const { GLib } = imports.gi;
|
||||
|
||||
const fn = (...args) => GLib.build_filenamev(args);
|
||||
|
||||
/**
|
||||
* @returns {string[]} - an ordered list of theme directories
|
||||
*/
|
||||
function getThemeDirs() {
|
||||
return [
|
||||
fn(GLib.get_home_dir(), '.themes'),
|
||||
fn(GLib.get_user_data_dir(), 'themes'),
|
||||
...GLib.get_system_data_dirs().map(dir => fn(dir, 'themes')),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string[]} - an ordered list of mode theme directories
|
||||
*/
|
||||
function getModeThemeDirs() {
|
||||
return GLib.get_system_data_dirs()
|
||||
.map(dir => fn(dir, 'gnome-shell', 'theme'));
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
EXTENSION_ID = window-list
|
||||
|
||||
EXTRA_MODULES = prefs.js
|
||||
|
||||
if CLASSIC_MODE
|
||||
EXTRA_MODULES += classic.css
|
||||
endif
|
||||
|
||||
include ../../extension.mk
|
||||
include ../../settings.mk
|
||||
@@ -4,36 +4,39 @@
|
||||
border-top-width: 1px;
|
||||
border-bottom-width: 0px;
|
||||
height: 2.25em ;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.bottom-panel .window-button > StWidget,
|
||||
.bottom-panel .window-picker-toggle > StWidget {
|
||||
color: #2e3436;
|
||||
background-color: #eee;
|
||||
border-radius: 3px;
|
||||
padding: 3px 6px 1px;
|
||||
box-shadow: none;
|
||||
text-shadow: none;
|
||||
border: 1px solid rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.bottom-panel .window-button > StWidget {
|
||||
background-gradient-drection: vertical;
|
||||
background-color: #fff;
|
||||
background-gradient-start: #fff;
|
||||
background-gradient-end: #eee;
|
||||
color: #000;
|
||||
-st-natural-width: 18.7em;
|
||||
max-width: 18.75em;
|
||||
color: #2e3436;
|
||||
background-color: #eee;
|
||||
border-radius: 2px;
|
||||
padding: 3px 6px 1px;
|
||||
box-shadow: inset -1px -1px 1px rgba(0,0,0,0.5);
|
||||
text-shadow: 0 0 transparent;
|
||||
}
|
||||
|
||||
.bottom-panel .window-button:hover > StWidget {
|
||||
.bottom-panel .window-button:hover > StWidget,
|
||||
.bottom-panel .window-picker-toggle:hover > StWidget {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.bottom-panel .window-button:active > StWidget,
|
||||
.bottom-panel .window-button:focus > StWidget {
|
||||
box-shadow: inset 1px 1px 2px rgba(0,0,0,0.5);
|
||||
box-shadow: inset 0 1px 3px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.bottom-panel .window-button.focused > StWidget {
|
||||
background-color: #ddd;
|
||||
box-shadow: inset 1px 1px 1px rgba(0,0,0,0.5);
|
||||
.bottom-panel .window-button.focused > StWidget,
|
||||
.bottom-panel .window-picker-toggle:checked > StWidget {
|
||||
background-color: #ccc;
|
||||
box-shadow: inset 0 1px 3px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.bottom-panel .window-button.focused:hover > StWidget {
|
||||
@@ -42,5 +45,24 @@
|
||||
|
||||
.bottom-panel .window-button.minimized > StWidget {
|
||||
color: #888;
|
||||
box-shadow: inset -1px -1px 1px rgba(0,0,0,0.5);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* workspace switcher */
|
||||
.window-list-workspace-indicator .workspace {
|
||||
border: 2px solid #f6f5f4;
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
.window-list-workspace-indicator .workspace.active {
|
||||
border-color: #888;
|
||||
}
|
||||
|
||||
.window-list-window-preview {
|
||||
background-color: #ededed;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.window-list-window-preview.active {
|
||||
background-color: #f6f5f4;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
13
extensions/window-list/meson.build
Normal file
13
extensions/window-list/meson.build
Normal file
@@ -0,0 +1,13 @@
|
||||
extension_data += configure_file(
|
||||
input: metadata_name + '.in',
|
||||
output: metadata_name,
|
||||
configuration: metadata_conf
|
||||
)
|
||||
extension_data += files('stylesheet.css')
|
||||
|
||||
extension_sources += files('prefs.js', 'windowPicker.js', 'workspaceIndicator.js')
|
||||
extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
|
||||
|
||||
if classic_mode_enabled
|
||||
extension_data += files('classic.css')
|
||||
endif
|
||||
@@ -4,7 +4,7 @@
|
||||
"settings-schema": "@gschemaname@",
|
||||
"gettext-domain": "@gettext_domain@",
|
||||
"name": "Window List",
|
||||
"description": "Display a window list at the bottom of the screen.\nThis extension is part of Classic Mode and is officially supported by GNOME. Please do not report bugs using the form below, use GNOME Bugzilla instead.",
|
||||
"description": "Display a window list at the bottom of the screen.\nThis extension is part of Classic Mode and is officially supported by GNOME. Please do not report bugs using the form below, use GNOME's GitLab instance instead.",
|
||||
"shell-version": [ "@shell_current@" ],
|
||||
"url": "@url@"
|
||||
}
|
||||
|
||||
@@ -15,6 +15,13 @@
|
||||
window list. Possible values are “never”, “auto” and “always”.
|
||||
</description>
|
||||
</key>
|
||||
<key name="display-all-workspaces" type="b">
|
||||
<default>false</default>
|
||||
<summary>Show windows from all workspaces</summary>
|
||||
<description>
|
||||
Whether to show windows from all workspaces or only the current one.
|
||||
</description>
|
||||
</key>
|
||||
<key name="show-on-all-monitors" type="b">
|
||||
<default>false</default>
|
||||
<summary>Show the window list on all monitors</summary>
|
||||
|
||||
@@ -1,87 +1,92 @@
|
||||
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
|
||||
/* exported init buildPrefsWidget */
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell-extensions');
|
||||
const _ = Gettext.gettext;
|
||||
const { Adw, Gio, GLib, GObject, Gtk } = imports.gi;
|
||||
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Me = ExtensionUtils.getCurrentExtension();
|
||||
const Convenience = Me.imports.convenience;
|
||||
|
||||
const _ = ExtensionUtils.gettext;
|
||||
|
||||
/** */
|
||||
function init() {
|
||||
Convenience.initTranslations();
|
||||
ExtensionUtils.initTranslations();
|
||||
}
|
||||
|
||||
const WindowListPrefsWidget = new GObject.Class({
|
||||
Name: 'WindowList.Prefs.Widget',
|
||||
GTypeName: 'WindowListPrefsWidget',
|
||||
Extends: Gtk.Grid,
|
||||
class WindowListPrefsWidget extends Adw.PreferencesPage {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
|
||||
_init: function(params) {
|
||||
this.parent(params);
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.margin = 24;
|
||||
this.row_spacing = 6;
|
||||
this.orientation = Gtk.Orientation.VERTICAL;
|
||||
this._actionGroup = new Gio.SimpleActionGroup();
|
||||
this.insert_action_group('window-list', this._actionGroup);
|
||||
|
||||
let groupingLabel = '<b>' + _("Window Grouping") + '</b>';
|
||||
this.add(new Gtk.Label({ label: groupingLabel, use_markup: true,
|
||||
halign: Gtk.Align.START }));
|
||||
this._settings = ExtensionUtils.getSettings();
|
||||
this._actionGroup.add_action(
|
||||
this._settings.create_action('grouping-mode'));
|
||||
this._actionGroup.add_action(
|
||||
this._settings.create_action('show-on-all-monitors'));
|
||||
this._actionGroup.add_action(
|
||||
this._settings.create_action('display-all-workspaces'));
|
||||
|
||||
let align = new Gtk.Alignment({ left_padding: 12 });
|
||||
this.add(align);
|
||||
const groupingGroup = new Adw.PreferencesGroup({
|
||||
title: _('Window Grouping'),
|
||||
});
|
||||
this.add(groupingGroup);
|
||||
|
||||
let grid = new Gtk.Grid({ orientation: Gtk.Orientation.VERTICAL,
|
||||
row_spacing: 6,
|
||||
column_spacing: 6 });
|
||||
align.add(grid);
|
||||
const modes = [
|
||||
{ mode: 'never', title: _('Never group windows') },
|
||||
{ mode: 'auto', title: _('Group windows when space is limited') },
|
||||
{ mode: 'always', title: _('Always group windows') },
|
||||
];
|
||||
|
||||
this._settings = Convenience.getSettings();
|
||||
let currentMode = this._settings.get_string('grouping-mode');
|
||||
let range = this._settings.get_range('grouping-mode');
|
||||
let modes = range.deep_unpack()[1].deep_unpack();
|
||||
|
||||
let modeLabels = {
|
||||
'never': _("Never group windows"),
|
||||
'auto': _("Group windows when space is limited"),
|
||||
'always': _("Always group windows")
|
||||
};
|
||||
|
||||
let radio = null;
|
||||
for (let i = 0; i < modes.length; i++) {
|
||||
let mode = modes[i];
|
||||
let label = modeLabels[mode];
|
||||
if (!label) {
|
||||
log('Unhandled option "%s" for grouping-mode'.format(mode));
|
||||
continue;
|
||||
}
|
||||
|
||||
radio = new Gtk.RadioButton({ active: currentMode == mode,
|
||||
label: label,
|
||||
group: radio });
|
||||
grid.add(radio);
|
||||
|
||||
radio.connect('toggled', Lang.bind(this, function(button) {
|
||||
if (button.active)
|
||||
this._settings.set_string('grouping-mode', mode);
|
||||
}));
|
||||
for (const { mode, title } of modes) {
|
||||
const check = new Gtk.CheckButton({
|
||||
action_name: 'window-list.grouping-mode',
|
||||
action_target: new GLib.Variant('s', mode),
|
||||
});
|
||||
const row = new Adw.ActionRow({
|
||||
activatable_widget: check,
|
||||
title,
|
||||
});
|
||||
row.add_prefix(check);
|
||||
groupingGroup.add(row);
|
||||
}
|
||||
|
||||
let check = new Gtk.CheckButton({ label: _("Show on all monitors"),
|
||||
margin_top: 6 });
|
||||
this._settings.bind('show-on-all-monitors', check, 'active', Gio.SettingsBindFlags.DEFAULT);
|
||||
this.add(check);
|
||||
const miscGroup = new Adw.PreferencesGroup();
|
||||
this.add(miscGroup);
|
||||
|
||||
let toggle = new Gtk.Switch({
|
||||
action_name: 'window-list.show-on-all-monitors',
|
||||
valign: Gtk.Align.CENTER,
|
||||
});
|
||||
let row = new Adw.ActionRow({
|
||||
title: _('Show on all monitors'),
|
||||
activatable_widget: toggle,
|
||||
});
|
||||
row.add_suffix(toggle);
|
||||
miscGroup.add(row);
|
||||
|
||||
toggle = new Gtk.Switch({
|
||||
action_name: 'window-list.display-all-workspaces',
|
||||
valign: Gtk.Align.CENTER,
|
||||
});
|
||||
this._settings.bind('display-all-workspaces',
|
||||
toggle, 'active', Gio.SettingsBindFlags.DEFAULT);
|
||||
row = new Adw.ActionRow({
|
||||
title: _('Show windows from all workspaces'),
|
||||
activatable_widget: toggle,
|
||||
});
|
||||
row.add_suffix(toggle);
|
||||
miscGroup.add(row);
|
||||
}
|
||||
});
|
||||
|
||||
function buildPrefsWidget() {
|
||||
let widget = new WindowListPrefsWidget();
|
||||
widget.show_all();
|
||||
|
||||
return widget;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Gtk.Widget} - the prefs widget
|
||||
*/
|
||||
function buildPrefsWidget() {
|
||||
return new WindowListPrefsWidget();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
.bottom-panel {
|
||||
/* .window-button-icon height +
|
||||
.window-button vertical padding +
|
||||
.window-button > StWidget vertical padding) */
|
||||
height: 2.25em;
|
||||
}
|
||||
|
||||
.window-list {
|
||||
spacing: 2px;
|
||||
font-size: 10pt;
|
||||
@@ -26,18 +19,31 @@
|
||||
spacing: 4px;
|
||||
}
|
||||
|
||||
.window-button > StWidget {
|
||||
-st-natural-width: 18.75em;
|
||||
max-width: 18.75em;
|
||||
.window-button > StWidget,
|
||||
.window-picker-toggle > StWidget {
|
||||
color: #bbb;
|
||||
background-color: black;
|
||||
border-radius: 4px;
|
||||
border-radius: 2px;
|
||||
padding: 3px 6px 1px;
|
||||
box-shadow: inset 1px 1px 4px rgba(255,255,255,0.5);
|
||||
text-shadow: 1px 1px 4px rgba(0,0,0,0.8);
|
||||
}
|
||||
|
||||
.window-button:hover > StWidget {
|
||||
.window-picker-toggle {
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.window-picker-toggle > StWidet {
|
||||
border: 1px solid rgba(255,255,255,0.3);
|
||||
}
|
||||
|
||||
.window-button > StWidget {
|
||||
-st-natural-width: 18.75em;
|
||||
max-width: 18.75em;
|
||||
}
|
||||
|
||||
.window-button:hover > StWidget,
|
||||
.window-picker-toggle:hover > StWidget {
|
||||
color: white;
|
||||
background-color: #1f1f1f;
|
||||
}
|
||||
@@ -47,12 +53,14 @@
|
||||
box-shadow: inset 2px 2px 4px rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
.window-button.focused > StWidget {
|
||||
.window-button.focused > StWidget,
|
||||
.window-picker-toggle:checked > StWidget {
|
||||
color: white;
|
||||
box-shadow: inset 1px 1px 4px rgba(255,255,255,0.7);
|
||||
}
|
||||
|
||||
.window-button.focused:active > StWidget {
|
||||
.window-button.focused:active > StWidget,
|
||||
.window-picker-toggle:checked:active > StWidget {
|
||||
box-shadow: inset 2px 2px 4px rgba(255,255,255,0.7);
|
||||
}
|
||||
|
||||
@@ -70,13 +78,36 @@
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.window-list-workspace-indicator {
|
||||
.window-list-workspace-indicator .status-label-bin {
|
||||
background-color: rgba(200, 200, 200, .3);
|
||||
border: 1px solid #cccccc;
|
||||
padding: 0 3px;
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
.window-list-workspace-indicator .workspaces-box {
|
||||
spacing: 3px;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.window-list-workspace-indicator > StWidget {
|
||||
background-color: rgba(200, 200, 200, .3);
|
||||
border: 1px solid #cccccc;
|
||||
.window-list-workspace-indicator .workspace {
|
||||
border: 2px solid #000;
|
||||
width: 52px;
|
||||
border-radius: 4px;
|
||||
background-color: #595959;
|
||||
}
|
||||
|
||||
.window-list-workspace-indicator .workspace.active {
|
||||
border-color: #fff;
|
||||
}
|
||||
|
||||
.window-list-window-preview {
|
||||
background-color: #bebebe;
|
||||
border: 1px solid #828282;
|
||||
}
|
||||
|
||||
.window-list-window-preview.active {
|
||||
background-color: #d4d4d4;
|
||||
}
|
||||
|
||||
.notification {
|
||||
|
||||
364
extensions/window-list/windowPicker.js
Normal file
364
extensions/window-list/windowPicker.js
Normal file
@@ -0,0 +1,364 @@
|
||||
/* exported WindowPicker, WindowPickerToggle */
|
||||
const { Clutter, GObject, Shell, St } = imports.gi;
|
||||
|
||||
const Layout = imports.ui.layout;
|
||||
const Main = imports.ui.main;
|
||||
const { WorkspacesDisplay } = imports.ui.workspacesView;
|
||||
const Workspace = imports.ui.workspace;
|
||||
|
||||
const { VIGNETTE_BRIGHTNESS } = imports.ui.lightbox;
|
||||
const {
|
||||
SIDE_CONTROLS_ANIMATION_TIME,
|
||||
OverviewAdjustment,
|
||||
ControlsState,
|
||||
} = imports.ui.overviewControls;
|
||||
|
||||
class MyWorkspacesDisplay extends WorkspacesDisplay {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
|
||||
constructor(controls, overviewAdjustment) {
|
||||
let workspaceManager = global.workspace_manager;
|
||||
|
||||
const workspaceAdjustment = new St.Adjustment({
|
||||
value: workspaceManager.get_active_workspace_index(),
|
||||
lower: 0,
|
||||
page_increment: 1,
|
||||
page_size: 1,
|
||||
step_increment: 0,
|
||||
upper: workspaceManager.n_workspaces,
|
||||
});
|
||||
|
||||
super(controls, workspaceAdjustment, overviewAdjustment);
|
||||
|
||||
this._workspaceAdjustment = workspaceAdjustment;
|
||||
this._workspaceAdjustment.actor = this;
|
||||
|
||||
this._nWorkspacesChangedId =
|
||||
workspaceManager.connect('notify::n-workspaces',
|
||||
this._updateAdjustment.bind(this));
|
||||
|
||||
this.add_constraint(
|
||||
new Layout.MonitorConstraint({
|
||||
primary: true,
|
||||
work_area: true,
|
||||
}));
|
||||
}
|
||||
|
||||
prepareToEnterOverview(...args) {
|
||||
if (!this._scrollEventId) {
|
||||
this._scrollEventId = Main.windowPicker.connect('scroll-event',
|
||||
this._onScrollEvent.bind(this));
|
||||
}
|
||||
|
||||
super.prepareToEnterOverview(...args);
|
||||
}
|
||||
|
||||
vfunc_hide(...args) {
|
||||
if (this._scrollEventId > 0)
|
||||
Main.windowPicker.disconnect(this._scrollEventId);
|
||||
this._scrollEventId = 0;
|
||||
|
||||
super.vfunc_hide(...args);
|
||||
}
|
||||
|
||||
_updateAdjustment() {
|
||||
let workspaceManager = global.workspace_manager;
|
||||
this._workspaceAdjustment.set({
|
||||
upper: workspaceManager.n_workspaces,
|
||||
value: workspaceManager.get_active_workspace_index(),
|
||||
});
|
||||
}
|
||||
|
||||
_onDestroy() {
|
||||
if (this._nWorkspacesChangedId)
|
||||
global.workspace_manager.disconnect(this._nWorkspacesChangedId);
|
||||
this._nWorkspacesChangedId = 0;
|
||||
|
||||
super._onDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
class MyWorkspace extends Workspace.Workspace {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
|
||||
this._adjChangedId =
|
||||
this._overviewAdjustment.connect('notify::value', () => {
|
||||
const { value: progress } = this._overviewAdjustment;
|
||||
const brightness = 1 - (1 - VIGNETTE_BRIGHTNESS) * progress;
|
||||
for (const bg of this._background?._backgroundGroup ?? []) {
|
||||
bg.content.set({
|
||||
vignette: true,
|
||||
brightness,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_onDestroy() {
|
||||
super._onDestroy();
|
||||
|
||||
if (this._adjChangedId)
|
||||
this._overviewAdjustment.disconnect(this._adjChangedId);
|
||||
this._adjChangedId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
class MyWorkspaceBackground extends Workspace.WorkspaceBackground {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
|
||||
_updateBorderRadius() {
|
||||
}
|
||||
|
||||
vfunc_allocate(box) {
|
||||
this.set_allocation(box);
|
||||
|
||||
const themeNode = this.get_theme_node();
|
||||
const contentBox = themeNode.get_content_box(box);
|
||||
|
||||
this._bin.allocate(contentBox);
|
||||
|
||||
const [contentWidth, contentHeight] = contentBox.get_size();
|
||||
const monitor = Main.layoutManager.monitors[this._monitorIndex];
|
||||
const xRatio = contentWidth / this._workarea.width;
|
||||
const yRatio = contentHeight / this._workarea.height;
|
||||
|
||||
const right = area => area.x + area.width;
|
||||
const bottom = area => area.y + area.height;
|
||||
|
||||
const offsets = {
|
||||
left: xRatio * (this._workarea.x - monitor.x),
|
||||
right: xRatio * (right(monitor) - right(this._workarea)),
|
||||
top: yRatio * (this._workarea.y - monitor.y),
|
||||
bottom: yRatio * (bottom(monitor) - bottom(this._workarea)),
|
||||
};
|
||||
|
||||
contentBox.set_origin(-offsets.left, -offsets.top);
|
||||
contentBox.set_size(
|
||||
offsets.left + contentWidth + offsets.right,
|
||||
offsets.top + contentHeight + offsets.bottom);
|
||||
this._backgroundGroup.allocate(contentBox);
|
||||
}
|
||||
}
|
||||
|
||||
var WindowPicker = class WindowPicker extends Clutter.Actor {
|
||||
static [GObject.signals] = {
|
||||
'open-state-changed': { param_types: [GObject.TYPE_BOOLEAN] },
|
||||
};
|
||||
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super({ reactive: true });
|
||||
|
||||
this._visible = false;
|
||||
this._modal = false;
|
||||
|
||||
this._overlayKeyId = 0;
|
||||
this._stageKeyPressId = 0;
|
||||
|
||||
this._adjustment = new OverviewAdjustment(this);
|
||||
|
||||
this.connect('destroy', this._onDestroy.bind(this));
|
||||
|
||||
global.bind_property('screen-width',
|
||||
this, 'width',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
global.bind_property('screen-height',
|
||||
this, 'height',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
|
||||
this._workspacesDisplay = new MyWorkspacesDisplay(this, this._adjustment);
|
||||
this.add_child(this._workspacesDisplay);
|
||||
|
||||
Main.uiGroup.insert_child_below(this, global.window_group);
|
||||
|
||||
if (!Main.sessionMode.hasOverview) {
|
||||
this._injectBackgroundShade();
|
||||
|
||||
this._overlayKeyId = global.display.connect('overlay-key', () => {
|
||||
if (!this._visible)
|
||||
this.open();
|
||||
else
|
||||
this.close();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_injectBackgroundShade() {
|
||||
this._origWorkspace = Workspace.Workspace;
|
||||
this._origWorkspaceBackground = Workspace.WorkspaceBackground;
|
||||
|
||||
Workspace.Workspace = MyWorkspace;
|
||||
Workspace.WorkspaceBackground = MyWorkspaceBackground;
|
||||
}
|
||||
|
||||
get visible() {
|
||||
return this._visible;
|
||||
}
|
||||
|
||||
open() {
|
||||
if (this._visible)
|
||||
return;
|
||||
|
||||
this._visible = true;
|
||||
|
||||
if (!this._syncGrab())
|
||||
return;
|
||||
|
||||
this._fakeOverviewVisible(true);
|
||||
this._workspacesDisplay.prepareToEnterOverview();
|
||||
Main.overview._animationInProgress = true;
|
||||
|
||||
this._adjustment.value = ControlsState.HIDDEN;
|
||||
this._adjustment.ease(ControlsState.WINDOW_PICKER, {
|
||||
duration: SIDE_CONTROLS_ANIMATION_TIME,
|
||||
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||||
onComplete: () => (Main.overview._animationInProgress = false),
|
||||
});
|
||||
|
||||
this._stageKeyPressId = global.stage.connect('key-press-event',
|
||||
(a, event) => {
|
||||
let sym = event.get_key_symbol();
|
||||
if (sym === Clutter.KEY_Escape) {
|
||||
this.close();
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
});
|
||||
|
||||
this.emit('open-state-changed', this._visible);
|
||||
}
|
||||
|
||||
close() {
|
||||
if (!this._visible)
|
||||
return;
|
||||
|
||||
this._visible = false;
|
||||
|
||||
if (!this._syncGrab())
|
||||
return;
|
||||
|
||||
this._workspacesDisplay.prepareToLeaveOverview();
|
||||
|
||||
Main.overview._animationInProgress = true;
|
||||
this._adjustment.ease(ControlsState.HIDDEN, {
|
||||
duration: SIDE_CONTROLS_ANIMATION_TIME,
|
||||
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||||
onComplete: () => {
|
||||
Main.overview._animationInProgress = false;
|
||||
this._workspacesDisplay.hide();
|
||||
this._fakeOverviewVisible(false);
|
||||
},
|
||||
});
|
||||
|
||||
global.stage.disconnect(this._stageKeyPressId);
|
||||
this._stageKeyPressId = 0;
|
||||
|
||||
this.emit('open-state-changed', this._visible);
|
||||
}
|
||||
|
||||
getWorkspacesBoxForState() {
|
||||
return this.allocation;
|
||||
}
|
||||
|
||||
_fakeOverviewVisible(visible) {
|
||||
// Fake overview state for WorkspacesDisplay
|
||||
Main.overview._visible = visible;
|
||||
|
||||
// Hide real windows
|
||||
Main.layoutManager._inOverview = visible;
|
||||
Main.layoutManager._updateVisibility();
|
||||
}
|
||||
|
||||
_syncGrab() {
|
||||
if (this._visible) {
|
||||
if (this._modal)
|
||||
return true;
|
||||
|
||||
const grab = Main.pushModal(global.stage, {
|
||||
actionMode: Shell.ActionMode.OVERVIEW,
|
||||
});
|
||||
if (grab.get_seat_state() !== Clutter.GrabState.NONE) {
|
||||
this._grab = grab;
|
||||
this._modal = true;
|
||||
} else {
|
||||
Main.popModal(grab);
|
||||
this.hide();
|
||||
return false;
|
||||
}
|
||||
} else if (this._modal) {
|
||||
Main.popModal(this._grab);
|
||||
this._modal = false;
|
||||
this._grab = null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
_onDestroy() {
|
||||
if (this._origWorkspace)
|
||||
Workspace.Workspace = this._origWorkspace;
|
||||
|
||||
if (this._origWorkspaceBackground)
|
||||
Workspace.WorkspaceBackground = this._origWorkspaceBackground;
|
||||
|
||||
if (this._monitorsChangedId)
|
||||
Main.layoutManager.disconnect(this._monitorsChangedId);
|
||||
this._monitorsChangedId = 0;
|
||||
|
||||
if (this._overlayKeyId)
|
||||
global.display.disconnect(this._overlayKeyId);
|
||||
this._overlayKeyId = 0;
|
||||
|
||||
if (this._stageKeyPressId)
|
||||
global.stage.disconnect(this._stageKeyPressId);
|
||||
this._stageKeyPressId = 0;
|
||||
}
|
||||
};
|
||||
|
||||
var WindowPickerToggle = class WindowPickerToggle extends St.Button {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
let iconBin = new St.Widget({
|
||||
layout_manager: new Clutter.BinLayout(),
|
||||
});
|
||||
iconBin.add_child(new St.Icon({
|
||||
icon_name: 'focus-windows-symbolic',
|
||||
icon_size: 16,
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
}));
|
||||
super({
|
||||
style_class: 'window-picker-toggle',
|
||||
child: iconBin,
|
||||
visible: !Main.sessionMode.hasOverview,
|
||||
toggle_mode: true,
|
||||
});
|
||||
|
||||
this.connect('notify::checked', () => {
|
||||
if (this.checked)
|
||||
Main.windowPicker.open();
|
||||
else
|
||||
Main.windowPicker.close();
|
||||
});
|
||||
|
||||
Main.windowPicker.connect('open-state-changed', () => {
|
||||
this.checked = Main.windowPicker.visible;
|
||||
});
|
||||
}
|
||||
};
|
||||
450
extensions/window-list/workspaceIndicator.js
Normal file
450
extensions/window-list/workspaceIndicator.js
Normal file
@@ -0,0 +1,450 @@
|
||||
/* exported WorkspaceIndicator */
|
||||
const { Clutter, Gio, GObject, Meta, St } = imports.gi;
|
||||
|
||||
const DND = imports.ui.dnd;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
const _ = ExtensionUtils.gettext;
|
||||
|
||||
const TOOLTIP_OFFSET = 6;
|
||||
const TOOLTIP_ANIMATION_TIME = 150;
|
||||
|
||||
const MAX_THUMBNAILS = 6;
|
||||
|
||||
class WindowPreview extends St.Button {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
|
||||
constructor(window) {
|
||||
super({
|
||||
style_class: 'window-list-window-preview',
|
||||
});
|
||||
|
||||
this._delegate = this;
|
||||
DND.makeDraggable(this, { restoreOnSuccess: true });
|
||||
|
||||
this._window = window;
|
||||
|
||||
this.connect('destroy', this._onDestroy.bind(this));
|
||||
|
||||
this._sizeChangedId = this._window.connect('size-changed',
|
||||
() => this.queue_relayout());
|
||||
this._positionChangedId = this._window.connect('position-changed',
|
||||
() => {
|
||||
this._updateVisible();
|
||||
this.queue_relayout();
|
||||
});
|
||||
this._minimizedChangedId = this._window.connect('notify::minimized',
|
||||
this._updateVisible.bind(this));
|
||||
|
||||
this._focusChangedId = global.display.connect('notify::focus-window',
|
||||
this._onFocusChanged.bind(this));
|
||||
this._onFocusChanged();
|
||||
}
|
||||
|
||||
// needed for DND
|
||||
get metaWindow() {
|
||||
return this._window;
|
||||
}
|
||||
|
||||
_onDestroy() {
|
||||
this._window.disconnect(this._sizeChangedId);
|
||||
this._window.disconnect(this._positionChangedId);
|
||||
this._window.disconnect(this._minimizedChangedId);
|
||||
global.display.disconnect(this._focusChangedId);
|
||||
}
|
||||
|
||||
_onFocusChanged() {
|
||||
if (global.display.focus_window === this._window)
|
||||
this.add_style_class_name('active');
|
||||
else
|
||||
this.remove_style_class_name('active');
|
||||
}
|
||||
|
||||
_updateVisible() {
|
||||
const monitor = Main.layoutManager.findIndexForActor(this);
|
||||
const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor);
|
||||
this.visible = this._window.get_frame_rect().overlap(workArea) &&
|
||||
this._window.window_type !== Meta.WindowType.DESKTOP &&
|
||||
this._window.showing_on_its_workspace();
|
||||
}
|
||||
}
|
||||
|
||||
class WorkspaceLayout extends Clutter.LayoutManager {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
|
||||
vfunc_get_preferred_width() {
|
||||
return [0, 0];
|
||||
}
|
||||
|
||||
vfunc_get_preferred_height() {
|
||||
return [0, 0];
|
||||
}
|
||||
|
||||
vfunc_allocate(container, box) {
|
||||
const monitor = Main.layoutManager.findIndexForActor(container);
|
||||
const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor);
|
||||
const hscale = box.get_width() / workArea.width;
|
||||
const vscale = box.get_height() / workArea.height;
|
||||
|
||||
for (const child of container) {
|
||||
const childBox = new Clutter.ActorBox();
|
||||
const frameRect = child.metaWindow.get_frame_rect();
|
||||
childBox.set_size(
|
||||
Math.round(Math.min(frameRect.width, workArea.width) * hscale),
|
||||
Math.round(Math.min(frameRect.height, workArea.height) * vscale));
|
||||
childBox.set_origin(
|
||||
Math.round((frameRect.x - workArea.x) * hscale),
|
||||
Math.round((frameRect.y - workArea.y) * vscale));
|
||||
child.allocate(childBox);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WorkspaceThumbnail extends St.Button {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
|
||||
constructor(index) {
|
||||
super({
|
||||
style_class: 'workspace',
|
||||
child: new Clutter.Actor({
|
||||
layout_manager: new WorkspaceLayout(),
|
||||
clip_to_allocation: true,
|
||||
}),
|
||||
});
|
||||
|
||||
this._tooltip = new St.Label({
|
||||
style_class: 'dash-label',
|
||||
visible: false,
|
||||
});
|
||||
Main.uiGroup.add_child(this._tooltip);
|
||||
|
||||
this.connect('destroy', this._onDestroy.bind(this));
|
||||
this.connect('notify::hover', this._syncTooltip.bind(this));
|
||||
|
||||
this._index = index;
|
||||
this._delegate = this; // needed for DND
|
||||
|
||||
this._windowPreviews = new Map();
|
||||
|
||||
let workspaceManager = global.workspace_manager;
|
||||
this._workspace = workspaceManager.get_workspace_by_index(index);
|
||||
|
||||
this._windowAddedId = this._workspace.connect('window-added',
|
||||
(ws, window) => {
|
||||
this._addWindow(window);
|
||||
});
|
||||
this._windowRemovedId = this._workspace.connect('window-removed',
|
||||
(ws, window) => {
|
||||
this._removeWindow(window);
|
||||
});
|
||||
this._restackedId = global.display.connect('restacked',
|
||||
this._onRestacked.bind(this));
|
||||
|
||||
this._workspace.list_windows().forEach(w => this._addWindow(w));
|
||||
this._onRestacked();
|
||||
}
|
||||
|
||||
acceptDrop(source) {
|
||||
if (!source.metaWindow)
|
||||
return false;
|
||||
|
||||
this._moveWindow(source.metaWindow);
|
||||
return true;
|
||||
}
|
||||
|
||||
handleDragOver(source) {
|
||||
if (source.metaWindow)
|
||||
return DND.DragMotionResult.MOVE_DROP;
|
||||
else
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
}
|
||||
|
||||
_addWindow(window) {
|
||||
if (this._windowPreviews.has(window))
|
||||
return;
|
||||
|
||||
let preview = new WindowPreview(window);
|
||||
preview.connect('clicked', (a, btn) => this.emit('clicked', btn));
|
||||
this._windowPreviews.set(window, preview);
|
||||
this.child.add_child(preview);
|
||||
}
|
||||
|
||||
_removeWindow(window) {
|
||||
let preview = this._windowPreviews.get(window);
|
||||
if (!preview)
|
||||
return;
|
||||
|
||||
this._windowPreviews.delete(window);
|
||||
preview.destroy();
|
||||
}
|
||||
|
||||
_onRestacked() {
|
||||
let lastPreview = null;
|
||||
let windows = global.get_window_actors().map(a => a.meta_window);
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
let preview = this._windowPreviews.get(windows[i]);
|
||||
if (!preview)
|
||||
continue;
|
||||
|
||||
this.child.set_child_above_sibling(preview, lastPreview);
|
||||
lastPreview = preview;
|
||||
}
|
||||
}
|
||||
|
||||
_moveWindow(window) {
|
||||
let monitorIndex = Main.layoutManager.findIndexForActor(this);
|
||||
if (monitorIndex !== window.get_monitor())
|
||||
window.move_to_monitor(monitorIndex);
|
||||
window.change_workspace_by_index(this._index, false);
|
||||
}
|
||||
|
||||
on_clicked() {
|
||||
let ws = global.workspace_manager.get_workspace_by_index(this._index);
|
||||
if (ws)
|
||||
ws.activate(global.get_current_time());
|
||||
}
|
||||
|
||||
_syncTooltip() {
|
||||
if (this.hover) {
|
||||
this._tooltip.set({
|
||||
text: Meta.prefs_get_workspace_name(this._index),
|
||||
visible: true,
|
||||
opacity: 0,
|
||||
});
|
||||
|
||||
const [stageX, stageY] = this.get_transformed_position();
|
||||
const thumbWidth = this.allocation.get_width();
|
||||
const tipWidth = this._tooltip.width;
|
||||
const tipHeight = this._tooltip.height;
|
||||
const xOffset = Math.floor((thumbWidth - tipWidth) / 2);
|
||||
const monitor = Main.layoutManager.findMonitorForActor(this);
|
||||
const x = Math.clamp(
|
||||
stageX + xOffset,
|
||||
monitor.x,
|
||||
monitor.x + monitor.width - tipWidth);
|
||||
const y = stageY - tipHeight - TOOLTIP_OFFSET;
|
||||
this._tooltip.set_position(x, y);
|
||||
}
|
||||
|
||||
this._tooltip.ease({
|
||||
opacity: this.hover ? 255 : 0,
|
||||
duration: TOOLTIP_ANIMATION_TIME,
|
||||
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||||
onComplete: () => (this._tooltip.visible = this.hover),
|
||||
});
|
||||
}
|
||||
|
||||
_onDestroy() {
|
||||
this._tooltip.destroy();
|
||||
|
||||
this._workspace.disconnect(this._windowAddedId);
|
||||
this._workspace.disconnect(this._windowRemovedId);
|
||||
global.display.disconnect(this._restackedId);
|
||||
}
|
||||
}
|
||||
|
||||
var WorkspaceIndicator = class WorkspaceIndicator extends PanelMenu.Button {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super(0.0, _('Workspace Indicator'), true);
|
||||
this.setMenu(new PopupMenu.PopupMenu(this, 0.0, St.Side.BOTTOM));
|
||||
this.add_style_class_name('window-list-workspace-indicator');
|
||||
this.remove_style_class_name('panel-button');
|
||||
this.menu.actor.remove_style_class_name('panel-menu');
|
||||
|
||||
let container = new St.Widget({
|
||||
layout_manager: new Clutter.BinLayout(),
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
});
|
||||
this.add_actor(container);
|
||||
|
||||
let workspaceManager = global.workspace_manager;
|
||||
|
||||
this._currentWorkspace = workspaceManager.get_active_workspace_index();
|
||||
this._statusLabel = new St.Label({ text: this._getStatusText() });
|
||||
|
||||
this._statusBin = new St.Bin({
|
||||
style_class: 'status-label-bin',
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
child: this._statusLabel,
|
||||
});
|
||||
container.add_actor(this._statusBin);
|
||||
|
||||
this._thumbnailsBox = new St.BoxLayout({
|
||||
style_class: 'workspaces-box',
|
||||
y_expand: true,
|
||||
reactive: true,
|
||||
});
|
||||
this._thumbnailsBox.connect('scroll-event',
|
||||
this._onScrollEvent.bind(this));
|
||||
container.add_actor(this._thumbnailsBox);
|
||||
|
||||
this._workspacesItems = [];
|
||||
|
||||
this._workspaceManagerSignals = [
|
||||
workspaceManager.connect('notify::n-workspaces',
|
||||
this._nWorkspacesChanged.bind(this)),
|
||||
workspaceManager.connect_after('workspace-switched',
|
||||
this._onWorkspaceSwitched.bind(this)),
|
||||
workspaceManager.connect('notify::layout-rows',
|
||||
this._updateThumbnailVisibility.bind(this)),
|
||||
];
|
||||
|
||||
this.connect('scroll-event', this._onScrollEvent.bind(this));
|
||||
this._updateMenu();
|
||||
this._updateThumbnails();
|
||||
this._updateThumbnailVisibility();
|
||||
|
||||
this._settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.wm.preferences' });
|
||||
this._settingsChangedId = this._settings.connect(
|
||||
'changed::workspace-names', this._updateMenuLabels.bind(this));
|
||||
}
|
||||
|
||||
_onDestroy() {
|
||||
for (let i = 0; i < this._workspaceManagerSignals.length; i++)
|
||||
global.workspace_manager.disconnect(this._workspaceManagerSignals[i]);
|
||||
|
||||
if (this._settingsChangedId) {
|
||||
this._settings.disconnect(this._settingsChangedId);
|
||||
this._settingsChangedId = 0;
|
||||
}
|
||||
|
||||
super._onDestroy();
|
||||
}
|
||||
|
||||
_updateThumbnailVisibility() {
|
||||
const { workspaceManager } = global;
|
||||
const vertical = workspaceManager.layout_rows === -1;
|
||||
const useMenu =
|
||||
vertical || workspaceManager.n_workspaces > MAX_THUMBNAILS;
|
||||
this.reactive = useMenu;
|
||||
|
||||
this._statusBin.visible = useMenu;
|
||||
this._thumbnailsBox.visible = !useMenu;
|
||||
}
|
||||
|
||||
_onWorkspaceSwitched() {
|
||||
let workspaceManager = global.workspace_manager;
|
||||
this._currentWorkspace = workspaceManager.get_active_workspace_index();
|
||||
|
||||
this._updateMenuOrnament();
|
||||
this._updateActiveThumbnail();
|
||||
|
||||
this._statusLabel.set_text(this._getStatusText());
|
||||
}
|
||||
|
||||
_nWorkspacesChanged() {
|
||||
this._updateMenu();
|
||||
this._updateThumbnails();
|
||||
this._updateThumbnailVisibility();
|
||||
}
|
||||
|
||||
_updateMenuOrnament() {
|
||||
for (let i = 0; i < this._workspacesItems.length; i++) {
|
||||
this._workspacesItems[i].setOrnament(i === this._currentWorkspace
|
||||
? PopupMenu.Ornament.DOT
|
||||
: PopupMenu.Ornament.NONE);
|
||||
}
|
||||
}
|
||||
|
||||
_updateActiveThumbnail() {
|
||||
let thumbs = this._thumbnailsBox.get_children();
|
||||
for (let i = 0; i < thumbs.length; i++) {
|
||||
if (i === this._currentWorkspace)
|
||||
thumbs[i].add_style_class_name('active');
|
||||
else
|
||||
thumbs[i].remove_style_class_name('active');
|
||||
}
|
||||
}
|
||||
|
||||
_getStatusText() {
|
||||
let workspaceManager = global.workspace_manager;
|
||||
let current = workspaceManager.get_active_workspace_index();
|
||||
let total = workspaceManager.n_workspaces;
|
||||
|
||||
return '%d / %d'.format(current + 1, total);
|
||||
}
|
||||
|
||||
_updateMenuLabels() {
|
||||
for (let i = 0; i < this._workspacesItems.length; i++) {
|
||||
let item = this._workspacesItems[i];
|
||||
let name = Meta.prefs_get_workspace_name(i);
|
||||
item.label.text = name;
|
||||
}
|
||||
}
|
||||
|
||||
_updateMenu() {
|
||||
let workspaceManager = global.workspace_manager;
|
||||
|
||||
this.menu.removeAll();
|
||||
this._workspacesItems = [];
|
||||
this._currentWorkspace = workspaceManager.get_active_workspace_index();
|
||||
|
||||
for (let i = 0; i < workspaceManager.n_workspaces; i++) {
|
||||
let name = Meta.prefs_get_workspace_name(i);
|
||||
let item = new PopupMenu.PopupMenuItem(name);
|
||||
item.workspaceId = i;
|
||||
|
||||
item.connect('activate', () => {
|
||||
this._activate(item.workspaceId);
|
||||
});
|
||||
|
||||
if (i === this._currentWorkspace)
|
||||
item.setOrnament(PopupMenu.Ornament.DOT);
|
||||
|
||||
this.menu.addMenuItem(item);
|
||||
this._workspacesItems[i] = item;
|
||||
}
|
||||
|
||||
this._statusLabel.set_text(this._getStatusText());
|
||||
}
|
||||
|
||||
_updateThumbnails() {
|
||||
let workspaceManager = global.workspace_manager;
|
||||
|
||||
this._thumbnailsBox.destroy_all_children();
|
||||
|
||||
for (let i = 0; i < workspaceManager.n_workspaces; i++) {
|
||||
let thumb = new WorkspaceThumbnail(i);
|
||||
this._thumbnailsBox.add_actor(thumb);
|
||||
}
|
||||
this._updateActiveThumbnail();
|
||||
}
|
||||
|
||||
_activate(index) {
|
||||
let workspaceManager = global.workspace_manager;
|
||||
|
||||
if (index >= 0 && index < workspaceManager.n_workspaces) {
|
||||
let metaWorkspace = workspaceManager.get_workspace_by_index(index);
|
||||
metaWorkspace.activate(global.get_current_time());
|
||||
}
|
||||
}
|
||||
|
||||
_onScrollEvent(actor, event) {
|
||||
let direction = event.get_scroll_direction();
|
||||
let diff = 0;
|
||||
if (direction === Clutter.ScrollDirection.DOWN)
|
||||
diff = 1;
|
||||
else if (direction === Clutter.ScrollDirection.UP)
|
||||
diff = -1;
|
||||
else
|
||||
return;
|
||||
|
||||
let newIndex = this._currentWorkspace + diff;
|
||||
this._activate(newIndex);
|
||||
}
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
EXTENSION_ID = windowsNavigator
|
||||
|
||||
include ../../extension.mk
|
||||
@@ -1,154 +1,194 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const St = imports.gi.St;
|
||||
/* exported init */
|
||||
const { Clutter, Graphene, GObject, St } = imports.gi;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const OverviewControls = imports.ui.overviewControls;
|
||||
const Workspace = imports.ui.workspace;
|
||||
const WorkspacesView = imports.ui.workspacesView;
|
||||
|
||||
function injectToFunction(parent, name, func) {
|
||||
let origin = parent[name];
|
||||
parent[name] = function() {
|
||||
let ret;
|
||||
ret = origin.apply(this, arguments);
|
||||
if (ret === undefined)
|
||||
ret = func.apply(this, arguments);
|
||||
return ret;
|
||||
const WINDOW_SLOT = 4;
|
||||
|
||||
class MyWorkspace extends Workspace.Workspace {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
return origin;
|
||||
}
|
||||
|
||||
let winInjections, workspaceInjections, workViewInjections, createdActors, connectedSignals;
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
|
||||
function resetState() {
|
||||
winInjections = { };
|
||||
workspaceInjections = { };
|
||||
workViewInjections = { };
|
||||
createdActors = [ ];
|
||||
connectedSignals = [ ];
|
||||
}
|
||||
if (this.metaWorkspace && this.metaWorkspace.index() < 9) {
|
||||
this._tip = new St.Label({
|
||||
style_class: 'extension-windowsNavigator-window-tooltip',
|
||||
visible: false,
|
||||
});
|
||||
this.add_actor(this._tip);
|
||||
|
||||
function enable() {
|
||||
resetState();
|
||||
|
||||
Workspace.WindowOverlay.prototype.showTooltip = function() {
|
||||
this._text.raise_top();
|
||||
this._text.show();
|
||||
this._text.text = (this._windowClone.slotId + 1).toString();
|
||||
this.connect('notify::scale-x', () => {
|
||||
this._tip.set_scale(1 / this.scale_x, 1 / this.scale_x);
|
||||
});
|
||||
} else {
|
||||
this._tip = null;
|
||||
}
|
||||
}
|
||||
winInjections['showTooltip'] = undefined;
|
||||
|
||||
Workspace.WindowOverlay.prototype.hideTooltip = function() {
|
||||
if (this._text && this._text.visible)
|
||||
this._text.hide();
|
||||
vfunc_allocate(box) {
|
||||
super.vfunc_allocate(box);
|
||||
|
||||
if (this._tip)
|
||||
this._tip.allocate_preferred_size(0, 0);
|
||||
}
|
||||
winInjections['hideTooltip'] = undefined;
|
||||
|
||||
Workspace.Workspace.prototype.showTooltip = function() {
|
||||
if (this._tip == null || this._actualGeometry == null)
|
||||
showTooltip() {
|
||||
if (!this._tip)
|
||||
return;
|
||||
this._tip.text = (this.metaWorkspace.index() + 1).toString();
|
||||
this._tip.show();
|
||||
this.set_child_below_sibling(this._tip, null);
|
||||
}
|
||||
|
||||
// Hand code this instead of using _getSpacingAndPadding
|
||||
// because that fails on empty workspaces
|
||||
let node = this.actor.get_theme_node();
|
||||
let padding = {
|
||||
left: node.get_padding(St.Side.LEFT),
|
||||
top: node.get_padding(St.Side.TOP),
|
||||
bottom: node.get_padding(St.Side.BOTTOM),
|
||||
right: node.get_padding(St.Side.RIGHT),
|
||||
hideTooltip() {
|
||||
if (this._tip)
|
||||
this._tip.hide();
|
||||
}
|
||||
|
||||
getWindowWithTooltip(id) {
|
||||
const { layoutManager } = this._container;
|
||||
const slot = layoutManager._windowSlots[id - 1];
|
||||
return slot ? slot[WINDOW_SLOT].metaWindow : null;
|
||||
}
|
||||
|
||||
showWindowsTooltips() {
|
||||
const { layoutManager } = this._container;
|
||||
for (let i = 0; i < layoutManager._windowSlots.length; i++) {
|
||||
if (layoutManager._windowSlots[i])
|
||||
layoutManager._windowSlots[i][WINDOW_SLOT].showTooltip(`${i + 1}`);
|
||||
}
|
||||
}
|
||||
|
||||
hideWindowsTooltips() {
|
||||
const { layoutManager } = this._container;
|
||||
for (let i in layoutManager._windowSlots) {
|
||||
if (layoutManager._windowSlots[i])
|
||||
layoutManager._windowSlots[i][WINDOW_SLOT].hideTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
// overriding _addWindowClone to apply the tooltip patch on the cloned
|
||||
// windowPreview
|
||||
_addWindowClone(metaWindow) {
|
||||
const clone = super._addWindowClone(metaWindow);
|
||||
|
||||
// appling the tooltip patch
|
||||
(function patchPreview() {
|
||||
this._text = new St.Label({
|
||||
style_class: 'extension-windowsNavigator-window-tooltip',
|
||||
visible: false,
|
||||
});
|
||||
|
||||
this._text.add_constraint(new Clutter.BindConstraint({
|
||||
source: this.windowContainer,
|
||||
coordinate: Clutter.BindCoordinate.POSITION,
|
||||
}));
|
||||
this._text.add_constraint(new Clutter.AlignConstraint({
|
||||
source: this.windowContainer,
|
||||
align_axis: Clutter.AlignAxis.X_AXIS,
|
||||
pivot_point: new Graphene.Point({ x: 0.5, y: -1 }),
|
||||
factor: this._closeButtonSide === St.Side.LEFT ? 1 : 0,
|
||||
}));
|
||||
this._text.add_constraint(new Clutter.AlignConstraint({
|
||||
source: this.windowContainer,
|
||||
align_axis: Clutter.AlignAxis.Y_AXIS,
|
||||
pivot_point: new Graphene.Point({ x: -1, y: 0.5 }),
|
||||
factor: 0,
|
||||
}));
|
||||
|
||||
this.add_child(this._text);
|
||||
}).call(clone);
|
||||
|
||||
clone.showTooltip = function (text) {
|
||||
this._text.set({ text });
|
||||
this._text.show();
|
||||
};
|
||||
|
||||
let area = Workspace.padArea(this._actualGeometry, padding);
|
||||
this._tip.x = area.x;
|
||||
this._tip.y = area.y;
|
||||
this._tip.show();
|
||||
this._tip.raise_top();
|
||||
}
|
||||
workspaceInjections['showTooltip'] = undefined;
|
||||
clone.hideTooltip = function () {
|
||||
if (this._text && this._text.visible)
|
||||
this._text.hide();
|
||||
};
|
||||
|
||||
Workspace.Workspace.prototype.hideTooltip = function() {
|
||||
if (this._tip == null)
|
||||
return;
|
||||
if (!this._tip.get_parent())
|
||||
return;
|
||||
this._tip.hide();
|
||||
return clone;
|
||||
}
|
||||
workspaceInjections['hideTooltip'] = undefined;
|
||||
}
|
||||
|
||||
Workspace.Workspace.prototype.getWindowWithTooltip = function(id) {
|
||||
for (let i = 0; i < this._windows.length; i++) {
|
||||
if ((this._windows[i].slotId + 1) == id)
|
||||
return this._windows[i].metaWindow;
|
||||
}
|
||||
return null;
|
||||
class MyWorkspacesView extends WorkspacesView.WorkspacesView {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
workspaceInjections['getWindowWithTooltip'] = undefined;
|
||||
|
||||
Workspace.Workspace.prototype.showWindowsTooltips = function() {
|
||||
for (let i in this._windowOverlays) {
|
||||
if (this._windowOverlays[i] != null)
|
||||
this._windowOverlays[i].showTooltip();
|
||||
}
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
|
||||
this._pickWorkspace = false;
|
||||
this._pickWindow = false;
|
||||
this._keyPressEventId =
|
||||
global.stage.connect('key-press-event', this._onKeyPress.bind(this));
|
||||
this._keyReleaseEventId =
|
||||
global.stage.connect('key-release-event', this._onKeyRelease.bind(this));
|
||||
}
|
||||
workspaceInjections['showWindowsTooltips'] = undefined;
|
||||
|
||||
Workspace.Workspace.prototype.hideWindowsTooltips = function() {
|
||||
for (let i in this._windowOverlays) {
|
||||
if (this._windowOverlays[i] != null)
|
||||
this._windowOverlays[i].hideTooltip();
|
||||
}
|
||||
_onDestroy() {
|
||||
super._onDestroy();
|
||||
|
||||
global.stage.disconnect(this._keyPressEventId);
|
||||
global.stage.disconnect(this._keyReleaseEventId);
|
||||
}
|
||||
workspaceInjections['hideWindowsTooltips'] = undefined;
|
||||
|
||||
WorkspacesView.WorkspacesView.prototype._hideTooltips = function() {
|
||||
if (global.stage.get_key_focus() == global.stage)
|
||||
_hideTooltips() {
|
||||
if (global.stage.get_key_focus() === global.stage)
|
||||
global.stage.set_key_focus(this._prevFocusActor);
|
||||
this._pickWindow = false;
|
||||
for (let i = 0; i < this._workspaces.length; i++)
|
||||
this._workspaces[i].hideWindowsTooltips();
|
||||
}
|
||||
workViewInjections['_hideTooltips'] = undefined;
|
||||
|
||||
WorkspacesView.WorkspacesView.prototype._hideWorkspacesTooltips = function() {
|
||||
_hideWorkspacesTooltips() {
|
||||
global.stage.set_key_focus(this._prevFocusActor);
|
||||
this._pickWorkspace = false;
|
||||
for (let i = 0; i < this._workspaces.length; i++)
|
||||
this._workspaces[i].hideTooltip();
|
||||
}
|
||||
workViewInjections['_hideWorkspacesTooltips'] = undefined;
|
||||
|
||||
WorkspacesView.WorkspacesView.prototype._onKeyRelease = function(s, o) {
|
||||
_onKeyRelease(s, o) {
|
||||
if (this._pickWindow &&
|
||||
(o.get_key_symbol() == Clutter.KEY_Alt_L ||
|
||||
o.get_key_symbol() == Clutter.KEY_Alt_R))
|
||||
(o.get_key_symbol() === Clutter.KEY_Alt_L ||
|
||||
o.get_key_symbol() === Clutter.KEY_Alt_R))
|
||||
this._hideTooltips();
|
||||
if (this._pickWorkspace &&
|
||||
(o.get_key_symbol() == Clutter.KEY_Control_L ||
|
||||
o.get_key_symbol() == Clutter.KEY_Control_R))
|
||||
(o.get_key_symbol() === Clutter.KEY_Control_L ||
|
||||
o.get_key_symbol() === Clutter.KEY_Control_R))
|
||||
this._hideWorkspacesTooltips();
|
||||
}
|
||||
workViewInjections['_onKeyRelease'] = undefined;
|
||||
|
||||
WorkspacesView.WorkspacesView.prototype._onKeyPress = function(s, o) {
|
||||
if(Main.overview.viewSelector._activePage != Main.overview.viewSelector._workspacesPage)
|
||||
_onKeyPress(s, o) {
|
||||
const { ControlsState } = OverviewControls;
|
||||
if (this._overviewAdjustment.value !== ControlsState.WINDOW_PICKER)
|
||||
return false;
|
||||
|
||||
if ((o.get_key_symbol() == Clutter.KEY_Alt_L ||
|
||||
o.get_key_symbol() == Clutter.KEY_Alt_R)
|
||||
&& !this._pickWorkspace) {
|
||||
let workspaceManager = global.workspace_manager;
|
||||
|
||||
if ((o.get_key_symbol() === Clutter.KEY_Alt_L ||
|
||||
o.get_key_symbol() === Clutter.KEY_Alt_R) &&
|
||||
!this._pickWorkspace) {
|
||||
this._prevFocusActor = global.stage.get_key_focus();
|
||||
global.stage.set_key_focus(null);
|
||||
this._active = global.screen.get_active_workspace_index();
|
||||
this._active = workspaceManager.get_active_workspace_index();
|
||||
this._pickWindow = true;
|
||||
this._workspaces[global.screen.get_active_workspace_index()].showWindowsTooltips();
|
||||
this._workspaces[workspaceManager.get_active_workspace_index()].showWindowsTooltips();
|
||||
return true;
|
||||
}
|
||||
if ((o.get_key_symbol() == Clutter.KEY_Control_L ||
|
||||
o.get_key_symbol() == Clutter.KEY_Control_R)
|
||||
&& !this._pickWindow) {
|
||||
if ((o.get_key_symbol() === Clutter.KEY_Control_L ||
|
||||
o.get_key_symbol() === Clutter.KEY_Control_R) &&
|
||||
!this._pickWindow) {
|
||||
this._prevFocusActor = global.stage.get_key_focus();
|
||||
global.stage.set_key_focus(null);
|
||||
this._pickWorkspace = true;
|
||||
@@ -157,17 +197,17 @@ function enable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (global.stage.get_key_focus() != global.stage)
|
||||
if (global.stage.get_key_focus() !== global.stage)
|
||||
return false;
|
||||
|
||||
// ignore shift presses, they're required to get numerals in azerty keyboards
|
||||
if ((this._pickWindow || this._pickWorkspace) &&
|
||||
(o.get_key_symbol() == Clutter.KEY_Shift_L ||
|
||||
o.get_key_symbol() == Clutter.KEY_Shift_R))
|
||||
(o.get_key_symbol() === Clutter.KEY_Shift_L ||
|
||||
o.get_key_symbol() === Clutter.KEY_Shift_R))
|
||||
return true;
|
||||
|
||||
if (this._pickWindow) {
|
||||
if (this._active != global.screen.get_active_workspace_index()) {
|
||||
if (this._active !== workspaceManager.get_active_workspace_index()) {
|
||||
this._hideTooltips();
|
||||
return false;
|
||||
}
|
||||
@@ -209,80 +249,28 @@ function enable() {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
workViewInjections['_onKeyPress'] = undefined;
|
||||
|
||||
winInjections['_init'] = injectToFunction(Workspace.WindowOverlay.prototype, '_init', function(windowClone, parentActor) {
|
||||
this._id = null;
|
||||
createdActors.push(this._text = new St.Label({ style_class: 'extension-windowsNavigator-window-tooltip' }));
|
||||
this._text.hide();
|
||||
parentActor.add_actor(this._text);
|
||||
});
|
||||
|
||||
winInjections['relayout'] = injectToFunction(Workspace.WindowOverlay.prototype, 'relayout', function(animate) {
|
||||
let [cloneX, cloneY, cloneWidth, cloneHeight] = this._windowClone.slot;
|
||||
|
||||
let textX = cloneX - 2;
|
||||
let textY = cloneY - 2;
|
||||
this._text.set_position(Math.floor(textX) + 5, Math.floor(textY) + 5);
|
||||
this._text.raise_top();
|
||||
});
|
||||
|
||||
workspaceInjections['_init'] = injectToFunction(Workspace.Workspace.prototype, '_init', function(metaWorkspace) {
|
||||
if (metaWorkspace && metaWorkspace.index() < 9) {
|
||||
createdActors.push(this._tip = new St.Label({ style_class: 'extension-windowsNavigator-window-tooltip',
|
||||
visible: false }));
|
||||
|
||||
this.actor.add_actor(this._tip);
|
||||
let signalId = this.actor.connect('notify::scale-x', Lang.bind(this, function() {
|
||||
this._tip.set_scale(1 / this.actor.scale_x, 1 / this.actor.scale_x);
|
||||
}));
|
||||
connectedSignals.push({ obj: this.actor, id: signalId });
|
||||
} else
|
||||
this._tip = null;
|
||||
});
|
||||
|
||||
workViewInjections['_init'] = injectToFunction(WorkspacesView.WorkspacesView.prototype, '_init', function(width, height, x, y, workspaces) {
|
||||
this._pickWorkspace = false;
|
||||
this._pickWindow = false;
|
||||
this._keyPressEventId = global.stage.connect('key-press-event', Lang.bind(this, this._onKeyPress));
|
||||
this._keyReleaseEventId = global.stage.connect('key-release-event', Lang.bind(this, this._onKeyRelease));
|
||||
connectedSignals.push({ obj: global.stage, id: this._keyPressEventId });
|
||||
connectedSignals.push({ obj: global.stage, id: this._keyReleaseEventId });
|
||||
});
|
||||
|
||||
workViewInjections['_onDestroy'] = injectToFunction(WorkspacesView.WorkspacesView.prototype, '_onDestroy', function() {
|
||||
global.stage.disconnect(this._keyPressEventId);
|
||||
global.stage.disconnect(this._keyReleaseEventId);
|
||||
connectedSignals = [ ];
|
||||
});
|
||||
}
|
||||
|
||||
function removeInjection(object, injection, name) {
|
||||
if (injection[name] === undefined)
|
||||
delete object[name];
|
||||
else
|
||||
object[name] = injection[name];
|
||||
}
|
||||
|
||||
function disable() {
|
||||
let i;
|
||||
|
||||
for (i in workspaceInjections)
|
||||
removeInjection(Workspace.Workspace.prototype, workspaceInjections, i);
|
||||
for (i in winInjections)
|
||||
removeInjection(Workspace.WindowOverlay.prototype, winInjections, i);
|
||||
for (i in workViewInjections)
|
||||
removeInjection(WorkspacesView.WorkspacesView.prototype, workViewInjections, i);
|
||||
|
||||
for each (i in connectedSignals)
|
||||
i.obj.disconnect(i.id);
|
||||
|
||||
for each (i in createdActors)
|
||||
i.destroy();
|
||||
|
||||
resetState();
|
||||
class Extension {
|
||||
constructor() {
|
||||
this._origWorkspace = Workspace.Workspace;
|
||||
this._origWorkspacesView = WorkspacesView.WorkspacesView;
|
||||
}
|
||||
|
||||
enable() {
|
||||
Workspace.Workspace = MyWorkspace;
|
||||
WorkspacesView.WorkspacesView = MyWorkspacesView;
|
||||
}
|
||||
|
||||
disable() {
|
||||
Workspace.Workspace = this._origWorkspace;
|
||||
WorkspacesView.WorkspacesView = this._origWorkspacesView;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Extension} - the extension's state object
|
||||
*/
|
||||
function init() {
|
||||
/* do nothing */
|
||||
return new Extension();
|
||||
}
|
||||
|
||||
6
extensions/windowsNavigator/meson.build
Normal file
6
extensions/windowsNavigator/meson.build
Normal file
@@ -0,0 +1,6 @@
|
||||
extension_data += configure_file(
|
||||
input: metadata_name + '.in',
|
||||
output: metadata_name,
|
||||
configuration: metadata_conf
|
||||
)
|
||||
extension_data += files('stylesheet.css')
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user