mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2025-12-09 16:25:10 +03:00
Compare commits
932 Commits
0.42
...
animated-b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d7a6ff331 | ||
|
|
23fc3674c9 | ||
|
|
34a7214dd8 | ||
|
|
26d7008c04 | ||
|
|
3b489e8e1a | ||
|
|
ccbf577f46 | ||
|
|
b903f18130 | ||
|
|
9523c89c51 | ||
|
|
aa6fb2fcce | ||
|
|
e4fa2e28ea | ||
|
|
b2245e2246 | ||
|
|
13a6b92e47 | ||
|
|
9ba008002b | ||
|
|
8914cf78a1 | ||
|
|
d360375b4f | ||
|
|
1caab194af | ||
|
|
31754eba5d | ||
|
|
3cfa16b8b7 | ||
|
|
f80d2bacf4 | ||
|
|
5df3717d94 | ||
|
|
68897f04a2 | ||
|
|
4cb6aeae36 | ||
|
|
0a765a35bf | ||
|
|
6c0b122fbc | ||
|
|
4da2bd90cb | ||
|
|
f0275192c6 | ||
|
|
df905a1d73 | ||
|
|
ad8ad06f44 | ||
|
|
d6b9e2df62 | ||
|
|
5c9b36556f | ||
|
|
80a8348a99 | ||
|
|
005c9f471e | ||
|
|
b40532a830 | ||
|
|
fc7a4408e9 | ||
|
|
93b5f0081d | ||
|
|
ce049ea3ee | ||
|
|
fcc39b2db5 | ||
|
|
cb70fb4e82 | ||
|
|
2593a43d72 | ||
|
|
e44ff5b72a | ||
|
|
22cb1b50a6 | ||
|
|
a42c413705 | ||
|
|
d59d38dc7c | ||
|
|
77582be7fd | ||
|
|
0cb50355b7 | ||
|
|
a2d66e91ff | ||
|
|
ccdb981917 | ||
|
|
d80b581ace | ||
|
|
53efb6711d | ||
|
|
1de6e875f9 | ||
|
|
95a15c3cf8 | ||
|
|
ab320684f5 | ||
|
|
a284b69a1e | ||
|
|
b590f41254 | ||
|
|
a97076ead5 | ||
|
|
0b6df8be1c | ||
|
|
150bab0b57 | ||
|
|
d3355eda65 | ||
|
|
fbf10e553d | ||
|
|
fb37be5734 | ||
|
|
54e6cefa67 | ||
|
|
33b25c1129 | ||
|
|
44d8545c09 | ||
|
|
7a2808243c | ||
|
|
1be84de26b | ||
|
|
78e37f7ab4 | ||
|
|
e49459fd8b | ||
|
|
52f6e7fc32 | ||
|
|
c8ea61dc79 | ||
|
|
16a769ea61 | ||
|
|
475781db91 | ||
|
|
4dae082cd5 | ||
|
|
57405b2f56 | ||
|
|
88576f68fd | ||
|
|
aa4e013097 | ||
|
|
d67cfc911b | ||
|
|
00a3ad738f | ||
|
|
1d39d34d7c | ||
|
|
5f6013edd4 | ||
|
|
3e198ecd28 | ||
|
|
d48b98f582 | ||
|
|
9b839231f7 | ||
|
|
dd80614465 | ||
|
|
8c2be1b406 | ||
|
|
8152b7dad6 | ||
|
|
fb4fe175d9 | ||
|
|
5e03eb9b51 | ||
|
|
ef25575f85 | ||
|
|
b77b338c7a | ||
|
|
0e4fe4e9bb | ||
|
|
f742f83834 | ||
|
|
e6e4e53a73 | ||
|
|
7c594ba7a9 | ||
|
|
0156a9a9d5 | ||
|
|
3facbc0900 | ||
|
|
78cef1b3c7 | ||
|
|
d907c469ed | ||
|
|
cc238d3e34 | ||
|
|
0f9b38895e | ||
|
|
8fa1eae352 | ||
|
|
e13fb25f14 | ||
|
|
e36f942129 | ||
|
|
d34619824c | ||
|
|
80297f113f | ||
|
|
80f51bfe1e | ||
|
|
f8b9f4c1fa | ||
|
|
587f431ef4 | ||
|
|
65a4f66d2c | ||
|
|
a253b6c0cf | ||
|
|
efcbc1fbdb | ||
|
|
e560f9cbd6 | ||
|
|
80235d53f4 | ||
|
|
892b9a732e | ||
|
|
d8a0a015e4 | ||
|
|
e60e3b9fae | ||
|
|
6715f01b8c | ||
|
|
465af9bc41 | ||
|
|
d10bcfc72f | ||
|
|
942e5b9cd1 | ||
|
|
51a90d32f8 | ||
|
|
ac46632e73 | ||
|
|
1192bef1ae | ||
|
|
b9ec382589 | ||
|
|
5ecf19ef4f | ||
|
|
9636809b4d | ||
|
|
ba1c1ed952 | ||
|
|
7452390614 | ||
|
|
69042e42b7 | ||
|
|
1e93deab2a | ||
|
|
16ea809bb3 | ||
|
|
78aa4343b7 | ||
|
|
6815109e15 | ||
|
|
e34fbcec58 | ||
|
|
bb2a21270b | ||
|
|
49b9ec9025 | ||
|
|
a2e896e102 | ||
|
|
2e1ef647a9 | ||
|
|
f0c314df80 | ||
|
|
4db39828ef | ||
|
|
b2d40825ac | ||
|
|
6df5d3334e | ||
|
|
0e982df90c | ||
|
|
3834d93c9d | ||
|
|
16920a5b82 | ||
|
|
d66e35fdde | ||
|
|
d93dde0a03 | ||
|
|
2d232124dd | ||
|
|
ac6702fcf7 | ||
|
|
c4b016c9c8 | ||
|
|
6baa583a28 | ||
|
|
82df2ecfa9 | ||
|
|
06b3de720a | ||
|
|
b0edd5659f | ||
|
|
bb5c2eea10 | ||
|
|
e31e4dfe3a | ||
|
|
caf2cd8487 | ||
|
|
15c6f11a5e | ||
|
|
a4ea88f4be | ||
|
|
36d5747fbf | ||
|
|
3d8c535ffa | ||
|
|
1c067d0284 | ||
|
|
b6be0462a5 | ||
|
|
cce91ea16d | ||
|
|
d756041b06 | ||
|
|
2d0eb0a05b | ||
|
|
02f3239669 | ||
|
|
14a9240c45 | ||
|
|
c659638fb4 | ||
|
|
fd15b63044 | ||
|
|
263e6c34b5 | ||
|
|
eb62a3dc17 | ||
|
|
161ee090a8 | ||
|
|
560ec437b9 | ||
|
|
ccd0597b35 | ||
|
|
c5c0a3768a | ||
|
|
5aa2d24d58 | ||
|
|
ae28c595f9 | ||
|
|
1d08ddda60 | ||
|
|
578379fd00 | ||
|
|
7c9f550d4c | ||
|
|
84d4510d70 | ||
|
|
fa194ec258 | ||
|
|
fd56de403d | ||
|
|
85fde46504 | ||
|
|
b283178979 | ||
|
|
e0dddfceba | ||
|
|
bddef38a7c | ||
|
|
b5f2f77944 | ||
|
|
fca0718ed0 | ||
|
|
0d44ade6ea | ||
|
|
08ca2aa266 | ||
|
|
fe15758e59 | ||
|
|
674efae184 | ||
|
|
4a65bc88d5 | ||
|
|
a8f3d59729 | ||
|
|
6018f83a22 | ||
|
|
0b6247851b | ||
|
|
8640dee053 | ||
|
|
824db2e3bd | ||
|
|
c2c79c4676 | ||
|
|
4795fe5687 | ||
|
|
d508f339c1 | ||
|
|
c7054537e7 | ||
|
|
b98b904023 | ||
|
|
253df9325d | ||
|
|
78a9cc1d0c | ||
|
|
b25fcc3381 | ||
|
|
a2c0df5891 | ||
|
|
dc33c26960 | ||
|
|
51d7bc2c37 | ||
|
|
cdbdccf1ad | ||
|
|
397c369114 | ||
|
|
6f9bbb184a | ||
|
|
b12c818862 | ||
|
|
9118dcf925 | ||
|
|
d333d0c9e4 | ||
|
|
7f9cf6f45c | ||
|
|
9b465cb550 | ||
|
|
9144b7206e | ||
|
|
dd14843f2e | ||
|
|
09a18b2305 | ||
|
|
31f2feee2e | ||
|
|
218bb62bfd | ||
|
|
694c2ad767 | ||
|
|
97943fcd38 | ||
|
|
77f33467d2 | ||
|
|
651454170d | ||
|
|
7ca48bd136 | ||
|
|
968e508bb5 | ||
|
|
a6d318a197 | ||
|
|
cd20f4086b | ||
|
|
ebd5905947 | ||
|
|
817a3c62bb | ||
|
|
f8f58400fe | ||
|
|
ef06840649 | ||
|
|
b17c14d62e | ||
|
|
19dba94064 | ||
|
|
601e24f9e7 | ||
|
|
c7f323ee13 | ||
|
|
e4522f3af4 | ||
|
|
79af461a5b | ||
|
|
2e8e07faf6 | ||
|
|
ecdb000818 | ||
|
|
999fd0d4da | ||
|
|
705dd9558f | ||
|
|
97ca866ffa | ||
|
|
543b977db7 | ||
|
|
ebb8a6d025 | ||
|
|
506543281e | ||
|
|
60322be22a | ||
|
|
e1f30f24a8 | ||
|
|
1759f6b25c | ||
|
|
6578f25cc9 | ||
|
|
8c26e0323f | ||
|
|
a5575894ab | ||
|
|
357823a027 | ||
|
|
a6d3f6b3eb | ||
|
|
ae4c69e75c | ||
|
|
31cadc532b | ||
|
|
6e8443473b | ||
|
|
cca4ab3cd8 | ||
|
|
dab0ee3306 | ||
|
|
c6d1ed91a7 | ||
|
|
a613a244f4 | ||
|
|
268fe15004 | ||
|
|
7bc9be686f | ||
|
|
751919ec5a | ||
|
|
da913b426e | ||
|
|
d8ef99cd8f | ||
|
|
d08a6d7dd3 | ||
|
|
896e9bca8e | ||
|
|
1df9597bb1 | ||
|
|
eaf55f2099 | ||
|
|
5018a1f9eb | ||
|
|
71ba8f55a7 | ||
|
|
b65db707ed | ||
|
|
ed62266a43 | ||
|
|
49913b7dad | ||
|
|
3eeeb9e00b | ||
|
|
bfb1642284 | ||
|
|
0544a605c3 | ||
|
|
3f5acda132 | ||
|
|
02b1ba2926 | ||
|
|
7f7f9e3c7c | ||
|
|
6fcee03752 | ||
|
|
5782ceeb5d | ||
|
|
f752db5892 | ||
|
|
bce58bc97b | ||
|
|
d373687bc4 | ||
|
|
e5e510c825 | ||
|
|
29064ec72f | ||
|
|
953eee1dc8 | ||
|
|
75f76f4875 | ||
|
|
ecfbe68c33 | ||
|
|
7f02eb9cf0 | ||
|
|
4ab90065dc | ||
|
|
d3e39a1359 | ||
|
|
60e5861de4 | ||
|
|
ca7f5045ae | ||
|
|
299bd67151 | ||
|
|
4d4bb3fd7f | ||
|
|
7fd64a1b73 | ||
|
|
e3e8765b91 | ||
|
|
b0997fb5d2 | ||
|
|
435cf05f9f | ||
|
|
37dab9fb22 | ||
|
|
943dfe0619 | ||
|
|
be7114d3e6 | ||
|
|
fb44c8fbe4 | ||
|
|
94375b7d36 | ||
|
|
8b585deb78 | ||
|
|
4d8b544aed | ||
|
|
548d651d29 | ||
|
|
0b342acec9 | ||
|
|
cc6d3c1b1a | ||
|
|
74a748d92e | ||
|
|
1de81d0af5 | ||
|
|
ff9ef21f67 | ||
|
|
266a546478 | ||
|
|
87407ca832 | ||
|
|
90282d4436 | ||
|
|
abbe6d6c1f | ||
|
|
a28f701e6f | ||
|
|
4cdc995a7f | ||
|
|
713a01bfa9 | ||
|
|
ac291b688d | ||
|
|
84f7e244f2 | ||
|
|
4a8207f367 | ||
|
|
9cfd4d27e9 | ||
|
|
1b23cfd747 | ||
|
|
c708205593 | ||
|
|
a22c6c8013 | ||
|
|
b576f473e5 | ||
|
|
0b127caa83 | ||
|
|
5801bf3bdf | ||
|
|
4507ce359d | ||
|
|
3e14f28dc2 | ||
|
|
a9dcf09d13 | ||
|
|
c8998c2bcf | ||
|
|
10bf1295bc | ||
|
|
1e869973d4 | ||
|
|
731c8962c9 | ||
|
|
294b8bb789 | ||
|
|
4f9b819f48 | ||
|
|
5318d5fa8e | ||
|
|
98b156bdde | ||
|
|
511dd02107 | ||
|
|
f1f7a2e7b6 | ||
|
|
d557cf5427 | ||
|
|
2b1c55ee67 | ||
|
|
925ddaa63a | ||
|
|
2b60b18d47 | ||
|
|
d502406fa2 | ||
|
|
afdbf711f7 | ||
|
|
39d2941099 | ||
|
|
b4f7b1d71d | ||
|
|
69061cd41c | ||
|
|
8ba7f7f961 | ||
|
|
5e5aa17e14 | ||
|
|
551f5fc929 | ||
|
|
4e7b0d11d0 | ||
|
|
06bc53692a | ||
|
|
007ee38cb4 | ||
|
|
2a732306a1 | ||
|
|
82192bef91 | ||
|
|
0c51dfe19c | ||
|
|
24a9fa1ccc | ||
|
|
14b06507cb | ||
|
|
b46233087b | ||
|
|
28fb2e2a08 | ||
|
|
943e211cf1 | ||
|
|
ad0a13004e | ||
|
|
04bb6a5275 | ||
|
|
d3c917eac1 | ||
|
|
4c13271a5b | ||
|
|
20027c2db7 | ||
|
|
6affc70a66 | ||
|
|
ab4c9bdeda | ||
|
|
b4a9c9b7f5 | ||
|
|
5e20d50abf | ||
|
|
53abbbbe56 | ||
|
|
1938cb586d | ||
|
|
50490ece84 | ||
|
|
f291cc2bd3 | ||
|
|
2542c8bd53 | ||
|
|
b457fd634e | ||
|
|
041fd0e0cd | ||
|
|
a983edde1e | ||
|
|
7eb642dd13 | ||
|
|
e0bc93371e | ||
|
|
db56486506 | ||
|
|
c99be13697 | ||
|
|
0830c78728 | ||
|
|
edade93054 | ||
|
|
8a72b30cbc | ||
|
|
ed9cb0f918 | ||
|
|
7e0915cb9c | ||
|
|
a51294d570 | ||
|
|
d962f218a1 | ||
|
|
7b248427f0 | ||
|
|
b99fb8b11f | ||
|
|
26250e790f | ||
|
|
b26dbe81f4 | ||
|
|
903212345b | ||
|
|
025f6564dc | ||
|
|
35f97368fa | ||
|
|
09e5c86488 | ||
|
|
8998371cae | ||
|
|
29e1dc6b55 | ||
|
|
439e63b52f | ||
|
|
eea341fb33 | ||
|
|
359eedf773 | ||
|
|
866751ffc1 | ||
|
|
38a3a0768d | ||
|
|
03b42749cd | ||
|
|
60fd78e082 | ||
|
|
9edaf58929 | ||
|
|
5000186f85 | ||
|
|
cacf0ea987 | ||
|
|
067501cbe7 | ||
|
|
9fe0cf496b | ||
|
|
9d0823038e | ||
|
|
5a05efefdd | ||
|
|
988d171bdd | ||
|
|
e6f72bf343 | ||
|
|
89c5a0c57b | ||
|
|
d97146393c | ||
|
|
1c52f1f76c | ||
|
|
9bd3a68115 | ||
|
|
f58780d36b | ||
|
|
6eb15ab437 | ||
|
|
00dc7004f5 | ||
|
|
8ec0e57235 | ||
|
|
d75dc9e70c | ||
|
|
ec2fccbb0e | ||
|
|
34861166e8 | ||
|
|
584fa0a26e | ||
|
|
6c48489d89 | ||
|
|
ba9c884a0c | ||
|
|
360f0bafe0 | ||
|
|
4327c13dca | ||
|
|
4f2256f713 | ||
|
|
5167cd368f | ||
|
|
ef7289d11a | ||
|
|
cb11d98bf7 | ||
|
|
992349da8c | ||
|
|
2e7637f274 | ||
|
|
1f8eaf4a64 | ||
|
|
46ac7a9dc7 | ||
|
|
0d86d39217 | ||
|
|
1f591f3d1b | ||
|
|
30c6ddba37 | ||
|
|
406eeaec96 | ||
|
|
2fe5652bc6 | ||
|
|
39bf68a6bd | ||
|
|
a7a4a19824 | ||
|
|
7f906ba0ea | ||
|
|
07bf6e4506 | ||
|
|
a331760321 | ||
|
|
d9c240d729 | ||
|
|
d9526c19e7 | ||
|
|
1798ccd284 | ||
|
|
ab1ce7fab1 | ||
|
|
e9b2f17171 | ||
|
|
d3bf4433b7 | ||
|
|
ba0f43455b | ||
|
|
638af4bcd7 | ||
|
|
5eab843d97 | ||
|
|
c55f0e239e | ||
|
|
32d9381745 | ||
|
|
77fc564e70 | ||
|
|
3b84314c45 | ||
|
|
5729c20386 | ||
|
|
a4d70d8095 | ||
|
|
8fcce349d5 | ||
|
|
5a94676a3a | ||
|
|
f32d72ee62 | ||
|
|
e35fc8620c | ||
|
|
277c288952 | ||
|
|
240b08e55c | ||
|
|
fe7f345661 | ||
|
|
c8db01c958 | ||
|
|
f456185f7d | ||
|
|
801b555835 | ||
|
|
eee177e64b | ||
|
|
63639f8e96 | ||
|
|
de1b0b1bb6 | ||
|
|
bbdd7fc2b4 | ||
|
|
6addb5c4b4 | ||
|
|
b47e0c88d6 | ||
|
|
d06993d940 | ||
|
|
d31f167b9e | ||
|
|
f12ee6c167 | ||
|
|
983b341f33 | ||
|
|
f3e6642f05 | ||
|
|
0a63990d21 | ||
|
|
6909bb4b03 | ||
|
|
620aa8bcee | ||
|
|
6db39d1860 | ||
|
|
1762ead89f | ||
|
|
d13ddeb944 | ||
|
|
1b5da0e1d1 | ||
|
|
7a2d0e7fcb | ||
|
|
477c3b6b1e | ||
|
|
95312c3650 | ||
|
|
98a3c4b0f5 | ||
|
|
6e990a7e31 | ||
|
|
8e49904f8d | ||
|
|
69f52c8abd | ||
|
|
d7b0754327 | ||
|
|
2a00de11f1 | ||
|
|
923cc51f3e | ||
|
|
c8f7478170 | ||
|
|
9006e835c6 | ||
|
|
f801d61929 | ||
|
|
a143e5777c | ||
|
|
bffac60bf8 | ||
|
|
bf500e46e7 | ||
|
|
4a2f79f390 | ||
|
|
c24ce7c5bc | ||
|
|
8a6a0c7971 | ||
|
|
de6e5bd800 | ||
|
|
e18a04f9e6 | ||
|
|
14fc652f4b | ||
|
|
9a876e747a | ||
|
|
f8ee8b27fb | ||
|
|
ce1a1487aa | ||
|
|
fe1e364a1d | ||
|
|
eabb052107 | ||
|
|
734f3621f1 | ||
|
|
ae8323e2f8 | ||
|
|
9612a81f2e | ||
|
|
2945a36cef | ||
|
|
b84dc5bfcc | ||
|
|
60486fd880 | ||
|
|
891091cebc | ||
|
|
1493ddcf41 | ||
|
|
4299c50537 | ||
|
|
14577c396d | ||
|
|
e9b566241d | ||
|
|
d39b08c035 | ||
|
|
69ac683c8c | ||
|
|
eafd0b3d06 | ||
|
|
310a4989dc | ||
|
|
3d0df51839 | ||
|
|
ede02aaaa5 | ||
|
|
beff149004 | ||
|
|
07db6e8fb0 | ||
|
|
46852c0780 | ||
|
|
a5e41c573f | ||
|
|
ed91aa4648 | ||
|
|
9a94395d30 | ||
|
|
04aa61c2bb | ||
|
|
035a13df54 | ||
|
|
e8a6f0ca3d | ||
|
|
1fc519b9de | ||
|
|
2bcf38e2e3 | ||
|
|
8eb44a68cb | ||
|
|
30c7b442a8 | ||
|
|
cee2211108 | ||
|
|
b7bcbccd45 | ||
|
|
d2ccb97eba | ||
|
|
39d56f2603 | ||
|
|
83e904dd2d | ||
|
|
110c787eba | ||
|
|
7c7ff289de | ||
|
|
617a35c51b | ||
|
|
73487ccf65 | ||
|
|
712bff9c99 | ||
|
|
eedfcf86aa | ||
|
|
f730848928 | ||
|
|
61d0574c5c | ||
|
|
2f01e01ec1 | ||
|
|
cbcf66df7f | ||
|
|
cfaeea039b | ||
|
|
a891d1eb54 | ||
|
|
4372052ef0 | ||
|
|
8734b062dc | ||
|
|
343451de65 | ||
|
|
144d65c776 | ||
|
|
a6815574f7 | ||
|
|
e5a116a0d4 | ||
|
|
0beef6b108 | ||
|
|
7341008449 | ||
|
|
49bd53194a | ||
|
|
baf4437efc | ||
|
|
b244f80f81 | ||
|
|
e41c91a42b | ||
|
|
b9a2e3ceac | ||
|
|
fa7dd3bdc4 | ||
|
|
9a8c68b846 | ||
|
|
698e33ddf4 | ||
|
|
909258ba14 | ||
|
|
2ad6bd1d23 | ||
|
|
510ffd41d8 | ||
|
|
4f00591c4e | ||
|
|
5b65ed87cd | ||
|
|
b0121c422d | ||
|
|
a9e9fad222 | ||
|
|
b5fc07acc7 | ||
|
|
140ebfdb92 | ||
|
|
37d0179de1 | ||
|
|
823d4b0fe2 | ||
|
|
dd1eacf4f0 | ||
|
|
86c33dd686 | ||
|
|
c6757cc61b | ||
|
|
a38cf284dd | ||
|
|
575b8e3f7f | ||
|
|
bc443f47f1 | ||
|
|
b631bcc0db | ||
|
|
5ccd92ece6 | ||
|
|
2f3c8868a7 | ||
|
|
6f7b5e8005 | ||
|
|
10d1e4b798 | ||
|
|
9d5934df14 | ||
|
|
be507de6c1 | ||
|
|
e5d3c08821 | ||
|
|
027b4ab7da | ||
|
|
fefea0d7ec | ||
|
|
33f30bfd19 | ||
|
|
e9d4b9961a | ||
|
|
b94248fe79 | ||
|
|
225975e0dd | ||
|
|
eac7492143 | ||
|
|
b3c40bf448 | ||
|
|
02f7cd77f4 | ||
|
|
7f8f3aa99b | ||
|
|
0bcdc14909 | ||
|
|
526c25a02b | ||
|
|
f48da9dab1 | ||
|
|
2e8dfda12e | ||
|
|
63da576d85 | ||
|
|
0ab4206540 | ||
|
|
212ae90401 | ||
|
|
d4e5d0be45 | ||
|
|
3520a0f1fb | ||
|
|
036090a947 | ||
|
|
dc570c683a | ||
|
|
9f85d34c91 | ||
|
|
16bf1fb6c3 | ||
|
|
47c4d508e0 | ||
|
|
e5d9060623 | ||
|
|
fdf28fc385 | ||
|
|
9015a4d56b | ||
|
|
38301454a6 | ||
|
|
9b3a22c4ca | ||
|
|
548dbc3649 | ||
|
|
3474129812 | ||
|
|
63193feebe | ||
|
|
51f22bfe75 | ||
|
|
7d0f7e1c8e | ||
|
|
dd8ab242fb | ||
|
|
60f3428da7 | ||
|
|
c6fec0a131 | ||
|
|
fdc43fc0d3 | ||
|
|
0b880aa335 | ||
|
|
74f50ec992 | ||
|
|
1bdf4532db | ||
|
|
f97783ddef | ||
|
|
1024d6fc07 | ||
|
|
3ec59d0c58 | ||
|
|
c43249316c | ||
|
|
ed5180ffd6 | ||
|
|
e9ec769340 | ||
|
|
5e16ff8dff | ||
|
|
364b6631ea | ||
|
|
48a18e53e3 | ||
|
|
bcc8282d73 | ||
|
|
15017ed49c | ||
|
|
50d36fe91b | ||
|
|
23e67a2908 | ||
|
|
0dab1b73cc | ||
|
|
3c086a92e2 | ||
|
|
647d72514b | ||
|
|
15328b4fd7 | ||
|
|
b49a498f9c | ||
|
|
8d14d5f87c | ||
|
|
a6db352ecd | ||
|
|
ccbb26c176 | ||
|
|
8f6af73541 | ||
|
|
a59f17fdb2 | ||
|
|
14222e40ad | ||
|
|
7d48bf06fe | ||
|
|
1d06a2c2e8 | ||
|
|
cf141f0e55 | ||
|
|
9113c31612 | ||
|
|
00b4e0a6fd | ||
|
|
e3cac95d37 | ||
|
|
64d850c583 | ||
|
|
2fe1b9e726 | ||
|
|
1315d847b9 | ||
|
|
b5954102b6 | ||
|
|
1c8ba0c538 | ||
|
|
be18317a6d | ||
|
|
88d2b8266e | ||
|
|
949ca5ddff | ||
|
|
3eb53b9648 | ||
|
|
e4a03ede1f | ||
|
|
cb65dc0e9d | ||
|
|
8ec907050e | ||
|
|
15ba00a902 | ||
|
|
89d0c301c2 | ||
|
|
2f47466f3b | ||
|
|
d70eca9774 | ||
|
|
95ce92fa18 | ||
|
|
b3db52b2ed | ||
|
|
c40912013d | ||
|
|
1c08e98c1c | ||
|
|
3f202a7cdc | ||
|
|
6f3aea8fc1 | ||
|
|
0896143838 | ||
|
|
ea94899a28 | ||
|
|
d2109cef86 | ||
|
|
cda146366c | ||
|
|
678b879a01 | ||
|
|
4c885c5e7b | ||
|
|
d5002b1c33 | ||
|
|
4f8b6d6b28 | ||
|
|
66dab41539 | ||
|
|
9e4940228d | ||
|
|
cbb11ebb03 | ||
|
|
073a25f381 | ||
|
|
40592ab876 | ||
|
|
bbfe624b51 | ||
|
|
a2af9e4c65 | ||
|
|
0123a8895f | ||
|
|
53854a4d13 | ||
|
|
4fdd44858f | ||
|
|
3c58879ce5 | ||
|
|
a7c6a881b3 | ||
|
|
ef065d31a0 | ||
|
|
d059d6b448 | ||
|
|
2d0a6f1bec | ||
|
|
a3cc5a1938 | ||
|
|
435068515a | ||
|
|
956001dbd7 | ||
|
|
460f0d9dee | ||
|
|
5155ec93c9 | ||
|
|
8bb8883e22 | ||
|
|
ffb7a6dfbb | ||
|
|
176de6f245 | ||
|
|
11f9740dbf | ||
|
|
42a91ba26c | ||
|
|
234003e2b1 | ||
|
|
534384438b | ||
|
|
ab51f35d5d | ||
|
|
511a4044d7 | ||
|
|
821efaff40 | ||
|
|
91bc994532 | ||
|
|
1323b46ac7 | ||
|
|
3a8b30ca8e | ||
|
|
923d58519f | ||
|
|
eabb1f84f6 | ||
|
|
cfbe44b946 | ||
|
|
81c35eab46 | ||
|
|
a1c7c29113 | ||
|
|
1293e2a074 | ||
|
|
b5deca7f22 | ||
|
|
604ba236c0 | ||
|
|
14df490b2a | ||
|
|
dd2f73e8ad | ||
|
|
56bfdc8ef9 | ||
|
|
91dbf1e144 | ||
|
|
e07ae90d09 | ||
|
|
5ef0c9aae1 | ||
|
|
aefed7c481 | ||
|
|
0d66d9f9a3 | ||
|
|
d0ffc4f979 | ||
|
|
f149d2b7cd | ||
|
|
21a12b8dd4 | ||
|
|
6c8b8e8949 | ||
|
|
539737d1c5 | ||
|
|
33ff5828da | ||
|
|
1fb0783808 | ||
|
|
b5e7aa8553 | ||
|
|
1d3ce76b27 | ||
|
|
0101171159 | ||
|
|
8b8ed0b9ff | ||
|
|
413b60e630 | ||
|
|
10b2a94c70 | ||
|
|
e337e5bbd8 | ||
|
|
6e55e0a183 | ||
|
|
8ee1d26935 | ||
|
|
80bdf69eaf | ||
|
|
18e838bffd | ||
|
|
d95b1b0ec4 | ||
|
|
d16a3c117b | ||
|
|
d04ec982ab | ||
|
|
cce99c803e | ||
|
|
19ed538573 | ||
|
|
a1f78345e6 | ||
|
|
f8c7ccf064 | ||
|
|
4d5242cd61 | ||
|
|
7ad176f98d | ||
|
|
57df7d28b5 | ||
|
|
f784ff2c84 | ||
|
|
a0f6affb68 | ||
|
|
0c679167fa | ||
|
|
4fe707e519 | ||
|
|
d83704b7cb | ||
|
|
2177ee45cc | ||
|
|
ccd4f99aea | ||
|
|
cd6b55c846 | ||
|
|
d923c8df81 | ||
|
|
59879f493e | ||
|
|
06cab0d4b5 | ||
|
|
a16db38a6f | ||
|
|
de93e19a80 | ||
|
|
47bb7d0de7 | ||
|
|
896e808db4 | ||
|
|
6fe6d1ffa0 | ||
|
|
4c6f7a66e2 | ||
|
|
4b5646ec88 | ||
|
|
66a5f350da | ||
|
|
f9e34cbab7 | ||
|
|
634f7b5ba3 | ||
|
|
7dbc6ff8a3 | ||
|
|
afccdc4749 | ||
|
|
c98ec041d4 | ||
|
|
9e0c62092e | ||
|
|
9aea006f50 | ||
|
|
c16c3759cf | ||
|
|
cbc1fe27ef | ||
|
|
f57dbf94c8 | ||
|
|
c0f15d2e6f | ||
|
|
cb525fafb6 | ||
|
|
5cae3a8141 | ||
|
|
8594e78287 | ||
|
|
5b8f922273 | ||
|
|
847b41752c | ||
|
|
7c08489cb3 | ||
|
|
605c77ecbc | ||
|
|
fd0c2a5cd1 | ||
|
|
a80790fc8e | ||
|
|
206d449d0d | ||
|
|
2323dc099f | ||
|
|
642583479f | ||
|
|
082e5842d0 | ||
|
|
c67ba02839 | ||
|
|
4c6cb7618f | ||
|
|
c15100f129 | ||
|
|
6dfb3cc84e | ||
|
|
18d8c7d086 | ||
|
|
ab3adf4ae3 | ||
|
|
7e6619af00 | ||
|
|
a7e2a10403 | ||
|
|
3a784375d0 | ||
|
|
b8c9433259 | ||
|
|
815d9d6012 | ||
|
|
feb91aa056 | ||
|
|
cd264586ca | ||
|
|
c6d561f2df | ||
|
|
6167c5f855 | ||
|
|
1a31cb96b8 | ||
|
|
9b8df64c35 | ||
|
|
a47565afec | ||
|
|
c2ee815cbe | ||
|
|
e45a2df6b6 | ||
|
|
a19979c233 | ||
|
|
e2a297fa40 | ||
|
|
df13b338b2 | ||
|
|
da9d7a0dee | ||
|
|
0374c65159 | ||
|
|
71b1e07ba6 | ||
|
|
c3781dc4b5 | ||
|
|
dc92d0913c | ||
|
|
a5adf29001 | ||
|
|
8861bfe4fa | ||
|
|
c8d280f418 | ||
|
|
09c98359af | ||
|
|
6f8a7471c2 | ||
|
|
4c141fe47c | ||
|
|
b37ff348fb | ||
|
|
09798d33b0 | ||
|
|
717ab95fbe | ||
|
|
3f616e3608 | ||
|
|
c590157561 | ||
|
|
2b50431081 | ||
|
|
6d38e44f91 | ||
|
|
9bc656a5c5 | ||
|
|
700bb9b567 | ||
|
|
8ccda81d9a | ||
|
|
3818790ced | ||
|
|
c34ce389a4 | ||
|
|
15718cdb46 | ||
|
|
10746a454a | ||
|
|
f0fd02e81f | ||
|
|
bfaac6d164 | ||
|
|
a909f1012a | ||
|
|
201581a07c | ||
|
|
8cef5ecf7e | ||
|
|
2c1075f471 | ||
|
|
1f5e08fdc6 | ||
|
|
c0408045ef | ||
|
|
c58f5a6ca7 | ||
|
|
ae445c9343 | ||
|
|
ad7ff2ba0b | ||
|
|
4b7ef6e853 | ||
|
|
87f2acc2d9 | ||
|
|
ec2fef02ed | ||
|
|
ebe0d74dbe | ||
|
|
029dc51f8b | ||
|
|
3fc85cd7b2 | ||
|
|
a46bdef079 | ||
|
|
3de489f693 | ||
|
|
eddb9eee46 | ||
|
|
5b0c96cd6d | ||
|
|
15ac77107f | ||
|
|
a7c906091c | ||
|
|
de870c546c | ||
|
|
2f3427e6ad | ||
|
|
203426bd55 | ||
|
|
16242080e0 | ||
|
|
57655d8859 | ||
|
|
62ffd57108 | ||
|
|
8db05f47b5 | ||
|
|
c684761eef | ||
|
|
0a8ece8c9c | ||
|
|
01058bde1b | ||
|
|
9c2c03cddb | ||
|
|
f0778a83a0 | ||
|
|
b86ae1f122 | ||
|
|
dfd6831b02 | ||
|
|
a4ddc13c1a | ||
|
|
fd63a1b7c2 | ||
|
|
d83c3689d0 | ||
|
|
d52bf9d318 | ||
|
|
80f56dec15 | ||
|
|
358c226b96 | ||
|
|
9de9983416 | ||
|
|
c9da4fcaf1 | ||
|
|
932ca6f9d4 |
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -15,8 +15,12 @@
|
||||
# BINARY FILES:
|
||||
# Disable line ending normalize on checkin.
|
||||
|
||||
*.dll binary
|
||||
*.dylib binary
|
||||
*.gif binary
|
||||
*.jar binary
|
||||
*.lib binary
|
||||
*.png binary
|
||||
*.sketch binary
|
||||
*.so binary
|
||||
*.zip binary
|
||||
|
||||
8
.gitbugtraq
Normal file
8
.gitbugtraq
Normal file
@@ -0,0 +1,8 @@
|
||||
# links issue numbers in git commit messages to issue tracker
|
||||
# https://github.com/mstrap/bugtraq
|
||||
# for SmartGit - https://www.syntevo.com/smartgit/
|
||||
|
||||
[bugtraq]
|
||||
url = "https://github.com/JFormDesigner/FlatLaf/issues/%BUGID%"
|
||||
loglinkregex = "#[0-9]{1,5}"
|
||||
logregex = "[0-9]{1,5}"
|
||||
175
.github/workflows/ci.yml
vendored
Normal file
175
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
# https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
|
||||
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
tags:
|
||||
- '[0-9]*'
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
# test against
|
||||
# - Java 1.8 (minimum requirement)
|
||||
# - Java 9 (first version with JPMS)
|
||||
# - Java LTS versions (11, 17, ...)
|
||||
# - lastest Java version(s)
|
||||
java:
|
||||
- 1.8
|
||||
- 9
|
||||
- 11 # LTS
|
||||
- 14
|
||||
- 15
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
if: matrix.java == '1.8'
|
||||
|
||||
- name: Setup Java ${{ matrix.java }}
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: ${{ matrix.java }}
|
||||
|
||||
- name: Cache Gradle wrapper
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
|
||||
|
||||
- name: Cache Gradle cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.gradle/caches
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
|
||||
restore-keys: ${{ runner.os }}-gradle
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
if: matrix.java == '11'
|
||||
with:
|
||||
name: FlatLaf-build-artifacts
|
||||
path: |
|
||||
flatlaf-*/build/libs
|
||||
!**/*-javadoc.jar
|
||||
!**/*-sources.jar
|
||||
|
||||
|
||||
snapshot:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
if: |
|
||||
github.event_name == 'push' &&
|
||||
github.ref == 'refs/heads/main' &&
|
||||
github.repository == 'JFormDesigner/FlatLaf'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Java 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
|
||||
- name: Cache Gradle wrapper
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
|
||||
|
||||
- name: Cache Gradle cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.gradle/caches
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
|
||||
restore-keys: ${{ runner.os }}-gradle
|
||||
|
||||
- name: Publish snapshot to oss.sonatype.org
|
||||
run: ./gradlew publish :flatlaf-theme-editor:build -Dorg.gradle.internal.publish.checksums.insecure=true
|
||||
env:
|
||||
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
|
||||
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
|
||||
|
||||
- name: Upload theme editor
|
||||
uses: sebastianpopp/ftp-action@releases/v2
|
||||
with:
|
||||
host: ${{ secrets.FTP_SERVER }}
|
||||
user: ${{ secrets.FTP_USERNAME }}
|
||||
password: ${{ secrets.FTP_PASSWORD }}
|
||||
forceSsl: true
|
||||
localDir: "flatlaf-theme-editor/build/libs"
|
||||
remoteDir: "snapshots"
|
||||
options: "--only-newer --no-recursion --verbose=1"
|
||||
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
if: |
|
||||
github.event_name == 'push' &&
|
||||
startsWith( github.ref, 'refs/tags/' ) &&
|
||||
github.repository == 'JFormDesigner/FlatLaf'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Java 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
|
||||
- name: Cache Gradle wrapper
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
|
||||
|
||||
- name: Cache Gradle cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.gradle/caches
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
|
||||
restore-keys: ${{ runner.os }}-gradle
|
||||
|
||||
- name: Release a new stable version to Maven Central
|
||||
run: ./gradlew publish :flatlaf-demo:build :flatlaf-theme-editor:build -Drelease=true
|
||||
env:
|
||||
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
|
||||
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
|
||||
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
|
||||
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
|
||||
|
||||
- name: Upload demo
|
||||
uses: sebastianpopp/ftp-action@releases/v2
|
||||
with:
|
||||
host: ${{ secrets.FTP_SERVER }}
|
||||
user: ${{ secrets.FTP_USERNAME }}
|
||||
password: ${{ secrets.FTP_PASSWORD }}
|
||||
forceSsl: true
|
||||
localDir: "flatlaf-demo/build/libs"
|
||||
remoteDir: "."
|
||||
options: "--only-newer --no-recursion --verbose=1"
|
||||
|
||||
- name: Upload theme editor
|
||||
uses: sebastianpopp/ftp-action@releases/v2
|
||||
with:
|
||||
host: ${{ secrets.FTP_SERVER }}
|
||||
user: ${{ secrets.FTP_USERNAME }}
|
||||
password: ${{ secrets.FTP_PASSWORD }}
|
||||
forceSsl: true
|
||||
localDir: "flatlaf-theme-editor/build/libs"
|
||||
remoteDir: "."
|
||||
options: "--only-newer --no-recursion --verbose=1"
|
||||
58
.github/workflows/natives.yml
vendored
Normal file
58
.github/workflows/natives.yml
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
# https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
|
||||
|
||||
name: Native Libraries
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
tags:
|
||||
- '[0-9]*'
|
||||
paths:
|
||||
- 'flatlaf-natives/flatlaf-natives-windows/**'
|
||||
- '.github/workflows/natives.yml'
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
paths:
|
||||
- 'flatlaf-natives/flatlaf-natives-windows/**'
|
||||
- '.github/workflows/natives.yml'
|
||||
|
||||
jobs:
|
||||
Windows:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
|
||||
- name: Setup Java 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
|
||||
- name: Cache Gradle wrapper
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
|
||||
|
||||
- name: Cache Gradle cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
!~/.gradle/caches/modules-2/modules-2.lock
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
|
||||
restore-keys: ${{ runner.os }}-gradle
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew :flatlaf-natives-windows:build
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: FlatLaf-natives-windows-build-artifacts
|
||||
path: |
|
||||
flatlaf-natives/flatlaf-natives-windows/build
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -9,3 +9,5 @@ out/
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.vs/
|
||||
.vscode/
|
||||
|
||||
39
.travis.yml
39
.travis.yml
@@ -1,39 +0,0 @@
|
||||
language: java
|
||||
sudo: false
|
||||
|
||||
jdk:
|
||||
- openjdk8
|
||||
- openjdk9
|
||||
- openjdk11
|
||||
- openjdk14
|
||||
|
||||
before_cache:
|
||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.gradle/caches/
|
||||
- $HOME/.gradle/wrapper/
|
||||
|
||||
before_install:
|
||||
- ./gradlew --version
|
||||
- java -version
|
||||
|
||||
stages:
|
||||
- name: test
|
||||
- name: snapshot
|
||||
if: branch = master AND type IN (push) AND tag IS blank
|
||||
- name: release
|
||||
if: type IN (push) AND tag IS present
|
||||
|
||||
jobs:
|
||||
include:
|
||||
# publish snapshot to oss.jfrog.org
|
||||
- stage: snapshot
|
||||
jdk: openjdk11
|
||||
script: ./gradlew artifactoryPublish
|
||||
|
||||
# release a new stable version to bintray
|
||||
- stage: release
|
||||
jdk: openjdk11
|
||||
script: ./gradlew bintrayUpload -Drelease=true
|
||||
705
CHANGELOG.md
705
CHANGELOG.md
@@ -1,6 +1,711 @@
|
||||
FlatLaf Change Log
|
||||
==================
|
||||
|
||||
## 2.0-SNAPSHOT
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- Styling:
|
||||
- Styling individual components using string in CSS syntax or `java.util.Map`.
|
||||
(PR #341)\
|
||||
E.g.: `mySlider.putClientProperty( "FlatLaf.style", "trackWidth: 2" );`
|
||||
- Style classes allow defining style rules at a single place (in UI defaults)
|
||||
and use them in any component. (PR #388)\
|
||||
E.g.: `mySlider.putClientProperty( "FlatLaf.styleClass", "myclass" );`
|
||||
- Typography defines several font styles for headers and various text sizes,
|
||||
which makes it easy to use consistent font styles across the application. (PR
|
||||
#396)
|
||||
- Native window decorations (Windows 10/11 only):
|
||||
- Unified backgrounds for window title bar is now enabled by default (window
|
||||
title bar has now same background color as window content). Bottom separator
|
||||
for menu bars is no longer painted (if unified background is enabled).
|
||||
- Show Windows 11 snap layouts menu when hovering the mouse over the maximize
|
||||
button. (issues #397 and #407)
|
||||
- Possibility to hide window title bar icon (for single window set client
|
||||
property `JRootPane.titleBarShowIcon` to `false`; for all windows set UI
|
||||
value `TitlePane.showIcon` to `false`).
|
||||
- OptionPane: Hide window title bar icon by default. Can be be made visibly by
|
||||
setting UI default `OptionPane.showIcon` to `true`. (issue #416)
|
||||
- No longer show the Java "duke/cup" icon if no window icon image is set.
|
||||
(issue #416)
|
||||
- TextField, FormattedTextField and PasswordField: Support leading and trailing
|
||||
icons (set client property `JTextField.leadingIcon` or
|
||||
`JTextField.trailingIcon` to an `Icon`). (PR #378; issue #368)
|
||||
- TextComponents: Double/triple-click-and-drag now extends selection by whole
|
||||
words/lines.
|
||||
- Theming improvements: Reworks core themes to make it easier to create new
|
||||
themes (e.g. reduced explicit colors by using color functions). **Note**:
|
||||
There are minor incompatible changes in FlatLaf properties files. (PR #390)
|
||||
- ToolBar:
|
||||
- Toolbars are no longer floatable by default (dots on left side of toolbar
|
||||
that allows dragging toolbar). Use `UIManager.put( "ToolBar.floatable", true
|
||||
)` if you want the old behavior.
|
||||
- Skip components with empty input map (e.g. `JLabel`) when using arrow keys
|
||||
to navigate in focusable buttons (if UI value `ToolBar.focusableButtons` is
|
||||
`true`).
|
||||
- Support arrow-keys-only navigation within focusable buttons of toolbar (if
|
||||
UI value `ToolBar.focusableButtons` is `true`):
|
||||
- arrow keys move focus within toolbar
|
||||
- tab-key moves focus out of toolbar
|
||||
- if moving focus into the toolbar, focus recently focused toolbar button
|
||||
- ComboBox, Spinner, TextField and subclasses: Support specifying width of
|
||||
border (see UI value `Component.borderWidth`).
|
||||
- CheckBox and RadioButton:
|
||||
- Made selected icon better recognizable in **FlatLaf Light** (use blue
|
||||
border), **Dark** and **Darcula** (use lighter border) themes. **IntelliJ**
|
||||
theme is not changed.
|
||||
- Support specifying width of icon border (see UI value
|
||||
`CheckBox.icon.borderWidth`).
|
||||
- Reworked icon UI defaults and added missing ones. **Note**: There are minor
|
||||
incompatible changes in FlatLaf properties files.
|
||||
- Slider: Support specifying width of thumb border (see UI value
|
||||
`Slider.thumbBorderWidth`).
|
||||
- TabbedPane: Optionally paint selected tab as card. (PR #343)
|
||||
- Added more color functions to class `ColorFunctions` for easy use in
|
||||
applications: `lighten()`, `darken()`, `saturate()`, `desaturate()`, `spin()`,
|
||||
`tint()`, `shade()` and `luma()`.
|
||||
- Support defining fonts in FlatLaf properties files. (issue #384)
|
||||
- Added method `FlatLaf.registerCustomDefaultsSource(URL packageUrl)` for JPMS.
|
||||
(issue #325)
|
||||
- Extras:
|
||||
- Added class `FlatDesktop` for easy integration into macOS screen menu
|
||||
(About, Preferences and Quit) when using Java 8.
|
||||
- `FlatSVGIcon`: Support loading SVG from `URL` (for JPMS), `URI`, `File` or
|
||||
`InputStream`. (issues #419 and #325)
|
||||
- `FlatSVGUtils`: Support loading SVG from `URL` (for JPMS). (issue #325)
|
||||
|
||||
|
||||
## 1.6.4
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- ComboBox: Fixed regression in FlatLaf 1.6.3 that makes selected item invisible
|
||||
in popup list if `DefaultListCellRenderer` is used as renderer. If using
|
||||
default renderer, it works. (issue #426)
|
||||
|
||||
|
||||
## 1.6.3
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- ComboBox (not editable): Fixed regression in FlatLaf 1.6.2 that may display
|
||||
text in non-editable combo boxes in bold. (issue #423)
|
||||
- Tree: Fixed editing cell issue with custom cell renderer and cell editor that
|
||||
use same component for rendering and editing. (issue #385)
|
||||
|
||||
|
||||
## 1.6.2
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- ComboBox (not editable): Fixed background painted outside of border if round
|
||||
edges are enabled (client property `JComponent.roundRect` is `true`). (similar
|
||||
to issue #382; regression since fixing #330 in FlatLaf 1.4)
|
||||
- ComboBox: Fixed `NullPointerException`, which may occur under special
|
||||
circumstances. (issue #408)
|
||||
- Table: Do not select text in cell editor when it gets focus (when
|
||||
`JTable.surrendersFocusOnKeystroke` is `true`) and
|
||||
`TextComponent.selectAllOnFocusPolicy` is `once` (the default) or `always`.
|
||||
(issue #395)
|
||||
- Linux: Fixed NPE when using `java.awt.TrayIcon`. (issue #405)
|
||||
- FileChooser: Workaround for crash on Windows with Java 17 32-bit (disabled
|
||||
Windows icons). Java 17 64-bit is not affected. (issue #403)
|
||||
- Native window decorations: Fixed layout loop, which may occur under special
|
||||
circumstances and slows down the application. (issue #420)
|
||||
|
||||
|
||||
## 1.6.1
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- Native window decorations: Catch `UnsatisfiedLinkError` when trying to load
|
||||
`jawt.dll` to avoid an application crash (Java 8 on Windows 10 only).
|
||||
|
||||
|
||||
## 1.6
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- InternalFrame: Double-click on icon in internal frame title bar now closes the
|
||||
internal frame. (issue #374)
|
||||
- IntelliJ Themes: Removed deprecated `install()` methods.
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- Menus: Fixed missing modifiers flags in `ActionEvent` (e.g. `Ctrl` key
|
||||
pressed) when running in Java 9+ on Linux, macOS. Occurs also on Windows in
|
||||
large popup menus that do not fit into the window. (issue #371; regression
|
||||
since FlatLaf 1.3)
|
||||
- OptionPane: Fixed `OptionPane.sameSizeButtons`, which did not work as expected
|
||||
when setting to `false`.
|
||||
- OptionPane: Fixed rendering of longer HTML text if it is passed as
|
||||
`StringBuilder`, `StringBuffer`, or any other object that returns HTML text in
|
||||
method `toString()`. (similar to issue #12)
|
||||
- ComboBox: Fixed popup border painting on HiDPI screens (e.g. at 150% scaling).
|
||||
- ComboBox: Fixed popup location if shown above of combo box (Java 8 only).
|
||||
- ComboBox (editable): Fixed wrong border of internal text field under special
|
||||
circumstances.
|
||||
- Spinner: Fixed painting of border corners on left side. (issue #382;
|
||||
regression since fixing #330 in FlatLaf 1.4)
|
||||
- TableHeader: Do not show resize cursor for last column if resizing last column
|
||||
is not possible because auto resize mode of table is not off. (issue #332)
|
||||
- TableHeader: Fixed missing trailing vertical separator line if used in upper
|
||||
left corner of scroll pane. (issue #332)
|
||||
- TextField, FormattedTextField, PasswordField and ComboBox: Fixed alignment of
|
||||
placeholder text in right-to-left component orientation.
|
||||
- Slider: Fixed calculation of baseline, which was wrong under some
|
||||
circumstances.
|
||||
|
||||
|
||||
## 1.5
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- SwingX: Added search and clear icons to `JXSearchField`. (issue #359)
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- Button and TextComponent: Do not apply minimum width/height if margins are
|
||||
set. (issue #364)
|
||||
- ComboBox and Spinner: Limit arrow button width if component has large
|
||||
preferred height. (issue #361)
|
||||
- FileChooser: Fixed missing (localized) texts when FlatLaf is loaded in special
|
||||
classloader (e.g. plugin system in Apache NetBeans).
|
||||
- InternalFrame: Limit internal frame bounds to parent bounds on resize. Also
|
||||
honor maximum size of internal frame. (issue #362)
|
||||
- Popup: Fixed incorrectly placed drop shadow for medium-weight popups in
|
||||
maximized windows. (issue #358)
|
||||
- Native window decorations (Windows 10 only):
|
||||
- Fixed occasional application crash in `flatlaf-windows.dll`. (issue #357)
|
||||
- When window is initially shown, fill background with window background color
|
||||
(instead of white), which avoids flickering in dark themes. (issue 339)
|
||||
- When resizing a window at the right/bottom edge, then first fill the new
|
||||
space with the window background color (instead of black) before the layout
|
||||
is updated.
|
||||
- When resizing a window at the left/top edge, then first fill the new space
|
||||
with the window background color (instead of garbage) before the layout is
|
||||
updated.
|
||||
|
||||
|
||||
## 1.4
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- TextField, FormattedTextField and PasswordField: Support adding extra padding
|
||||
(set client property `JTextField.padding` to an `Insets`).
|
||||
- PasswordField: UI delegate `FlatPasswordFieldUI` now extends `FlatTextFieldUI`
|
||||
(instead of `BasicPasswordFieldUI`) to avoid duplicate code and for easier
|
||||
extensibility.
|
||||
- Table and PopupFactory: Use `StackWalker` in Java 9+ for better performance.
|
||||
(issue #334)
|
||||
- ToolBar: Paint focus indicator for focused button in toolbar. (issue #346)
|
||||
- ToolBar: Support focusable buttons in toolbar (set UI value
|
||||
`ToolBar.focusableButtons` to `true`). (issue #346)
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- ComboBox (editable) and Spinner: Increased size of internal text field to the
|
||||
component border so that it behaves like plain text field (mouse click to left
|
||||
of text now positions caret to first character instead of opening ComboBox
|
||||
popup; mouse cursor is now of type "text" within the whole component, except
|
||||
for arrow buttons). (issue #330)
|
||||
- ComboBox (not editable): Increased size of internal renderer pane to the
|
||||
component border so that it can paint within the whole component. Also
|
||||
increase combo box size if a custom renderer uses a border with insets that
|
||||
are larger than the default combo box padding (`2,6,2,6`).
|
||||
- Fixed component heights at `1.25x`, `1.75x` and `2.25x` scaling factors (Java
|
||||
8 only) so that Button, ComboBox, Spinner and TextField components (including
|
||||
subclasses) have same heights. This increases heights of Button and TextField
|
||||
components by:
|
||||
- `2px` at `1.75x` in **Light** and **Dark** themes
|
||||
- `2px` at `1.25x` and `2.25x` in **IntelliJ** and **Darcula** themes
|
||||
- OptionPane: Do not make child components, which are derived from `JPanel`,
|
||||
non-opaque. (issue #349)
|
||||
- OptionPane: Align wrapped lines to the right if component orientation is
|
||||
right-to-left. (issue #350)
|
||||
- PasswordField: Caps lock icon no longer painted over long text. (issue #172)
|
||||
- PasswordField: Paint caps lock icon on left side in right-to-left component
|
||||
orientation.
|
||||
- Window decorations: Window title bar width is no longer considered when
|
||||
calculating preferred/minimum width of window. (issue #351)
|
||||
|
||||
|
||||
## 1.3
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- TextComponents, ComboBox and Spinner: Support different background color when
|
||||
component is focused (use UI values `TextField.focusedBackground`,
|
||||
`PasswordField.focusedBackground`, `FormattedTextField.focusedBackground`,
|
||||
`TextArea.focusedBackground`, `TextPane.focusedBackground`,
|
||||
`EditorPane.focusedBackground`, `ComboBox.focusedBackground`,
|
||||
`ComboBox.buttonFocusedBackground`, `ComboBox.popupBackground` and
|
||||
`Spinner.focusedBackground`). (issue #335)
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- Fixed white lines at bottom and right side of window (in dark themes on HiDPI
|
||||
screens with scaling enabled).
|
||||
- ScrollBar: Fixed left/top arrow icon location (if visible). (issue #329)
|
||||
- Spinner: Fixed up/down arrow icon location.
|
||||
- ToolTip: Fixed positioning of huge tooltips. (issue #333)
|
||||
|
||||
|
||||
## 1.2
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- Renamed `Flat*Laf.install()` methods to `Flat*Laf.setup()` to avoid confusion
|
||||
with `UIManager.installLookAndFeel(LookAndFeelInfo info)`. The old
|
||||
`Flat*Laf.install()` methods are still there, but marked as deprecated. They
|
||||
will be removed in a future version.
|
||||
- Button and ToggleButton: Support borderless button style (set client property
|
||||
`JButton.buttonType` to `borderless`). (PR #276)
|
||||
- ComboBox: Support using as cell renderer (e.g. in `JTable`).
|
||||
- DesktopPane: Improved layout of iconified internal frames in dock:
|
||||
- Always placed at bottom-left in desktop pane.
|
||||
- Newly iconified frames are added to the right side of the dock.
|
||||
- If frame is deiconified, dock is compacted (icons move to the left).
|
||||
- If dock is wider than desktop width, additional rows are used.
|
||||
- If desktop pane is resized, layout of dock is updated.
|
||||
- TableHeader: Moved table header column border painting from
|
||||
`FlatTableHeaderUI` to new border `FlatTableHeaderBorder` to improve
|
||||
compatibility with custom table header implementations. (issue #228)
|
||||
- Linux: Enable text anti-aliasing if no Gnome or KDE Desktop properties are
|
||||
available. (issue #218)
|
||||
- IntelliJ Themes: Added "Material Theme UI Lite / GitHub Dark" theme.
|
||||
- JIDE Common Layer: Improved support for `JideTabbedPane`. (PR #306)
|
||||
- Extras: `FlatSVGIcon` improvements:
|
||||
- Each icon can now have its own color filter. (PR #303)
|
||||
- Use mapper function in color filter to dynamically map colors. (PR #303)
|
||||
- Color filter supports light and dark themes.
|
||||
- Getters for icon name, classloader, etc.
|
||||
- Extras: UI Inspector: Show class hierarchies when pressing <kbd>Alt</kbd> key
|
||||
and prettified class names (dimmed package name).
|
||||
- Extras: `FlatSVGUtils.createWindowIconImages()` now returns a single
|
||||
multi-resolution image that creates requested image sizes on demand from SVG
|
||||
(only on Windows with Java 9+).
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- CheckBox and RadioButton: Do not fill background if used as cell renderer,
|
||||
except if cell is selected or has different background color. (issue #311)
|
||||
- DesktopPane:
|
||||
- Fixed missing preview of iconified internal frames in dock when using a
|
||||
custom desktop manager. (PR #294)
|
||||
- Fixed incomplete preview of iconified internal frames in dock when switching
|
||||
LaF.
|
||||
- On HiDPI screens, use high-resolution images for preview of iconified
|
||||
internal frames in dock.
|
||||
- PopupFactory: Fixed occasional `NullPointerException` in
|
||||
`FlatPopupFactory.fixToolTipLocation()`. (issue #305)
|
||||
- Tree: Fill cell background if
|
||||
`DefaultTreeCellRenderer.setBackgroundNonSelectionColor(Color)` was used.
|
||||
(issue #322)
|
||||
- IntelliJ Themes: Fixed background colors of DesktopPane and DesktopIcon in all
|
||||
themes.
|
||||
- Native window decorations:
|
||||
- Fixed slow application startup under particular conditions. (e.g. incomplete
|
||||
custom JRE) (issue #319)
|
||||
- Fixed occasional double window title bar when creating many frames or
|
||||
dialogs. (issue #315)
|
||||
- Fixed broken maximizing window (under special conditions) when restoring
|
||||
frame state at startup.
|
||||
- Title icon: For multi-resolution images now use `getResolutionVariant(width,
|
||||
height)` (instead of `getResolutionVariants()`) to allow creation of
|
||||
requested size on demand. This also avoids creation of all resolution
|
||||
variants.
|
||||
- Double-click at upper-left corner of maximized frame did not close window.
|
||||
(issue #326)
|
||||
- Linux: Fixed/improved detection of user font settings. (issue #309)
|
||||
|
||||
|
||||
## 1.1.2
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- Native window decorations: Added API to check whether current platform
|
||||
supports window decorations (`FlatLaf.supportsNativeWindowDecorations()`) and
|
||||
to toggle window decorations of all windows
|
||||
(`FlatLaf.setUseNativeWindowDecorations(boolean)`).
|
||||
- Native window decorations: Support changing title bar background and
|
||||
foreground colors per window. (set client properties
|
||||
`JRootPane.titleBarBackground` and `JRootPane.titleBarForeground` on root pane
|
||||
to a `java.awt.Color`).
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- Native window decorations: Fixed loading of native library when using Java
|
||||
Platform Module System (JPMS) for application. (issue #289)
|
||||
- Native window decorations: Removed superfluous pixel-line at top of screen
|
||||
when window is maximized. (issue #296)
|
||||
- Window decorations: Fixed random window title bar background in cases were
|
||||
background is not filled by custom window or root pane components and unified
|
||||
background is enabled.
|
||||
- IntelliJ Themes: Fixed window title bar background if unified background is
|
||||
enabled.
|
||||
- IntelliJ Themes: Fixed system colors.
|
||||
- Button and ToggleButton: Do not paint background of disabled (and unselected)
|
||||
toolBar buttons. (issue #292; regression since fixing #112)
|
||||
- ComboBox and Spinner: Fixed too wide arrow button if component is higher than
|
||||
preferred. (issue #302)
|
||||
- SplitPane: `JSplitPane.setContinuousLayout(false)` did not work. (issue #301)
|
||||
- TabbedPane: Fixed NPE when creating/modifying in another thread. (issue #299)
|
||||
- Fixed crash when running in Webswing. (issue #290)
|
||||
|
||||
|
||||
## 1.1.1
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- Native window decorations: Support disabling native window decorations per
|
||||
window. (set client property `JRootPane.useWindowDecorations` on root pane to
|
||||
`false`).
|
||||
- Support running on WinPE. (issue #279)
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- Native window decorations: Fixed missing animations when minimizing,
|
||||
maximizing or restoring a window using window title bar buttons. (issue #282)
|
||||
- Native window decorations: Fixed broken maximizing window when restoring frame
|
||||
state at startup. (issue #283)
|
||||
- Native window decorations: Fixed double window title bar when first disposing
|
||||
a window with `frame.dispose()` and then showing it again with
|
||||
`frame.setVisible(true)`. (issue #277)
|
||||
- Custom window decorations: Fixed NPE in `FlatTitlePane.findHorizontalGlue()`.
|
||||
(issue #275)
|
||||
- Custom window decorations: Fixed right aligned progress bar in embedded menu
|
||||
bar was overlapping window title. (issue #272)
|
||||
- Fixed missing focus indicators in heavy-weight popups. (issue #273)
|
||||
- InternalFrame: Fixed translucent internal frame menu bar background if
|
||||
`TitlePane.unifiedBackground` is `true`. (issue #274)
|
||||
- Extras: UI Inspector: Fixed `InaccessibleObjectException` when running in Java 16.
|
||||
|
||||
|
||||
## 1.1
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- Windows 10 only:
|
||||
- Native window decorations for Windows 10 enables dark frame/dialog title bar
|
||||
and embedded menu bar with all JREs, while still having native Windows 10
|
||||
border drop shadows, resize behavior, window snapping and system window
|
||||
menu. (PR #267)
|
||||
- Custom window decorations: Support right aligned components in `JFrame`
|
||||
title bar with embedded menu bar (using `Box.createHorizontalGlue()`). (PR
|
||||
#268)
|
||||
- Custom window decorations: Improved centering of window title with embedded
|
||||
menu bar. (PR #268; issue #252)
|
||||
- Custom window decorations: Support unified backgrounds for window title bar,
|
||||
menu bar and main content. If enabled with `UIManager.put(
|
||||
"TitlePane.unifiedBackground", true );` then window title bar and menu bar
|
||||
use same background color as main content. (PR #268; issue #254)
|
||||
- JIDE Common Layer: Support `JideButton`, `JideLabel`, `JideSplitButton`,
|
||||
`JideToggleButton` and `JideToggleSplitButton`.
|
||||
- JIDE Common Layer: The library on Maven Central no longer depends on
|
||||
`com.jidesoft:jide-oss:3.6.18` to avoid problems when another JIDE library
|
||||
should be used. (issue #270)
|
||||
- SwingX: The library on Maven Central no longer depends on
|
||||
`org.swinglabs.swingx:swingx-all:1.6.5-1` to avoid problems when another
|
||||
SwingX library should be used.
|
||||
- Support running in [JetBrains Projector](https://jetbrains.com/projector/).
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- IntelliJ Themes: Fixed text color of CheckBoxMenuItem and RadioButtonMenuItem
|
||||
in all "Arc" themes. (issue #259)
|
||||
|
||||
|
||||
## 1.0
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- Extras: UI Inspector: Tooltip is no longer limited to window bounds.
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- TabbedPane: Custom `TabbedPane.selectedForeground` color did not work when
|
||||
`TabbedPane.foreground` has also custom color. (issue #257)
|
||||
- FileChooser: Fixed display of date in details view if current user is selected
|
||||
in "Look in" combobox. (Windows 10 only; issue #249)
|
||||
- Table: Fixed wrong grid line thickness in dragged column on HiDPI screens on
|
||||
Java 9+. (issue #236)
|
||||
- PopupFactory: Fixed `NullPointerException` when `PopupFactory.getPopup()` is
|
||||
invoked with parameter `owner` set to `null`.
|
||||
|
||||
|
||||
## 1.0-rc3
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- Extras:
|
||||
- UI Inspector: Use HTML in tooltip. Display color value in same format as
|
||||
used in FlatLaf properties files. Added color preview.
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- Label and ToolTip: Fixed font sizes for `<code>`, `<kbd>`, `<big>`, `<small>`
|
||||
and `<samp>` tags in HTML text.
|
||||
- Fixed color of `<address>` tag in HTML text.
|
||||
- IntelliJ Themes: Fixed table header background when dragging column in "Dark
|
||||
Flat" and "Light Flat" themes.
|
||||
- CheckBox: Fixed background of check boxes in JIDE `CheckBoxTree`. (regression
|
||||
in 1.0-rc2)
|
||||
|
||||
|
||||
## 1.0-rc2
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- Button:
|
||||
- In "Flat Light" theme, use a slightly thinner border for focused buttons
|
||||
(because they already have light blue background).
|
||||
- In "Flat Dark" theme, use slightly wider border for focused buttons.
|
||||
- CheckBox and RadioButton: In "Flat Dark" theme, use blueish background for
|
||||
focused components.
|
||||
- Tree: Support disabling wide selection per component. (set client property
|
||||
`JTree.wideSelection` to `false`). (PR #245)
|
||||
- Tree: Support disabling selection painting per component. Then the tree cell
|
||||
renderer is responsible for selection painting. (set client property
|
||||
`JTree.paintSelection` to `false`).
|
||||
- JIDE Common Layer: Support `JidePopupMenu`.
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- Button: Fixed behavior of <kbd>Enter</kbd> key on focused button on Windows
|
||||
and Linux, which now clicks the focused button (instead of the default
|
||||
button).
|
||||
- On Windows, this is a regression in 1.0-rc1.
|
||||
- On macOS, the <kbd>Enter</kbd> key always clicks the default button, which
|
||||
is the platform behavior.
|
||||
- On all platforms, the default button can be always clicked with
|
||||
<kbd>Ctrl+Enter</kbd> keys, even if another button is focused.
|
||||
- CheckBox and RadioButton: Fill component background as soon as background
|
||||
color is different to default background color, even if component is not
|
||||
opaque (which is the default). This paints selection if using the component as
|
||||
cell renderer in Table, Tree or List.
|
||||
- TextComponents: Border of focused non-editable text components had wrong
|
||||
color.
|
||||
- Custom window decorations: Fixed top window border in dark themes when running
|
||||
in JetBrains Runtime.
|
||||
|
||||
|
||||
## 1.0-rc1
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- Button: Disabled `Button.defaultButtonFollowsFocus` on Windows (as on other
|
||||
platforms). If you like to keep the old behavior in your application, use:
|
||||
`if(SystemInfo.isWindows)
|
||||
UIManager.put("Button.defaultButtonFollowsFocus",true);`.
|
||||
- ComboBox, Spinner and SplitPaneDivider: Added pressed feedback to arrow
|
||||
buttons.
|
||||
- Slider: Support per component custom thumb and track colors via
|
||||
`JSlider.setForeground(Color)` and `JSlider.setBackground(Color)`.
|
||||
- Slider: Improved thumb hover and pressed colors.
|
||||
- TextComponent: Clip placeholder text if it does not fit into visible area. (PR
|
||||
#229)
|
||||
- macOS: Improved font rendering on macOS when using JetBrains Runtime. (PRs
|
||||
#237, #239 and #241)
|
||||
- Extras: UI defaults inspector:
|
||||
- Support embedding UI defaults inspector panel into any window. See
|
||||
`FlatUIDefaultsInspector.createInspectorPanel()`.
|
||||
- Copy selected keys and values into clipboard via context menu.
|
||||
- Support wildcard matching in filter (`*` matches any number of characters,
|
||||
`?` matches a single character, `^` beginning of line, `$` end of line).
|
||||
- IntelliJ Themes:
|
||||
- Added hover and pressed feedback to Button, CheckBox, RadioButton and
|
||||
ToggleButton. (issue #176)
|
||||
- Added "Material Theme UI Lite / Moonlight" theme.
|
||||
- Updated "Dracula", "Gradianto" and "Material Theme UI Lite" themes.
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- Button and ToggleButton: Threat Unicode surrogate character pair as single
|
||||
character and make button square. (issue #234)
|
||||
- Button and ToggleButton: ToolBar buttons now respect explicitly set background
|
||||
color. If no background color is set, then the button background is not
|
||||
painted anymore. (issue #191)
|
||||
- ToggleButton: Tab style buttons (client property `JButton.buttonType` is
|
||||
`tab`) now respect explicitly set background color.
|
||||
- TabbedPane: Fixed `IndexOutOfBoundsException` when using tooltip text on close
|
||||
buttons and closing last/rightmost tab. (issue #235)
|
||||
- TabbedPane: Fixed scrolling tabs with touchpads and high-resolution mouse
|
||||
wheels.
|
||||
- Extras: Added missing export of package
|
||||
`com.formdev.flatlaf.extras.components` to Java 9 module descriptor.
|
||||
- JIDE Common Layer:
|
||||
- Invoke `LookAndFeelFactory.installJideExtension()` when using FlatLaf UI
|
||||
delegates. (issue #230)
|
||||
- RangeSlider: Fixed slider focused colors in IntelliJ themes.
|
||||
- IntelliJ Themes:
|
||||
- Fixed menu item check colors.
|
||||
- Fixed `MenuItem.underlineSelectionColor`.
|
||||
- Fixed List, Tree and Table `selectionInactiveForeground` in light Arc
|
||||
themes.
|
||||
- Fixed List and Table background colors in Material UI Lite themes.
|
||||
- Fixed menu accelerator colors in Monocai theme. (issue #243)
|
||||
|
||||
|
||||
## 0.46
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- Slider and JIDE RangeSlider: Clicking on track now immediately moves the thumb
|
||||
to mouse location and starts dragging the thumb. Use `UIManager.put(
|
||||
"Slider.scrollOnTrackClick", true )` to enable old behavior that scrolls the
|
||||
thumb when clicking on track.
|
||||
- Slider: Snap to ticks is now done while dragging the thumb. Use
|
||||
`UIManager.put( "Slider.snapToTicksOnReleased", true )` to enable old behavior
|
||||
that snaps to ticks on mouse released.
|
||||
- Extras: Added standard component extension classes that provides easy access
|
||||
to FlatLaf specific client properties (see package
|
||||
`com.formdev.flatlaf.extras.components`).
|
||||
- Extras: Renamed tri-state check box class from
|
||||
`com.formdev.flatlaf.extras.TriStateCheckBox` to
|
||||
`com.formdev.flatlaf.extras.components.FlatTriStateCheckBox`. Also
|
||||
changed/improved API and added javadoc.
|
||||
- Extras: Renamed SVG utility class from `com.formdev.flatlaf.extras.SVGUtils`
|
||||
to `com.formdev.flatlaf.extras.FlatSVGUtils`.
|
||||
- IntelliJ Themes: Added flag whether a theme is dark to
|
||||
`FlatAllIJThemes.INFOS`. (issue #221)
|
||||
- JIDE Common Layer: Support `TristateCheckBox`.
|
||||
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- Slider: Fixed painting of colored track if `JSlider.inverted` is `true`.
|
||||
- Table and TableHeader: Fixed missing right vertical grid line if using table
|
||||
as row header in scroll pane. (issues #152 and #46)
|
||||
- TableHeader: Fixed position of column separators in right-to-left component
|
||||
orientation.
|
||||
- ToolTip: Fixed drop shadow for wide tooltips on Windows and Java 9+. (issue
|
||||
#224)
|
||||
- SwingX: Fixed striping background highlighting color (e.g. alternating table
|
||||
rows) in dark themes.
|
||||
- Fixed: If text antialiasing is disabled (in OS system settings or via
|
||||
`-Dawt.useSystemAAFontSettings=off`), then some components still did use
|
||||
antialiasing to render text (not-editable ComboBox, ProgressBar, Slider,
|
||||
TabbedPane and multiline ToolTip). (issue #227)
|
||||
|
||||
|
||||
## 0.45
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- Slider: New design, added hover and pressed feedback and improved customizing.
|
||||
(PR #214)
|
||||
- JIDE Common Layer: Support `RangeSlider`. (PR #209)
|
||||
- IntelliJ Themes:
|
||||
- Added "Gradianto Nature Green" theme.
|
||||
- Updated "Arc Dark", "Cyan", "Dark purple", "Gradianto", "Gray", "Gruvbox"
|
||||
and "One Dark" themes.
|
||||
- TabbedPane: Support hiding tab area if it contains only one tab. (set client
|
||||
property `JTabbedPane.hideTabAreaWithOneTab` to `true`)
|
||||
- MenuBar: Support different underline menu selection style UI defaults for
|
||||
`MenuBar` and `MenuItem`. (PR #217; issue #216)
|
||||
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- Table: Do not paint last vertical grid line if auto-resize mode is not off.
|
||||
(issue #46)
|
||||
- Table: Fixed unstable grid line thickness when scaled on HiDPI screens. (issue
|
||||
#152)
|
||||
- TabbedPane: No longer add (internal) tab close button component as child to
|
||||
`JTabbedPane`. (issue #219)
|
||||
- Custom window decorations: Title bar was not hidden if window is in
|
||||
full-screen mode. (issue #212)
|
||||
|
||||
|
||||
## 0.44
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- TabbedPane: In scroll tab layout, added "Show Hidden Tabs" button to trailing
|
||||
side of tab area. If pressed, it shows a popup menu that contains (partly)
|
||||
hidden tabs and selecting one activates that tab. (PR #190; issue #40)
|
||||
- TabbedPane: Support forward/backward scroll arrow buttons on both sides of tab
|
||||
area. Backward button on left side, forward button on right side. Not
|
||||
applicable scroll buttons are hidden. (PR #211; issue #40)
|
||||
- TabbedPane: Support specifying default tab layout policy for all tabbed panes
|
||||
in the application via UI value `TabbedPane.tabLayoutPolicy`. E.g. invoke
|
||||
`UIManager.put( "TabbedPane.tabLayoutPolicy", "scroll" );` to use scroll
|
||||
layout.
|
||||
- TabbedPane: Support tab scrolling with mouse wheel (in scroll tab layout). (PR
|
||||
#187; issue #40)
|
||||
- TabbedPane: Repeat scrolling as long as scroll arrow buttons are pressed. (PR
|
||||
#187; issue #40)
|
||||
- TabbedPane: Support adding custom components to left and right sides of tab
|
||||
area. (set client property `JTabbedPane.leadingComponent` or
|
||||
`JTabbedPane.trailingComponent` to a `java.awt.Component`) (PR #192; issue
|
||||
#40)
|
||||
- TabbedPane: Support closable tabs. (PR #193; issues #31 and #40)
|
||||
- TabbedPane: Support minimum or maximum tab widths. (set client property
|
||||
`JTabbedPane.minimumTabWidth` or `JTabbedPane.maximumTabWidth` to an integer)
|
||||
(PR #199)
|
||||
- TabbedPane: Support alignment of tab area. (set client property
|
||||
`JTabbedPane.tabAreaAlignment` to `"leading"`, `"trailing"`, `"center"` or
|
||||
`"fill"`) (PR #199)
|
||||
- TabbedPane: Support horizontal alignment of tab title and icon. (set client
|
||||
property `JTabbedPane.tabAlignment` to `SwingConstants.LEADING`,
|
||||
`SwingConstants.TRAILING` or `SwingConstants.CENTER`)
|
||||
- TabbedPane: Support equal and compact tab width modes. (set client property
|
||||
`JTabbedPane.tabWidthMode` to `"preferred"`, `"equal"` or `"compact"`) (PR
|
||||
#199)
|
||||
- TabbedPane: Support left, right, top and bottom tab icon placement. (set
|
||||
client property `JTabbedPane.tabIconPlacement` to `SwingConstants.LEADING`,
|
||||
`SwingConstants.TRAILING`, `SwingConstants.TOP` or `SwingConstants.BOTTOM`)
|
||||
(PR #199)
|
||||
- Support painting separator line between window title and content (use UI value
|
||||
`TitlePane.borderColor`). (issue #184)
|
||||
- Extras: `FlatSVGIcon` now allows specifying icon width and height in
|
||||
constructors. (issue #196)
|
||||
- SplitPane: Hide not applicable expand/collapse buttons. Added tooltips to
|
||||
expand/collapse buttons. (issue #198)
|
||||
- SplitPane: Added grip to divider. Can be disabled with `UIManager.put(
|
||||
"SplitPaneDivider.style", "plain" )`. (issue #179)
|
||||
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- Custom window decorations: Not visible menu bar is now ignored in layout.
|
||||
- Popups using `JToolTip` components did not respect their location. (issue
|
||||
#188; regression in 0.42 in fix for #164)
|
||||
- IntelliJ Themes: Added suffix "(Material)" to names of all Material UI Lite
|
||||
themes to avoid duplicate theme names. (issue #201)
|
||||
- Extras: `FlatSVGIcon` icons were not painted in disabled labels and disabled
|
||||
tabs. (issue #205)
|
||||
|
||||
|
||||
## 0.43
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- TabbedPane: Made tabs separator color lighter in dark themes so that it is
|
||||
easier to recognize the tabbed pane.
|
||||
- TabbedPane: Added top and bottom tab insets to avoid that large tab icons are
|
||||
painted over active tab underline.
|
||||
- TabbedPane: Support hiding separator between tabs and content area (set client
|
||||
property `JTabbedPane.showContentSeparator` to `false`).
|
||||
- CheckBoxMenuItem and RadioButtonMenuItem: Improved checkmark background colors
|
||||
of selected menu items that have also an icon. This makes it is easier to
|
||||
recognize selected menu items.
|
||||
- Windows: Made scaling compatible with Windows OS scaling, which distinguish
|
||||
between "screen scaling" and "text scaling". (issue #175)
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- ComboBox: If using own `JTextField` as editor, default text field border is
|
||||
now removed to avoid duplicate border.
|
||||
- ComboBox: Limit popup width to screen width for very long items. (issue #182)
|
||||
- FileChooser: Fixed localizing special Windows folders (e.g. "Documents") and
|
||||
enabled hiding known file extensions (if enabled in Windows Explorer). (issue
|
||||
#178)
|
||||
- Spinner: Fixed `NullPointerException` in case that arrow buttons were removed
|
||||
to create button-less spinner. (issue #181)
|
||||
|
||||
|
||||
## 0.42
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
177
README.md
177
README.md
@@ -37,7 +37,7 @@ Requires Java 8 or newer.
|
||||
Download
|
||||
--------
|
||||
|
||||
FlatLaf binaries are available on **JCenter** and **Maven Central**.
|
||||
FlatLaf binaries are available on **Maven Central**.
|
||||
|
||||
If you use Maven or Gradle, add a dependency with following coordinates to your
|
||||
build script:
|
||||
@@ -48,16 +48,16 @@ build script:
|
||||
|
||||
Otherwise download `flatlaf-<version>.jar` here:
|
||||
|
||||
[](https://bintray.com/jformdesigner/flatlaf/flatlaf/_latestVersion)
|
||||
[](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf)
|
||||
|
||||
|
||||
### Snapshots
|
||||
|
||||
FlatLaf snapshot binaries are available in
|
||||
[JFrog Artifactory](https://oss.jfrog.org/artifactory/oss-snapshot-local/com/formdev/).
|
||||
To access the latest snapshot, change the FlatLaf version(s) in the dependencies
|
||||
FlatLaf snapshot binaries are available on
|
||||
[Sonatype OSSRH](https://oss.sonatype.org/content/repositories/snapshots/com/formdev/flatlaf/).
|
||||
To access the latest snapshot, change the FlatLaf version in your dependencies
|
||||
to `<version>-SNAPSHOT` (e.g. `0.27-SNAPSHOT`) and add the repository
|
||||
`https://oss.jfrog.org/artifactory/oss-snapshot-local` to your build (see
|
||||
`https://oss.sonatype.org/content/repositories/snapshots/` to your build (see
|
||||
[Maven](https://maven.apache.org/guides/mini/guide-multiple-repositories.html)
|
||||
and
|
||||
[Gradle](https://docs.gradle.org/current/userguide/declaring_repositories.html#sec:declaring_custom_repository)
|
||||
@@ -67,57 +67,136 @@ docs).
|
||||
Addons
|
||||
------
|
||||
|
||||
- [IntelliJ Themes Pack](flatlaf-intellij-themes)
|
||||
- [Extras](flatlaf-extras)
|
||||
- [SwingX](flatlaf-swingx)
|
||||
- [JIDE Common Layer](flatlaf-jide-oss)
|
||||
- [IntelliJ Themes Pack](flatlaf-intellij-themes) - bundles many popular
|
||||
open-source 3rd party themes
|
||||
- [Extras](flatlaf-extras) - SVG icon, tri-state check box, UI inspectors, and
|
||||
more
|
||||
- [SwingX](flatlaf-swingx) - support for SwingX components
|
||||
- [JIDE Common Layer](flatlaf-jide-oss) - support for JIDE Common Layer
|
||||
components
|
||||
|
||||
|
||||
Projects using FlatLaf
|
||||
----------------------
|
||||
Getting started
|
||||
---------------
|
||||
|
||||
- [NetBeans](https://netbeans.apache.org/) 11.3
|
||||
- [jclasslib bytecode viewer](https://github.com/ingokegel/jclasslib) 5.5
|
||||
- [KeyStore Explorer](https://keystore-explorer.org/) 5.4.3
|
||||
- [OWASP Zed Attack Proxy (ZAP)](https://www.zaproxy.org/) (in weekly releases)
|
||||
-  [jAlbum](https://jalbum.net/) 21 (commercial)
|
||||
- [XMLmind XML Editor](https://www.xmlmind.com/xmleditor/) 9.3 (commercial)
|
||||
- [Total Validator](https://www.totalvalidator.com/) 15 (commercial)
|
||||
- [j-lawyer](https://github.com/jlawyerorg/j-lawyer-org)
|
||||
- [MegaMek](https://github.com/MegaMek/megamek) v0.47.4 and
|
||||
[MekHQ](https://github.com/MegaMek/mekhq) v0.47.5
|
||||
- [GUIslice Builder](https://github.com/ImpulseAdventure/GUIslice-Builder)
|
||||
0.13.b024
|
||||
- [Rest Suite](https://github.com/supanadit/restsuite)
|
||||
- [ControllerBuddy](https://github.com/bwRavencl/ControllerBuddy)
|
||||
- [SpringRemote](https://github.com/HaleyWang/SpringRemote)
|
||||
- [mendelson AS2](https://sourceforge.net/projects/mec-as2/),
|
||||
[AS4](https://sourceforge.net/projects/mendelson-as4/) and
|
||||
[OFTP2](https://sourceforge.net/projects/mendelson-oftp2/) (open-source) and
|
||||
[mendelson AS2](https://mendelson-e-c.com/as2/),
|
||||
[AS4](https://mendelson-e-c.com/as4/) and
|
||||
[OFTP2](https://mendelson-e-c.com/oftp2) (commercial)
|
||||
- [MeteoInfo](https://github.com/meteoinfo/MeteoInfo) 2.2
|
||||
- [lsfusion platform](https://github.com/lsfusion/platform)
|
||||
- [Jes - Die Java-E<>R](https://www.jes-eur.de)
|
||||
- [Mapton](https://mapton.org/) 2.0
|
||||
([source code](https://github.com/trixon/mapton)) based on NetBeans platform
|
||||
- [Pseudo Assembler IDE](https://github.com/tomasz-herman/PseudoAssemblerIDE)
|
||||
- [Sound Analysis](https://github.com/tomasz-herman/SoundAnalysis)
|
||||
- [RemoteLight](https://github.com/Drumber/RemoteLight) - Multifunctional LED
|
||||
Control Software
|
||||
- and more...
|
||||
To use FlatLaf, add following code to your main method before you create any
|
||||
Swing component:
|
||||
|
||||
~~~java
|
||||
FlatLightLaf.setup();
|
||||
|
||||
Buzz
|
||||
----
|
||||
|
||||
- [What others say about FlatLaf on Twitter](https://twitter.com/search?f=live&q=flatlaf)
|
||||
- [FlatLaf announcement on Reddit](https://www.reddit.com/r/java/comments/dl0hu3/flatlaf_flat_look_and_feel/)
|
||||
// create UI here...
|
||||
~~~
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
For more information and documentation visit
|
||||
[FlatLaf Home](https://www.formdev.com/flatlaf/)
|
||||
[FlatLaf Home](https://www.formdev.com/flatlaf/):
|
||||
|
||||
- [Themes](https://www.formdev.com/flatlaf/themes/)
|
||||
- [Customizing](https://www.formdev.com/flatlaf/customizing/)
|
||||
- [How to Customize](https://www.formdev.com/flatlaf/how-to-customize/)
|
||||
- [Properties Files](https://www.formdev.com/flatlaf/properties-files/)
|
||||
- [Client Properties](https://www.formdev.com/flatlaf/client-properties/)
|
||||
- [System Properties](https://www.formdev.com/flatlaf/system-properties/)
|
||||
|
||||
|
||||
Buzz
|
||||
----
|
||||
|
||||
- [What others say about FlatLaf on Twitter](https://twitter.com/search?f=live&q=flatlaf)
|
||||
- [FlatLaf 1.0 announcement on Reddit](https://www.reddit.com/r/java/comments/lsbcwe/flatlaf_10_swing_look_and_feel/)
|
||||
- [FlatLaf announcement on Reddit](https://www.reddit.com/r/java/comments/dl0hu3/flatlaf_flat_look_and_feel/)
|
||||
|
||||
|
||||
Applications using FlatLaf
|
||||
--------------------------
|
||||
|
||||
- [Apache NetBeans](https://netbeans.apache.org/) 11.3 - IDE for Java, PHP, HTML
|
||||
and much more
|
||||
- [jclasslib bytecode viewer](https://github.com/ingokegel/jclasslib) 5.5
|
||||
- [KeyStore Explorer](https://keystore-explorer.org/) 5.4.3
|
||||
- 
|
||||
[install4j](https://www.ej-technologies.com/products/install4j/overview.html)
|
||||
9.0 (**commercial**) - the powerful multi-platform Java installer builder
|
||||
-  [DbVisualizer](https://www.dbvis.com/) 12.0
|
||||
(**commercial**) - the universal database tool for developers, analysts and
|
||||
DBAs
|
||||
-  [MagicPlot](https://magicplot.com/) 3.0
|
||||
(**commercial**) - Software for nonlinear fitting, plotting and data analysis
|
||||
- 
|
||||
[Thermo-Calc](https://thermocalc.com/products/thermo-calc/) 2021a
|
||||
(**commercial**) - Thermodynamics and Properties Software
|
||||
- [OWASP ZAP](https://www.zaproxy.org/) 2.10 - the worlds most widely used web
|
||||
app scanner
|
||||
- 
|
||||
[Burp Suite Professional and Community Edition](https://portswigger.net/burp/pro)
|
||||
2020.11.2 (**commercial**) - the leading software for web security testing
|
||||
- 
|
||||
[BurpCustomizer](https://github.com/CoreyD97/BurpCustomizer) - adds more
|
||||
FlatLaf themes to Burp Suite
|
||||
- [JOSM](https://josm.openstreetmap.de/) - an extensible editor for
|
||||
[OpenStreetMap](https://www.openstreetmap.org/) (requires FlatLaf JOSM plugin)
|
||||
- [jAlbum](https://jalbum.net/) 21 (**commercial**) - creates photo album
|
||||
websites
|
||||
- [XMLmind XML Editor](https://www.xmlmind.com/xmleditor/) 9.3 (**commercial**)
|
||||
- [Total Validator](https://www.totalvalidator.com/) 15 (**commercial**) -
|
||||
checks your website
|
||||
- [j-lawyer](https://github.com/jlawyerorg/j-lawyer-org) - Kanzleisoftware
|
||||
- [MegaMek](https://github.com/MegaMek/megamek) v0.47.4 and
|
||||
[MekHQ](https://github.com/MegaMek/mekhq) v0.47.5 - a turn-based sci-fi board
|
||||
game
|
||||
- [GUIslice Builder](https://github.com/ImpulseAdventure/GUIslice-Builder)
|
||||
0.13.b024 - GUI builder for
|
||||
[GUIslice](https://github.com/ImpulseAdventure/GUIslice), a lightweight GUI
|
||||
framework for embedded displays
|
||||
- [Rest Suite](https://github.com/supanadit/restsuite) - Rest API testing
|
||||
- [ControllerBuddy](https://github.com/bwRavencl/ControllerBuddy) - advanced
|
||||
gamepad mapping software
|
||||
- [SpringRemote](https://github.com/HaleyWang/SpringRemote) - remote Linux SSH
|
||||
connections manager
|
||||
- [jEnTunnel](https://github.com/ggrandes/jentunnel) - manage SSH Tunnels made
|
||||
easy
|
||||
- [mendelson AS2](https://sourceforge.net/projects/mec-as2/),
|
||||
[AS4](https://sourceforge.net/projects/mendelson-as4/) and
|
||||
[OFTP2](https://sourceforge.net/projects/mendelson-oftp2/) (open-source) and
|
||||
[mendelson AS2](https://mendelson-e-c.com/as2/),
|
||||
[AS4](https://mendelson-e-c.com/as4/) and
|
||||
[OFTP2](https://mendelson-e-c.com/oftp2) (**commercial**)
|
||||
-  [IGMAS+](https://www.gfz-potsdam.de/igmas) -
|
||||
Interactive Gravity and Magnetic Application System
|
||||
- [MeteoInfo](https://github.com/meteoinfo/MeteoInfo) 2.2 - GIS and scientific
|
||||
computation environment for meteorological community
|
||||
- [lsfusion platform](https://github.com/lsfusion/platform) 4 - information
|
||||
systems development platform
|
||||
- [JPass](https://github.com/gaborbata/jpass) - password manager with strong
|
||||
encryption
|
||||
- [Jes - Die Java-E<>R](https://www.jes-eur.de)
|
||||
- [Mapton](https://mapton.org/) 2.0
|
||||
([source code](https://github.com/trixon/mapton)) - some kind of map
|
||||
application (based on NetBeans platform)
|
||||
- [Pseudo Assembler IDE](https://github.com/tomasz-herman/PseudoAssemblerIDE) -
|
||||
IDE for Pseudo-Assembler
|
||||
- [Linotte](https://github.com/cpc6128/LangageLinotte) 3.1 - French programming
|
||||
language created to learn programming
|
||||
- [MEKA](https://github.com/Waikato/meka) 1.9.3 - multi-label classifiers and
|
||||
evaluation procedures using the Weka machine learning framework
|
||||
- [Shutter Encoder](https://www.shutterencoder.com/) 14.2
|
||||
([source code](https://github.com/paulpacifico/shutter-encoder)) -
|
||||
professional video converter and compression tool (screenshots show **old**
|
||||
look)
|
||||
- [Sound Analysis](https://github.com/tomasz-herman/SoundAnalysis) - analyze
|
||||
sound files in time or frequency domain
|
||||
- [RemoteLight](https://github.com/Drumber/RemoteLight) - multifunctional LED
|
||||
control software
|
||||
- [ThunderFocus](https://github.com/marcocipriani01/ThunderFocus) -
|
||||
Arduino-based telescope focuser
|
||||
- [Novel-Grabber](https://github.com/Flameish/Novel-Grabber) - download novels
|
||||
from any webnovel and lightnovel site
|
||||
- [lectureStudio](https://www.lecturestudio.org/) 4.3.1060 - digitize your
|
||||
lectures with ease
|
||||
- [Android Tool](https://github.com/fast-geek/Android-Tool) - makes popular adb
|
||||
and fastboot commands easier to use
|
||||
- and more...
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
val releaseVersion = "0.42"
|
||||
val developmentVersion = "0.43-SNAPSHOT"
|
||||
val releaseVersion = "1.6.4"
|
||||
val developmentVersion = "2.0-SNAPSHOT"
|
||||
|
||||
version = if( java.lang.Boolean.getBoolean( "release" ) ) releaseVersion else developmentVersion
|
||||
|
||||
@@ -23,7 +23,7 @@ allprojects {
|
||||
version = rootProject.version
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,17 +40,6 @@ println( "Java ${System.getProperty( "java.version" )}" )
|
||||
println()
|
||||
|
||||
|
||||
extra["bintray.user"] = System.getenv( "BINTRAY_USER" ) ?: System.getProperty( "bintray.user" )
|
||||
extra["bintray.key"] = System.getenv( "BINTRAY_KEY" ) ?: System.getProperty( "bintray.key" )
|
||||
|
||||
// if true, do not upload to bintray
|
||||
extra["bintray.dryRun"] = false
|
||||
|
||||
// if true, uploaded artifacts are visible to all
|
||||
// if false, only visible to owner when logged into bintray
|
||||
extra["bintray.publish"] = true
|
||||
|
||||
|
||||
allprojects {
|
||||
tasks {
|
||||
withType<JavaCompile>().configureEach {
|
||||
@@ -58,19 +47,35 @@ allprojects {
|
||||
targetCompatibility = "1.8"
|
||||
|
||||
options.encoding = "ISO-8859-1"
|
||||
options.isDeprecation = false
|
||||
}
|
||||
|
||||
withType<Jar>().configureEach {
|
||||
// manifest for all created JARs
|
||||
manifest.attributes(mapOf(
|
||||
manifest.attributes(
|
||||
"Implementation-Vendor" to "FormDev Software GmbH",
|
||||
"Implementation-Copyright" to "Copyright (C) ${java.time.LocalDate.now().year} FormDev Software GmbH. All rights reserved.",
|
||||
"Implementation-Version" to project.version))
|
||||
"Implementation-Copyright" to "Copyright (C) 2019-${java.time.LocalDate.now().year} FormDev Software GmbH. All rights reserved.",
|
||||
"Implementation-Version" to project.version
|
||||
)
|
||||
|
||||
// add META-INF/LICENSE to all created JARs
|
||||
from("${rootDir}/LICENSE") {
|
||||
into("META-INF")
|
||||
from( "${rootDir}/LICENSE" ) {
|
||||
into( "META-INF" )
|
||||
}
|
||||
}
|
||||
|
||||
withType<Javadoc>().configureEach {
|
||||
options {
|
||||
this as StandardJavadocDocletOptions
|
||||
|
||||
title = "${project.name} $version"
|
||||
header = title
|
||||
isUse = true
|
||||
tags = listOf( "uiDefault", "clientProperty" )
|
||||
addStringOption( "Xdoclint:all,-missing", "-Xdoclint:all,-missing" )
|
||||
links( "https://docs.oracle.com/en/java/javase/11/docs/api/" )
|
||||
}
|
||||
isFailOnError = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,15 +20,5 @@ plugins {
|
||||
|
||||
// required for kotlin-dsl or embedded-kotlin plugins
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// NOTE: keep plugin versions in sync with settings.gradle.kts
|
||||
|
||||
// "com.jfrog.bintray" plugin
|
||||
implementation( "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4" )
|
||||
|
||||
// "com.jfrog.artifactory" plugin
|
||||
implementation( "org.jfrog.buildinfo:build-info-extractor-gradle:4.13.0" )
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
@@ -27,6 +27,10 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
add( "java9Compile", sourceSets.main.get().output )
|
||||
}
|
||||
|
||||
tasks {
|
||||
named<JavaCompile>( "compileJava9Java" ) {
|
||||
sourceCompatibility = "9"
|
||||
|
||||
@@ -33,9 +33,17 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
|
||||
sourceSets {
|
||||
create( "module-info" ) {
|
||||
java {
|
||||
// include "src/main/java" here to get compile errors if classes are
|
||||
// include "src/main/java" and "src/main/java9" here to get compile errors if classes are
|
||||
// used from other modules that are not specified in module dependencies
|
||||
setSrcDirs( listOf( "src/main/module-info", "src/main/java" ) )
|
||||
setSrcDirs( listOf( "src/main/module-info", "src/main/java", "src/main/java9" ) )
|
||||
|
||||
// exclude Java 8 source file if an equally named Java 9+ source file exists
|
||||
exclude {
|
||||
if( it.isDirectory )
|
||||
return@exclude false
|
||||
val java9file = file( "${projectDir}/src/main/java9/${it.path}" )
|
||||
java9file.exists() && java9file != it.file
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,7 +56,8 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
|
||||
dependsOn( extension.paths )
|
||||
|
||||
options.compilerArgs.add( "--module-path" )
|
||||
options.compilerArgs.add( configurations.runtimeClasspath.get().asPath )
|
||||
options.compilerArgs.add( configurations.runtimeClasspath.get().asPath
|
||||
+ File.pathSeparator + configurations.compileClasspath.get().asPath )
|
||||
}
|
||||
|
||||
jar {
|
||||
|
||||
@@ -26,8 +26,7 @@ val extension = project.extensions.create<PublishExtension>( "flatlafPublish" )
|
||||
|
||||
plugins {
|
||||
`maven-publish`
|
||||
id( "com.jfrog.bintray" )
|
||||
id( "com.jfrog.artifactory" )
|
||||
signing
|
||||
}
|
||||
|
||||
publishing {
|
||||
@@ -63,54 +62,51 @@ publishing {
|
||||
}
|
||||
|
||||
scm {
|
||||
connection.set( "scm:git:git://github.com/JFormDesigner/FlatLaf.git" )
|
||||
url.set( "https://github.com/JFormDesigner/FlatLaf" )
|
||||
}
|
||||
|
||||
issueManagement {
|
||||
system.set( "GitHub" )
|
||||
url.set( "https://github.com/JFormDesigner/FlatLaf/issues" )
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
name = "OSSRH"
|
||||
|
||||
val releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
|
||||
val snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots/"
|
||||
url = uri( if( java.lang.Boolean.getBoolean( "release" ) ) releasesRepoUrl else snapshotsRepoUrl )
|
||||
|
||||
credentials {
|
||||
// get from gradle.properties
|
||||
val ossrhUsername: String? by project
|
||||
val ossrhPassword: String? by project
|
||||
|
||||
username = System.getenv( "OSSRH_USERNAME" ) ?: ossrhUsername
|
||||
password = System.getenv( "OSSRH_PASSWORD" ) ?: ossrhPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bintray {
|
||||
user = rootProject.extra["bintray.user"] as String?
|
||||
key = rootProject.extra["bintray.key"] as String?
|
||||
signing {
|
||||
// get from gradle.properties
|
||||
val signingKey: String? by project
|
||||
val signingPassword: String? by project
|
||||
|
||||
setPublications( "maven" )
|
||||
val key = System.getenv( "SIGNING_KEY" ) ?: signingKey
|
||||
val password = System.getenv( "SIGNING_PASSWORD" ) ?: signingPassword
|
||||
|
||||
with( pkg ) {
|
||||
repo = "flatlaf"
|
||||
afterEvaluate {
|
||||
this@with.name = extension.artifactId
|
||||
}
|
||||
setLicenses( "Apache-2.0" )
|
||||
vcsUrl = "https://github.com/JFormDesigner/FlatLaf"
|
||||
|
||||
with( version ) {
|
||||
name = project.version.toString()
|
||||
}
|
||||
|
||||
publish = rootProject.extra["bintray.publish"] as Boolean
|
||||
dryRun = rootProject.extra["bintray.dryRun"] as Boolean
|
||||
}
|
||||
useInMemoryPgpKeys( key, password )
|
||||
sign( publishing.publications["maven"] )
|
||||
}
|
||||
|
||||
artifactory {
|
||||
setContextUrl( "https://oss.jfrog.org" )
|
||||
|
||||
publish( closureOf<org.jfrog.gradle.plugin.artifactory.dsl.PublisherConfig> {
|
||||
repository( delegateClosureOf<groovy.lang.GroovyObject> {
|
||||
setProperty( "repoKey", "oss-snapshot-local" )
|
||||
setProperty( "username", rootProject.extra["bintray.user"] as String? )
|
||||
setProperty( "password", rootProject.extra["bintray.key"] as String? )
|
||||
} )
|
||||
|
||||
defaults( delegateClosureOf<groovy.lang.GroovyObject> {
|
||||
invokeMethod( "publications", "maven" )
|
||||
setProperty( "publishArtifacts", true )
|
||||
setProperty( "publishPom", true )
|
||||
} )
|
||||
} )
|
||||
|
||||
resolve( delegateClosureOf<org.jfrog.gradle.plugin.artifactory.dsl.ResolverConfig> {
|
||||
setProperty( "repoKey", "jcenter" )
|
||||
} )
|
||||
// disable signing of snapshots
|
||||
tasks.withType<Sign>().configureEach {
|
||||
onlyIf { java.lang.Boolean.getBoolean( "release" ) }
|
||||
}
|
||||
|
||||
2151
compare1.txt
Normal file
2151
compare1.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -21,12 +21,33 @@ plugins {
|
||||
`flatlaf-publish`
|
||||
}
|
||||
|
||||
val sigtest = configurations.create( "sigtest" )
|
||||
|
||||
dependencies {
|
||||
testImplementation( "org.junit.jupiter:junit-jupiter-api:5.7.2" )
|
||||
testImplementation( "org.junit.jupiter:junit-jupiter-params" )
|
||||
testRuntimeOnly( "org.junit.jupiter:junit-jupiter-engine" )
|
||||
|
||||
// https://github.com/jtulach/netbeans-apitest
|
||||
sigtest( "org.netbeans.tools:sigtest-maven-plugin:1.4" )
|
||||
}
|
||||
|
||||
java {
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
tasks {
|
||||
compileJava {
|
||||
// generate JNI headers
|
||||
options.headerOutputDirectory.set( buildDir.resolve( "generated/jni-headers" ) )
|
||||
}
|
||||
|
||||
processResources {
|
||||
// build native libraries
|
||||
dependsOn( ":flatlaf-natives-windows:assemble" )
|
||||
}
|
||||
|
||||
jar {
|
||||
archiveBaseName.set( "flatlaf" )
|
||||
|
||||
@@ -35,22 +56,67 @@ tasks {
|
||||
}
|
||||
}
|
||||
|
||||
javadoc {
|
||||
options {
|
||||
this as StandardJavadocDocletOptions
|
||||
use( true )
|
||||
tags = listOf( "uiDefault", "clientProperty" )
|
||||
addStringOption( "Xdoclint:all,-missing", "-Xdoclint:all,-missing" )
|
||||
named<Jar>( "sourcesJar" ) {
|
||||
archiveBaseName.set( "flatlaf" )
|
||||
}
|
||||
|
||||
named<Jar>( "javadocJar" ) {
|
||||
archiveBaseName.set( "flatlaf" )
|
||||
}
|
||||
|
||||
check {
|
||||
dependsOn( "sigtestCheck" )
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
||||
}
|
||||
|
||||
register( "sigtestGenerate" ) {
|
||||
group = "verification"
|
||||
dependsOn( "jar" )
|
||||
|
||||
doLast {
|
||||
ant.withGroovyBuilder {
|
||||
"taskdef"(
|
||||
"name" to "sigtest",
|
||||
"classname" to "org.netbeans.apitest.Sigtest",
|
||||
"classpath" to sigtest.asPath )
|
||||
|
||||
"sigtest"(
|
||||
"action" to "generate",
|
||||
"fileName" to "${project.name}-sigtest.txt",
|
||||
"classpath" to jar.get().outputs.files.asPath,
|
||||
"packages" to "com.formdev.flatlaf,com.formdev.flatlaf.util",
|
||||
"version" to version,
|
||||
"release" to "1.8", // Java version
|
||||
"failonerror" to "true" )
|
||||
}
|
||||
}
|
||||
isFailOnError = false
|
||||
}
|
||||
|
||||
named<Jar>("sourcesJar" ) {
|
||||
archiveBaseName.set( "flatlaf" )
|
||||
}
|
||||
register( "sigtestCheck" ) {
|
||||
group = "verification"
|
||||
dependsOn( "jar" )
|
||||
|
||||
named<Jar>("javadocJar" ) {
|
||||
archiveBaseName.set( "flatlaf" )
|
||||
doLast {
|
||||
ant.withGroovyBuilder {
|
||||
"taskdef"(
|
||||
"name" to "sigtest",
|
||||
"classname" to "org.netbeans.apitest.Sigtest",
|
||||
"classpath" to sigtest.asPath )
|
||||
|
||||
"sigtest"(
|
||||
"action" to "check",
|
||||
"fileName" to "${project.name}-sigtest.txt",
|
||||
"classpath" to jar.get().outputs.files.asPath,
|
||||
"packages" to "com.formdev.flatlaf,com.formdev.flatlaf.util",
|
||||
"version" to version,
|
||||
"release" to "1.8", // Java version
|
||||
"failonerror" to "true" )
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1029
flatlaf-core/flatlaf-core-sigtest.txt
Normal file
1029
flatlaf-core/flatlaf-core-sigtest.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -19,19 +19,29 @@ package com.formdev.flatlaf;
|
||||
import java.awt.Color;
|
||||
import java.util.Objects;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.SwingConstants;
|
||||
|
||||
/**
|
||||
* Defines/documents own client properties used in FlatLaf.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
public interface FlatClientProperties
|
||||
{
|
||||
//---- JButton ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Specifies type of a button.
|
||||
* <p>
|
||||
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||
* <strong>Allowed Values</strong> {@link #BUTTON_TYPE_SQUARE}, {@link #BUTTON_TYPE_ROUND_RECT},
|
||||
* {@link #BUTTON_TYPE_TAB}, {@link #BUTTON_TYPE_HELP} and {@link BUTTON_TYPE_TOOLBAR_BUTTON}
|
||||
* <strong>Allowed Values</strong>
|
||||
* {@link #BUTTON_TYPE_SQUARE},
|
||||
* {@link #BUTTON_TYPE_ROUND_RECT},
|
||||
* {@link #BUTTON_TYPE_TAB},
|
||||
* {@link #BUTTON_TYPE_HELP},
|
||||
* {@link #BUTTON_TYPE_TOOLBAR_BUTTON} or
|
||||
* {@link #BUTTON_TYPE_BORDERLESS}
|
||||
*/
|
||||
String BUTTON_TYPE = "JButton.buttonType";
|
||||
|
||||
@@ -80,6 +90,16 @@ public interface FlatClientProperties
|
||||
*/
|
||||
String BUTTON_TYPE_TOOLBAR_BUTTON = "toolBarButton";
|
||||
|
||||
/**
|
||||
* Paint the button without a border in the unfocused state.
|
||||
* <p>
|
||||
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}
|
||||
*
|
||||
* @see #BUTTON_TYPE
|
||||
* @since 1.2
|
||||
*/
|
||||
String BUTTON_TYPE_BORDERLESS = "borderless";
|
||||
|
||||
/**
|
||||
* Specifies selected state of a checkbox.
|
||||
* <p>
|
||||
@@ -99,17 +119,70 @@ public interface FlatClientProperties
|
||||
/**
|
||||
* Specifies whether the button preferred size will be made square (quadratically).
|
||||
* <p>
|
||||
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}
|
||||
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||
*/
|
||||
String SQUARE_SIZE = "JButton.squareSize";
|
||||
|
||||
//---- JComponent ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Specifies the style of a component as String in CSS syntax ("key1: value1; key2: value2; ...")
|
||||
* or as {@link java.util.Map}<String, Object> with binary values.
|
||||
* <p>
|
||||
* The keys are the same as used in UI defaults, but without component type prefix.
|
||||
* E.g. for UI default {@code Slider.thumbSize} use key {@code thumbSize}.
|
||||
* <p>
|
||||
* The syntax of the CSS values is the same as used in FlatLaf properties files
|
||||
* (<a href="https://www.formdev.com/flatlaf/properties-files/">https://www.formdev.com/flatlaf/properties-files/</a>),
|
||||
* but some features are not supported (e.g. variables).
|
||||
* When using a map, the values are not parsed from a string. They must be binary.
|
||||
* <p>
|
||||
* <strong>Components</strong> {@link javax.swing.JComponent}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.String} or {@link java.util.Map}<String, Object><br>
|
||||
*
|
||||
* @since 2
|
||||
*/
|
||||
String STYLE = "FlatLaf.style";
|
||||
|
||||
/**
|
||||
* Specifies the style class(es) of a component as String (single class or multiple classes separated by space characters)
|
||||
* or as {@code String[]} or {@link java.util.List}<String> (multiple classes).
|
||||
* <p>
|
||||
* The style rules must be defined in UI defaults either as strings (in CSS syntax)
|
||||
* or as {@link java.util.Map}<String, Object> (with binary values).
|
||||
* The key must be in syntax: {@code [style]type.styleClass}, where the type is optional.
|
||||
* E.g. in FlatLaf properties file:
|
||||
* <pre>{@code
|
||||
* [style]Button.primary = borderColor: #08f; background: #08f; foreground: #fff
|
||||
* [style].secondary = borderColor: #0f8; background: #0f8
|
||||
* }</pre>
|
||||
* or in Java code:
|
||||
* <pre>{@code
|
||||
* UIManager.put( "[style]Button.primary", "borderColor: #08f; background: #08f; foreground: #fff" );
|
||||
* UIManager.put( "[style].secondary", "borderColor: #0f8; background: #0f8" );
|
||||
* }</pre>
|
||||
* The rule "Button.primary" can be applied to buttons only.
|
||||
* The rule ".secondary" can be applied to any component.
|
||||
* <p>
|
||||
* To have similar behavior as in CSS, first the rule without type is applied,
|
||||
* then the rule with type.
|
||||
* E.g. setting style class to "foo" on a {@code JButton} uses rules
|
||||
* from UI default keys "[style].foo" and "[style]Button.foo".
|
||||
* <p>
|
||||
* <strong>Components</strong> {@link javax.swing.JComponent}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.String}, {@code String[]} or {@link java.util.List}<String><br>
|
||||
*
|
||||
* @since 2
|
||||
*/
|
||||
String STYLE_CLASS = "FlatLaf.styleClass";
|
||||
|
||||
/**
|
||||
* Specifies minimum width of a component.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JButton}, {@link javax.swing.JToggleButton},
|
||||
* {@link javax.swing.JComboBox}, {@link javax.swing.JSpinner} and {@link javax.swing.text.JTextComponent}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Integer}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Integer}
|
||||
*/
|
||||
String MINIMUM_WIDTH = "JComponent.minimumWidth";
|
||||
|
||||
@@ -117,10 +190,19 @@ public interface FlatClientProperties
|
||||
* Specifies minimum height of a component.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Integer}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Integer}
|
||||
*/
|
||||
String MINIMUM_HEIGHT = "JComponent.minimumHeight";
|
||||
|
||||
/**
|
||||
* Paint the component with round edges.
|
||||
* <p>
|
||||
* <strong>Components</strong> {@link javax.swing.JComboBox}, {@link javax.swing.JSpinner},
|
||||
* {@link javax.swing.JTextField}, {@link javax.swing.JFormattedTextField} and {@link javax.swing.JPasswordField}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||
*/
|
||||
String COMPONENT_ROUND_RECT = "JComponent.roundRect";
|
||||
|
||||
/**
|
||||
* Specifies the outline color of the component border.
|
||||
* <p>
|
||||
@@ -129,9 +211,12 @@ public interface FlatClientProperties
|
||||
* {@link javax.swing.JScrollPane}, {@link javax.swing.JSpinner},
|
||||
* {@link javax.swing.JTextField} and {@link javax.swing.JToggleButton}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.String} or {@link java.awt.Color} or {@link java.awt.Color}[2]<br>
|
||||
* <strong>Allowed Values</strong> {@link #OUTLINE_ERROR}, {@link #OUTLINE_WARNING},
|
||||
* any color (type {@link java.awt.Color}) or an array of two colors (type {@link java.awt.Color}[2])
|
||||
* where the first color is for focused state and the second for unfocused state
|
||||
* <strong>Allowed Values</strong>
|
||||
* {@link #OUTLINE_ERROR},
|
||||
* {@link #OUTLINE_WARNING},
|
||||
* any color (type {@link java.awt.Color}) or
|
||||
* an array of two colors (type {@link java.awt.Color}[2]) where the first color
|
||||
* is for focused state and the second for unfocused state
|
||||
*/
|
||||
String OUTLINE = "JComponent.outline";
|
||||
|
||||
@@ -150,13 +235,25 @@ public interface FlatClientProperties
|
||||
String OUTLINE_WARNING = "warning";
|
||||
|
||||
/**
|
||||
* Paint the component with round edges.
|
||||
* Specifies a callback that is invoked to check whether a component is permanent focus owner.
|
||||
* Used to paint focus indicators.
|
||||
* <p>
|
||||
* <strong>Components</strong> {@link javax.swing.JComboBox}, {@link javax.swing.JSpinner},
|
||||
* {@link javax.swing.JTextField}, {@link javax.swing.JFormattedTextField} and {@link javax.swing.JPasswordField}
|
||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||
* May be useful in special cases for custom components.
|
||||
* <p>
|
||||
* Use a {@link java.util.function.Predicate} that receives the component as parameter:
|
||||
* <pre>{@code
|
||||
* myComponent.putClientProperty( "JComponent.focusOwner",
|
||||
* (Predicate<JComponent>) c -> {
|
||||
* return ...; // check here
|
||||
* } );
|
||||
* }</pre>
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
|
||||
* <strong>Value type</strong> {@link java.util.function.Predicate}<javax.swing.JComponent>
|
||||
*/
|
||||
String COMPONENT_ROUND_RECT = "JComponent.roundRect";
|
||||
String COMPONENT_FOCUS_OWNER = "JComponent.focusOwner";
|
||||
|
||||
//---- Popup --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Specifies whether a drop shadow is painted if the component is shown in a popup
|
||||
@@ -167,6 +264,17 @@ public interface FlatClientProperties
|
||||
*/
|
||||
String POPUP_DROP_SHADOW_PAINTED = "Popup.dropShadowPainted";
|
||||
|
||||
/**
|
||||
* Specifies whether a heavy weight window should be used if the component is shown in a popup
|
||||
* or if the component is the owner of another component that is shown in a popup.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||
*/
|
||||
String POPUP_FORCE_HEAVY_WEIGHT = "Popup.forceHeavyWeight";
|
||||
|
||||
//---- JProgressBar -------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Specifies whether the progress bar has always the larger height even if no string is painted.
|
||||
* <p>
|
||||
@@ -183,15 +291,90 @@ public interface FlatClientProperties
|
||||
*/
|
||||
String PROGRESS_BAR_SQUARE = "JProgressBar.square";
|
||||
|
||||
//---- JRootPane ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Specifies whether the menu bar is embedded into the title pane if custom
|
||||
* window decorations are enabled. Default is {@code true}.
|
||||
* Specifies whether FlatLaf native window decorations should be used
|
||||
* for {@code JFrame} or {@code JDialog}.
|
||||
* <p>
|
||||
* Setting this enables/disables using FlatLaf native window decorations
|
||||
* for the window that contains the root pane.
|
||||
* <p>
|
||||
* This client property has lower priority than system property
|
||||
* {@link FlatSystemProperties#USE_WINDOW_DECORATIONS}, but higher priority
|
||||
* than UI default {@code TitlePane.useWindowDecorations}.
|
||||
* <p>
|
||||
* (requires Window 10)
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||
*
|
||||
* @since 1.1.1
|
||||
*/
|
||||
String USE_WINDOW_DECORATIONS = "JRootPane.useWindowDecorations";
|
||||
|
||||
/**
|
||||
* Specifies whether the menu bar is embedded into the window title pane
|
||||
* if window decorations are enabled.
|
||||
* <p>
|
||||
* Setting this enables/disables embedding
|
||||
* for the window that contains the root pane.
|
||||
* <p>
|
||||
* This client property has lower priority than system property
|
||||
* {@link FlatSystemProperties#MENUBAR_EMBEDDED}, but higher priority
|
||||
* than UI default {@code TitlePane.menuBarEmbedded}.
|
||||
* <p>
|
||||
* (requires Window 10)
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||
*/
|
||||
String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded";
|
||||
|
||||
/**
|
||||
* Specifies whether the window icon should be shown in the window title bar
|
||||
* (requires enabled window decorations).
|
||||
* <p>
|
||||
* Setting this shows/hides the windows icon
|
||||
* for the {@code JFrame} or {@code JDialog} that contains the root pane.
|
||||
* <p>
|
||||
* This client property has higher priority than UI default {@code TitlePane.showIcon}.
|
||||
* <p>
|
||||
* (requires Window 10)
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||
*
|
||||
* @since 2
|
||||
*/
|
||||
String TITLE_BAR_SHOW_ICON = "JRootPane.titleBarShowIcon";
|
||||
|
||||
/**
|
||||
* Background color of window title bar (requires enabled window decorations).
|
||||
* <p>
|
||||
* (requires Window 10)
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||
* <strong>Value type</strong> {@link java.awt.Color}
|
||||
*
|
||||
* @since 1.1.2
|
||||
*/
|
||||
String TITLE_BAR_BACKGROUND = "JRootPane.titleBarBackground";
|
||||
|
||||
/**
|
||||
* Foreground color of window title bar (requires enabled window decorations).
|
||||
* <p>
|
||||
* (requires Window 10)
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||
* <strong>Value type</strong> {@link java.awt.Color}
|
||||
*
|
||||
* @since 1.1.2
|
||||
*/
|
||||
String TITLE_BAR_FOREGROUND = "JRootPane.titleBarForeground";
|
||||
|
||||
//---- JScrollBar / JScrollPane -------------------------------------------
|
||||
|
||||
/**
|
||||
* Specifies whether the decrease/increase arrow buttons of a scrollbar are shown.
|
||||
* <p>
|
||||
@@ -203,11 +386,42 @@ public interface FlatClientProperties
|
||||
/**
|
||||
* Specifies whether the scroll pane uses smooth scrolling.
|
||||
* <p>
|
||||
* <strong>Component</strong> {{@link javax.swing.JScrollPane}<br>
|
||||
* <strong>Component</strong> {@link javax.swing.JScrollPane}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||
*/
|
||||
String SCROLL_PANE_SMOOTH_SCROLLING = "JScrollPane.smoothScrolling";
|
||||
|
||||
//---- JTabbedPane --------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Specifies type of the selected tab.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||
* <strong>Allowed Values</strong>
|
||||
* {@link #TABBED_PANE_TAB_TYPE_UNDERLINED} or
|
||||
* {@link #TABBED_PANE_TAB_TYPE_CARD}
|
||||
*
|
||||
* @since 2
|
||||
*/
|
||||
String TABBED_PANE_TAB_TYPE = "JTabbedPane.tabType";
|
||||
|
||||
/**
|
||||
* Paint the selected tab underlined.
|
||||
*
|
||||
* @see #TABBED_PANE_TAB_TYPE
|
||||
* @since 2
|
||||
*/
|
||||
String TABBED_PANE_TAB_TYPE_UNDERLINED = "underlined";
|
||||
|
||||
/**
|
||||
* Paint the selected tab as card.
|
||||
*
|
||||
* @see #TABBED_PANE_TAB_TYPE
|
||||
* @since 2
|
||||
*/
|
||||
String TABBED_PANE_TAB_TYPE_CARD = "card";
|
||||
|
||||
/**
|
||||
* Specifies whether separators are shown between tabs.
|
||||
* <p>
|
||||
@@ -216,6 +430,14 @@ public interface FlatClientProperties
|
||||
*/
|
||||
String TABBED_PANE_SHOW_TAB_SEPARATORS = "JTabbedPane.showTabSeparators";
|
||||
|
||||
/**
|
||||
* Specifies whether the separator between tabs area and content area should be shown.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||
*/
|
||||
String TABBED_PANE_SHOW_CONTENT_SEPARATOR = "JTabbedPane.showContentSeparator";
|
||||
|
||||
/**
|
||||
* Specifies whether a full border is painted around a tabbed pane.
|
||||
* <p>
|
||||
@@ -225,21 +447,356 @@ public interface FlatClientProperties
|
||||
String TABBED_PANE_HAS_FULL_BORDER = "JTabbedPane.hasFullBorder";
|
||||
|
||||
/**
|
||||
* Specifies the height of a tab.
|
||||
* Specifies whether the tab area should be hidden if it contains only one tab.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||
*/
|
||||
String TABBED_PANE_HIDE_TAB_AREA_WITH_ONE_TAB = "JTabbedPane.hideTabAreaWithOneTab";
|
||||
|
||||
/**
|
||||
* Specifies the minimum width of a tab.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
|
||||
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Integer}
|
||||
*/
|
||||
String TABBED_PANE_MINIMUM_TAB_WIDTH = "JTabbedPane.minimumTabWidth";
|
||||
|
||||
/**
|
||||
* Specifies the maximum width of a tab.
|
||||
* <p>
|
||||
* Applied only if tab does not have a custom tab component
|
||||
* (see {@link javax.swing.JTabbedPane#setTabComponentAt(int, java.awt.Component)}).
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
|
||||
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Integer}
|
||||
*/
|
||||
String TABBED_PANE_MAXIMUM_TAB_WIDTH = "JTabbedPane.maximumTabWidth";
|
||||
|
||||
/**
|
||||
* Specifies the minimum height of a tab.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Integer}
|
||||
*
|
||||
* @see #TABBED_PANE_TAB_INSETS
|
||||
*/
|
||||
String TABBED_PANE_TAB_HEIGHT = "JTabbedPane.tabHeight";
|
||||
|
||||
/**
|
||||
* Specifies the insets of a tab.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
|
||||
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
|
||||
* <strong>Value type</strong> {@link java.awt.Insets}
|
||||
*
|
||||
* @see #TABBED_PANE_TAB_HEIGHT
|
||||
*/
|
||||
String TABBED_PANE_TAB_INSETS = "JTabbedPane.tabInsets";
|
||||
|
||||
/**
|
||||
* Specifies the insets of the tab area.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||
* <strong>Value type</strong> {@link java.awt.Insets}
|
||||
*/
|
||||
String TABBED_PANE_TAB_AREA_INSETS = "JTabbedPane.tabAreaInsets";
|
||||
|
||||
/**
|
||||
* Specifies whether tabs are closable.
|
||||
* If set to {@code true} on a tabbed pane component, all tabs in that tabbed pane are closable.
|
||||
* To make individual tabs closable, set it to {@code true} on a tab content component.
|
||||
* <p>
|
||||
* Note that you have to specify a callback (see {@link #TABBED_PANE_TAB_CLOSABLE})
|
||||
* that is invoked when the user clicks a tab close button.
|
||||
* The callback is responsible for closing the tab.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
|
||||
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||
*
|
||||
* @see #TABBED_PANE_TAB_CLOSE_CALLBACK
|
||||
*/
|
||||
String TABBED_PANE_TAB_CLOSABLE = "JTabbedPane.tabClosable";
|
||||
|
||||
/**
|
||||
* Specifies the tooltip text used for tab close buttons.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
|
||||
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
|
||||
* <strong>Value type</strong> {@link java.lang.String}
|
||||
*
|
||||
* @see #TABBED_PANE_TAB_CLOSABLE
|
||||
*/
|
||||
String TABBED_PANE_TAB_CLOSE_TOOLTIPTEXT = "JTabbedPane.tabCloseToolTipText";
|
||||
|
||||
/**
|
||||
* Specifies the callback that is invoked when a tab close button is clicked.
|
||||
* The callback is responsible for closing the tab.
|
||||
* <p>
|
||||
* Either use a {@link java.util.function.IntConsumer} that receives the tab index as parameter:
|
||||
* <pre>{@code
|
||||
* myTabbedPane.putClientProperty( "JTabbedPane.tabCloseCallback",
|
||||
* (IntConsumer) tabIndex -> {
|
||||
* // close tab here
|
||||
* } );
|
||||
* }</pre>
|
||||
* Or use a {@link java.util.function.BiConsumer}<javax.swing.JTabbedPane, Integer>
|
||||
* that receives the tabbed pane and the tab index as parameters:
|
||||
* <pre>{@code
|
||||
* myTabbedPane.putClientProperty( "JTabbedPane.tabCloseCallback",
|
||||
* (BiConsumer<JTabbedPane, Integer>) (tabbedPane, tabIndex) -> {
|
||||
* // close tab here
|
||||
* } );
|
||||
* }</pre>
|
||||
* If you need to check whether a modifier key (e.g. Alt or Shift) was pressed
|
||||
* while the user clicked the tab close button, use {@link java.awt.EventQueue#getCurrentEvent}
|
||||
* to get current event, check whether it is a {@link java.awt.event.MouseEvent}
|
||||
* and invoke its methods. E.g.
|
||||
* <pre>{@code
|
||||
* AWTEvent e = EventQueue.getCurrentEvent();
|
||||
* boolean shift = (e instanceof MouseEvent) ? ((MouseEvent)e).isShiftDown() : false;
|
||||
* }</pre>
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
|
||||
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
|
||||
* <strong>Value type</strong> {@link java.util.function.IntConsumer}
|
||||
* or {@link java.util.function.BiConsumer}<javax.swing.JTabbedPane, Integer>
|
||||
*
|
||||
* @see #TABBED_PANE_TAB_CLOSABLE
|
||||
*/
|
||||
String TABBED_PANE_TAB_CLOSE_CALLBACK = "JTabbedPane.tabCloseCallback";
|
||||
|
||||
/**
|
||||
* Specifies the display policy for the "more tabs" button,
|
||||
* which shows a popup menu with the (partly) hidden tabs.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||
* <strong>Allowed Values</strong>
|
||||
* {@link #TABBED_PANE_POLICY_NEVER} or
|
||||
* {@link #TABBED_PANE_POLICY_AS_NEEDED}
|
||||
*/
|
||||
String TABBED_PANE_TABS_POPUP_POLICY = "JTabbedPane.tabsPopupPolicy";
|
||||
|
||||
/**
|
||||
* Specifies the display policy for the forward/backward scroll arrow buttons.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||
* <strong>Allowed Values</strong>
|
||||
* {@link #TABBED_PANE_POLICY_NEVER},
|
||||
* {@link #TABBED_PANE_POLICY_AS_NEEDED} or
|
||||
* {@link #TABBED_PANE_POLICY_AS_NEEDED_SINGLE}
|
||||
*/
|
||||
String TABBED_PANE_SCROLL_BUTTONS_POLICY = "JTabbedPane.scrollButtonsPolicy";
|
||||
|
||||
/**
|
||||
* Display never.
|
||||
*
|
||||
* @see #TABBED_PANE_TABS_POPUP_POLICY
|
||||
* @see #TABBED_PANE_SCROLL_BUTTONS_POLICY
|
||||
*/
|
||||
String TABBED_PANE_POLICY_NEVER = "never";
|
||||
|
||||
/**
|
||||
* Display only when needed.
|
||||
* <p>
|
||||
* If used for {@link #TABBED_PANE_SCROLL_BUTTONS_POLICY}, both scroll arrow buttons
|
||||
* are either shown or hidden. Buttons are disabled if scrolling in that
|
||||
* direction is not applicable.
|
||||
*
|
||||
* @see #TABBED_PANE_TABS_POPUP_POLICY
|
||||
* @see #TABBED_PANE_SCROLL_BUTTONS_POLICY
|
||||
*/
|
||||
String TABBED_PANE_POLICY_AS_NEEDED = "asNeeded";
|
||||
|
||||
/**
|
||||
* Display single button only when needed.
|
||||
* <p>
|
||||
* If scroll button placement is trailing, then this option is ignored
|
||||
* and both buttons are shown or hidden as needed.
|
||||
*
|
||||
* @see #TABBED_PANE_SCROLL_BUTTONS_POLICY
|
||||
*/
|
||||
String TABBED_PANE_POLICY_AS_NEEDED_SINGLE = "asNeededSingle";
|
||||
|
||||
/**
|
||||
* Specifies the placement of the forward/backward scroll arrow buttons.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||
* <strong>Allowed Values</strong>
|
||||
* {@link #TABBED_PANE_PLACEMENT_BOTH} or
|
||||
* {@link #TABBED_PANE_PLACEMENT_TRAILING}
|
||||
*/
|
||||
String TABBED_PANE_SCROLL_BUTTONS_PLACEMENT = "JTabbedPane.scrollButtonsPlacement";
|
||||
|
||||
/**
|
||||
* The forward/backward scroll arrow buttons are placed on both sides of the tab area.
|
||||
* The backward scroll button at the left/top side.
|
||||
* The forward scroll button at the right/bottom side.
|
||||
*
|
||||
* @see #TABBED_PANE_SCROLL_BUTTONS_PLACEMENT
|
||||
*/
|
||||
String TABBED_PANE_PLACEMENT_BOTH = "both";
|
||||
|
||||
/**
|
||||
* The forward/backward scroll arrow buttons are placed on the trailing side of the tab area.
|
||||
*
|
||||
* @see #TABBED_PANE_SCROLL_BUTTONS_PLACEMENT
|
||||
*/
|
||||
String TABBED_PANE_PLACEMENT_TRAILING = "trailing";
|
||||
|
||||
/**
|
||||
* Specifies the alignment of the tab area.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Integer} or {@link java.lang.String}<br>
|
||||
* <strong>Allowed Values</strong>
|
||||
* {@link SwingConstants#LEADING} (default)
|
||||
* {@link SwingConstants#TRAILING},
|
||||
* {@link SwingConstants#CENTER},
|
||||
* {@link #TABBED_PANE_ALIGN_LEADING} (default),
|
||||
* {@link #TABBED_PANE_ALIGN_TRAILING},
|
||||
* {@link #TABBED_PANE_ALIGN_CENTER} or
|
||||
* {@link #TABBED_PANE_ALIGN_FILL}
|
||||
*/
|
||||
String TABBED_PANE_TAB_AREA_ALIGNMENT = "JTabbedPane.tabAreaAlignment";
|
||||
|
||||
/**
|
||||
* Specifies the horizontal alignment of the tab title and icon.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
|
||||
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Integer} or {@link java.lang.String}<br>
|
||||
* <strong>Allowed Values</strong>
|
||||
* {@link SwingConstants#LEADING},
|
||||
* {@link SwingConstants#TRAILING},
|
||||
* {@link SwingConstants#CENTER} (default),
|
||||
* {@link #TABBED_PANE_ALIGN_LEADING},
|
||||
* {@link #TABBED_PANE_ALIGN_TRAILING} or
|
||||
* {@link #TABBED_PANE_ALIGN_CENTER} (default)
|
||||
*/
|
||||
String TABBED_PANE_TAB_ALIGNMENT = "JTabbedPane.tabAlignment";
|
||||
|
||||
/**
|
||||
* Align to the leading edge.
|
||||
*
|
||||
* @see #TABBED_PANE_TAB_AREA_ALIGNMENT
|
||||
* @see #TABBED_PANE_TAB_ALIGNMENT
|
||||
*/
|
||||
String TABBED_PANE_ALIGN_LEADING = "leading";
|
||||
|
||||
/**
|
||||
* Align to the trailing edge.
|
||||
*
|
||||
* @see #TABBED_PANE_TAB_AREA_ALIGNMENT
|
||||
* @see #TABBED_PANE_TAB_ALIGNMENT
|
||||
*/
|
||||
String TABBED_PANE_ALIGN_TRAILING = "trailing";
|
||||
|
||||
/**
|
||||
* Align to center.
|
||||
*
|
||||
* @see #TABBED_PANE_TAB_AREA_ALIGNMENT
|
||||
* @see #TABBED_PANE_TAB_ALIGNMENT
|
||||
*/
|
||||
String TABBED_PANE_ALIGN_CENTER = "center";
|
||||
|
||||
/**
|
||||
* Stretch to fill all available space.
|
||||
*
|
||||
* @see #TABBED_PANE_TAB_AREA_ALIGNMENT
|
||||
*/
|
||||
String TABBED_PANE_ALIGN_FILL = "fill";
|
||||
|
||||
/**
|
||||
* Specifies how the tabs should be sized.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||
* <strong>Allowed Values</strong>
|
||||
* {@link #TABBED_PANE_TAB_WIDTH_MODE_PREFERRED} (default),
|
||||
* {@link #TABBED_PANE_TAB_WIDTH_MODE_EQUAL} or
|
||||
* {@link #TABBED_PANE_TAB_WIDTH_MODE_COMPACT}
|
||||
*/
|
||||
String TABBED_PANE_TAB_WIDTH_MODE = "JTabbedPane.tabWidthMode";
|
||||
|
||||
/**
|
||||
* Tab width is adjusted to tab icon and title.
|
||||
*
|
||||
* @see #TABBED_PANE_TAB_WIDTH_MODE
|
||||
*/
|
||||
String TABBED_PANE_TAB_WIDTH_MODE_PREFERRED = "preferred";
|
||||
|
||||
/**
|
||||
* All tabs in a tabbed pane has same width.
|
||||
*
|
||||
* @see #TABBED_PANE_TAB_WIDTH_MODE
|
||||
*/
|
||||
String TABBED_PANE_TAB_WIDTH_MODE_EQUAL = "equal";
|
||||
|
||||
/**
|
||||
* Unselected tabs are smaller because they show only the tab icon, but no tab title.
|
||||
* Selected tabs show both.
|
||||
*
|
||||
* @see #TABBED_PANE_TAB_WIDTH_MODE
|
||||
*/
|
||||
String TABBED_PANE_TAB_WIDTH_MODE_COMPACT = "compact";
|
||||
|
||||
/**
|
||||
* Specifies the tab icon placement (relative to tab title).
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Integer}<br>
|
||||
* <strong>Allowed Values</strong>
|
||||
* {@link SwingConstants#LEADING} (default),
|
||||
* {@link SwingConstants#TRAILING},
|
||||
* {@link SwingConstants#TOP} or
|
||||
* {@link SwingConstants#BOTTOM}
|
||||
*/
|
||||
String TABBED_PANE_TAB_ICON_PLACEMENT = "JTabbedPane.tabIconPlacement";
|
||||
|
||||
/**
|
||||
* Specifies a component that will be placed at the leading edge of the tabs area.
|
||||
* <p>
|
||||
* For top and bottom tab placement, the layed out component size will be
|
||||
* the preferred component width and the tab area height.<br>
|
||||
* For left and right tab placement, the layed out component size will be
|
||||
* the tab area width and the preferred component height.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||
* <strong>Value type</strong> {@link java.awt.Component}
|
||||
*/
|
||||
String TABBED_PANE_LEADING_COMPONENT = "JTabbedPane.leadingComponent";
|
||||
|
||||
/**
|
||||
* Specifies a component that will be placed at the trailing edge of the tabs area.
|
||||
* <p>
|
||||
* For top and bottom tab placement, the layed out component size will be
|
||||
* the available horizontal space (minimum is preferred component width) and the tab area height.<br>
|
||||
* For left and right tab placement, the layed out component size will be
|
||||
* the tab area width and the available vertical space (minimum is preferred component height).
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||
* <strong>Value type</strong> {@link java.awt.Component}
|
||||
*/
|
||||
String TABBED_PANE_TRAILING_COMPONENT = "JTabbedPane.trailingComponent";
|
||||
|
||||
//---- JTextField ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Specifies whether all text is selected when the text component gains focus.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
|
||||
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||
* <strong>Allowed Values</strong> {@link #SELECT_ALL_ON_FOCUS_POLICY_NEVER},
|
||||
* {@link #SELECT_ALL_ON_FOCUS_POLICY_ONCE} (default) or
|
||||
* {@link #SELECT_ALL_ON_FOCUS_POLICY_ALWAYS}
|
||||
* <strong>Allowed Values</strong>
|
||||
* {@link #SELECT_ALL_ON_FOCUS_POLICY_NEVER},
|
||||
* {@link #SELECT_ALL_ON_FOCUS_POLICY_ONCE} (default) or
|
||||
* {@link #SELECT_ALL_ON_FOCUS_POLICY_ALWAYS}
|
||||
*/
|
||||
String SELECT_ALL_ON_FOCUS_POLICY = "JTextField.selectAllOnFocusPolicy";
|
||||
|
||||
@@ -274,6 +831,40 @@ public interface FlatClientProperties
|
||||
*/
|
||||
String PLACEHOLDER_TEXT = "JTextField.placeholderText";
|
||||
|
||||
/**
|
||||
* Specifies the padding of the text.
|
||||
* This changes the location and size of the text view within the component bounds,
|
||||
* but does not affect the size of the component.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
|
||||
* <strong>Value type</strong> {@link java.awt.Insets}
|
||||
*
|
||||
* @since 1.4
|
||||
*/
|
||||
String TEXT_FIELD_PADDING = "JTextField.padding";
|
||||
|
||||
/**
|
||||
* Specifies an icon that will be placed at the leading edge of the text field.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
|
||||
* <strong>Value type</strong> {@link javax.swing.Icon}
|
||||
*
|
||||
* @since 2
|
||||
*/
|
||||
String TEXT_FIELD_LEADING_ICON = "JTextField.leadingIcon";
|
||||
|
||||
/**
|
||||
* Specifies an icon that will be placed at the trailing edge of the text field.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
|
||||
* <strong>Value type</strong> {@link javax.swing.Icon}
|
||||
*
|
||||
* @since 2
|
||||
*/
|
||||
String TEXT_FIELD_TRAILING_ICON = "JTextField.trailingIcon";
|
||||
|
||||
//---- JToggleButton ------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Height of underline if toggle button type is {@link #BUTTON_TYPE_TAB}.
|
||||
* <p>
|
||||
@@ -298,6 +889,27 @@ public interface FlatClientProperties
|
||||
*/
|
||||
String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground";
|
||||
|
||||
//---- JTree --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Override if a tree shows a wide selection. Default is {@code true}.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTree}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||
*/
|
||||
String TREE_WIDE_SELECTION = "JTree.wideSelection";
|
||||
|
||||
/**
|
||||
* Specifies whether tree item selection is painted. Default is {@code true}.
|
||||
* If set to {@code false}, then the tree cell renderer is responsible for painting selection.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTree}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||
*/
|
||||
String TREE_PAINT_SELECTION = "JTree.paintSelection";
|
||||
|
||||
//---- helper methods -----------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks whether a client property of a component has the given value.
|
||||
*/
|
||||
@@ -319,8 +931,7 @@ public interface FlatClientProperties
|
||||
* If the client property is not set, or not a {@link Boolean}, defaultValue is returned.
|
||||
*/
|
||||
static Boolean clientPropertyBooleanStrict( JComponent c, String key, Boolean defaultValue ) {
|
||||
Object value = c.getClientProperty( key );
|
||||
return (value instanceof Boolean) ? (Boolean) value : defaultValue;
|
||||
return clientProperty( c, key, defaultValue, Boolean.class );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -337,17 +948,18 @@ public interface FlatClientProperties
|
||||
* If the client property is not set, or not a color, defaultValue is returned.
|
||||
*/
|
||||
static Color clientPropertyColor( JComponent c, String key, Color defaultValue ) {
|
||||
Object value = c.getClientProperty( key );
|
||||
return (value instanceof Color) ? (Color) value : defaultValue;
|
||||
return clientProperty( c, key, defaultValue, Color.class );
|
||||
}
|
||||
|
||||
static int clientPropertyChoice( JComponent c, String key, String... choices ) {
|
||||
/**
|
||||
* Returns the value of the specified client property if it is an instance of
|
||||
* the specified type. Otherwise, defaultValue is returned.
|
||||
*
|
||||
* @since 2
|
||||
*/
|
||||
@SuppressWarnings( "unchecked" )
|
||||
static <T> T clientProperty( JComponent c, String key, T defaultValue, Class<T> type ) {
|
||||
Object value = c.getClientProperty( key );
|
||||
for( int i = 0; i < choices.length; i++ ) {
|
||||
if( choices[i].equals( value ) )
|
||||
return i;
|
||||
|
||||
}
|
||||
return -1;
|
||||
return type.isInstance( value ) ? (T) value : defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,23 +16,50 @@
|
||||
|
||||
package com.formdev.flatlaf;
|
||||
|
||||
import javax.swing.UIManager;
|
||||
|
||||
/**
|
||||
* A Flat LaF that has a dark color scheme and looks like Darcula LaF.
|
||||
*
|
||||
* The UI defaults are loaded from FlatDarculaLaf.properties, FlatDarkLaf.properties and FlatLaf.properties
|
||||
* <p>
|
||||
* The UI defaults are loaded from {@code FlatDarculaLaf.properties},
|
||||
* {@code FlatDarkLaf.properties} and {@code FlatLaf.properties}.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
public class FlatDarculaLaf
|
||||
extends FlatDarkLaf
|
||||
{
|
||||
public static boolean install( ) {
|
||||
return install( new FlatDarculaLaf() );
|
||||
public static final String NAME = "FlatLaf Darcula";
|
||||
|
||||
/**
|
||||
* Sets the application look and feel to this LaF
|
||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||
*/
|
||||
public static boolean setup() {
|
||||
return setup( new FlatDarculaLaf() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install() {
|
||||
return setup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds this look and feel to the set of available look and feels.
|
||||
* <p>
|
||||
* Useful if your application uses {@link UIManager#getInstalledLookAndFeels()}
|
||||
* to query available LaFs and display them to the user in a combobox.
|
||||
*/
|
||||
public static void installLafInfo() {
|
||||
installLafInfo( NAME, FlatDarculaLaf.class );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "FlatLaf Darcula";
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,23 +16,49 @@
|
||||
|
||||
package com.formdev.flatlaf;
|
||||
|
||||
import javax.swing.UIManager;
|
||||
|
||||
/**
|
||||
* A Flat LaF that has a dark color scheme.
|
||||
*
|
||||
* The UI defaults are loaded from FlatDarkLaf.properties and FlatLaf.properties
|
||||
* <p>
|
||||
* The UI defaults are loaded from {@code FlatDarkLaf.properties} and {@code FlatLaf.properties}.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
public class FlatDarkLaf
|
||||
extends FlatLaf
|
||||
{
|
||||
public static boolean install( ) {
|
||||
return install( new FlatDarkLaf() );
|
||||
public static final String NAME = "FlatLaf Dark";
|
||||
|
||||
/**
|
||||
* Sets the application look and feel to this LaF
|
||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||
*/
|
||||
public static boolean setup() {
|
||||
return setup( new FlatDarkLaf() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install() {
|
||||
return setup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds this look and feel to the set of available look and feels.
|
||||
* <p>
|
||||
* Useful if your application uses {@link UIManager#getInstalledLookAndFeels()}
|
||||
* to query available LaFs and display them to the user in a combobox.
|
||||
*/
|
||||
public static void installLafInfo() {
|
||||
installLafInfo( NAME, FlatDarkLaf.class );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "FlatLaf Dark";
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -42,10 +42,12 @@ class FlatInputMaps
|
||||
}
|
||||
|
||||
private static void initBasicInputMaps( UIDefaults defaults ) {
|
||||
defaults.put( "Button.focusInputMap", new UIDefaults.LazyInputMap( new Object[] {
|
||||
"SPACE", "pressed",
|
||||
"released SPACE", "released"
|
||||
} ) );
|
||||
if( SystemInfo.isMacOS ) {
|
||||
defaults.put( "Button.focusInputMap", new UIDefaults.LazyInputMap( new Object[] {
|
||||
"SPACE", "pressed",
|
||||
"released SPACE", "released"
|
||||
} ) );
|
||||
}
|
||||
|
||||
modifyInputMap( defaults, "ComboBox.ancestorInputMap",
|
||||
"SPACE", "spacePopup",
|
||||
|
||||
@@ -16,23 +16,50 @@
|
||||
|
||||
package com.formdev.flatlaf;
|
||||
|
||||
import javax.swing.UIManager;
|
||||
|
||||
/**
|
||||
* A Flat LaF that has a light color scheme and looks like IntelliJ LaF.
|
||||
*
|
||||
* The UI defaults are loaded from FlatIntelliJLaf.properties, FlatLightLaf.properties and FlatLaf.properties
|
||||
* <p>
|
||||
* The UI defaults are loaded from {@code FlatIntelliJLaf.properties},
|
||||
* {@code FlatLightLaf.properties} and {@code FlatLaf.properties}.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
public class FlatIntelliJLaf
|
||||
extends FlatLightLaf
|
||||
{
|
||||
public static boolean install( ) {
|
||||
return install( new FlatIntelliJLaf() );
|
||||
public static final String NAME = "FlatLaf IntelliJ";
|
||||
|
||||
/**
|
||||
* Sets the application look and feel to this LaF
|
||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||
*/
|
||||
public static boolean setup() {
|
||||
return setup( new FlatIntelliJLaf() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install() {
|
||||
return setup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds this look and feel to the set of available look and feels.
|
||||
* <p>
|
||||
* Useful if your application uses {@link UIManager#getInstalledLookAndFeels()}
|
||||
* to query available LaFs and display them to the user in a combobox.
|
||||
*/
|
||||
public static void installLafInfo() {
|
||||
installLafInfo( NAME, FlatIntelliJLaf.class );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "FlatLaf IntelliJ";
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,23 +16,49 @@
|
||||
|
||||
package com.formdev.flatlaf;
|
||||
|
||||
import javax.swing.UIManager;
|
||||
|
||||
/**
|
||||
* A Flat LaF that has a light color scheme.
|
||||
*
|
||||
* The UI defaults are loaded from FlatLightLaf.properties and FlatLaf.properties
|
||||
* <p>
|
||||
* The UI defaults are loaded from {@code FlatLightLaf.properties} and {@code FlatLaf.properties}.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
public class FlatLightLaf
|
||||
extends FlatLaf
|
||||
{
|
||||
public static boolean install( ) {
|
||||
return install( new FlatLightLaf() );
|
||||
public static final String NAME = "FlatLaf Light";
|
||||
|
||||
/**
|
||||
* Sets the application look and feel to this LaF
|
||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||
*/
|
||||
public static boolean setup() {
|
||||
return setup( new FlatLightLaf() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install() {
|
||||
return setup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds this look and feel to the set of available look and feels.
|
||||
* <p>
|
||||
* Useful if your application uses {@link UIManager#getInstalledLookAndFeels()}
|
||||
* to query available LaFs and display them to the user in a combobox.
|
||||
*/
|
||||
public static void installLafInfo() {
|
||||
installLafInfo( NAME, FlatLightLaf.class );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "FlatLaf Light";
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -88,6 +88,10 @@ public class FlatPropertiesLaf
|
||||
return dark;
|
||||
}
|
||||
|
||||
public Properties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ArrayList<Class<?>> getLafClassesForDefaultsLoading() {
|
||||
ArrayList<Class<?>> lafClasses = new ArrayList<>();
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
|
||||
package com.formdev.flatlaf;
|
||||
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFrame;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
/**
|
||||
* Defines/documents own system properties used in FlatLaf.
|
||||
@@ -35,6 +34,8 @@ public interface FlatSystemProperties
|
||||
* To replace the Java 9+ system scale factor, use system property "sun.java2d.uiScale",
|
||||
* which has the same syntax as this one.
|
||||
* <p>
|
||||
* Since FlatLaf 1.1.2: Scale factors less then 100% are allowed.
|
||||
* <p>
|
||||
* <strong>Allowed Values</strong> e.g. {@code 1.5}, {@code 1.5x}, {@code 150%} or {@code 144dpi} (96dpi is 100%)<br>
|
||||
*/
|
||||
String UI_SCALE = "flatlaf.uiScale";
|
||||
@@ -47,6 +48,17 @@ public interface FlatSystemProperties
|
||||
*/
|
||||
String UI_SCALE_ENABLED = "flatlaf.uiScale.enabled";
|
||||
|
||||
/**
|
||||
* Specifies whether values smaller than 100% are allowed for the user scale factor
|
||||
* (see {@link UIScale#getUserScaleFactor()}).
|
||||
* <p>
|
||||
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||
* <strong>Default</strong> {@code false}
|
||||
*
|
||||
* @since 1.1.2
|
||||
*/
|
||||
String UI_SCALE_ALLOW_SCALE_DOWN = "flatlaf.uiScale.allowScaleDown";
|
||||
|
||||
/**
|
||||
* Specifies whether Ubuntu font should be used on Ubuntu Linux.
|
||||
* By default, if not running in a JetBrains Runtime, the Liberation Sans font
|
||||
@@ -58,11 +70,18 @@ public interface FlatSystemProperties
|
||||
String USE_UBUNTU_FONT = "flatlaf.useUbuntuFont";
|
||||
|
||||
/**
|
||||
* Specifies whether custom look and feel window decorations should be used
|
||||
* Specifies whether native window decorations should be used
|
||||
* when creating {@code JFrame} or {@code JDialog}.
|
||||
* <p>
|
||||
* If this system property is set, FlatLaf invokes {@link JFrame#setDefaultLookAndFeelDecorated(boolean)}
|
||||
* and {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} on LaF initialization.
|
||||
* Setting this to {@code true} forces using native window decorations
|
||||
* even if they are not enabled by the application.<br>
|
||||
* Setting this to {@code false} disables using native window decorations.
|
||||
* <p>
|
||||
* This system property has higher priority than client property
|
||||
* {@link FlatClientProperties#USE_WINDOW_DECORATIONS} and
|
||||
* UI default {@code TitlePane.useWindowDecorations}.
|
||||
* <p>
|
||||
* (requires Window 10)
|
||||
* <p>
|
||||
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||
* <strong>Default</strong> none
|
||||
@@ -76,8 +95,10 @@ public interface FlatSystemProperties
|
||||
* <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime</a>
|
||||
* (based on OpenJDK).
|
||||
* <p>
|
||||
* Setting this to {@code true} forces using JetBrains Runtime custom window decorations
|
||||
* even if they are not enabled by the application.
|
||||
* Setting this to {@code false} disables using JetBrains Runtime custom window decorations.
|
||||
* Then FlatLaf native window decorations are used.
|
||||
* <p>
|
||||
* (requires Window 10)
|
||||
* <p>
|
||||
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||
* <strong>Default</strong> {@code true}
|
||||
@@ -85,12 +106,30 @@ public interface FlatSystemProperties
|
||||
String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations";
|
||||
|
||||
/**
|
||||
* Specifies whether menubar is embedded into custom window decorations.
|
||||
* Specifies whether the menu bar is embedded into the window title pane
|
||||
* if window decorations are enabled.
|
||||
* <p>
|
||||
* Setting this to {@code true} forces embedding.<br>
|
||||
* Setting this to {@code false} disables embedding.
|
||||
* <p>
|
||||
* This system property has higher priority than client property
|
||||
* {@link FlatClientProperties#MENU_BAR_EMBEDDED} and
|
||||
* UI default {@code TitlePane.menuBarEmbedded}.
|
||||
* <p>
|
||||
* (requires Window 10)
|
||||
* <p>
|
||||
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||
* <strong>Default</strong> none
|
||||
*/
|
||||
String MENUBAR_EMBEDDED = "flatlaf.menuBarEmbedded";
|
||||
|
||||
/**
|
||||
* Specifies whether animations are enabled.
|
||||
* <p>
|
||||
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||
* <strong>Default</strong> {@code true}
|
||||
*/
|
||||
String MENUBAR_EMBEDDED = "flatlaf.menuBarEmbedded";
|
||||
String ANIMATION = "flatlaf.animation";
|
||||
|
||||
/**
|
||||
* Specifies whether vertical text position is corrected when UI is scaled on HiDPI screens.
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.formdev.flatlaf;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
@@ -29,11 +30,12 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.UIDefaults;
|
||||
import javax.swing.plaf.ColorUIResource;
|
||||
import com.formdev.flatlaf.json.Json;
|
||||
import com.formdev.flatlaf.json.ParseException;
|
||||
import com.formdev.flatlaf.util.ColorFunctions;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
import com.formdev.flatlaf.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -56,6 +58,8 @@ public class IntelliJTheme
|
||||
public final boolean dark;
|
||||
public final String author;
|
||||
|
||||
private final boolean isMaterialUILite;
|
||||
|
||||
private final Map<String, String> colors;
|
||||
private final Map<String, Object> ui;
|
||||
private final Map<String, Object> icons;
|
||||
@@ -64,20 +68,28 @@ public class IntelliJTheme
|
||||
|
||||
/**
|
||||
* Loads a IntelliJ .theme.json file from the given input stream,
|
||||
* creates a Laf instance for it and installs it.
|
||||
* creates a Laf instance for it and sets it up.
|
||||
*
|
||||
* The input stream is automatically closed.
|
||||
* Using a buffered input stream is not necessary.
|
||||
*/
|
||||
public static boolean install( InputStream in ) {
|
||||
public static boolean setup( InputStream in ) {
|
||||
try {
|
||||
return FlatLaf.install( createLaf( in ) );
|
||||
return FlatLaf.setup( createLaf( in ) );
|
||||
} catch( Exception ex ) {
|
||||
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: Failed to load IntelliJ theme", ex );
|
||||
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to load IntelliJ theme", ex );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup(InputStream)} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install( InputStream in ) {
|
||||
return setup( in );
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a IntelliJ .theme.json file from the given input stream and
|
||||
* creates a Laf instance for it.
|
||||
@@ -119,6 +131,8 @@ public class IntelliJTheme
|
||||
dark = Boolean.parseBoolean( (String) json.get( "dark" ) );
|
||||
author = (String) json.get( "author" );
|
||||
|
||||
isMaterialUILite = author.equals( "Mallowigi" );
|
||||
|
||||
colors = (Map<String, String>) json.get( "colors" );
|
||||
ui = (Map<String, Object>) json.get( "ui" );
|
||||
icons = (Map<String, Object>) json.get( "icons" );
|
||||
@@ -156,6 +170,11 @@ public class IntelliJTheme
|
||||
defaults.put( "Button.disabledBackground", panelBackground );
|
||||
defaults.put( "ToggleButton.disabledBackground", panelBackground );
|
||||
|
||||
// fix Button borders
|
||||
copyIfNotSet( defaults, "Button.focusedBorderColor", "Component.focusedBorderColor", uiKeys );
|
||||
defaults.put( "Button.hoverBorderColor", defaults.get( "Button.focusedBorderColor" ) );
|
||||
defaults.put( "HelpButton.hoverBorderColor", defaults.get( "Button.focusedBorderColor" ) );
|
||||
|
||||
// IDEA uses a SVG icon for the help button, but paints the background with Button.startBackground and Button.endBackground
|
||||
Object helpButtonBackground = defaults.get( "Button.startBackground" );
|
||||
Object helpButtonBorderColor = defaults.get( "Button.startBorderColor" );
|
||||
@@ -205,6 +224,18 @@ public class IntelliJTheme
|
||||
if( !uiKeys.contains( "ToggleButton.foreground" ) && uiKeys.contains( "Button.foreground" ) )
|
||||
defaults.put( "ToggleButton.foreground", defaults.get( "Button.foreground" ) );
|
||||
|
||||
// fix DesktopPane background (use Panel.background and make it 5% darker/lighter)
|
||||
Color desktopBackgroundBase = defaults.getColor( "Panel.background" );
|
||||
Color desktopBackground = ColorFunctions.applyFunctions( desktopBackgroundBase,
|
||||
new ColorFunctions.HSLIncreaseDecrease( 2, dark, 5, false, true ) );
|
||||
defaults.put( "Desktop.background", new ColorUIResource( desktopBackground ) );
|
||||
|
||||
// fix List and Table background colors in Material UI Lite themes
|
||||
if( isMaterialUILite ) {
|
||||
defaults.put( "List.background", defaults.get( "Tree.background" ) );
|
||||
defaults.put( "Table.background", defaults.get( "Tree.background" ) );
|
||||
}
|
||||
|
||||
// limit tree row height
|
||||
int rowHeight = defaults.getInt( "Tree.rowHeight" );
|
||||
if( rowHeight > 22 )
|
||||
@@ -218,17 +249,25 @@ public class IntelliJTheme
|
||||
// search for theme specific UI defaults keys
|
||||
ArrayList<String> themeSpecificKeys = new ArrayList<>();
|
||||
for( Object key : defaults.keySet() ) {
|
||||
if( key instanceof String && ((String)key).startsWith( "[" ) )
|
||||
if( key instanceof String && ((String)key).startsWith( "[" ) && !((String)key).startsWith( "[style]" ) )
|
||||
themeSpecificKeys.add( (String) key );
|
||||
}
|
||||
|
||||
// remove theme specific UI defaults and remember only those for current theme
|
||||
Map<Object, Object> themeSpecificDefaults = new HashMap<>();
|
||||
String currentThemePrefix = '[' + name.replace( ' ', '_' ) + ']';
|
||||
String currentThemeAndAuthorPrefix = '[' + name.replace( ' ', '_' ) + "---" + author.replace( ' ', '_' ) + ']';
|
||||
String currentAuthorPrefix = "[author-" + author.replace( ' ', '_' ) + ']';
|
||||
String allThemesPrefix = "[*]";
|
||||
String[] prefixes = { currentThemePrefix, currentThemeAndAuthorPrefix, currentAuthorPrefix, allThemesPrefix };
|
||||
for( String key : themeSpecificKeys ) {
|
||||
Object value = defaults.remove( key );
|
||||
if( key.startsWith( currentThemePrefix ) )
|
||||
themeSpecificDefaults.put( key.substring( currentThemePrefix.length() ), value );
|
||||
for( String prefix : prefixes ) {
|
||||
if( key.startsWith( prefix ) ) {
|
||||
themeSpecificDefaults.put( key.substring( prefix.length() ), value );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return themeSpecificDefaults;
|
||||
@@ -269,7 +308,6 @@ public class IntelliJTheme
|
||||
uiKeys.add( key );
|
||||
|
||||
// fix ComboBox size and Spinner border in all Material UI Lite themes
|
||||
boolean isMaterialUILite = author.equals( "Mallowigi" );
|
||||
if( isMaterialUILite && (key.equals( "ComboBox.padding" ) || key.equals( "Spinner.border" )) )
|
||||
return; // ignore
|
||||
|
||||
@@ -300,9 +338,9 @@ public class IntelliJTheme
|
||||
|
||||
// parse value
|
||||
try {
|
||||
uiValue = UIDefaultsLoader.parseValue( key, valueStr );
|
||||
uiValue = UIDefaultsLoader.parseValue( key, valueStr, null );
|
||||
} catch( RuntimeException ex ) {
|
||||
UIDefaultsLoader.logParseError( Level.CONFIG, key, valueStr, ex );
|
||||
UIDefaultsLoader.logParseError( key, valueStr, ex, false );
|
||||
return; // ignore invalid value
|
||||
}
|
||||
}
|
||||
@@ -322,6 +360,10 @@ public class IntelliJTheme
|
||||
|
||||
// replace all values in UI defaults that match the wildcard key
|
||||
for( Object k : defaultsKeysCache ) {
|
||||
if( k.equals( "Desktop.background" ) ||
|
||||
k.equals( "DesktopIcon.background" ) )
|
||||
continue;
|
||||
|
||||
if( k instanceof String ) {
|
||||
// support replacing of mapped keys
|
||||
// (e.g. set ComboBox.buttonEditableBackground to *.background
|
||||
@@ -381,7 +423,7 @@ public class IntelliJTheme
|
||||
}
|
||||
|
||||
/**
|
||||
* Because IDEA uses SVGs for check boxes and radio buttons the colors for
|
||||
* Because IDEA uses SVGs for check boxes and radio buttons, the colors for
|
||||
* this two components are specified in "icons > ColorPalette".
|
||||
* FlatLaf uses vector icons and expects colors for the two components in UI defaults.
|
||||
*/
|
||||
@@ -453,29 +495,47 @@ public class IntelliJTheme
|
||||
}
|
||||
}
|
||||
|
||||
// remove hover and pressed colors
|
||||
// update hover, pressed and focused colors
|
||||
if( checkboxModified ) {
|
||||
// for non-filled checkbox/radiobutton used in dark themes
|
||||
defaults.remove( "CheckBox.icon.focusWidth" );
|
||||
defaults.remove( "CheckBox.icon.hoverBorderColor" );
|
||||
defaults.remove( "CheckBox.icon.focusedBackground" );
|
||||
defaults.remove( "CheckBox.icon.hoverBackground" );
|
||||
defaults.remove( "CheckBox.icon.pressedBackground" );
|
||||
defaults.remove( "CheckBox.icon.selectedFocusedBackground" );
|
||||
defaults.remove( "CheckBox.icon.selectedHoverBackground" );
|
||||
defaults.remove( "CheckBox.icon.selectedPressedBackground" );
|
||||
defaults.put( "CheckBox.icon.hoverBorderColor", defaults.get( "CheckBox.icon.focusedBorderColor" ) );
|
||||
|
||||
// for filled checkbox/radiobutton used in light themes
|
||||
defaults.remove( "CheckBox.icon[filled].focusWidth" );
|
||||
defaults.remove( "CheckBox.icon[filled].hoverBorderColor" );
|
||||
defaults.remove( "CheckBox.icon[filled].focusedBackground" );
|
||||
defaults.remove( "CheckBox.icon[filled].hoverBackground" );
|
||||
defaults.remove( "CheckBox.icon[filled].pressedBackground" );
|
||||
defaults.remove( "CheckBox.icon[filled].selectedFocusedBackground" );
|
||||
defaults.remove( "CheckBox.icon[filled].selectedHoverBackground" );
|
||||
defaults.remove( "CheckBox.icon[filled].selectedPressedBackground" );
|
||||
defaults.put( "CheckBox.icon[filled].hoverBorderColor", defaults.get( "CheckBox.icon[filled].focusedBorderColor" ) );
|
||||
defaults.put( "CheckBox.icon[filled].focusedSelectedBackground", defaults.get( "CheckBox.icon[filled].selectedBackground" ) );
|
||||
|
||||
if( dark ) {
|
||||
// IDEA Darcula checkBoxFocused.svg, checkBoxSelectedFocused.svg,
|
||||
// radioFocused.svg and radioSelectedFocused.svg
|
||||
// use opacity=".65" for the border
|
||||
// --> add alpha to focused border colors
|
||||
String[] focusedBorderColorKeys = new String[] {
|
||||
"CheckBox.icon.focusedBorderColor",
|
||||
"CheckBox.icon.focusedSelectedBorderColor",
|
||||
"CheckBox.icon[filled].focusedBorderColor",
|
||||
"CheckBox.icon[filled].focusedSelectedBorderColor",
|
||||
};
|
||||
for( String key : focusedBorderColorKeys ) {
|
||||
Color color = defaults.getColor( key );
|
||||
if( color != null ) {
|
||||
defaults.put( key, new ColorUIResource( new Color(
|
||||
(color.getRGB() & 0xffffff) | 0xa6000000, true ) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void copyIfNotSet( UIDefaults defaults, String destKey, String srcKey, Set<String> uiKeys ) {
|
||||
if( !uiKeys.contains( destKey ) )
|
||||
defaults.put( destKey, defaults.get( srcKey ) );
|
||||
}
|
||||
|
||||
/** Rename UI default keys (key --> value). */
|
||||
private static Map<String, String> uiKeyMapping = new HashMap<>();
|
||||
/** Copy UI default keys (value --> key). */
|
||||
private static Map<String, String> uiKeyCopying = new HashMap<>();
|
||||
private static Map<String, String> uiKeyInverseMapping = new HashMap<>();
|
||||
private static Map<String, String> checkboxKeyMapping = new HashMap<>();
|
||||
@@ -489,6 +549,8 @@ public class IntelliJTheme
|
||||
uiKeyMapping.put( "ComboBox.ArrowButton.disabledIconColor", "ComboBox.buttonDisabledArrowColor" );
|
||||
uiKeyMapping.put( "ComboBox.ArrowButton.iconColor", "ComboBox.buttonArrowColor" );
|
||||
uiKeyMapping.put( "ComboBox.ArrowButton.nonEditableBackground", "ComboBox.buttonBackground" );
|
||||
uiKeyCopying.put( "ComboBox.buttonSeparatorColor", "Component.borderColor" );
|
||||
uiKeyCopying.put( "ComboBox.buttonDisabledSeparatorColor", "Component.disabledBorderColor" );
|
||||
|
||||
// Component
|
||||
uiKeyMapping.put( "Component.inactiveErrorFocusColor", "Component.error.borderColor" );
|
||||
@@ -505,6 +567,7 @@ public class IntelliJTheme
|
||||
uiKeyCopying.put( "CheckBoxMenuItem.margin", "MenuItem.margin" );
|
||||
uiKeyCopying.put( "RadioButtonMenuItem.margin", "MenuItem.margin" );
|
||||
uiKeyMapping.put( "PopupMenu.border", "PopupMenu.borderInsets" );
|
||||
uiKeyCopying.put( "MenuItem.underlineSelectionColor", "TabbedPane.underlineColor" );
|
||||
|
||||
// IDEA uses List.selectionBackground also for menu selection
|
||||
uiKeyCopying.put( "Menu.selectionBackground", "List.selectionBackground" );
|
||||
@@ -529,6 +592,13 @@ public class IntelliJTheme
|
||||
|
||||
// Slider
|
||||
uiKeyMapping.put( "Slider.trackWidth", "" ); // ignore (used in Material Theme UI Lite)
|
||||
uiKeyCopying.put( "Slider.trackValueColor", "ProgressBar.foreground" );
|
||||
uiKeyCopying.put( "Slider.thumbColor", "ProgressBar.foreground" );
|
||||
uiKeyCopying.put( "Slider.trackColor", "ProgressBar.background" );
|
||||
|
||||
// Spinner
|
||||
uiKeyCopying.put( "Spinner.buttonSeparatorColor", "Component.borderColor" );
|
||||
uiKeyCopying.put( "Spinner.buttonDisabledSeparatorColor", "Component.disabledBorderColor" );
|
||||
|
||||
// TitlePane
|
||||
uiKeyCopying.put( "TitlePane.inactiveBackground", "TitlePane.background" );
|
||||
@@ -554,7 +624,7 @@ public class IntelliJTheme
|
||||
checkboxKeyMapping.put( "Checkbox.Background.Selected", "CheckBox.icon.selectedBackground" );
|
||||
checkboxKeyMapping.put( "Checkbox.Border.Selected", "CheckBox.icon.selectedBorderColor" );
|
||||
checkboxKeyMapping.put( "Checkbox.Foreground.Selected", "CheckBox.icon.checkmarkColor" );
|
||||
checkboxKeyMapping.put( "Checkbox.Focus.Thin.Selected", "CheckBox.icon.selectedFocusedBorderColor" );
|
||||
checkboxKeyMapping.put( "Checkbox.Focus.Thin.Selected", "CheckBox.icon.focusedSelectedBorderColor" );
|
||||
|
||||
checkboxDuplicateColors.put( "Checkbox.Background.Default.Dark", "Checkbox.Background.Selected.Dark" );
|
||||
checkboxDuplicateColors.put( "Checkbox.Border.Default.Dark", "Checkbox.Border.Selected.Dark" );
|
||||
@@ -583,7 +653,7 @@ public class IntelliJTheme
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return theme.name;
|
||||
return getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -28,7 +28,8 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
import com.formdev.flatlaf.util.StringUtils;
|
||||
import com.formdev.flatlaf.util.SystemInfo;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
@@ -54,24 +55,38 @@ class LinuxFontPolicy
|
||||
|
||||
String family = "";
|
||||
int style = Font.PLAIN;
|
||||
int size = 10;
|
||||
double dsize = 10;
|
||||
|
||||
// parse pango font description
|
||||
// see https://developer.gnome.org/pango/1.46/pango-Fonts.html#pango-font-description-from-string
|
||||
StringTokenizer st = new StringTokenizer( (String) fontName );
|
||||
while( st.hasMoreTokens() ) {
|
||||
String word = st.nextToken();
|
||||
|
||||
if( word.equalsIgnoreCase( "italic" ) )
|
||||
// remove trailing ',' (e.g. in "Ubuntu Condensed, 11" or "Ubuntu Condensed, Bold 11")
|
||||
if( word.endsWith( "," ) )
|
||||
word = word.substring( 0, word.length() - 1 ).trim();
|
||||
|
||||
String lword = word.toLowerCase();
|
||||
if( lword.equals( "italic" ) || lword.equals( "oblique" ) )
|
||||
style |= Font.ITALIC;
|
||||
else if( word.equalsIgnoreCase( "bold" ) )
|
||||
else if( lword.equals( "bold" ) )
|
||||
style |= Font.BOLD;
|
||||
else if( Character.isDigit( word.charAt( 0 ) ) ) {
|
||||
try {
|
||||
size = Integer.parseInt( word );
|
||||
dsize = Double.parseDouble( word );
|
||||
} catch( NumberFormatException ex ) {
|
||||
// ignore
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
// remove '-' from "Semi-Bold", "Extra-Light", etc
|
||||
if( lword.startsWith( "semi-" ) || lword.startsWith( "demi-" ) )
|
||||
word = word.substring( 0, 4 ) + word.substring( 5 );
|
||||
else if( lword.startsWith( "extra-" ) || lword.startsWith( "ultra-" ) )
|
||||
word = word.substring( 0, 5 ) + word.substring( 6 );
|
||||
|
||||
family = family.isEmpty() ? word : (family + ' ' + word);
|
||||
}
|
||||
}
|
||||
|
||||
// Ubuntu font is rendered poorly (except if running in JetBrains VM)
|
||||
@@ -82,8 +97,8 @@ class LinuxFontPolicy
|
||||
family = "Liberation Sans";
|
||||
|
||||
// scale font size
|
||||
double dsize = size * getGnomeFontScale();
|
||||
size = (int) (dsize + 0.5);
|
||||
dsize *= getGnomeFontScale();
|
||||
int size = (int) (dsize + 0.5);
|
||||
if( size < 1 )
|
||||
size = 1;
|
||||
|
||||
@@ -92,7 +107,37 @@ class LinuxFontPolicy
|
||||
if( logicalFamily != null )
|
||||
family = logicalFamily;
|
||||
|
||||
return createFont( family, style, size, dsize );
|
||||
return createFontEx( family, style, size, dsize );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a font for the given family, style and size.
|
||||
* If the font family does not match any font on the system,
|
||||
* then the last word (usually a font weight) from the family name is removed and tried again.
|
||||
* E.g. family 'URW Bookman Light' is not found, but 'URW Bookman' is found.
|
||||
* If still not found, then font of family 'Dialog' is returned.
|
||||
*/
|
||||
private static Font createFontEx( String family, int style, int size, double dsize ) {
|
||||
for(;;) {
|
||||
Font font = createFont( family, style, size, dsize );
|
||||
|
||||
// if the font family does not match any font on the system, "Dialog" family is returned
|
||||
if( !"Dialog".equals( font.getFamily() ) || "Dialog".equals( family ) )
|
||||
return font;
|
||||
|
||||
// find last word in family
|
||||
int index = family.lastIndexOf( ' ' );
|
||||
if( index < 0 )
|
||||
return createFont( "Dialog", style, size, dsize );
|
||||
|
||||
// check whether last work contains some font weight (e.g. Ultra-Bold or Heavy)
|
||||
String lastWord = family.substring( index + 1 ).toLowerCase();
|
||||
if( lastWord.contains( "bold" ) || lastWord.contains( "heavy" ) || lastWord.contains( "black" ) )
|
||||
style |= Font.BOLD;
|
||||
|
||||
// remove last word from family and try again
|
||||
family = family.substring( 0, index );
|
||||
}
|
||||
}
|
||||
|
||||
private static Font createFont( String family, int style, int size, double dsize ) {
|
||||
@@ -172,7 +217,7 @@ class LinuxFontPolicy
|
||||
if( "1".equals( strs.get( 5 ) ) )
|
||||
style |= Font.ITALIC;
|
||||
} catch( RuntimeException ex ) {
|
||||
FlatLaf.LOG.log( Level.CONFIG, "FlatLaf: Failed to parse 'font=" + generalFont + "'.", ex );
|
||||
LoggingFacade.INSTANCE.logConfig( "FlatLaf: Failed to parse 'font=" + generalFont + "'.", ex );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +231,7 @@ class LinuxFontPolicy
|
||||
if( dpi < 50 )
|
||||
dpi = 50;
|
||||
} catch( NumberFormatException ex ) {
|
||||
FlatLaf.LOG.log( Level.CONFIG, "FlatLaf: Failed to parse 'forceFontDPI=" + forceFontDPI + "'.", ex );
|
||||
LoggingFacade.INSTANCE.logConfig( "FlatLaf: Failed to parse 'forceFontDPI=" + forceFontDPI + "'.", ex );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +270,7 @@ class LinuxFontPolicy
|
||||
while( (line = reader.readLine()) != null )
|
||||
lines.add( line );
|
||||
} catch( IOException ex ) {
|
||||
FlatLaf.LOG.log( Level.CONFIG, "FlatLaf: Failed to read '" + filename + "'.", ex );
|
||||
LoggingFacade.INSTANCE.logConfig( "FlatLaf: Failed to read '" + filename + "'.", ex );
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
@@ -264,6 +309,9 @@ class LinuxFontPolicy
|
||||
* - running on JetBrains Runtime 11 or later and scaling is enabled in system Settings
|
||||
*/
|
||||
private static boolean isSystemScaling() {
|
||||
if( GraphicsEnvironment.isHeadless() )
|
||||
return true;
|
||||
|
||||
GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment()
|
||||
.getDefaultScreenDevice().getDefaultConfiguration();
|
||||
return UIScale.getSystemScaleFactor( gc ) > 1;
|
||||
|
||||
@@ -28,6 +28,7 @@ import java.awt.event.WindowEvent;
|
||||
import java.awt.event.WindowListener;
|
||||
import java.lang.ref.WeakReference;
|
||||
import javax.swing.AbstractButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JMenu;
|
||||
@@ -137,10 +138,17 @@ class MnemonicHandler
|
||||
// get menu bar and first menu
|
||||
Component c = e.getComponent();
|
||||
JRootPane rootPane = SwingUtilities.getRootPane( c );
|
||||
Window window = (rootPane != null) ? SwingUtilities.getWindowAncestor( rootPane ) : null;
|
||||
JMenuBar menuBar = (rootPane != null) ? rootPane.getJMenuBar() : null;
|
||||
if( menuBar == null && window instanceof JFrame )
|
||||
menuBar = ((JFrame)window).getJMenuBar();
|
||||
if( menuBar == null ) {
|
||||
// get menu bar from frame/dialog because there
|
||||
// may be multiple nested root panes in a frame/dialog
|
||||
// (e.g. each internal frame has its own root pane)
|
||||
Window window = SwingUtilities.getWindowAncestor( c );
|
||||
if( window instanceof JFrame )
|
||||
menuBar = ((JFrame)window).getJMenuBar();
|
||||
else if( window instanceof JDialog )
|
||||
menuBar = ((JDialog)window).getJMenuBar();
|
||||
}
|
||||
JMenu firstMenu = (menuBar != null) ? menuBar.getMenu( 0 ) : null;
|
||||
|
||||
// select first menu and show mnemonics
|
||||
|
||||
@@ -18,11 +18,16 @@ package com.formdev.flatlaf;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.Insets;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StreamTokenizer;
|
||||
import java.io.StringReader;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -33,9 +38,10 @@ import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Properties;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.UIDefaults;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.UIDefaults.ActiveValue;
|
||||
import javax.swing.UIDefaults.LazyValue;
|
||||
import javax.swing.plaf.ColorUIResource;
|
||||
@@ -48,6 +54,8 @@ import com.formdev.flatlaf.util.ColorFunctions.ColorFunction;
|
||||
import com.formdev.flatlaf.util.DerivedColor;
|
||||
import com.formdev.flatlaf.util.GrayFilter;
|
||||
import com.formdev.flatlaf.util.HSLColor;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
import com.formdev.flatlaf.util.SoftCache;
|
||||
import com.formdev.flatlaf.util.StringUtils;
|
||||
import com.formdev.flatlaf.util.SystemInfo;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
@@ -70,7 +78,13 @@ class UIDefaultsLoader
|
||||
private static final String VARIABLE_PREFIX = "@";
|
||||
private static final String PROPERTY_PREFIX = "$";
|
||||
private static final String OPTIONAL_PREFIX = "?";
|
||||
private static final String GLOBAL_PREFIX = "*.";
|
||||
private static final String WILDCARD_PREFIX = "*.";
|
||||
|
||||
private static final String KEY_VARIABLES = "FlatLaf.internal.variables";
|
||||
|
||||
private static int parseColorDepth;
|
||||
|
||||
private static final SoftCache<String, Object> fontCache = new SoftCache<>();
|
||||
|
||||
static void loadDefaultsFromProperties( Class<?> lookAndFeelClass, List<FlatDefaultsAddon> addons,
|
||||
Properties additionalDefaults, boolean dark, UIDefaults defaults )
|
||||
@@ -119,7 +133,7 @@ class UIDefaultsLoader
|
||||
addonClassLoaders.add( addonClassLoader );
|
||||
}
|
||||
|
||||
// load custom properties files (usually provides by applications)
|
||||
// load custom properties files (usually provided by applications)
|
||||
List<Object> customDefaultsSources = FlatLaf.getCustomDefaultsSources();
|
||||
int size = (customDefaultsSources != null) ? customDefaultsSources.size() : 0;
|
||||
for( int i = 0; i < size; i++ ) {
|
||||
@@ -144,6 +158,18 @@ class UIDefaultsLoader
|
||||
properties.load( in );
|
||||
}
|
||||
}
|
||||
} else if( source instanceof URL ) {
|
||||
// load from package URL
|
||||
URL packageUrl = (URL) source;
|
||||
for( Class<?> lafClass : lafClasses ) {
|
||||
URL propertiesUrl = new URL( packageUrl + lafClass.getSimpleName() + ".properties" );
|
||||
|
||||
try( InputStream in = propertiesUrl.openStream() ) {
|
||||
properties.load( in );
|
||||
} catch( FileNotFoundException ex ) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
} else if( source instanceof File ) {
|
||||
// load from folder
|
||||
File folder = (File) source;
|
||||
@@ -198,27 +224,30 @@ class UIDefaultsLoader
|
||||
}
|
||||
}
|
||||
|
||||
// get (and remove) globals, which override all other defaults that end with same suffix
|
||||
HashMap<String, String> globals = new HashMap<>();
|
||||
// get (and remove) wildcard replacements, which override all other defaults that end with same suffix
|
||||
HashMap<String, String> wildcards = new HashMap<>();
|
||||
Iterator<Entry<Object, Object>> it = properties.entrySet().iterator();
|
||||
while( it.hasNext() ) {
|
||||
Entry<Object, Object> e = it.next();
|
||||
String key = (String) e.getKey();
|
||||
if( key.startsWith( GLOBAL_PREFIX ) ) {
|
||||
globals.put( key.substring( GLOBAL_PREFIX.length() ), (String) e.getValue() );
|
||||
if( key.startsWith( WILDCARD_PREFIX ) ) {
|
||||
wildcards.put( key.substring( WILDCARD_PREFIX.length() ), (String) e.getValue() );
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// override UI defaults with globals
|
||||
for( Object okey : defaults.keySet() ) {
|
||||
if( okey instanceof String && ((String)okey).contains( "." ) ) {
|
||||
String key = (String) okey;
|
||||
String globalKey = key.substring( key.lastIndexOf( '.' ) + 1 );
|
||||
String globalValue = globals.get( globalKey );
|
||||
if( globalValue != null && !properties.containsKey( key ) )
|
||||
properties.put( key, globalValue );
|
||||
}
|
||||
// override UI defaults with wildcard replacements
|
||||
for( Object key : defaults.keySet() ) {
|
||||
int dot;
|
||||
if( !(key instanceof String) ||
|
||||
properties.containsKey( key ) ||
|
||||
(dot = ((String)key).lastIndexOf( '.' )) < 0 )
|
||||
continue;
|
||||
|
||||
String wildcardKey = ((String)key).substring( dot + 1 );
|
||||
String wildcardValue = wildcards.get( wildcardKey );
|
||||
if( wildcardValue != null )
|
||||
properties.put( key, wildcardValue );
|
||||
}
|
||||
|
||||
Function<String, String> propertiesGetter = key -> {
|
||||
@@ -229,25 +258,35 @@ class UIDefaultsLoader
|
||||
};
|
||||
|
||||
// parse and add properties to UI defaults
|
||||
Map<String, String> variables = new HashMap<>( 50 );
|
||||
for( Map.Entry<Object, Object> e : properties.entrySet() ) {
|
||||
String key = (String) e.getKey();
|
||||
if( key.startsWith( VARIABLE_PREFIX ) )
|
||||
if( key.startsWith( VARIABLE_PREFIX ) ) {
|
||||
variables.put( key, (String) e.getValue() );
|
||||
continue;
|
||||
}
|
||||
|
||||
String value = resolveValue( (String) e.getValue(), propertiesGetter );
|
||||
try {
|
||||
defaults.put( key, parseValue( key, value, null, resolver, addonClassLoaders ) );
|
||||
defaults.put( key, parseValue( key, value, null, null, resolver, addonClassLoaders ) );
|
||||
} catch( RuntimeException ex ) {
|
||||
logParseError( Level.SEVERE, key, value, ex );
|
||||
logParseError( key, value, ex, true );
|
||||
}
|
||||
}
|
||||
|
||||
// remember variables in defaults to allow using them in styles
|
||||
defaults.put( KEY_VARIABLES, variables );
|
||||
} catch( IOException ex ) {
|
||||
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: Failed to load properties files.", ex );
|
||||
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to load properties files.", ex );
|
||||
}
|
||||
}
|
||||
|
||||
static void logParseError( Level level, String key, String value, RuntimeException ex ) {
|
||||
FlatLaf.LOG.log( level, "FlatLaf: Failed to parse: '" + key + '=' + value + '\'', ex );
|
||||
static void logParseError( String key, String value, RuntimeException ex, boolean severe ) {
|
||||
String message = "FlatLaf: Failed to parse: '" + key + '=' + value + '\'';
|
||||
if( severe )
|
||||
LoggingFacade.INSTANCE.logSevere( message, ex );
|
||||
else
|
||||
LoggingFacade.INSTANCE.logConfig( message, ex );
|
||||
}
|
||||
|
||||
static String resolveValue( String value, Function<String, String> propertiesGetter ) {
|
||||
@@ -279,16 +318,47 @@ class UIDefaultsLoader
|
||||
return resolveValue( newValue, propertiesGetter );
|
||||
}
|
||||
|
||||
enum ValueType { UNKNOWN, STRING, BOOLEAN, CHARACTER, INTEGER, FLOAT, BORDER, ICON, INSETS, DIMENSION, COLOR,
|
||||
static String resolveValueFromUIManager( String value ) {
|
||||
if( value.startsWith( VARIABLE_PREFIX ) ) {
|
||||
@SuppressWarnings( "unchecked" )
|
||||
Map<String, String> variables = (Map<String, String>) UIManager.get( KEY_VARIABLES );
|
||||
String newValue = (variables != null) ? variables.get( value ) : null;
|
||||
if( newValue == null )
|
||||
throw new IllegalArgumentException( "variable '" + value + "' not found" );
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
if( !value.startsWith( PROPERTY_PREFIX ) )
|
||||
return value;
|
||||
|
||||
String key = value.substring( PROPERTY_PREFIX.length() );
|
||||
Object newValue = UIManager.get( key );
|
||||
if( newValue == null )
|
||||
throw new IllegalArgumentException( "property '" + key + "' not found" );
|
||||
|
||||
// convert binary color to string
|
||||
if( newValue instanceof Color ) {
|
||||
Color color = (Color) newValue;
|
||||
int alpha = color.getAlpha();
|
||||
return String.format( (alpha != 255) ? "#%06x%02x" : "#%06x", color.getRGB() & 0xffffff, alpha );
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException( "property value type '" + newValue.getClass().getName() + "' not supported in references" );
|
||||
}
|
||||
|
||||
enum ValueType { UNKNOWN, STRING, BOOLEAN, CHARACTER, INTEGER, INTEGERORFLOAT, FLOAT, BORDER, ICON, INSETS, DIMENSION, COLOR, FONT,
|
||||
SCALEDINTEGER, SCALEDFLOAT, SCALEDINSETS, SCALEDDIMENSION, INSTANCE, CLASS, GRAYFILTER, NULL, LAZY }
|
||||
|
||||
private static ValueType[] tempResultValueType = new ValueType[1];
|
||||
private static Map<Class<?>, ValueType> javaValueTypes;
|
||||
private static Map<String, ValueType> knownValueTypes;
|
||||
|
||||
static Object parseValue( String key, String value ) {
|
||||
return parseValue( key, value, null, v -> v, Collections.emptyList() );
|
||||
static Object parseValue( String key, String value, Class<?> valueType ) {
|
||||
return parseValue( key, value, valueType, null, v -> v, Collections.emptyList() );
|
||||
}
|
||||
|
||||
static Object parseValue( String key, String value, ValueType[] resultValueType,
|
||||
static Object parseValue( String key, String value, Class<?> javaValueType, ValueType[] resultValueType,
|
||||
Function<String, String> resolver, List<ClassLoader> addonClassLoaders )
|
||||
{
|
||||
if( resultValueType == null )
|
||||
@@ -296,67 +366,146 @@ class UIDefaultsLoader
|
||||
|
||||
value = value.trim();
|
||||
|
||||
// null, false, true
|
||||
switch( value ) {
|
||||
case "null": resultValueType[0] = ValueType.NULL; return null;
|
||||
case "false": resultValueType[0] = ValueType.BOOLEAN; return false;
|
||||
case "true": resultValueType[0] = ValueType.BOOLEAN; return true;
|
||||
// null
|
||||
if( value.equals( "null" ) || value.isEmpty() ) {
|
||||
resultValueType[0] = ValueType.NULL;
|
||||
return null;
|
||||
}
|
||||
|
||||
// check for function "lazy"
|
||||
// Syntax: lazy(uiKey)
|
||||
if( value.startsWith( "lazy(" ) && value.endsWith( ")" ) ) {
|
||||
resultValueType[0] = ValueType.LAZY;
|
||||
String uiKey = value.substring( 5, value.length() - 1 ).trim();
|
||||
return (LazyValue) t -> {
|
||||
return lazyUIManagerGet( uiKey );
|
||||
};
|
||||
// check for function "if"
|
||||
// Syntax: if(condition,trueValue,falseValue)
|
||||
// - condition: evaluates to true if:
|
||||
// - is not "null"
|
||||
// - is not "false"
|
||||
// - is not an integer with zero value
|
||||
// - trueValue: used if condition is true
|
||||
// - falseValue: used if condition is false
|
||||
if( value.startsWith( "if(" ) && value.endsWith( ")" ) ) {
|
||||
List<String> params = splitFunctionParams( value.substring( 3, value.length() - 1 ), ',' );
|
||||
if( params.size() != 3 )
|
||||
throwMissingParametersException( value );
|
||||
|
||||
boolean ifCondition = parseCondition( params.get( 0 ), resolver, addonClassLoaders );
|
||||
String ifValue = params.get( ifCondition ? 1 : 2 );
|
||||
return parseValue( key, resolver.apply( ifValue ), javaValueType, resultValueType, resolver, addonClassLoaders );
|
||||
}
|
||||
|
||||
ValueType valueType = ValueType.UNKNOWN;
|
||||
|
||||
// check whether value type is specified in the value
|
||||
if( value.startsWith( "#" ) )
|
||||
valueType = ValueType.COLOR;
|
||||
else if( value.startsWith( "\"" ) && value.endsWith( "\"" ) ) {
|
||||
valueType = ValueType.STRING;
|
||||
value = value.substring( 1, value.length() - 1 );
|
||||
} else if( value.startsWith( TYPE_PREFIX ) ) {
|
||||
int end = value.indexOf( TYPE_PREFIX_END );
|
||||
if( end != -1 ) {
|
||||
try {
|
||||
String typeStr = value.substring( TYPE_PREFIX.length(), end );
|
||||
valueType = ValueType.valueOf( typeStr.toUpperCase( Locale.ENGLISH ) );
|
||||
if( javaValueType != null ) {
|
||||
if( javaValueTypes == null ) {
|
||||
// create lazy
|
||||
javaValueTypes = new HashMap<>();
|
||||
javaValueTypes.put( String.class, ValueType.STRING );
|
||||
javaValueTypes.put( boolean.class, ValueType.BOOLEAN );
|
||||
javaValueTypes.put( Boolean.class, ValueType.BOOLEAN );
|
||||
javaValueTypes.put( char.class, ValueType.CHARACTER );
|
||||
javaValueTypes.put( Character.class, ValueType.CHARACTER );
|
||||
javaValueTypes.put( int.class, ValueType.INTEGER );
|
||||
javaValueTypes.put( Integer.class, ValueType.INTEGER );
|
||||
javaValueTypes.put( float.class, ValueType.FLOAT );
|
||||
javaValueTypes.put( Float.class, ValueType.FLOAT );
|
||||
javaValueTypes.put( Border.class, ValueType.BORDER );
|
||||
javaValueTypes.put( Icon.class, ValueType.ICON );
|
||||
javaValueTypes.put( Insets.class, ValueType.INSETS );
|
||||
javaValueTypes.put( Dimension.class, ValueType.DIMENSION );
|
||||
javaValueTypes.put( Color.class, ValueType.COLOR );
|
||||
javaValueTypes.put( Font.class, ValueType.FONT );
|
||||
}
|
||||
|
||||
// remove type from value
|
||||
value = value.substring( end + TYPE_PREFIX_END.length() );
|
||||
} catch( IllegalArgumentException ex ) {
|
||||
// ignore
|
||||
// map java value type to parser value type
|
||||
valueType = javaValueTypes.get( javaValueType );
|
||||
if( valueType == null )
|
||||
throw new IllegalArgumentException( "unsupported value type '" + javaValueType.getName() + "'" );
|
||||
|
||||
// remove '"' from strings
|
||||
if( valueType == ValueType.STRING && value.startsWith( "\"" ) && value.endsWith( "\"" ) )
|
||||
value = value.substring( 1, value.length() - 1 );
|
||||
} else {
|
||||
// false, true
|
||||
switch( value ) {
|
||||
case "false": resultValueType[0] = ValueType.BOOLEAN; return false;
|
||||
case "true": resultValueType[0] = ValueType.BOOLEAN; return true;
|
||||
}
|
||||
|
||||
// check for function "lazy"
|
||||
// Syntax: lazy(uiKey)
|
||||
if( value.startsWith( "lazy(" ) && value.endsWith( ")" ) ) {
|
||||
resultValueType[0] = ValueType.LAZY;
|
||||
String uiKey = StringUtils.substringTrimmed( value, 5, value.length() - 1 );
|
||||
return (LazyValue) t -> {
|
||||
return lazyUIManagerGet( uiKey );
|
||||
};
|
||||
}
|
||||
|
||||
// check whether value type is specified in the value
|
||||
if( value.startsWith( "#" ) )
|
||||
valueType = ValueType.COLOR;
|
||||
else if( value.startsWith( TYPE_PREFIX ) ) {
|
||||
int end = value.indexOf( TYPE_PREFIX_END );
|
||||
if( end != -1 ) {
|
||||
try {
|
||||
String typeStr = value.substring( TYPE_PREFIX.length(), end );
|
||||
valueType = ValueType.valueOf( typeStr.toUpperCase( Locale.ENGLISH ) );
|
||||
|
||||
// remove type from value
|
||||
value = value.substring( end + TYPE_PREFIX_END.length() );
|
||||
} catch( IllegalArgumentException ex ) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// determine value type from key
|
||||
if( valueType == ValueType.UNKNOWN ) {
|
||||
if( key.endsWith( "ground" ) || key.endsWith( "Color" ) )
|
||||
valueType = ValueType.COLOR;
|
||||
else if( key.endsWith( ".border" ) || key.endsWith( "Border" ) )
|
||||
valueType = ValueType.BORDER;
|
||||
else if( key.endsWith( ".icon" ) || key.endsWith( "Icon" ) )
|
||||
valueType = ValueType.ICON;
|
||||
else if( key.endsWith( ".margin" ) || key.endsWith( ".padding" ) ||
|
||||
key.endsWith( "Margins" ) || key.endsWith( "Insets" ) )
|
||||
valueType = ValueType.INSETS;
|
||||
else if( key.endsWith( "Size" ) )
|
||||
valueType = ValueType.DIMENSION;
|
||||
else if( key.endsWith( "Width" ) || key.endsWith( "Height" ) )
|
||||
valueType = ValueType.INTEGER;
|
||||
else if( key.endsWith( "Char" ) )
|
||||
valueType = ValueType.CHARACTER;
|
||||
else if( key.endsWith( "UI" ) )
|
||||
valueType = ValueType.STRING;
|
||||
else if( key.endsWith( "grayFilter" ) )
|
||||
valueType = ValueType.GRAYFILTER;
|
||||
if( valueType == ValueType.UNKNOWN ) {
|
||||
if( knownValueTypes == null ) {
|
||||
// create lazy
|
||||
knownValueTypes = new HashMap<>();
|
||||
// SplitPane
|
||||
knownValueTypes.put( "SplitPane.dividerSize", ValueType.INTEGER );
|
||||
knownValueTypes.put( "SplitPaneDivider.gripDotSize", ValueType.INTEGER );
|
||||
knownValueTypes.put( "dividerSize", ValueType.INTEGER );
|
||||
knownValueTypes.put( "gripDotSize", ValueType.INTEGER );
|
||||
// TabbedPane
|
||||
knownValueTypes.put( "TabbedPane.closeCrossPlainSize", ValueType.FLOAT );
|
||||
knownValueTypes.put( "TabbedPane.closeCrossFilledSize", ValueType.FLOAT );
|
||||
knownValueTypes.put( "closeCrossPlainSize", ValueType.FLOAT );
|
||||
knownValueTypes.put( "closeCrossFilledSize", ValueType.FLOAT );
|
||||
// Table
|
||||
knownValueTypes.put( "Table.intercellSpacing", ValueType.DIMENSION );
|
||||
knownValueTypes.put( "intercellSpacing", ValueType.DIMENSION );
|
||||
}
|
||||
|
||||
valueType = knownValueTypes.getOrDefault( key, ValueType.UNKNOWN );
|
||||
}
|
||||
|
||||
// determine value type from key
|
||||
if( valueType == ValueType.UNKNOWN ) {
|
||||
if( key.endsWith( "UI" ) )
|
||||
valueType = ValueType.STRING;
|
||||
else if( key.endsWith( "Color" ) ||
|
||||
(key.endsWith( "ground" ) &&
|
||||
(key.endsWith( ".background" ) || key.endsWith( "Background" ) || key.equals( "background" ) ||
|
||||
key.endsWith( ".foreground" ) || key.endsWith( "Foreground" ) || key.equals( "foreground" ))) )
|
||||
valueType = ValueType.COLOR;
|
||||
else if( key.endsWith( ".font" ) || key.endsWith( "Font" ) || key.equals( "font" ) )
|
||||
valueType = ValueType.FONT;
|
||||
else if( key.endsWith( ".border" ) || key.endsWith( "Border" ) || key.equals( "border" ) )
|
||||
valueType = ValueType.BORDER;
|
||||
else if( key.endsWith( ".icon" ) || key.endsWith( "Icon" ) || key.equals( "icon" ) )
|
||||
valueType = ValueType.ICON;
|
||||
else if( key.endsWith( ".margin" ) || key.equals( "margin" ) ||
|
||||
key.endsWith( ".padding" ) || key.equals( "padding" ) ||
|
||||
key.endsWith( "Margins" ) || key.endsWith( "Insets" ) )
|
||||
valueType = ValueType.INSETS;
|
||||
else if( key.endsWith( "Size" ) )
|
||||
valueType = ValueType.DIMENSION;
|
||||
else if( key.endsWith( "Width" ) || key.endsWith( "Height" ) )
|
||||
valueType = ValueType.INTEGERORFLOAT;
|
||||
else if( key.endsWith( "Char" ) )
|
||||
valueType = ValueType.CHARACTER;
|
||||
else if( key.endsWith( "grayFilter" ) )
|
||||
valueType = ValueType.GRAYFILTER;
|
||||
}
|
||||
}
|
||||
|
||||
resultValueType[0] = valueType;
|
||||
@@ -364,14 +513,17 @@ class UIDefaultsLoader
|
||||
// parse value
|
||||
switch( valueType ) {
|
||||
case STRING: return value;
|
||||
case BOOLEAN: return parseBoolean( value );
|
||||
case CHARACTER: return parseCharacter( value );
|
||||
case INTEGER: return parseInteger( value, true );
|
||||
case INTEGERORFLOAT:return parseIntegerOrFloat( value, true );
|
||||
case FLOAT: return parseFloat( value, true );
|
||||
case BORDER: return parseBorder( value, resolver, addonClassLoaders );
|
||||
case ICON: return parseInstance( value, addonClassLoaders );
|
||||
case INSETS: return parseInsets( value );
|
||||
case DIMENSION: return parseDimension( value );
|
||||
case COLOR: return parseColorOrFunction( value, resolver, true );
|
||||
case FONT: return parseFont( value );
|
||||
case SCALEDINTEGER: return parseScaledInteger( value );
|
||||
case SCALEDFLOAT: return parseScaledFloat( value );
|
||||
case SCALEDINSETS: return parseScaledInsets( value );
|
||||
@@ -381,6 +533,12 @@ class UIDefaultsLoader
|
||||
case GRAYFILTER: return parseGrayFilter( value );
|
||||
case UNKNOWN:
|
||||
default:
|
||||
// string
|
||||
if( value.startsWith( "\"" ) && value.endsWith( "\"" ) ) {
|
||||
resultValueType[0] = ValueType.STRING;
|
||||
return value.substring( 1, value.length() - 1 );
|
||||
}
|
||||
|
||||
// colors
|
||||
Object color = parseColorOrFunction( value, resolver, false );
|
||||
if( color != null ) {
|
||||
@@ -408,10 +566,24 @@ class UIDefaultsLoader
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean parseCondition( String condition,
|
||||
Function<String, String> resolver, List<ClassLoader> addonClassLoaders )
|
||||
{
|
||||
try {
|
||||
Object conditionValue = parseValue( "", resolver.apply( condition ), null, null, resolver, addonClassLoaders );
|
||||
return (conditionValue != null &&
|
||||
!conditionValue.equals( false ) &&
|
||||
!conditionValue.equals( 0 ) );
|
||||
} catch( IllegalArgumentException ex ) {
|
||||
// ignore errors (e.g. variable or property not found) and evaluate to false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static Object parseBorder( String value, Function<String, String> resolver, List<ClassLoader> addonClassLoaders ) {
|
||||
if( value.indexOf( ',' ) >= 0 ) {
|
||||
// top,left,bottom,right[,lineColor[,lineThickness]]
|
||||
List<String> parts = split( value, ',' );
|
||||
List<String> parts = splitFunctionParams( value, ',' );
|
||||
Insets insets = parseInsets( value );
|
||||
ColorUIResource lineColor = (parts.size() >= 5)
|
||||
? (ColorUIResource) parseColorOrFunction( resolver.apply( parts.get( 4 ) ), resolver, true )
|
||||
@@ -430,9 +602,9 @@ class UIDefaultsLoader
|
||||
private static Object parseInstance( String value, List<ClassLoader> addonClassLoaders ) {
|
||||
return (LazyValue) t -> {
|
||||
try {
|
||||
return findClass( value, addonClassLoaders ).newInstance();
|
||||
} catch( InstantiationException | IllegalAccessException | ClassNotFoundException ex ) {
|
||||
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: Failed to instantiate '" + value + "'.", ex );
|
||||
return findClass( value, addonClassLoaders ).getDeclaredConstructor().newInstance();
|
||||
} catch( Exception ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to instantiate '" + value + "'.", ex );
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@@ -443,7 +615,7 @@ class UIDefaultsLoader
|
||||
try {
|
||||
return findClass( value, addonClassLoaders );
|
||||
} catch( ClassNotFoundException ex ) {
|
||||
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: Failed to find class '" + value + "'.", ex );
|
||||
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to find class '" + value + "'.", ex );
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@@ -468,7 +640,7 @@ class UIDefaultsLoader
|
||||
}
|
||||
|
||||
private static Insets parseInsets( String value ) {
|
||||
List<String> numbers = split( value, ',' );
|
||||
List<String> numbers = StringUtils.split( value, ',', true, false );
|
||||
try {
|
||||
return new InsetsUIResource(
|
||||
Integer.parseInt( numbers.get( 0 ) ),
|
||||
@@ -481,7 +653,7 @@ class UIDefaultsLoader
|
||||
}
|
||||
|
||||
private static Dimension parseDimension( String value ) {
|
||||
List<String> numbers = split( value, ',' );
|
||||
List<String> numbers = StringUtils.split( value, ',', true, false );
|
||||
try {
|
||||
return new DimensionUIResource(
|
||||
Integer.parseInt( numbers.get( 0 ) ),
|
||||
@@ -569,27 +741,63 @@ class UIDefaultsLoader
|
||||
return null;
|
||||
}
|
||||
|
||||
String function = value.substring( 0, paramsStart ).trim();
|
||||
String function = StringUtils.substringTrimmed( value, 0, paramsStart );
|
||||
List<String> params = splitFunctionParams( value.substring( paramsStart + 1, value.length() - 1 ), ',' );
|
||||
if( params.isEmpty() )
|
||||
throw new IllegalArgumentException( "missing parameters in function '" + value + "'" );
|
||||
throwMissingParametersException( value );
|
||||
|
||||
switch( function ) {
|
||||
case "rgb": return parseColorRgbOrRgba( false, params, resolver, reportError );
|
||||
case "rgba": return parseColorRgbOrRgba( true, params, resolver, reportError );
|
||||
case "hsl": return parseColorHslOrHsla( false, params );
|
||||
case "hsla": return parseColorHslOrHsla( true, params );
|
||||
case "lighten": return parseColorHSLIncreaseDecrease( 2, true, params, resolver, reportError );
|
||||
case "darken": return parseColorHSLIncreaseDecrease( 2, false, params, resolver, reportError );
|
||||
case "saturate": return parseColorHSLIncreaseDecrease( 1, true, params, resolver, reportError );
|
||||
case "desaturate": return parseColorHSLIncreaseDecrease( 1, false, params, resolver, reportError );
|
||||
if( parseColorDepth > 100 )
|
||||
throw new IllegalArgumentException( "endless recursion in color function '" + value + "'" );
|
||||
|
||||
parseColorDepth++;
|
||||
try {
|
||||
switch( function ) {
|
||||
case "if": return parseColorIf( value, params, resolver, reportError );
|
||||
case "rgb": return parseColorRgbOrRgba( false, params, resolver, reportError );
|
||||
case "rgba": return parseColorRgbOrRgba( true, params, resolver, reportError );
|
||||
case "hsl": return parseColorHslOrHsla( false, params );
|
||||
case "hsla": return parseColorHslOrHsla( true, params );
|
||||
case "lighten": return parseColorHSLIncreaseDecrease( 2, true, params, resolver, reportError );
|
||||
case "darken": return parseColorHSLIncreaseDecrease( 2, false, params, resolver, reportError );
|
||||
case "saturate": return parseColorHSLIncreaseDecrease( 1, true, params, resolver, reportError );
|
||||
case "desaturate": return parseColorHSLIncreaseDecrease( 1, false, params, resolver, reportError );
|
||||
case "fadein": return parseColorHSLIncreaseDecrease( 3, true, params, resolver, reportError );
|
||||
case "fadeout": return parseColorHSLIncreaseDecrease( 3, false, params, resolver, reportError );
|
||||
case "fade": return parseColorFade( params, resolver, reportError );
|
||||
case "spin": return parseColorSpin( params, resolver, reportError );
|
||||
case "changeHue": return parseColorChange( 0, params, resolver, reportError );
|
||||
case "changeSaturation":return parseColorChange( 1, params, resolver, reportError );
|
||||
case "changeLightness": return parseColorChange( 2, params, resolver, reportError );
|
||||
case "changeAlpha": return parseColorChange( 3, params, resolver, reportError );
|
||||
case "mix": return parseColorMix( null, params, resolver, reportError );
|
||||
case "tint": return parseColorMix( "#fff", params, resolver, reportError );
|
||||
case "shade": return parseColorMix( "#000", params, resolver, reportError );
|
||||
case "contrast": return parseColorContrast( params, resolver, reportError );
|
||||
}
|
||||
} finally {
|
||||
parseColorDepth--;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException( "unknown color function '" + value + "'" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Syntax: rgb(red,green,blue) or rgba(red,green,blue,alpha) or rgba(color,alpha)
|
||||
* Syntax: if(condition,trueValue,falseValue)
|
||||
* <p>
|
||||
* This "if" function is only used if the "if" is passed as parameter to another
|
||||
* color function. Otherwise the general "if" function is used.
|
||||
*/
|
||||
private static Object parseColorIf( String value, List<String> params, Function<String, String> resolver, boolean reportError ) {
|
||||
if( params.size() != 3 )
|
||||
throwMissingParametersException( value );
|
||||
|
||||
boolean ifCondition = parseCondition( params.get( 0 ), resolver, Collections.emptyList() );
|
||||
String ifValue = params.get( ifCondition ? 1 : 2 );
|
||||
return parseColorOrFunction( resolver.apply( ifValue ), resolver, reportError );
|
||||
}
|
||||
|
||||
/**
|
||||
* Syntax: rgb(red,green,blue) or rgba(red,green,blue,alpha)
|
||||
* - red: an integer 0-255 or a percentage 0-100%
|
||||
* - green: an integer 0-255 or a percentage 0-100%
|
||||
* - blue: an integer 0-255 or a percentage 0-100%
|
||||
@@ -600,6 +808,8 @@ class UIDefaultsLoader
|
||||
{
|
||||
if( hasAlpha && params.size() == 2 ) {
|
||||
// syntax rgba(color,alpha), which allows adding alpha to any color
|
||||
// NOTE: this syntax is deprecated
|
||||
// use fade(color,alpha) instead
|
||||
String colorStr = params.get( 0 );
|
||||
int alpha = parseInteger( params.get( 1 ), 0, 255, true );
|
||||
|
||||
@@ -636,7 +846,8 @@ class UIDefaultsLoader
|
||||
|
||||
/**
|
||||
* Syntax: lighten(color,amount[,options]) or darken(color,amount[,options]) or
|
||||
* saturate(color,amount[,options]) or desaturate(color,amount[,options])
|
||||
* saturate(color,amount[,options]) or desaturate(color,amount[,options]) or
|
||||
* fadein(color,amount[,options]) or fadeout(color,amount[,options])
|
||||
* - color: a color (e.g. #f00) or a color function
|
||||
* - amount: percentage 0-100%
|
||||
* - options: [relative] [autoInverse] [noAutoInverse] [lazy] [derived]
|
||||
@@ -676,6 +887,145 @@ class UIDefaultsLoader
|
||||
};
|
||||
}
|
||||
|
||||
// parse base color, apply function and create derived color
|
||||
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
|
||||
}
|
||||
|
||||
/**
|
||||
* Syntax: fade(color,amount[,options])
|
||||
* - color: a color (e.g. #f00) or a color function
|
||||
* - amount: percentage 0-100%
|
||||
* - options: [derived]
|
||||
*/
|
||||
private static Object parseColorFade( List<String> params, Function<String, String> resolver, boolean reportError ) {
|
||||
String colorStr = params.get( 0 );
|
||||
int amount = parsePercentage( params.get( 1 ) );
|
||||
boolean derived = false;
|
||||
|
||||
if( params.size() > 2 ) {
|
||||
String options = params.get( 2 );
|
||||
derived = options.contains( "derived" );
|
||||
}
|
||||
|
||||
// create function
|
||||
ColorFunction function = new ColorFunctions.Fade( amount );
|
||||
|
||||
// parse base color, apply function and create derived color
|
||||
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
|
||||
}
|
||||
|
||||
/**
|
||||
* Syntax: spin(color,angle[,options])
|
||||
* - color: a color (e.g. #f00) or a color function
|
||||
* - angle: number of degrees to rotate
|
||||
* - options: [derived]
|
||||
*/
|
||||
private static Object parseColorSpin( List<String> params, Function<String, String> resolver, boolean reportError ) {
|
||||
String colorStr = params.get( 0 );
|
||||
int amount = parseInteger( params.get( 1 ), true );
|
||||
boolean derived = false;
|
||||
|
||||
if( params.size() > 2 ) {
|
||||
String options = params.get( 2 );
|
||||
derived = options.contains( "derived" );
|
||||
}
|
||||
|
||||
// create function
|
||||
ColorFunction function = new ColorFunctions.HSLIncreaseDecrease( 0, true, amount, false, false );
|
||||
|
||||
// parse base color, apply function and create derived color
|
||||
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
|
||||
}
|
||||
|
||||
/**
|
||||
* Syntax: changeHue(color,value[,options]) or
|
||||
* changeSaturation(color,value[,options]) or
|
||||
* changeLightness(color,value[,options]) or
|
||||
* changeAlpha(color,value[,options])
|
||||
* - color: a color (e.g. #f00) or a color function
|
||||
* - value: for hue: number of degrees; otherwise: percentage 0-100%
|
||||
* - options: [derived]
|
||||
*/
|
||||
private static Object parseColorChange( int hslIndex,
|
||||
List<String> params, Function<String, String> resolver, boolean reportError )
|
||||
{
|
||||
String colorStr = params.get( 0 );
|
||||
int value = (hslIndex == 0)
|
||||
? parseInteger( params.get( 1 ), true )
|
||||
: parsePercentage( params.get( 1 ) );
|
||||
boolean derived = false;
|
||||
|
||||
if( params.size() > 2 ) {
|
||||
String options = params.get( 2 );
|
||||
derived = options.contains( "derived" );
|
||||
}
|
||||
|
||||
// create function
|
||||
ColorFunction function = new ColorFunctions.HSLChange( hslIndex, value );
|
||||
|
||||
// parse base color, apply function and create derived color
|
||||
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
|
||||
}
|
||||
|
||||
/**
|
||||
* Syntax: mix(color1,color2[,weight]) or
|
||||
* tint(color[,weight]) or
|
||||
* shade(color[,weight])
|
||||
* - color1: a color (e.g. #f00) or a color function
|
||||
* - color2: a color (e.g. #f00) or a color function
|
||||
* - weight: the weight (in range 0-100%) to mix the two colors
|
||||
* larger weight uses more of first color, smaller weight more of second color
|
||||
*/
|
||||
private static Object parseColorMix( String color1Str, List<String> params, Function<String, String> resolver, boolean reportError ) {
|
||||
int i = 0;
|
||||
if( color1Str == null )
|
||||
color1Str = params.get( i++ );
|
||||
String color2Str = params.get( i++ );
|
||||
int weight = (params.size() > i) ? parsePercentage( params.get( i ) ) : 50;
|
||||
|
||||
// parse second color
|
||||
ColorUIResource color2 = (ColorUIResource) parseColorOrFunction( resolver.apply( color2Str ), resolver, reportError );
|
||||
if( color2 == null )
|
||||
return null;
|
||||
|
||||
// create function
|
||||
ColorFunction function = new ColorFunctions.Mix( color2, weight );
|
||||
|
||||
// parse first color, apply function and create mixed color
|
||||
return parseFunctionBaseColor( color1Str, function, false, resolver, reportError );
|
||||
}
|
||||
|
||||
/**
|
||||
* Syntax: contrast(color,dark,light[,threshold])
|
||||
* - color: a color to compare against
|
||||
* - dark: a designated dark color (e.g. #000) or a color function
|
||||
* - light: a designated light color (e.g. #fff) or a color function
|
||||
* - threshold: the threshold (in range 0-100%) to specify where the transition
|
||||
* from "dark" to "light" is (default is 43%)
|
||||
*/
|
||||
private static Object parseColorContrast( List<String> params, Function<String, String> resolver, boolean reportError ) {
|
||||
String colorStr = params.get( 0 );
|
||||
String darkStr = params.get( 1 );
|
||||
String lightStr = params.get( 2 );
|
||||
int threshold = (params.size() > 3) ? parsePercentage( params.get( 3 ) ) : 43;
|
||||
|
||||
// parse color to compare against
|
||||
ColorUIResource color = (ColorUIResource) parseColorOrFunction( resolver.apply( colorStr ), resolver, reportError );
|
||||
if( color == null )
|
||||
return null;
|
||||
|
||||
// check luma and determine whether to use dark or light color
|
||||
String darkOrLightColor = (ColorFunctions.luma( color ) * 100 < threshold)
|
||||
? lightStr
|
||||
: darkStr;
|
||||
|
||||
// parse dark or light color
|
||||
return parseColorOrFunction( resolver.apply( darkOrLightColor ), resolver, reportError );
|
||||
}
|
||||
|
||||
private static Object parseFunctionBaseColor( String colorStr, ColorFunction function,
|
||||
boolean derived, Function<String, String> resolver, boolean reportError )
|
||||
{
|
||||
// parse base color
|
||||
String resolvedColorStr = resolver.apply( colorStr );
|
||||
ColorUIResource baseColor = (ColorUIResource) parseColorOrFunction( resolvedColorStr, resolver, reportError );
|
||||
@@ -703,6 +1053,107 @@ class UIDefaultsLoader
|
||||
return new ColorUIResource( newColor );
|
||||
}
|
||||
|
||||
/**
|
||||
* Syntax: [normal] [bold|+bold|-bold] [italic|+italic|-italic] [<size>|+<incr>|-<decr>|<percent>%] [family[, family]] [$baseFontKey]
|
||||
*/
|
||||
private static Object parseFont( String value ) {
|
||||
Object font = fontCache.get( value );
|
||||
if( font != null )
|
||||
return font;
|
||||
|
||||
int style = -1;
|
||||
int styleChange = 0;
|
||||
int absoluteSize = 0;
|
||||
int relativeSize = 0;
|
||||
float scaleSize = 0;
|
||||
List<String> families = null;
|
||||
String baseFontKey = null;
|
||||
|
||||
// use StreamTokenizer to split string because it supports quoted strings
|
||||
StreamTokenizer st = new StreamTokenizer( new StringReader( value ) );
|
||||
st.resetSyntax();
|
||||
st.wordChars( ' ' + 1, 255 );
|
||||
st.whitespaceChars( 0, ' ' );
|
||||
st.whitespaceChars( ',', ',' ); // ignore ','
|
||||
st.quoteChar( '"' );
|
||||
st.quoteChar( '\'' );
|
||||
|
||||
try {
|
||||
while( st.nextToken() != StreamTokenizer.TT_EOF ) {
|
||||
String param = st.sval;
|
||||
switch( param ) {
|
||||
// font style
|
||||
case "normal":
|
||||
style = 0;
|
||||
break;
|
||||
|
||||
case "bold":
|
||||
if( style == -1 )
|
||||
style = 0;
|
||||
style |= Font.BOLD;
|
||||
break;
|
||||
|
||||
case "italic":
|
||||
if( style == -1 )
|
||||
style = 0;
|
||||
style |= Font.ITALIC;
|
||||
break;
|
||||
|
||||
case "+bold": styleChange |= Font.BOLD; break;
|
||||
case "-bold": styleChange |= Font.BOLD << 16; break;
|
||||
case "+italic": styleChange |= Font.ITALIC; break;
|
||||
case "-italic": styleChange |= Font.ITALIC << 16; break;
|
||||
|
||||
default:
|
||||
char firstChar = param.charAt( 0 );
|
||||
if( Character.isDigit( firstChar ) || firstChar == '+' || firstChar == '-' ) {
|
||||
// font size
|
||||
if( absoluteSize != 0 || relativeSize != 0 || scaleSize != 0 )
|
||||
throw new IllegalArgumentException( "size specified more than once in '" + value + "'" );
|
||||
|
||||
if( firstChar == '+' || firstChar == '-' )
|
||||
relativeSize = parseInteger( param, true );
|
||||
else if( param.endsWith( "%" ) )
|
||||
scaleSize = parseInteger( param.substring( 0, param.length() - 1 ), true ) / 100f;
|
||||
else
|
||||
absoluteSize = parseInteger( param, true );
|
||||
} else if( firstChar == '$' ) {
|
||||
// reference to base font
|
||||
if( baseFontKey != null )
|
||||
throw new IllegalArgumentException( "baseFontKey specified more than once in '" + value + "'" );
|
||||
|
||||
baseFontKey = param.substring( 1 );
|
||||
} else {
|
||||
// font family
|
||||
if( families == null )
|
||||
families = Collections.singletonList( param );
|
||||
else {
|
||||
if( families.size() == 1 )
|
||||
families = new ArrayList<>( families );
|
||||
families.add( param );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch( IOException ex ) {
|
||||
throw new IllegalArgumentException( ex );
|
||||
}
|
||||
|
||||
if( style != -1 && styleChange != 0 )
|
||||
throw new IllegalArgumentException( "can not mix absolute style (e.g. 'bold') with derived style (e.g. '+italic') in '" + value + "'" );
|
||||
if( styleChange != 0 ) {
|
||||
if( (styleChange & Font.BOLD) != 0 && (styleChange & (Font.BOLD << 16)) != 0 )
|
||||
throw new IllegalArgumentException( "can not use '+bold' and '-bold' in '" + value + "'" );
|
||||
if( (styleChange & Font.ITALIC) != 0 && (styleChange & (Font.ITALIC << 16)) != 0 )
|
||||
throw new IllegalArgumentException( "can not use '+italic' and '-italic' in '" + value + "'" );
|
||||
}
|
||||
|
||||
font = new FlatLaf.ActiveFont( baseFontKey, families, style, styleChange, absoluteSize, relativeSize, scaleSize );
|
||||
fontCache.put( value, font );
|
||||
return font;
|
||||
}
|
||||
|
||||
private static int parsePercentage( String value ) {
|
||||
if( !value.endsWith( "%" ) )
|
||||
throw new NumberFormatException( "invalid percentage '" + value + "'" );
|
||||
@@ -719,6 +1170,14 @@ class UIDefaultsLoader
|
||||
return val;
|
||||
}
|
||||
|
||||
private static Boolean parseBoolean( String value ) {
|
||||
switch( value ) {
|
||||
case "false": return false;
|
||||
case "true": return true;
|
||||
}
|
||||
throw new IllegalArgumentException( "invalid boolean '" + value + "'" );
|
||||
}
|
||||
|
||||
private static Character parseCharacter( String value ) {
|
||||
if( value.length() != 1 )
|
||||
throw new IllegalArgumentException( "invalid character '" + value + "'" );
|
||||
@@ -747,6 +1206,20 @@ class UIDefaultsLoader
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Number parseIntegerOrFloat( String value, boolean reportError ) {
|
||||
try {
|
||||
return Integer.parseInt( value );
|
||||
} catch( NumberFormatException ex ) {
|
||||
try {
|
||||
return Float.parseFloat( value );
|
||||
} catch( NumberFormatException ex2 ) {
|
||||
if( reportError )
|
||||
throw new NumberFormatException( "invalid integer or float '" + value + "'" );
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Float parseFloat( String value, boolean reportError ) {
|
||||
try {
|
||||
return Float.parseFloat( value );
|
||||
@@ -786,7 +1259,7 @@ class UIDefaultsLoader
|
||||
}
|
||||
|
||||
private static Object parseGrayFilter( String value ) {
|
||||
List<String> numbers = split( value, ',' );
|
||||
List<String> numbers = StringUtils.split( value, ',', true, false );
|
||||
try {
|
||||
int brightness = Integer.parseInt( numbers.get( 0 ) );
|
||||
int contrast = Integer.parseInt( numbers.get( 1 ) );
|
||||
@@ -800,20 +1273,6 @@ class UIDefaultsLoader
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Split string and trim parts.
|
||||
*/
|
||||
private static List<String> split( String str, char delim ) {
|
||||
List<String> result = StringUtils.split( str, delim );
|
||||
|
||||
// trim strings
|
||||
int size = result.size();
|
||||
for( int i = 0; i < size; i++ )
|
||||
result.set( i, result.get( i ).trim() );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits function parameters and allows using functions as parameters.
|
||||
* In other words: Delimiters surrounded by '(' and ')' are ignored.
|
||||
@@ -830,11 +1289,11 @@ class UIDefaultsLoader
|
||||
else if( ch == ')' )
|
||||
nestLevel--;
|
||||
else if( nestLevel == 0 && ch == delim ) {
|
||||
strs.add( str.substring( start, i ).trim() );
|
||||
strs.add( StringUtils.substringTrimmed( str, start, i ) );
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
strs.add( str.substring( start ).trim() );
|
||||
strs.add( StringUtils.substringTrimmed( str, start ) );
|
||||
|
||||
return strs;
|
||||
}
|
||||
@@ -843,7 +1302,7 @@ class UIDefaultsLoader
|
||||
* For use in LazyValue to get value for given key from UIManager and report error
|
||||
* if not found. If key is prefixed by '?', then no error is reported.
|
||||
*/
|
||||
private static Object lazyUIManagerGet( String uiKey ) {
|
||||
static Object lazyUIManagerGet( String uiKey ) {
|
||||
boolean optional = false;
|
||||
if( uiKey.startsWith( OPTIONAL_PREFIX ) ) {
|
||||
uiKey = uiKey.substring( OPTIONAL_PREFIX.length() );
|
||||
@@ -852,7 +1311,11 @@ class UIDefaultsLoader
|
||||
|
||||
Object value = UIManager.get( uiKey );
|
||||
if( value == null && !optional )
|
||||
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: '" + uiKey + "' not found in UI defaults." );
|
||||
LoggingFacade.INSTANCE.logSevere( "FlatLaf: '" + uiKey + "' not found in UI defaults.", null );
|
||||
return value;
|
||||
}
|
||||
|
||||
private static void throwMissingParametersException( String value ) {
|
||||
throw new IllegalArgumentException( "missing parameters in function '" + value + "'" );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public abstract class FlatAbstractIcon
|
||||
{
|
||||
protected final int width;
|
||||
protected final int height;
|
||||
protected final Color color;
|
||||
protected Color color;
|
||||
|
||||
public FlatAbstractIcon( int width, int height, Color color ) {
|
||||
this.width = width;
|
||||
@@ -69,7 +69,13 @@ public abstract class FlatAbstractIcon
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void paintIcon( Component c, Graphics2D g2 );
|
||||
/**
|
||||
* Paint the icon at {@code [0,0]} location.
|
||||
* <p>
|
||||
* The given graphics context is scaled.
|
||||
* Use unscaled coordinates, width and height for painting.
|
||||
*/
|
||||
protected abstract void paintIcon( Component c, Graphics2D g );
|
||||
|
||||
@Override
|
||||
public int getIconWidth() {
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2020 FormDev Software GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.formdev.flatlaf.icons;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import com.formdev.flatlaf.util.AnimatedIcon;
|
||||
import com.formdev.flatlaf.util.AnimatedPainter;
|
||||
|
||||
/**
|
||||
* Base class for animated icons that scales width and height, creates and initializes
|
||||
* a scaled graphics context for icon painting.
|
||||
* <p>
|
||||
* Subclasses do not need to scale icon painting.
|
||||
* <p>
|
||||
* This class does not store any state information (needed for animation) in its instance.
|
||||
* Instead a client property is set on the painted component.
|
||||
* This makes it possible to use a shared icon instance for multiple components.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
public abstract class FlatAnimatedIcon
|
||||
extends FlatAbstractIcon
|
||||
implements AnimatedIcon
|
||||
{
|
||||
public FlatAnimatedIcon( int width, int height, Color color ) {
|
||||
super( width, height, color );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintIcon( Component c, Graphics g, int x, int y ) {
|
||||
super.paintIcon( c, g, x, y );
|
||||
AnimatedPainter.saveRepaintLocation( this, c, x, y );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
paintWithAnimation( c, g, 0, 0, getIconWidth(), getIconHeight() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates painting to {@link #paintIconAnimated(Component, Graphics2D, float[])}.
|
||||
* Ignores the given bounds because {@code [x,y]} are always {@code [0,0]} and
|
||||
* {@code [width,height]} are scaled, but painting code should use unscaled width
|
||||
* and height because given graphics context is scaled.
|
||||
*
|
||||
* @since 2
|
||||
*/
|
||||
@Override
|
||||
public void paintAnimated( Component c, Graphics2D g, int x, int y, int width, int height, float[] animatedValues ) {
|
||||
paintIconAnimated( c, g, animatedValues );
|
||||
}
|
||||
|
||||
/**
|
||||
* Paint the icon at {@code 0,0} location.
|
||||
* <p>
|
||||
* The given graphics context is scaled.
|
||||
* Use unscaled coordinates, width and height for painting.
|
||||
*
|
||||
* @since 2
|
||||
*/
|
||||
protected abstract void paintIconAnimated( Component c, Graphics2D g, float[] animatedValues );
|
||||
}
|
||||
@@ -21,13 +21,17 @@ import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.TableHeaderUI;
|
||||
import javax.swing.table.JTableHeader;
|
||||
import com.formdev.flatlaf.ui.FlatTableHeaderUI;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
|
||||
/**
|
||||
* "ascendingSort" icon for {@link javax.swing.table.JTableHeader}.
|
||||
*
|
||||
* @uiDefault Component.arrowType String triangle (default) or chevron
|
||||
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||
* @uiDefault Table.sortIconColor Color
|
||||
*
|
||||
* @author Karl Tauber
|
||||
@@ -35,8 +39,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
public class FlatAscendingSortIcon
|
||||
extends FlatAbstractIcon
|
||||
{
|
||||
protected final boolean chevron = "chevron".equals( UIManager.getString( "Component.arrowType" ) );
|
||||
protected final Color sortIconColor = UIManager.getColor( "Table.sortIconColor" );
|
||||
protected boolean chevron = FlatUIUtils.isChevron( UIManager.getString( "Component.arrowType" ) );
|
||||
protected Color sortIconColor = UIManager.getColor( "Table.sortIconColor" );
|
||||
|
||||
public FlatAscendingSortIcon() {
|
||||
super( 10, 5, null );
|
||||
@@ -44,7 +48,28 @@ public class FlatAscendingSortIcon
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
boolean chevron = this.chevron;
|
||||
Color sortIconColor = this.sortIconColor;
|
||||
|
||||
// Because this icons are always shared for all table headers,
|
||||
// get icon specific style from FlatTableHeaderUI.
|
||||
JTableHeader tableHeader = (JTableHeader) SwingUtilities.getAncestorOfClass( JTableHeader.class, c );
|
||||
if( tableHeader != null ) {
|
||||
TableHeaderUI ui = tableHeader.getUI();
|
||||
if( ui instanceof FlatTableHeaderUI ) {
|
||||
FlatTableHeaderUI fui = (FlatTableHeaderUI) ui;
|
||||
if( fui.arrowType != null )
|
||||
chevron = FlatUIUtils.isChevron( fui.arrowType );
|
||||
if( fui.sortIconColor != null )
|
||||
sortIconColor = fui.sortIconColor;
|
||||
}
|
||||
}
|
||||
|
||||
g.setColor( sortIconColor );
|
||||
paintArrow( c, g, chevron );
|
||||
}
|
||||
|
||||
protected void paintArrow( Component c, Graphics2D g, boolean chevron ) {
|
||||
if( chevron ) {
|
||||
// chevron arrow
|
||||
Path2D path = FlatUIUtils.createPath( false, 1,4, 5,0, 9,4 );
|
||||
|
||||
@@ -16,12 +16,14 @@
|
||||
|
||||
package com.formdev.flatlaf.icons;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
|
||||
/**
|
||||
@@ -38,13 +40,22 @@ public class FlatCapsLockIcon
|
||||
super( 16, 16, UIManager.getColor( "PasswordField.capsLockIconColor" ) );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
public Object applyStyleProperty( String key, Object value ) {
|
||||
Object oldValue;
|
||||
switch( key ) {
|
||||
case "capsLockIconColor": oldValue = color; color = (Color) value; return oldValue;
|
||||
default: throw new UnknownStyleException( key );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
/*
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<rect width="16" height="16" fill="#6E6E6E" rx="3"/>
|
||||
<rect width="6" height="2" x="5" y="12" fill="#FFF"/>
|
||||
<rect width="6" height="2" x="5" y="11.5" fill="#FFF"/>
|
||||
<path fill="#FFF" d="M2,8 L8,2 L14,8 L11,8 L11,10 L5,10 L5,8 L2,8 Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
@@ -52,7 +63,7 @@ public class FlatCapsLockIcon
|
||||
|
||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||
path.append( new RoundRectangle2D.Float( 0, 0, 16, 16, 6, 6 ), false );
|
||||
path.append( new Rectangle2D.Float( 5, 12, 6, 2 ), false );
|
||||
path.append( new Rectangle2D.Float( 5, 11.5f, 6, 2 ), false );
|
||||
path.append( FlatUIUtils.createPath( 2,8, 8,2, 14,8, 11,8, 11,10, 5,10, 5,8 ), false );
|
||||
g.fill( path );
|
||||
}
|
||||
|
||||
@@ -23,82 +23,115 @@ import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
import java.util.Map;
|
||||
import javax.swing.AbstractButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.ui.FlatButtonUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
|
||||
/**
|
||||
* Icon for {@link javax.swing.JCheckBox}.
|
||||
*
|
||||
* Note: If Component.focusWidth is greater than zero, then the outline focus border
|
||||
* <p>
|
||||
* <strong>Note</strong>:
|
||||
* If Component.focusWidth is greater than zero, then the outer focus border
|
||||
* is painted outside of the icon bounds. Make sure that the checkbox
|
||||
* has margins, which are equal or greater than focusWidth.
|
||||
*
|
||||
* @uiDefault CheckBox.icon.style String optional; "outline"/null (default) or "filled"
|
||||
* @uiDefault CheckBox.icon.style String optional; "outlined"/null (default) or "filled"
|
||||
* @uiDefault Component.focusWidth int
|
||||
* @uiDefault Component.borderWidth int
|
||||
* @uiDefault Component.focusColor Color
|
||||
* @uiDefault CheckBox.icon.focusWidth int optional; defaults to Component.focusWidth
|
||||
* @uiDefault CheckBox.icon.focusWidth int or float optional; defaults to Component.focusWidth
|
||||
* @uiDefault CheckBox.icon.borderWidth int or float optional; defaults to Component.borderWidth
|
||||
* @uiDefault CheckBox.icon.selectedBorderWidth int or float optional; defaults to CheckBox.icon.borderWidth
|
||||
* @uiDefault CheckBox.icon.disabledSelectedBorderWidth int or float optional; defaults to CheckBox.icon.selectedBorderWidth
|
||||
* @uiDefault CheckBox.arc int
|
||||
*
|
||||
* @uiDefault CheckBox.icon.focusColor Color optional; defaults to Component.focusColor
|
||||
* @uiDefault CheckBox.icon.borderColor Color
|
||||
* @uiDefault CheckBox.icon.background Color
|
||||
* @uiDefault CheckBox.icon.selectedBorderColor Color
|
||||
* @uiDefault CheckBox.icon.selectedBackground Color
|
||||
* @uiDefault CheckBox.icon.checkmarkColor Color
|
||||
*
|
||||
* @uiDefault CheckBox.icon.disabledBorderColor Color
|
||||
* @uiDefault CheckBox.icon.disabledBackground Color
|
||||
* @uiDefault CheckBox.icon.disabledSelectedBorderColor Color optional; CheckBox.icon.disabledBorderColor is used if not specified
|
||||
* @uiDefault CheckBox.icon.disabledSelectedBackground Color optional; CheckBox.icon.disabledBackground is used if not specified
|
||||
* @uiDefault CheckBox.icon.disabledCheckmarkColor Color
|
||||
* @uiDefault CheckBox.icon.focusedBorderColor Color
|
||||
*
|
||||
* @uiDefault CheckBox.icon.focusedBorderColor Color optional
|
||||
* @uiDefault CheckBox.icon.focusedBackground Color optional
|
||||
* @uiDefault CheckBox.icon.selectedFocusedBorderColor Color optional
|
||||
* @uiDefault CheckBox.icon.selectedFocusedBackground Color optional
|
||||
* @uiDefault CheckBox.icon.focusedSelectedBorderColor Color optional; CheckBox.icon.focusedBorderColor is used if not specified
|
||||
* @uiDefault CheckBox.icon.focusedSelectedBackground Color optional; CheckBox.icon.focusedBackground is used if not specified
|
||||
* @uiDefault CheckBox.icon.focusedCheckmarkColor Color optional; CheckBox.icon.checkmarkColor is used if not specified
|
||||
*
|
||||
* @uiDefault CheckBox.icon.hoverBorderColor Color optional
|
||||
* @uiDefault CheckBox.icon.hoverBackground Color optional
|
||||
* @uiDefault CheckBox.icon.selectedHoverBackground Color optional
|
||||
* @uiDefault CheckBox.icon.hoverSelectedBorderColor Color optional; CheckBox.icon.hoverBorderColor is used if not specified
|
||||
* @uiDefault CheckBox.icon.hoverSelectedBackground Color optional; CheckBox.icon.hoverBackground is used if not specified
|
||||
* @uiDefault CheckBox.icon.hoverCheckmarkColor Color optional; CheckBox.icon.checkmarkColor is used if not specified
|
||||
*
|
||||
* @uiDefault CheckBox.icon.pressedBorderColor Color optional
|
||||
* @uiDefault CheckBox.icon.pressedBackground Color optional
|
||||
* @uiDefault CheckBox.icon.selectedPressedBackground Color optional
|
||||
* @uiDefault CheckBox.arc int
|
||||
* @uiDefault CheckBox.icon.pressedSelectedBorderColor Color optional; CheckBox.icon.pressedBorderColor is used if not specified
|
||||
* @uiDefault CheckBox.icon.pressedSelectedBackground Color optional; CheckBox.icon.pressedBackground is used if not specified
|
||||
* @uiDefault CheckBox.icon.pressedCheckmarkColor Color optional; CheckBox.icon.checkmarkColor is used if not specified
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
public class FlatCheckBoxIcon
|
||||
extends FlatAbstractIcon
|
||||
{
|
||||
protected final String style = UIManager.getString( "CheckBox.icon.style" );
|
||||
public final int focusWidth = getUIInt( "CheckBox.icon.focusWidth",
|
||||
UIManager.getInt( "Component.focusWidth" ), style );
|
||||
protected final Color focusColor = FlatUIUtils.getUIColor( "CheckBox.icon.focusColor",
|
||||
UIManager.getColor( "Component.focusColor" ) );
|
||||
protected final int arc = FlatUIUtils.getUIInt( "CheckBox.arc", 2 );
|
||||
protected final String style = UIManager.getString( getPropertyPrefix() + "icon.style" );
|
||||
@Styleable protected float focusWidth = getUIFloat( "CheckBox.icon.focusWidth", UIManager.getInt( "Component.focusWidth" ), style );
|
||||
@Styleable protected Color focusColor = FlatUIUtils.getUIColor( "CheckBox.icon.focusColor", UIManager.getColor( "Component.focusColor" ) );
|
||||
/** @since 2 */ @Styleable protected float borderWidth = getUIFloat( "CheckBox.icon.borderWidth", FlatUIUtils.getUIFloat( "Component.borderWidth", 1 ), style );
|
||||
/** @since 2 */ @Styleable protected float selectedBorderWidth = getUIFloat( "CheckBox.icon.selectedBorderWidth", Float.MIN_VALUE, style );
|
||||
/** @since 2 */ @Styleable protected float disabledSelectedBorderWidth = getUIFloat( "CheckBox.icon.disabledSelectedBorderWidth", Float.MIN_VALUE, style );
|
||||
@Styleable protected int arc = FlatUIUtils.getUIInt( "CheckBox.arc", 2 );
|
||||
|
||||
// enabled
|
||||
protected final Color borderColor = getUIColor( "CheckBox.icon.borderColor", style );
|
||||
protected final Color background = getUIColor( "CheckBox.icon.background", style );
|
||||
protected final Color selectedBorderColor = getUIColor( "CheckBox.icon.selectedBorderColor", style );
|
||||
protected final Color selectedBackground = getUIColor( "CheckBox.icon.selectedBackground", style );
|
||||
protected final Color checkmarkColor = getUIColor( "CheckBox.icon.checkmarkColor", style );
|
||||
@Styleable protected Color borderColor = getUIColor( "CheckBox.icon.borderColor", style );
|
||||
@Styleable protected Color background = getUIColor( "CheckBox.icon.background", style );
|
||||
@Styleable protected Color selectedBorderColor = getUIColor( "CheckBox.icon.selectedBorderColor", style );
|
||||
@Styleable protected Color selectedBackground = getUIColor( "CheckBox.icon.selectedBackground", style );
|
||||
@Styleable protected Color checkmarkColor = getUIColor( "CheckBox.icon.checkmarkColor", style );
|
||||
|
||||
// disabled
|
||||
protected final Color disabledBorderColor = getUIColor( "CheckBox.icon.disabledBorderColor", style );
|
||||
protected final Color disabledBackground = getUIColor( "CheckBox.icon.disabledBackground", style );
|
||||
protected final Color disabledCheckmarkColor = getUIColor( "CheckBox.icon.disabledCheckmarkColor", style );
|
||||
@Styleable protected Color disabledBorderColor = getUIColor( "CheckBox.icon.disabledBorderColor", style );
|
||||
@Styleable protected Color disabledBackground = getUIColor( "CheckBox.icon.disabledBackground", style );
|
||||
/** @since 2 */ @Styleable protected Color disabledSelectedBorderColor = getUIColor( "CheckBox.icon.disabledSelectedBorderColor", style );
|
||||
/** @since 2 */ @Styleable protected Color disabledSelectedBackground = getUIColor( "CheckBox.icon.disabledSelectedBackground", style );
|
||||
@Styleable protected Color disabledCheckmarkColor = getUIColor( "CheckBox.icon.disabledCheckmarkColor", style );
|
||||
|
||||
// focused
|
||||
protected final Color focusedBorderColor = getUIColor( "CheckBox.icon.focusedBorderColor", style );
|
||||
protected final Color focusedBackground = getUIColor( "CheckBox.icon.focusedBackground", style );
|
||||
protected final Color selectedFocusedBorderColor = getUIColor( "CheckBox.icon.selectedFocusedBorderColor", style );
|
||||
protected final Color selectedFocusedBackground = getUIColor( "CheckBox.icon.selectedFocusedBackground", style );
|
||||
protected final Color selectedFocusedCheckmarkColor = getUIColor( "CheckBox.icon.selectedFocusedCheckmarkColor", style );
|
||||
@Styleable protected Color focusedBorderColor = getUIColor( "CheckBox.icon.focusedBorderColor", style );
|
||||
@Styleable protected Color focusedBackground = getUIColor( "CheckBox.icon.focusedBackground", style );
|
||||
/** @since 2 */ @Styleable protected Color focusedSelectedBorderColor = getUIColor( "CheckBox.icon.focusedSelectedBorderColor", style );
|
||||
/** @since 2 */ @Styleable protected Color focusedSelectedBackground = getUIColor( "CheckBox.icon.focusedSelectedBackground", style );
|
||||
/** @since 2 */ @Styleable protected Color focusedCheckmarkColor = getUIColor( "CheckBox.icon.focusedCheckmarkColor", style );
|
||||
|
||||
// hover
|
||||
protected final Color hoverBorderColor = getUIColor( "CheckBox.icon.hoverBorderColor", style );
|
||||
protected final Color hoverBackground = getUIColor( "CheckBox.icon.hoverBackground", style );
|
||||
protected final Color selectedHoverBackground = getUIColor( "CheckBox.icon.selectedHoverBackground", style );
|
||||
@Styleable protected Color hoverBorderColor = getUIColor( "CheckBox.icon.hoverBorderColor", style );
|
||||
@Styleable protected Color hoverBackground = getUIColor( "CheckBox.icon.hoverBackground", style );
|
||||
/** @since 2 */ @Styleable protected Color hoverSelectedBorderColor = getUIColor( "CheckBox.icon.hoverSelectedBorderColor", style );
|
||||
/** @since 2 */ @Styleable protected Color hoverSelectedBackground = getUIColor( "CheckBox.icon.hoverSelectedBackground", style );
|
||||
/** @since 2 */ @Styleable protected Color hoverCheckmarkColor = getUIColor( "CheckBox.icon.hoverCheckmarkColor", style );
|
||||
|
||||
// pressed
|
||||
protected final Color pressedBackground = getUIColor( "CheckBox.icon.pressedBackground", style );
|
||||
protected final Color selectedPressedBackground = getUIColor( "CheckBox.icon.selectedPressedBackground", style );
|
||||
/** @since 2 */ @Styleable protected Color pressedBorderColor = getUIColor( "CheckBox.icon.pressedBorderColor", style );
|
||||
@Styleable protected Color pressedBackground = getUIColor( "CheckBox.icon.pressedBackground", style );
|
||||
/** @since 2 */ @Styleable protected Color pressedSelectedBorderColor = getUIColor( "CheckBox.icon.pressedSelectedBorderColor", style );
|
||||
/** @since 2 */ @Styleable protected Color pressedSelectedBackground = getUIColor( "CheckBox.icon.pressedSelectedBackground", style );
|
||||
/** @since 2 */ @Styleable protected Color pressedCheckmarkColor = getUIColor( "CheckBox.icon.pressedCheckmarkColor", style );
|
||||
|
||||
protected String getPropertyPrefix() {
|
||||
return "CheckBox.";
|
||||
}
|
||||
|
||||
protected static Color getUIColor( String key, String style ) {
|
||||
if( style != null ) {
|
||||
@@ -109,13 +142,14 @@ public class FlatCheckBoxIcon
|
||||
return UIManager.getColor( key );
|
||||
}
|
||||
|
||||
protected static int getUIInt( String key, int defaultValue, String style ) {
|
||||
/** @since 2 */
|
||||
protected static float getUIFloat( String key, float defaultValue, String style ) {
|
||||
if( style != null ) {
|
||||
Object value = UIManager.get( styleKey( key, style ) );
|
||||
if( value instanceof Integer )
|
||||
return (Integer) value;
|
||||
float value = FlatUIUtils.getUIFloat( styleKey( key, style ), Float.MIN_VALUE );
|
||||
if( value != Float.MIN_VALUE )
|
||||
return value;
|
||||
}
|
||||
return FlatUIUtils.getUIInt( key, defaultValue );
|
||||
return FlatUIUtils.getUIFloat( key, defaultValue );
|
||||
}
|
||||
|
||||
private static String styleKey( String key, String style ) {
|
||||
@@ -128,79 +162,135 @@ public class FlatCheckBoxIcon
|
||||
super( ICON_SIZE, ICON_SIZE, null );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
public Object applyStyleProperty( String key, Object value ) {
|
||||
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
public Map<String, Class<?>> getStyleableInfos() {
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g2 ) {
|
||||
boolean indeterminate = c instanceof JComponent && clientPropertyEquals( (JComponent) c, SELECTED_STATE, SELECTED_STATE_INDETERMINATE );
|
||||
boolean selected = indeterminate || (c instanceof AbstractButton && ((AbstractButton)c).isSelected());
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
boolean indeterminate = isIndeterminate( c );
|
||||
boolean selected = indeterminate || isSelected( c );
|
||||
boolean isFocused = FlatUIUtils.isPermanentFocusOwner( c );
|
||||
float bw = selected
|
||||
? (disabledSelectedBorderWidth != Float.MIN_VALUE && !c.isEnabled()
|
||||
? disabledSelectedBorderWidth
|
||||
: (selectedBorderWidth != Float.MIN_VALUE ? selectedBorderWidth : borderWidth))
|
||||
: borderWidth;
|
||||
|
||||
// paint focused border
|
||||
if( isFocused && focusWidth > 0 && FlatButtonUI.isFocusPainted( c ) ) {
|
||||
g2.setColor( focusColor );
|
||||
paintFocusBorder( g2 );
|
||||
g.setColor( getFocusColor( c ) );
|
||||
paintFocusBorder( c, g );
|
||||
}
|
||||
|
||||
// paint border
|
||||
g2.setColor( FlatButtonUI.buttonStateColor( c,
|
||||
selected ? selectedBorderColor : borderColor,
|
||||
disabledBorderColor,
|
||||
selected && selectedFocusedBorderColor != null ? selectedFocusedBorderColor : focusedBorderColor,
|
||||
hoverBorderColor,
|
||||
null ) );
|
||||
paintBorder( g2 );
|
||||
g.setColor( getBorderColor( c, selected ) );
|
||||
paintBorder( c, g, bw );
|
||||
|
||||
// paint background
|
||||
g2.setColor( FlatUIUtils.deriveColor( FlatButtonUI.buttonStateColor( c,
|
||||
selected ? selectedBackground : background,
|
||||
disabledBackground,
|
||||
(selected && selectedFocusedBackground != null) ? selectedFocusedBackground : focusedBackground,
|
||||
(selected && selectedHoverBackground != null) ? selectedHoverBackground : hoverBackground,
|
||||
(selected && selectedPressedBackground != null) ? selectedPressedBackground : pressedBackground ),
|
||||
background ) );
|
||||
paintBackground( g2 );
|
||||
Color bg = FlatUIUtils.deriveColor( getBackground( c, selected ),
|
||||
selected ? selectedBackground : background );
|
||||
if( bg.getAlpha() < 255 ) {
|
||||
// fill background with default color before filling with non-opaque background
|
||||
g.setColor( selected ? selectedBackground : background );
|
||||
paintBackground( c, g, bw );
|
||||
}
|
||||
g.setColor( bg );
|
||||
paintBackground( c, g, bw );
|
||||
|
||||
// paint checkmark
|
||||
if( selected || indeterminate ) {
|
||||
g2.setColor( c.isEnabled()
|
||||
? ((selected && isFocused && selectedFocusedCheckmarkColor != null)
|
||||
? selectedFocusedCheckmarkColor
|
||||
: checkmarkColor)
|
||||
: disabledCheckmarkColor );
|
||||
if( selected ) {
|
||||
g.setColor( getCheckmarkColor( c ) );
|
||||
if( indeterminate )
|
||||
paintIndeterminate( g2 );
|
||||
paintIndeterminate( c, g );
|
||||
else
|
||||
paintCheckmark( g2 );
|
||||
paintCheckmark( c, g );
|
||||
}
|
||||
}
|
||||
|
||||
protected void paintFocusBorder( Graphics2D g2 ) {
|
||||
// the outline focus border is painted outside of the icon
|
||||
int wh = ICON_SIZE - 1 + (focusWidth * 2);
|
||||
int arcwh = arc + (focusWidth * 2);
|
||||
g2.fillRoundRect( -focusWidth + 1, -focusWidth, wh, wh, arcwh, arcwh );
|
||||
protected void paintFocusBorder( Component c, Graphics2D g ) {
|
||||
// the outer focus border is painted outside of the icon
|
||||
float wh = ICON_SIZE - 1 + (focusWidth * 2);
|
||||
float arcwh = arc + (focusWidth * 2);
|
||||
g.fill( new RoundRectangle2D.Float( -focusWidth + 1, -focusWidth, wh, wh, arcwh, arcwh ) );
|
||||
}
|
||||
|
||||
protected void paintBorder( Graphics2D g2 ) {
|
||||
protected void paintBorder( Component c, Graphics2D g, float borderWidth ) {
|
||||
if( borderWidth == 0 )
|
||||
return;
|
||||
|
||||
int arcwh = arc;
|
||||
g2.fillRoundRect( 1, 0, 14, 14, arcwh, arcwh );
|
||||
g.fillRoundRect( 1, 0, 14, 14, arcwh, arcwh );
|
||||
}
|
||||
|
||||
protected void paintBackground( Graphics2D g2 ) {
|
||||
int arcwh = arc - 1;
|
||||
g2.fillRoundRect( 2, 1, 12, 12, arcwh, arcwh );
|
||||
protected void paintBackground( Component c, Graphics2D g, float borderWidth ) {
|
||||
float xy = borderWidth;
|
||||
float wh = 14 - (borderWidth * 2);
|
||||
float arcwh = arc - borderWidth;
|
||||
g.fill( new RoundRectangle2D.Float( 1 + xy, xy, wh, wh, arcwh, arcwh ) );
|
||||
}
|
||||
|
||||
protected void paintCheckmark( Graphics2D g2 ) {
|
||||
protected void paintCheckmark( Component c, Graphics2D g ) {
|
||||
Path2D.Float path = new Path2D.Float();
|
||||
path.moveTo( 4.5f, 7.5f );
|
||||
path.lineTo( 6.6f, 10f );
|
||||
path.lineTo( 11.25f, 3.5f );
|
||||
|
||||
g2.setStroke( new BasicStroke( 1.9f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
|
||||
g2.draw( path );
|
||||
g.setStroke( new BasicStroke( 1.9f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
|
||||
g.draw( path );
|
||||
}
|
||||
|
||||
protected void paintIndeterminate( Graphics2D g2 ) {
|
||||
g2.fill( new RoundRectangle2D.Float( 3.75f, 5.75f, 8.5f, 2.5f, 2f, 2f ) );
|
||||
protected void paintIndeterminate( Component c, Graphics2D g ) {
|
||||
g.fill( new RoundRectangle2D.Float( 3.75f, 5.75f, 8.5f, 2.5f, 2f, 2f ) );
|
||||
}
|
||||
|
||||
protected boolean isIndeterminate( Component c ) {
|
||||
return c instanceof JComponent && clientPropertyEquals( (JComponent) c, SELECTED_STATE, SELECTED_STATE_INDETERMINATE );
|
||||
}
|
||||
|
||||
protected boolean isSelected( Component c ) {
|
||||
return c instanceof AbstractButton && ((AbstractButton)c).isSelected();
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
public float getFocusWidth() {
|
||||
return focusWidth;
|
||||
}
|
||||
|
||||
protected Color getFocusColor( Component c ) {
|
||||
return focusColor;
|
||||
}
|
||||
|
||||
protected Color getBorderColor( Component c, boolean selected ) {
|
||||
return FlatButtonUI.buttonStateColor( c,
|
||||
selected ? selectedBorderColor : borderColor,
|
||||
(selected && disabledSelectedBorderColor != null) ? disabledSelectedBorderColor : disabledBorderColor,
|
||||
(selected && focusedSelectedBorderColor != null) ? focusedSelectedBorderColor : focusedBorderColor,
|
||||
(selected && hoverSelectedBorderColor != null) ? hoverSelectedBorderColor : hoverBorderColor,
|
||||
(selected && pressedSelectedBorderColor != null) ? pressedSelectedBorderColor : pressedBorderColor );
|
||||
}
|
||||
|
||||
protected Color getBackground( Component c, boolean selected ) {
|
||||
return FlatButtonUI.buttonStateColor( c,
|
||||
selected ? selectedBackground : background,
|
||||
(selected && disabledSelectedBackground != null) ? disabledSelectedBackground : disabledBackground,
|
||||
(selected && focusedSelectedBackground != null) ? focusedSelectedBackground : focusedBackground,
|
||||
(selected && hoverSelectedBackground != null) ? hoverSelectedBackground : hoverBackground,
|
||||
(selected && pressedSelectedBackground != null) ? pressedSelectedBackground : pressedBackground );
|
||||
}
|
||||
|
||||
protected Color getCheckmarkColor( Component c ) {
|
||||
return FlatButtonUI.buttonStateColor( c,
|
||||
checkmarkColor,
|
||||
disabledCheckmarkColor,
|
||||
focusedCheckmarkColor,
|
||||
hoverCheckmarkColor,
|
||||
pressedCheckmarkColor );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,15 +21,18 @@ import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.util.Map;
|
||||
import javax.swing.AbstractButton;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
|
||||
/**
|
||||
* Icon for {@link javax.swing.JCheckBoxMenuItem}.
|
||||
*
|
||||
* @uiDefault MenuItemCheckBox.icon.checkmarkColor Color
|
||||
* @uiDefault MenuItemCheckBox.icon.disabledCheckmarkColor Color
|
||||
* @uiDefault CheckBoxMenuItem.icon.checkmarkColor Color
|
||||
* @uiDefault CheckBoxMenuItem.icon.disabledCheckmarkColor Color
|
||||
* @uiDefault MenuItem.selectionForeground Color
|
||||
* @uiDefault MenuItem.selectionType String
|
||||
*
|
||||
@@ -38,14 +41,24 @@ import javax.swing.UIManager;
|
||||
public class FlatCheckBoxMenuItemIcon
|
||||
extends FlatAbstractIcon
|
||||
{
|
||||
protected final Color checkmarkColor = UIManager.getColor( "MenuItemCheckBox.icon.checkmarkColor" );
|
||||
protected final Color disabledCheckmarkColor = UIManager.getColor( "MenuItemCheckBox.icon.disabledCheckmarkColor" );
|
||||
protected final Color selectionForeground = UIManager.getColor( "MenuItem.selectionForeground" );
|
||||
@Styleable protected Color checkmarkColor = UIManager.getColor( "CheckBoxMenuItem.icon.checkmarkColor" );
|
||||
@Styleable protected Color disabledCheckmarkColor = UIManager.getColor( "CheckBoxMenuItem.icon.disabledCheckmarkColor" );
|
||||
@Styleable protected Color selectionForeground = UIManager.getColor( "MenuItem.selectionForeground" );
|
||||
|
||||
public FlatCheckBoxMenuItemIcon() {
|
||||
super( 15, 15, null );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
public Object applyStyleProperty( String key, Object value ) {
|
||||
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
public Map<String, Class<?>> getStyleableInfos() {
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g2 ) {
|
||||
boolean selected = (c instanceof AbstractButton) && ((AbstractButton)c).isSelected();
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2021 FormDev Software GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.formdev.flatlaf.icons;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Line2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.util.Map;
|
||||
import javax.swing.AbstractButton;
|
||||
import javax.swing.ButtonModel;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
|
||||
/**
|
||||
* "clear" icon for search fields.
|
||||
*
|
||||
* @uiDefault SearchField.clearIconColor Color
|
||||
* @uiDefault SearchField.clearIconHoverColor Color
|
||||
* @uiDefault SearchField.clearIconPressedColor Color
|
||||
*
|
||||
* @author Karl Tauber
|
||||
* @since 1.5
|
||||
*/
|
||||
public class FlatClearIcon
|
||||
extends FlatAbstractIcon
|
||||
{
|
||||
@Styleable protected Color clearIconColor = UIManager.getColor( "SearchField.clearIconColor" );
|
||||
@Styleable protected Color clearIconHoverColor = UIManager.getColor( "SearchField.clearIconHoverColor" );
|
||||
@Styleable protected Color clearIconPressedColor = UIManager.getColor( "SearchField.clearIconPressedColor" );
|
||||
|
||||
public FlatClearIcon() {
|
||||
super( 16, 16, null );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
public Object applyStyleProperty( String key, Object value ) {
|
||||
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
public Map<String, Class<?>> getStyleableInfos() {
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
if( c instanceof AbstractButton ) {
|
||||
ButtonModel model = ((AbstractButton)c).getModel();
|
||||
if( model.isPressed() || model.isRollover() ) {
|
||||
/*
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<path fill="#7F8B91" fill-opacity=".5" fill-rule="evenodd" d="M8,1.75 C11.4517797,1.75 14.25,4.54822031 14.25,8 C14.25,11.4517797 11.4517797,14.25 8,14.25 C4.54822031,14.25 1.75,11.4517797 1.75,8 C1.75,4.54822031 4.54822031,1.75 8,1.75 Z M10.5,4.5 L8,7 L5.5,4.5 L4.5,5.5 L7,8 L4.5,10.5 L5.5,11.5 L8,9 L10.5,11.5 L11.5,10.5 L9,8 L11.5,5.5 L10.5,4.5 Z"/>
|
||||
</svg>
|
||||
*/
|
||||
|
||||
// paint filled circle with cross
|
||||
g.setColor( model.isPressed() ? clearIconPressedColor : clearIconHoverColor );
|
||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||
path.append( new Ellipse2D.Float( 1.75f, 1.75f, 12.5f, 12.5f ), false );
|
||||
path.append( FlatUIUtils.createPath( 4.5,5.5, 5.5,4.5, 8,7, 10.5,4.5, 11.5,5.5, 9,8, 11.5,10.5, 10.5,11.5, 8,9, 5.5,11.5, 4.5,10.5, 7,8 ), false );
|
||||
g.fill( path );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<path fill="none" stroke="#7F8B91" stroke-linecap="square" stroke-opacity=".5" d="M5,5 L11,11 M5,11 L11,5"/>
|
||||
</svg>
|
||||
*/
|
||||
|
||||
// paint cross
|
||||
g.setColor( clearIconColor );
|
||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||
path.append( new Line2D.Float( 5,5, 11,11 ), false );
|
||||
path.append( new Line2D.Float( 5,11, 11,5 ), false );
|
||||
g.draw( path );
|
||||
}
|
||||
}
|
||||
@@ -17,34 +17,28 @@
|
||||
package com.formdev.flatlaf.icons;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
|
||||
/**
|
||||
* "descendingSort" icon for {@link javax.swing.table.JTableHeader}.
|
||||
*
|
||||
* @uiDefault Component.arrowType String triangle (default) or chevron
|
||||
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||
* @uiDefault Table.sortIconColor Color
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
public class FlatDescendingSortIcon
|
||||
extends FlatAbstractIcon
|
||||
extends FlatAscendingSortIcon
|
||||
{
|
||||
protected final boolean chevron = "chevron".equals( UIManager.getString( "Component.arrowType" ) );
|
||||
protected final Color sortIconColor = UIManager.getColor( "Table.sortIconColor" );
|
||||
|
||||
public FlatDescendingSortIcon() {
|
||||
super( 10, 5, null );
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
g.setColor( sortIconColor );
|
||||
protected void paintArrow( Component c, Graphics2D g, boolean chevron ) {
|
||||
if( chevron ) {
|
||||
// chevron arrow
|
||||
Path2D path = FlatUIUtils.createPath( false, 1,0, 5,4, 9,0 );
|
||||
|
||||
@@ -22,8 +22,11 @@ import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.util.Map;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.ui.FlatButtonUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
|
||||
/**
|
||||
@@ -31,6 +34,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
*
|
||||
* @uiDefault Component.focusWidth int
|
||||
* @uiDefault Component.focusColor Color
|
||||
* @uiDefault HelpButton.innerFocusWidth int or float optional; defaults to Component.innerFocusWidth
|
||||
* @uiDefault HelpButton.borderWidth int optional; default is 1
|
||||
* @uiDefault HelpButton.borderColor Color
|
||||
* @uiDefault HelpButton.disabledBorderColor Color
|
||||
* @uiDefault HelpButton.focusedBorderColor Color
|
||||
@@ -48,27 +53,37 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
public class FlatHelpButtonIcon
|
||||
extends FlatAbstractIcon
|
||||
{
|
||||
protected final int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
||||
protected final Color focusColor = UIManager.getColor( "Component.focusColor" );
|
||||
@Styleable protected int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
||||
@Styleable protected Color focusColor = UIManager.getColor( "Component.focusColor" );
|
||||
@Styleable protected float innerFocusWidth = FlatUIUtils.getUIFloat( "HelpButton.innerFocusWidth", FlatUIUtils.getUIFloat( "Component.innerFocusWidth", 0 ) );
|
||||
@Styleable protected int borderWidth = FlatUIUtils.getUIInt( "HelpButton.borderWidth", 1 );
|
||||
|
||||
protected final Color borderColor = UIManager.getColor( "HelpButton.borderColor" );
|
||||
protected final Color disabledBorderColor = UIManager.getColor( "HelpButton.disabledBorderColor" );
|
||||
protected final Color focusedBorderColor = UIManager.getColor( "HelpButton.focusedBorderColor" );
|
||||
protected final Color hoverBorderColor = UIManager.getColor( "HelpButton.hoverBorderColor" );
|
||||
protected final Color background = UIManager.getColor( "HelpButton.background" );
|
||||
protected final Color disabledBackground = UIManager.getColor( "HelpButton.disabledBackground" );
|
||||
protected final Color focusedBackground = UIManager.getColor( "HelpButton.focusedBackground" );
|
||||
protected final Color hoverBackground = UIManager.getColor( "HelpButton.hoverBackground" );
|
||||
protected final Color pressedBackground = UIManager.getColor( "HelpButton.pressedBackground" );
|
||||
protected final Color questionMarkColor = UIManager.getColor( "HelpButton.questionMarkColor" );
|
||||
protected final Color disabledQuestionMarkColor = UIManager.getColor( "HelpButton.disabledQuestionMarkColor" );
|
||||
|
||||
protected final int iconSize = 22 + (focusWidth * 2);
|
||||
@Styleable protected Color borderColor = UIManager.getColor( "HelpButton.borderColor" );
|
||||
@Styleable protected Color disabledBorderColor = UIManager.getColor( "HelpButton.disabledBorderColor" );
|
||||
@Styleable protected Color focusedBorderColor = UIManager.getColor( "HelpButton.focusedBorderColor" );
|
||||
@Styleable protected Color hoverBorderColor = UIManager.getColor( "HelpButton.hoverBorderColor" );
|
||||
@Styleable protected Color background = UIManager.getColor( "HelpButton.background" );
|
||||
@Styleable protected Color disabledBackground = UIManager.getColor( "HelpButton.disabledBackground" );
|
||||
@Styleable protected Color focusedBackground = UIManager.getColor( "HelpButton.focusedBackground" );
|
||||
@Styleable protected Color hoverBackground = UIManager.getColor( "HelpButton.hoverBackground" );
|
||||
@Styleable protected Color pressedBackground = UIManager.getColor( "HelpButton.pressedBackground" );
|
||||
@Styleable protected Color questionMarkColor = UIManager.getColor( "HelpButton.questionMarkColor" );
|
||||
@Styleable protected Color disabledQuestionMarkColor = UIManager.getColor( "HelpButton.disabledQuestionMarkColor" );
|
||||
|
||||
public FlatHelpButtonIcon() {
|
||||
super( 0, 0, null );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
public Object applyStyleProperty( String key, Object value ) {
|
||||
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
public Map<String, Class<?>> getStyleableInfos() {
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g2 ) {
|
||||
/*
|
||||
@@ -84,12 +99,18 @@ public class FlatHelpButtonIcon
|
||||
boolean enabled = c.isEnabled();
|
||||
boolean focused = FlatUIUtils.isPermanentFocusOwner( c );
|
||||
|
||||
// paint focused border
|
||||
float xy = 0.5f;
|
||||
float wh = iconSize() - 1;
|
||||
|
||||
// paint outer focus border
|
||||
if( focused && FlatButtonUI.isFocusPainted( c ) ) {
|
||||
g2.setColor( focusColor );
|
||||
g2.fill( new Ellipse2D.Float( 0.5f, 0.5f, iconSize - 1, iconSize - 1 ) );
|
||||
g2.fill( new Ellipse2D.Float( xy, xy, wh, wh ) );
|
||||
}
|
||||
|
||||
xy += focusWidth;
|
||||
wh -= (focusWidth * 2);
|
||||
|
||||
// paint border
|
||||
g2.setColor( FlatButtonUI.buttonStateColor( c,
|
||||
borderColor,
|
||||
@@ -97,7 +118,19 @@ public class FlatHelpButtonIcon
|
||||
focusedBorderColor,
|
||||
hoverBorderColor,
|
||||
null ) );
|
||||
g2.fill( new Ellipse2D.Float( focusWidth + 0.5f, focusWidth + 0.5f, 21, 21 ) );
|
||||
g2.fill( new Ellipse2D.Float( xy, xy, wh, wh ) );
|
||||
|
||||
xy += borderWidth;
|
||||
wh -= (borderWidth * 2);
|
||||
|
||||
// paint inner focus border
|
||||
if( innerFocusWidth > 0 && focused && FlatButtonUI.isFocusPainted( c ) ) {
|
||||
g2.setColor( focusColor );
|
||||
g2.fill( new Ellipse2D.Float( xy, xy, wh, wh ) );
|
||||
|
||||
xy += innerFocusWidth;
|
||||
wh -= (innerFocusWidth * 2);
|
||||
}
|
||||
|
||||
// paint background
|
||||
g2.setColor( FlatUIUtils.deriveColor( FlatButtonUI.buttonStateColor( c,
|
||||
@@ -106,7 +139,7 @@ public class FlatHelpButtonIcon
|
||||
focusedBackground,
|
||||
hoverBackground,
|
||||
pressedBackground ), background ) );
|
||||
g2.fill( new Ellipse2D.Float( focusWidth + 1.5f, focusWidth + 1.5f, 19, 19 ) );
|
||||
g2.fill( new Ellipse2D.Float( xy, xy, wh, wh ) );
|
||||
|
||||
// paint question mark
|
||||
Path2D q = new Path2D.Float();
|
||||
@@ -129,11 +162,15 @@ public class FlatHelpButtonIcon
|
||||
|
||||
@Override
|
||||
public int getIconWidth() {
|
||||
return scale( iconSize );
|
||||
return scale( iconSize() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIconHeight() {
|
||||
return scale( iconSize );
|
||||
return scale( iconSize() );
|
||||
}
|
||||
|
||||
private int iconSize() {
|
||||
return 22 + (focusWidth * 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,14 +21,17 @@ import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.util.Map;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
|
||||
/**
|
||||
* "arrow" icon for {@link javax.swing.JMenu}.
|
||||
*
|
||||
* @uiDefault Component.arrowType String triangle (default) or chevron
|
||||
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||
* @uiDefault Menu.icon.arrowColor Color
|
||||
* @uiDefault Menu.icon.disabledArrowColor Color
|
||||
* @uiDefault Menu.selectionForeground Color
|
||||
@@ -39,22 +42,32 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
public class FlatMenuArrowIcon
|
||||
extends FlatAbstractIcon
|
||||
{
|
||||
protected final boolean chevron = "chevron".equals( UIManager.getString( "Component.arrowType" ) );
|
||||
protected final Color arrowColor = UIManager.getColor( "Menu.icon.arrowColor" );
|
||||
protected final Color disabledArrowColor = UIManager.getColor( "Menu.icon.disabledArrowColor" );
|
||||
protected final Color selectionForeground = UIManager.getColor( "Menu.selectionForeground" );
|
||||
@Styleable protected String arrowType = UIManager.getString( "Component.arrowType" );
|
||||
@Styleable protected Color arrowColor = UIManager.getColor( "Menu.icon.arrowColor" );
|
||||
@Styleable protected Color disabledArrowColor = UIManager.getColor( "Menu.icon.disabledArrowColor" );
|
||||
@Styleable protected Color selectionForeground = UIManager.getColor( "Menu.selectionForeground" );
|
||||
|
||||
public FlatMenuArrowIcon() {
|
||||
super( 6, 10, null );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
public Object applyStyleProperty( String key, Object value ) {
|
||||
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
public Map<String, Class<?>> getStyleableInfos() {
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
if( !c.getComponentOrientation().isLeftToRight() )
|
||||
g.rotate( Math.toRadians( 180 ), width / 2., height / 2. );
|
||||
|
||||
g.setColor( getArrowColor( c ) );
|
||||
if( chevron ) {
|
||||
if( FlatUIUtils.isChevron( arrowType ) ) {
|
||||
// chevron arrow
|
||||
Path2D path = FlatUIUtils.createPath( false, 1,1, 5,5, 1,9 );
|
||||
g.setStroke( new BasicStroke( 1f ) );
|
||||
|
||||
@@ -16,45 +16,59 @@
|
||||
|
||||
package com.formdev.flatlaf.icons;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
|
||||
/**
|
||||
* Icon for {@link javax.swing.JRadioButton}.
|
||||
*
|
||||
* Note: If Component.focusWidth is greater than zero, then the outline focus border
|
||||
* <p>
|
||||
* <strong>Note</strong>:
|
||||
* If Component.focusWidth is greater than zero, then the outer focus border
|
||||
* is painted outside of the icon bounds. Make sure that the radiobutton
|
||||
* has margins, which are equal or greater than focusWidth.
|
||||
*
|
||||
* @uiDefault RadioButton.icon.centerDiameter int
|
||||
* @uiDefault RadioButton.icon.style String optional; "outlined"/null (default) or "filled"
|
||||
* @uiDefault RadioButton.icon.centerDiameter int or float
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
public class FlatRadioButtonIcon
|
||||
extends FlatCheckBoxIcon
|
||||
{
|
||||
protected final int centerDiameter = getUIInt( "RadioButton.icon.centerDiameter", 8, style );
|
||||
@Styleable protected float centerDiameter = getUIFloat( "RadioButton.icon.centerDiameter", 8, style );
|
||||
|
||||
@Override
|
||||
protected void paintFocusBorder( Graphics2D g2 ) {
|
||||
// the outline focus border is painted outside of the icon
|
||||
int wh = ICON_SIZE + (focusWidth * 2);
|
||||
g2.fillOval( -focusWidth, -focusWidth, wh, wh );
|
||||
protected String getPropertyPrefix() {
|
||||
return "RadioButton.";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintBorder( Graphics2D g2 ) {
|
||||
g2.fillOval( 0, 0, 15, 15 );
|
||||
protected void paintFocusBorder( Component c, Graphics2D g ) {
|
||||
// the outer focus border is painted outside of the icon
|
||||
float wh = ICON_SIZE + (focusWidth * 2);
|
||||
g.fill( new Ellipse2D.Float( -focusWidth, -focusWidth, wh, wh ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintBackground( Graphics2D g2 ) {
|
||||
g2.fillOval( 1, 1, 13, 13 );
|
||||
protected void paintBorder( Component c, Graphics2D g, float borderWidth ) {
|
||||
if( borderWidth == 0 )
|
||||
return;
|
||||
|
||||
g.fillOval( 0, 0, 15, 15 );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintCheckmark( Graphics2D g2 ) {
|
||||
protected void paintBackground( Component c, Graphics2D g, float borderWidth ) {
|
||||
float xy = borderWidth;
|
||||
float wh = 15 - (borderWidth * 2);
|
||||
g.fill( new Ellipse2D.Float( xy, xy, wh, wh ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintCheckmark( Component c, Graphics2D g ) {
|
||||
float xy = (ICON_SIZE - centerDiameter) / 2f;
|
||||
g2.fill( new Ellipse2D.Float( xy, xy, centerDiameter, centerDiameter ) );
|
||||
g.fill( new Ellipse2D.Float( xy, xy, centerDiameter, centerDiameter ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2021 FormDev Software GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.formdev.flatlaf.icons;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Area;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.util.Map;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.ui.FlatButtonUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
|
||||
/**
|
||||
* "search" icon for search fields.
|
||||
*
|
||||
* @uiDefault SearchField.searchIconColor Color
|
||||
* @uiDefault SearchField.searchIconHoverColor Color
|
||||
* @uiDefault SearchField.searchIconPressedColor Color
|
||||
*
|
||||
* @author Karl Tauber
|
||||
* @since 1.5
|
||||
*/
|
||||
public class FlatSearchIcon
|
||||
extends FlatAbstractIcon
|
||||
{
|
||||
@Styleable protected Color searchIconColor = UIManager.getColor( "SearchField.searchIconColor" );
|
||||
@Styleable protected Color searchIconHoverColor = UIManager.getColor( "SearchField.searchIconHoverColor" );
|
||||
@Styleable protected Color searchIconPressedColor = UIManager.getColor( "SearchField.searchIconPressedColor" );
|
||||
|
||||
public FlatSearchIcon() {
|
||||
super( 16, 16, null );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
public Object applyStyleProperty( String key, Object value ) {
|
||||
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
public Map<String, Class<?>> getStyleableInfos() {
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
/*
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<g fill="none" fill-opacity=".9" fill-rule="evenodd">
|
||||
<polygon fill="#7F8B91" points="10.813 9.75 14 12.938 12.938 14 9.75 10.813"/>
|
||||
<path fill="#7F8B91" d="M7,2 C9.76142375,2 12,4.23857625 12,7 C12,9.76142375 9.76142375,12 7,12 C4.23857625,12 2,9.76142375 2,7 C2,4.23857625 4.23857625,2 7,2 Z M7,3 C4.790861,3 3,4.790861 3,7 C3,9.209139 4.790861,11 7,11 C9.209139,11 11,9.209139 11,7 C11,4.790861 9.209139,3 7,3 Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
*/
|
||||
|
||||
g.setColor( FlatButtonUI.buttonStateColor( c, searchIconColor, searchIconColor,
|
||||
null, searchIconHoverColor, searchIconPressedColor ) );
|
||||
|
||||
// paint magnifier
|
||||
Area area = new Area( new Ellipse2D.Float( 2, 2, 10, 10 ) );
|
||||
area.subtract( new Area( new Ellipse2D.Float( 3, 3, 8, 8 ) ) );
|
||||
area.add( new Area( FlatUIUtils.createPath( 10.813,9.75, 14,12.938, 12.938,14, 9.75,10.813 ) ) );
|
||||
g.fill( area );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2021 FormDev Software GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.formdev.flatlaf.icons;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
|
||||
/**
|
||||
* "search with history" icon for search fields.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
* @since 1.5
|
||||
*/
|
||||
public class FlatSearchWithHistoryIcon
|
||||
extends FlatSearchIcon
|
||||
{
|
||||
public FlatSearchWithHistoryIcon() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
/*
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<g fill="none" fill-opacity=".9" fill-rule="evenodd">
|
||||
<polygon fill="#7F8B91" points="8.813 9.75 12 12.938 10.938 14 7.75 10.813"/>
|
||||
<path fill="#7F8B91" d="M5,2 C7.76142375,2 10,4.23857625 10,7 C10,9.76142375 7.76142375,12 5,12 C2.23857625,12 0,9.76142375 0,7 C0,4.23857625 2.23857625,2 5,2 Z M5,3 C2.790861,3 1,4.790861 1,7 C1,9.209139 2.790861,11 5,11 C7.209139,11 9,9.209139 9,7 C9,4.790861 7.209139,3 5,3 Z"/>
|
||||
<polygon fill="#7F8B91" points="11 7 16 7 13.5 10"/>
|
||||
</g>
|
||||
</svg>
|
||||
*/
|
||||
|
||||
// paint magnifier
|
||||
g.translate( -2, 0 );
|
||||
super.paintIcon( c, g );
|
||||
g.translate( 2, 0 );
|
||||
|
||||
// paint history arrow
|
||||
g.fill( FlatUIUtils.createPath( 11,7, 16,7, 13.5,10 ) );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2020 FormDev Software GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.formdev.flatlaf.icons;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Line2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.util.Map;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.ui.FlatButtonUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
|
||||
/**
|
||||
* "close" icon for closable tabs in {@link javax.swing.JTabbedPane}.
|
||||
*
|
||||
* @uiDefault TabbedPane.closeSize Dimension
|
||||
* @uiDefault TabbedPane.closeArc int
|
||||
* @uiDefault TabbedPane.closeCrossPlainSize float
|
||||
* @uiDefault TabbedPane.closeCrossFilledSize float
|
||||
* @uiDefault TabbedPane.closeCrossLineWidth float
|
||||
* @uiDefault TabbedPane.closeBackground Color
|
||||
* @uiDefault TabbedPane.closeForeground Color
|
||||
* @uiDefault TabbedPane.closeHoverBackground Color
|
||||
* @uiDefault TabbedPane.closeHoverForeground Color
|
||||
* @uiDefault TabbedPane.closePressedBackground Color
|
||||
* @uiDefault TabbedPane.closePressedForeground Color
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
public class FlatTabbedPaneCloseIcon
|
||||
extends FlatAbstractIcon
|
||||
{
|
||||
@Styleable protected Dimension closeSize = UIManager.getDimension( "TabbedPane.closeSize" );
|
||||
@Styleable protected int closeArc = UIManager.getInt( "TabbedPane.closeArc" );
|
||||
@Styleable protected float closeCrossPlainSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossPlainSize", 7.5f );
|
||||
@Styleable protected float closeCrossFilledSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossFilledSize", closeCrossPlainSize );
|
||||
@Styleable protected float closeCrossLineWidth = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossLineWidth", 1f );
|
||||
@Styleable protected Color closeBackground = UIManager.getColor( "TabbedPane.closeBackground" );
|
||||
@Styleable protected Color closeForeground = UIManager.getColor( "TabbedPane.closeForeground" );
|
||||
@Styleable protected Color closeHoverBackground = UIManager.getColor( "TabbedPane.closeHoverBackground" );
|
||||
@Styleable protected Color closeHoverForeground = UIManager.getColor( "TabbedPane.closeHoverForeground" );
|
||||
@Styleable protected Color closePressedBackground = UIManager.getColor( "TabbedPane.closePressedBackground" );
|
||||
@Styleable protected Color closePressedForeground = UIManager.getColor( "TabbedPane.closePressedForeground" );
|
||||
|
||||
public FlatTabbedPaneCloseIcon() {
|
||||
super( 16, 16, null );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
public Object applyStyleProperty( String key, Object value ) {
|
||||
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
public Map<String, Class<?>> getStyleableInfos() {
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
// paint background
|
||||
Color bg = FlatButtonUI.buttonStateColor( c, closeBackground, null, null, closeHoverBackground, closePressedBackground );
|
||||
if( bg != null ) {
|
||||
g.setColor( FlatUIUtils.deriveColor( bg, c.getBackground() ) );
|
||||
g.fillRoundRect( (width - closeSize.width) / 2, (height - closeSize.height) / 2,
|
||||
closeSize.width, closeSize.height, closeArc, closeArc );
|
||||
}
|
||||
|
||||
// set cross color
|
||||
Color fg = FlatButtonUI.buttonStateColor( c, closeForeground, null, null, closeHoverForeground, closePressedForeground );
|
||||
g.setColor( FlatUIUtils.deriveColor( fg, c.getForeground() ) );
|
||||
|
||||
float mx = width / 2;
|
||||
float my = height / 2;
|
||||
float r = ((bg != null) ? closeCrossFilledSize : closeCrossPlainSize) / 2;
|
||||
|
||||
// paint cross
|
||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||
path.append( new Line2D.Float( mx - r, my - r, mx + r, my + r ), false );
|
||||
path.append( new Line2D.Float( mx - r, my + r, mx + r, my - r ), false );
|
||||
g.setStroke( new BasicStroke( closeCrossLineWidth ) );
|
||||
g.draw( path );
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,8 @@ public class FlatTreeClosedIcon
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
FlatTreeCollapsedIcon.setStyleColorFromTreeUI( c, g, ui -> ui.iconClosedColor );
|
||||
|
||||
/*
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<polygon fill="#6E6E6E" fill-rule="evenodd" points="1 2 6 2 8 4 15 4 15 13 1 13"/>
|
||||
|
||||
@@ -19,13 +19,18 @@ package com.formdev.flatlaf.icons;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.util.function.Function;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.TreeUI;
|
||||
import com.formdev.flatlaf.ui.FlatTreeUI;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
|
||||
/**
|
||||
* "collapsed" icon for {@link javax.swing.JTree}.
|
||||
*
|
||||
* @uiDefault Component.arrowType String triangle (default) or chevron
|
||||
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||
* @uiDefault Tree.icon.collapsedColor Color
|
||||
*
|
||||
* @author Karl Tauber
|
||||
@@ -41,13 +46,17 @@ public class FlatTreeCollapsedIcon
|
||||
|
||||
FlatTreeCollapsedIcon( Color color ) {
|
||||
super( 11, 11, color );
|
||||
chevron = "chevron".equals( UIManager.getString( "Component.arrowType" ) );
|
||||
chevron = FlatUIUtils.isChevron( UIManager.getString( "Component.arrowType" ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
setStyleColorFromTreeUI( c, g );
|
||||
rotate( c, g );
|
||||
|
||||
String arrowType = getStyleFromTreeUI( c, ui -> ui.iconArrowType );
|
||||
boolean chevron = (arrowType != null) ? FlatUIUtils.isChevron( arrowType ) : this.chevron;
|
||||
|
||||
if( chevron ) {
|
||||
// chevron arrow
|
||||
g.fill( FlatUIUtils.createPath( 3,1, 3,2.5, 6,5.5, 3,8.5, 3,10, 4.5,10, 9,5.5, 4.5,1 ) );
|
||||
@@ -57,8 +66,34 @@ public class FlatTreeCollapsedIcon
|
||||
}
|
||||
}
|
||||
|
||||
void setStyleColorFromTreeUI( Component c, Graphics2D g ) {
|
||||
setStyleColorFromTreeUI( c, g, ui -> ui.iconCollapsedColor );
|
||||
}
|
||||
|
||||
void rotate( Component c, Graphics2D g ) {
|
||||
if( !c.getComponentOrientation().isLeftToRight() )
|
||||
g.rotate( Math.toRadians( 180 ), width / 2., height / 2. );
|
||||
}
|
||||
|
||||
/**
|
||||
* Because this icons are always shared for all trees,
|
||||
* get icon specific style from FlatTreeUI.
|
||||
*/
|
||||
static <T> T getStyleFromTreeUI( Component c, Function<FlatTreeUI, T> f ) {
|
||||
JTree tree = (c instanceof JTree)
|
||||
? (JTree) c
|
||||
: (JTree) SwingUtilities.getAncestorOfClass( JTree.class, c );
|
||||
if( tree != null ) {
|
||||
TreeUI ui = tree.getUI();
|
||||
if( ui instanceof FlatTreeUI )
|
||||
return f.apply( (FlatTreeUI) ui );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static void setStyleColorFromTreeUI( Component c, Graphics2D g, Function<FlatTreeUI, Color> f ) {
|
||||
Color color = getStyleFromTreeUI( c, f );
|
||||
if( color != null )
|
||||
g.setColor( color );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,11 @@ public class FlatTreeExpandedIcon
|
||||
super( UIManager.getColor( "Tree.icon.expandedColor" ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
void setStyleColorFromTreeUI( Component c, Graphics2D g ) {
|
||||
setStyleColorFromTreeUI( c, g, ui -> ui.iconExpandedColor );
|
||||
}
|
||||
|
||||
@Override
|
||||
void rotate( Component c, Graphics2D g ) {
|
||||
g.rotate( Math.toRadians( 90 ), width / 2., height / 2. );
|
||||
|
||||
@@ -37,6 +37,8 @@ public class FlatTreeLeafIcon
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
FlatTreeCollapsedIcon.setStyleColorFromTreeUI( c, g, ui -> ui.iconLeafColor );
|
||||
|
||||
/*
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
|
||||
@@ -37,6 +37,8 @@ public class FlatTreeOpenIcon
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
FlatTreeCollapsedIcon.setStyleColorFromTreeUI( c, g, ui -> ui.iconOpenColor );
|
||||
|
||||
/*
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2021 FormDev Software GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.formdev.flatlaf.resources;
|
||||
|
||||
/**
|
||||
* The only purpose of this file is to add a .class file to this package to make it non-empty.
|
||||
* Otherwise the compiler outputs a warning because this package is opend in module-info.java.
|
||||
* Also when using --patch-module (e.g. from an IDE), an error would occur for empty packages.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
interface EmptyPackage
|
||||
{
|
||||
}
|
||||
@@ -17,16 +17,13 @@
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Shape;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.geom.Path2D;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.plaf.UIResource;
|
||||
import javax.swing.plaf.basic.BasicArrowButton;
|
||||
@@ -42,42 +39,34 @@ public class FlatArrowButton
|
||||
{
|
||||
public static final int DEFAULT_ARROW_WIDTH = 8;
|
||||
|
||||
private final boolean chevron;
|
||||
private final Color foreground;
|
||||
private final Color disabledForeground;
|
||||
private final Color hoverForeground;
|
||||
private final Color hoverBackground;
|
||||
private final Color pressedBackground;
|
||||
protected boolean chevron;
|
||||
protected Color foreground;
|
||||
protected Color disabledForeground;
|
||||
protected Color hoverForeground;
|
||||
protected Color hoverBackground;
|
||||
protected Color pressedForeground;
|
||||
protected Color pressedBackground;
|
||||
|
||||
private int arrowWidth = DEFAULT_ARROW_WIDTH;
|
||||
private int xOffset = 0;
|
||||
private int yOffset = 0;
|
||||
private float xOffset = 0;
|
||||
private float yOffset = 0;
|
||||
|
||||
private boolean hover;
|
||||
private boolean pressed;
|
||||
|
||||
public FlatArrowButton( int direction, String type, Color foreground, Color disabledForeground,
|
||||
Color hoverForeground, Color hoverBackground )
|
||||
{
|
||||
this( direction, type, foreground, disabledForeground, hoverForeground, hoverBackground, null );
|
||||
}
|
||||
|
||||
public FlatArrowButton( int direction, String type, Color foreground, Color disabledForeground,
|
||||
Color hoverForeground, Color hoverBackground, Color pressedBackground )
|
||||
Color hoverForeground, Color hoverBackground, Color pressedForeground, Color pressedBackground )
|
||||
{
|
||||
super( direction, Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE );
|
||||
|
||||
this.chevron = "chevron".equals( type );
|
||||
this.foreground = foreground;
|
||||
this.disabledForeground = disabledForeground;
|
||||
this.hoverForeground = hoverForeground;
|
||||
this.hoverBackground = hoverBackground;
|
||||
this.pressedBackground = pressedBackground;
|
||||
updateStyle( type, foreground, disabledForeground, hoverForeground, hoverBackground,
|
||||
pressedForeground, pressedBackground );
|
||||
|
||||
setOpaque( false );
|
||||
setBorder( null );
|
||||
|
||||
if( hoverForeground != null || hoverBackground != null || pressedBackground != null ) {
|
||||
if( hoverForeground != null || hoverBackground != null ||
|
||||
pressedForeground != null || pressedBackground != null )
|
||||
{
|
||||
addMouseListener( new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseEntered( MouseEvent e ) {
|
||||
@@ -106,6 +95,19 @@ public class FlatArrowButton
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
public void updateStyle( String type, Color foreground, Color disabledForeground,
|
||||
Color hoverForeground, Color hoverBackground, Color pressedForeground, Color pressedBackground )
|
||||
{
|
||||
this.chevron = FlatUIUtils.isChevron( type );
|
||||
this.foreground = foreground;
|
||||
this.disabledForeground = disabledForeground;
|
||||
this.hoverForeground = hoverForeground;
|
||||
this.hoverBackground = hoverBackground;
|
||||
this.pressedForeground = pressedForeground;
|
||||
this.pressedBackground = pressedBackground;
|
||||
}
|
||||
|
||||
public int getArrowWidth() {
|
||||
return arrowWidth;
|
||||
}
|
||||
@@ -122,19 +124,19 @@ public class FlatArrowButton
|
||||
return pressed;
|
||||
}
|
||||
|
||||
public int getXOffset() {
|
||||
public float getXOffset() {
|
||||
return xOffset;
|
||||
}
|
||||
|
||||
public void setXOffset( int xOffset ) {
|
||||
public void setXOffset( float xOffset ) {
|
||||
this.xOffset = xOffset;
|
||||
}
|
||||
|
||||
public int getYOffset() {
|
||||
public float getYOffset() {
|
||||
return yOffset;
|
||||
}
|
||||
|
||||
public void setYOffset( int yOffset ) {
|
||||
public void setYOffset( float yOffset ) {
|
||||
this.yOffset = yOffset;
|
||||
}
|
||||
|
||||
@@ -142,6 +144,25 @@ public class FlatArrowButton
|
||||
return background;
|
||||
}
|
||||
|
||||
protected Color deriveForeground( Color foreground ) {
|
||||
return FlatUIUtils.deriveColor( foreground, this.foreground );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the color used to paint the arrow.
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
protected Color getArrowColor() {
|
||||
return isEnabled()
|
||||
? (pressedForeground != null && isPressed()
|
||||
? pressedForeground
|
||||
: (hoverForeground != null && isHover()
|
||||
? hoverForeground
|
||||
: foreground))
|
||||
: disabledForeground;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
return scale( super.getPreferredSize() );
|
||||
@@ -154,97 +175,42 @@ public class FlatArrowButton
|
||||
|
||||
@Override
|
||||
public void paint( Graphics g ) {
|
||||
Graphics2D g2 = (Graphics2D)g;
|
||||
FlatUIUtils.setRenderingHints( g2 );
|
||||
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
boolean enabled = isEnabled();
|
||||
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
||||
|
||||
// paint hover or pressed background
|
||||
if( enabled ) {
|
||||
if( isEnabled() ) {
|
||||
Color background = (pressedBackground != null && isPressed())
|
||||
? deriveBackground( pressedBackground )
|
||||
: ((hoverBackground != null && isHover())
|
||||
? deriveBackground( hoverBackground )
|
||||
? pressedBackground
|
||||
: (hoverBackground != null && isHover()
|
||||
? hoverBackground
|
||||
: null);
|
||||
|
||||
if( background != null ) {
|
||||
g.setColor( background );
|
||||
g.fillRect( 0, 0, width, height );
|
||||
g.setColor( deriveBackground( background ) );
|
||||
paintBackground( (Graphics2D) g );
|
||||
}
|
||||
}
|
||||
|
||||
int direction = getDirection();
|
||||
// paint arrow
|
||||
g.setColor( deriveForeground( getArrowColor() ) );
|
||||
paintArrow( (Graphics2D) g );
|
||||
|
||||
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||
}
|
||||
|
||||
protected void paintBackground( Graphics2D g ) {
|
||||
g.fillRect( 0, 0, getWidth(), getHeight() );
|
||||
}
|
||||
|
||||
protected void paintArrow( Graphics2D g ) {
|
||||
boolean vert = (direction == NORTH || direction == SOUTH);
|
||||
|
||||
// compute width/height
|
||||
int w = scale( arrowWidth + (chevron ? 0 : 1) );
|
||||
int h = scale( (arrowWidth / 2) + (chevron ? 0 : 1) );
|
||||
|
||||
// rotate width/height
|
||||
int rw = vert ? w : h;
|
||||
int rh = vert ? h : w;
|
||||
|
||||
// chevron lines end 1px outside of width/height
|
||||
if( chevron ) {
|
||||
// add 1px to width/height for position calculation only
|
||||
rw++;
|
||||
rh++;
|
||||
}
|
||||
|
||||
int x = Math.round( (width - rw) / 2f + scale( (float) xOffset ) );
|
||||
int y = Math.round( (height - rh) / 2f + scale( (float) yOffset ) );
|
||||
int x = 0;
|
||||
|
||||
// move arrow for round borders
|
||||
Container parent = getParent();
|
||||
if( vert && parent instanceof JComponent && FlatUIUtils.hasRoundBorder( (JComponent) parent ) )
|
||||
x -= scale( parent.getComponentOrientation().isLeftToRight() ? 1 : -1 );
|
||||
|
||||
// paint arrow
|
||||
g.setColor( enabled
|
||||
? (isHover() && hoverForeground != null ? hoverForeground : foreground)
|
||||
: disabledForeground );
|
||||
g.translate( x, y );
|
||||
/*debug
|
||||
debugPaint( g2, vert, rw, rh );
|
||||
debug*/
|
||||
Shape arrowShape = createArrowShape( direction, chevron, w, h );
|
||||
if( chevron ) {
|
||||
g2.setStroke( new BasicStroke( scale( 1f ) ) );
|
||||
g2.draw( arrowShape );
|
||||
} else {
|
||||
// triangle
|
||||
g2.fill( arrowShape );
|
||||
}
|
||||
g.translate( -x, -y );
|
||||
FlatUIUtils.paintArrow( g, x, 0, getWidth(), getHeight(), getDirection(), chevron, arrowWidth, xOffset, yOffset );
|
||||
}
|
||||
|
||||
public static Shape createArrowShape( int direction, boolean chevron, float w, float h ) {
|
||||
switch( direction ) {
|
||||
case NORTH: return FlatUIUtils.createPath( !chevron, 0,h, (w / 2f),0, w,h );
|
||||
case SOUTH: return FlatUIUtils.createPath( !chevron, 0,0, (w / 2f),h, w,0 );
|
||||
case WEST: return FlatUIUtils.createPath( !chevron, h,0, 0,(w / 2f), h,w );
|
||||
case EAST: return FlatUIUtils.createPath( !chevron, 0,0, h,(w / 2f), 0,w );
|
||||
default: return new Path2D.Float();
|
||||
}
|
||||
}
|
||||
|
||||
/*debug
|
||||
private void debugPaint( Graphics g, boolean vert, int w, int h ) {
|
||||
Color oldColor = g.getColor();
|
||||
g.setColor( Color.red );
|
||||
g.drawRect( 0, 0, w - 1, h - 1 );
|
||||
|
||||
int xy1 = -2;
|
||||
int xy2 = h + 1;
|
||||
for( int i = 0; i < 20; i++ ) {
|
||||
g.drawRect( vert ? 0 : xy1, vert ? xy1 : 0, 0, 0 );
|
||||
g.drawRect( vert ? 0 : xy2, vert ? xy2 : 0, 0, 0 );
|
||||
xy1 -= 2;
|
||||
xy2 += 2;
|
||||
}
|
||||
g.setColor( oldColor );
|
||||
}
|
||||
debug*/
|
||||
}
|
||||
|
||||
@@ -22,35 +22,35 @@ import java.awt.Component;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Insets;
|
||||
import java.awt.KeyboardFocusManager;
|
||||
import java.awt.Paint;
|
||||
import java.util.Map;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSpinner;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.JViewport;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.basic.BasicBorders;
|
||||
import javax.swing.text.JTextComponent;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableBorder;
|
||||
import com.formdev.flatlaf.util.DerivedColor;
|
||||
|
||||
/**
|
||||
* Border for various components (e.g. {@link javax.swing.JTextField}).
|
||||
*
|
||||
* <p>
|
||||
* There is empty space around the component border, if Component.focusWidth is greater than zero,
|
||||
* which is used to paint outer focus border.
|
||||
*
|
||||
* <p>
|
||||
* Because there is empty space (if outer focus border is not painted),
|
||||
* UI delegates that use this border (or subclasses) must invoke
|
||||
* {@link FlatUIUtils#paintParentBackground} to paint the empty space correctly.
|
||||
* {@link FlatUIUtils#paintParentBackground} to fill the empty space correctly.
|
||||
*
|
||||
* @uiDefault Component.focusWidth int
|
||||
* @uiDefault Component.innerFocusWidth int or float
|
||||
* @uiDefault Component.innerOutlineWidth int or float
|
||||
* @uiDefault Component.borderWidth int or float
|
||||
*
|
||||
* @uiDefault Component.focusColor Color
|
||||
* @uiDefault Component.borderColor Color
|
||||
* @uiDefault Component.disabledBorderColor Color
|
||||
@@ -66,20 +66,40 @@ import com.formdev.flatlaf.util.DerivedColor;
|
||||
*/
|
||||
public class FlatBorder
|
||||
extends BasicBorders.MarginBorder
|
||||
implements StyleableBorder
|
||||
{
|
||||
protected final int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
||||
protected final float innerFocusWidth = FlatUIUtils.getUIFloat( "Component.innerFocusWidth", 0 );
|
||||
protected final float innerOutlineWidth = FlatUIUtils.getUIFloat( "Component.innerOutlineWidth", 0 );
|
||||
protected final Color focusColor = UIManager.getColor( "Component.focusColor" );
|
||||
protected final Color borderColor = UIManager.getColor( "Component.borderColor" );
|
||||
protected final Color disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
|
||||
protected final Color focusedBorderColor = UIManager.getColor( "Component.focusedBorderColor" );
|
||||
@Styleable protected int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
||||
@Styleable protected float innerFocusWidth = FlatUIUtils.getUIFloat( "Component.innerFocusWidth", 0 );
|
||||
@Styleable protected float innerOutlineWidth = FlatUIUtils.getUIFloat( "Component.innerOutlineWidth", 0 );
|
||||
/** @since 2 */ @Styleable protected float borderWidth = FlatUIUtils.getUIFloat( "Component.borderWidth", 1 );
|
||||
|
||||
protected final Color errorBorderColor = UIManager.getColor( "Component.error.borderColor" );
|
||||
protected final Color errorFocusedBorderColor = UIManager.getColor( "Component.error.focusedBorderColor" );
|
||||
protected final Color warningBorderColor = UIManager.getColor( "Component.warning.borderColor" );
|
||||
protected final Color warningFocusedBorderColor = UIManager.getColor( "Component.warning.focusedBorderColor" );
|
||||
protected final Color customBorderColor = UIManager.getColor( "Component.custom.borderColor" );
|
||||
@Styleable protected Color focusColor = UIManager.getColor( "Component.focusColor" );
|
||||
@Styleable protected Color borderColor = UIManager.getColor( "Component.borderColor" );
|
||||
@Styleable protected Color disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
|
||||
@Styleable protected Color focusedBorderColor = UIManager.getColor( "Component.focusedBorderColor" );
|
||||
|
||||
@Styleable(dot=true) protected Color errorBorderColor = UIManager.getColor( "Component.error.borderColor" );
|
||||
@Styleable(dot=true) protected Color errorFocusedBorderColor = UIManager.getColor( "Component.error.focusedBorderColor" );
|
||||
@Styleable(dot=true) protected Color warningBorderColor = UIManager.getColor( "Component.warning.borderColor" );
|
||||
@Styleable(dot=true) protected Color warningFocusedBorderColor = UIManager.getColor( "Component.warning.focusedBorderColor" );
|
||||
@Styleable(dot=true) protected Color customBorderColor = UIManager.getColor( "Component.custom.borderColor" );
|
||||
|
||||
// only used via styling (not in UI defaults, but has likewise client properties)
|
||||
/** @since 2 */ @Styleable protected String outline;
|
||||
/** @since 2 */ @Styleable protected Color outlineColor;
|
||||
/** @since 2 */ @Styleable protected Color outlineFocusedColor;
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Object applyStyleProperty( String key, Object value ) {
|
||||
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos() {
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||
@@ -88,24 +108,29 @@ public class FlatBorder
|
||||
FlatUIUtils.setRenderingHints( g2 );
|
||||
|
||||
float focusWidth = scale( (float) getFocusWidth( c ) );
|
||||
float borderWidth = scale( (float) getBorderWidth( c ) );
|
||||
float focusInnerWidth = 0;
|
||||
float borderWidth = scale( getBorderWidth( c ) );
|
||||
float arc = scale( (float) getArc( c ) );
|
||||
Color outlineColor = getOutlineColor( c );
|
||||
Color focusColor = null;
|
||||
|
||||
// paint outer border
|
||||
if( outlineColor != null || isFocused( c ) ) {
|
||||
float innerWidth = !isCellEditor( c ) && !(c instanceof JScrollPane)
|
||||
? (outlineColor != null ? innerOutlineWidth : innerFocusWidth)
|
||||
? (outlineColor != null ? innerOutlineWidth : getInnerFocusWidth( c ))
|
||||
: 0;
|
||||
|
||||
g2.setColor( (outlineColor != null) ? outlineColor : getFocusColor( c ) );
|
||||
FlatUIUtils.paintComponentOuterBorder( g2, x, y, width, height,
|
||||
focusWidth, borderWidth + scale( innerWidth ), arc );
|
||||
if( focusWidth > 0 || innerWidth > 0 ) {
|
||||
focusColor = (outlineColor != null) ? outlineColor : getFocusColor( c );
|
||||
focusInnerWidth = borderWidth + scale( innerWidth );
|
||||
}
|
||||
}
|
||||
|
||||
// paint border
|
||||
g2.setPaint( (outlineColor != null) ? outlineColor : getBorderColor( c ) );
|
||||
FlatUIUtils.paintComponentBorder( g2, x, y, width, height, focusWidth, borderWidth, arc );
|
||||
Paint borderColor = (outlineColor != null) ? outlineColor : getBorderColor( c );
|
||||
FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height,
|
||||
focusWidth, 1, focusInnerWidth, borderWidth, arc,
|
||||
focusColor, borderColor, null );
|
||||
} finally {
|
||||
g2.dispose();
|
||||
}
|
||||
@@ -120,6 +145,17 @@ public class FlatBorder
|
||||
return null;
|
||||
|
||||
Object outline = ((JComponent)c).getClientProperty( FlatClientProperties.OUTLINE );
|
||||
if( outline == null )
|
||||
outline = this.outline;
|
||||
if( outline == null ) {
|
||||
if( outlineColor != null && outlineFocusedColor != null )
|
||||
outline = new Color[] { outlineFocusedColor, outlineColor };
|
||||
else if( outlineColor != null )
|
||||
outline = outlineColor;
|
||||
else if( outlineFocusedColor != null )
|
||||
outline = outlineFocusedColor;
|
||||
}
|
||||
|
||||
if( outline instanceof String ) {
|
||||
switch( (String) outline ) {
|
||||
case FlatClientProperties.OUTLINE_ERROR:
|
||||
@@ -159,41 +195,17 @@ public class FlatBorder
|
||||
return false;
|
||||
}
|
||||
|
||||
return c.isEnabled() && (!(c instanceof JTextComponent) || ((JTextComponent)c).isEditable());
|
||||
return c.isEnabled();
|
||||
}
|
||||
|
||||
protected boolean isFocused( Component c ) {
|
||||
if( c instanceof JScrollPane ) {
|
||||
JViewport viewport = ((JScrollPane)c).getViewport();
|
||||
Component view = (viewport != null) ? viewport.getView() : null;
|
||||
if( view != null ) {
|
||||
if( FlatUIUtils.isPermanentFocusOwner( view ) )
|
||||
return true;
|
||||
|
||||
if( (view instanceof JTable && ((JTable)view).isEditing()) ||
|
||||
(view instanceof JTree && ((JTree)view).isEditing()) )
|
||||
{
|
||||
Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
|
||||
if( focusOwner != null )
|
||||
return SwingUtilities.isDescendingFrom( focusOwner, view );
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else if( c instanceof JComboBox && ((JComboBox<?>)c).isEditable() ) {
|
||||
Component editorComponent = ((JComboBox<?>)c).getEditor().getEditorComponent();
|
||||
return (editorComponent != null) ? FlatUIUtils.isPermanentFocusOwner( editorComponent ) : false;
|
||||
} else if( c instanceof JSpinner ) {
|
||||
if( FlatUIUtils.isPermanentFocusOwner( c ) )
|
||||
return true;
|
||||
|
||||
JComponent editor = ((JSpinner)c).getEditor();
|
||||
if( editor instanceof JSpinner.DefaultEditor ) {
|
||||
JTextField textField = ((JSpinner.DefaultEditor)editor).getTextField();
|
||||
if( textField != null )
|
||||
return FlatUIUtils.isPermanentFocusOwner( textField );
|
||||
}
|
||||
return false;
|
||||
} else
|
||||
if( c instanceof JScrollPane )
|
||||
return FlatScrollPaneUI.isPermanentFocusOwner( (JScrollPane) c );
|
||||
else if( c instanceof JComboBox )
|
||||
return FlatComboBoxUI.isPermanentFocusOwner( (JComboBox<?>) c );
|
||||
else if( c instanceof JSpinner )
|
||||
return FlatSpinnerUI.isPermanentFocusOwner( (JSpinner) c );
|
||||
else
|
||||
return FlatUIUtils.isPermanentFocusOwner( c );
|
||||
}
|
||||
|
||||
@@ -204,13 +216,14 @@ public class FlatBorder
|
||||
@Override
|
||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||
float focusWidth = scale( (float) getFocusWidth( c ) );
|
||||
float ow = focusWidth + scale( (float) getLineWidth( c ) );
|
||||
int ow = Math.round( focusWidth + scale( (float) getLineWidth( c ) ) );
|
||||
|
||||
insets = super.getBorderInsets( c, insets );
|
||||
insets.top = Math.round( scale( (float) insets.top ) + ow );
|
||||
insets.left = Math.round( scale( (float) insets.left ) + ow );
|
||||
insets.bottom = Math.round( scale( (float) insets.bottom ) + ow );
|
||||
insets.right = Math.round( scale( (float) insets.right ) + ow );
|
||||
|
||||
insets.top = scale( insets.top ) + ow;
|
||||
insets.left = scale( insets.left ) + ow;
|
||||
insets.bottom = scale( insets.bottom ) + ow;
|
||||
insets.right = scale( insets.right ) + ow;
|
||||
|
||||
if( isCellEditor( c ) ) {
|
||||
// remove top and bottom insets if used as cell editor
|
||||
@@ -236,6 +249,13 @@ public class FlatBorder
|
||||
return focusWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the (unscaled) thickness of the inner focus border.
|
||||
*/
|
||||
protected float getInnerFocusWidth( Component c ) {
|
||||
return innerFocusWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the (unscaled) line thickness used to compute the border insets.
|
||||
* This may be different to {@link #getBorderWidth}.
|
||||
@@ -248,8 +268,8 @@ public class FlatBorder
|
||||
* Returns the (unscaled) line thickness used to paint the border.
|
||||
* This may be different to {@link #getLineWidth}.
|
||||
*/
|
||||
protected int getBorderWidth( Component c ) {
|
||||
return getLineWidth( c );
|
||||
protected float getBorderWidth( Component c ) {
|
||||
return borderWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,67 +20,115 @@ import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.GradientPaint;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Paint;
|
||||
import javax.swing.AbstractButton;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.UIResource;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
/**
|
||||
* Border for {@link javax.swing.JButton}.
|
||||
*
|
||||
* @uiDefault Button.arc int
|
||||
* @uiDefault Button.innerFocusWidth int or float optional; defaults to Component.innerFocusWidth
|
||||
* @uiDefault Button.borderWidth int or float optional; defaults to Component.borderWidth
|
||||
*
|
||||
* @uiDefault Button.borderColor Color
|
||||
* @uiDefault Button.startBorderColor Color optional; if set, a gradient paint is used and Button.borderColor is ignored
|
||||
* @uiDefault Button.endBorderColor Color optional; if set, a gradient paint is used
|
||||
* @uiDefault Button.disabledBorderColor Color
|
||||
* @uiDefault Button.focusedBorderColor Color
|
||||
* @uiDefault Button.hoverBorderColor Color optional
|
||||
*
|
||||
* @uiDefault Button.default.borderWidth int or float
|
||||
* @uiDefault Button.default.borderColor Color
|
||||
* @uiDefault Button.default.startBorderColor Color optional; if set, a gradient paint is used and Button.default.borderColor is ignored
|
||||
* @uiDefault Button.default.endBorderColor Color optional; if set, a gradient paint is used
|
||||
* @uiDefault Button.default.hoverBorderColor Color optional
|
||||
* @uiDefault Button.default.focusedBorderColor Color
|
||||
* @uiDefault Button.default.focusColor Color
|
||||
* @uiDefault Button.borderWidth int
|
||||
* @uiDefault Button.default.borderWidth int
|
||||
* @uiDefault Button.default.hoverBorderColor Color optional
|
||||
*
|
||||
* @uiDefault Button.toolbar.focusWidth int or float optional; default is 1.5
|
||||
* @uiDefault Button.toolbar.focusColor Color optional; defaults to Component.focusColor
|
||||
* @uiDefault Button.toolbar.margin Insets
|
||||
* @uiDefault Button.toolbar.spacingInsets Insets
|
||||
* @uiDefault Button.arc int
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
public class FlatButtonBorder
|
||||
extends FlatBorder
|
||||
{
|
||||
protected final Color borderColor = FlatUIUtils.getUIColor( "Button.startBorderColor", "Button.borderColor" );
|
||||
protected final Color endBorderColor = UIManager.getColor( "Button.endBorderColor" );
|
||||
protected final Color disabledBorderColor = UIManager.getColor( "Button.disabledBorderColor" );
|
||||
protected final Color focusedBorderColor = UIManager.getColor( "Button.focusedBorderColor" );
|
||||
protected final Color hoverBorderColor = UIManager.getColor( "Button.hoverBorderColor" );
|
||||
protected final Color defaultBorderColor = FlatUIUtils.getUIColor( "Button.default.startBorderColor", "Button.default.borderColor" );
|
||||
protected final Color defaultEndBorderColor = UIManager.getColor( "Button.default.endBorderColor" );
|
||||
protected final Color defaultHoverBorderColor = UIManager.getColor( "Button.default.hoverBorderColor" );
|
||||
protected final Color defaultFocusedBorderColor = UIManager.getColor( "Button.default.focusedBorderColor" );
|
||||
protected final Color defaultFocusColor = UIManager.getColor( "Button.default.focusColor" );
|
||||
protected final int borderWidth = UIManager.getInt( "Button.borderWidth" );
|
||||
protected final int defaultBorderWidth = UIManager.getInt( "Button.default.borderWidth" );
|
||||
protected final Insets toolbarMargin = UIManager.getInsets( "Button.toolbar.margin" );
|
||||
protected final Insets toolbarSpacingInsets = UIManager.getInsets( "Button.toolbar.spacingInsets" );
|
||||
protected final int arc = UIManager.getInt( "Button.arc" );
|
||||
@Styleable protected int arc = UIManager.getInt( "Button.arc" );
|
||||
|
||||
protected Color endBorderColor = UIManager.getColor( "Button.endBorderColor" );
|
||||
@Styleable protected Color hoverBorderColor = UIManager.getColor( "Button.hoverBorderColor" );
|
||||
|
||||
@Styleable(dot=true) protected float defaultBorderWidth = FlatUIUtils.getUIFloat( "Button.default.borderWidth", 1 );
|
||||
@Styleable(dot=true) protected Color defaultBorderColor = FlatUIUtils.getUIColor( "Button.default.startBorderColor", "Button.default.borderColor" );
|
||||
protected Color defaultEndBorderColor = UIManager.getColor( "Button.default.endBorderColor" );
|
||||
@Styleable(dot=true) protected Color defaultFocusedBorderColor = UIManager.getColor( "Button.default.focusedBorderColor" );
|
||||
@Styleable(dot=true) protected Color defaultFocusColor = UIManager.getColor( "Button.default.focusColor" );
|
||||
@Styleable(dot=true) protected Color defaultHoverBorderColor = UIManager.getColor( "Button.default.hoverBorderColor" );
|
||||
|
||||
/** @since 1.4 */ @Styleable(dot=true) protected float toolbarFocusWidth = FlatUIUtils.getUIFloat( "Button.toolbar.focusWidth", 1.5f );
|
||||
/** @since 1.4 */ @Styleable(dot=true) protected Color toolbarFocusColor = UIManager.getColor( "Button.toolbar.focusColor" );
|
||||
@Styleable(dot=true) protected Insets toolbarMargin = UIManager.getInsets( "Button.toolbar.margin" );
|
||||
@Styleable(dot=true) protected Insets toolbarSpacingInsets = UIManager.getInsets( "Button.toolbar.spacingInsets" );
|
||||
|
||||
public FlatButtonBorder() {
|
||||
innerFocusWidth = FlatUIUtils.getUIFloat( "Button.innerFocusWidth", innerFocusWidth );
|
||||
borderWidth = FlatUIUtils.getUIFloat( "Button.borderWidth", borderWidth );
|
||||
|
||||
borderColor = FlatUIUtils.getUIColor( "Button.startBorderColor", "Button.borderColor" );
|
||||
disabledBorderColor = UIManager.getColor( "Button.disabledBorderColor" );
|
||||
focusedBorderColor = UIManager.getColor( "Button.focusedBorderColor" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||
if( FlatButtonUI.isContentAreaFilled( c ) &&
|
||||
!FlatButtonUI.isToolBarButton( c ) &&
|
||||
(!FlatButtonUI.isBorderlessButton( c ) || FlatUIUtils.isPermanentFocusOwner( c )) &&
|
||||
!FlatButtonUI.isHelpButton( c ) &&
|
||||
!FlatToggleButtonUI.isTabButton( c ) )
|
||||
super.paintBorder( c, g, x, y, width, height );
|
||||
else if( FlatButtonUI.isToolBarButton( c ) && isFocused( c ) )
|
||||
paintToolBarFocus( c, g, x, y, width, height );
|
||||
}
|
||||
|
||||
/** @since 1.4 */
|
||||
protected void paintToolBarFocus( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
try {
|
||||
FlatUIUtils.setRenderingHints( g2 );
|
||||
|
||||
float focusWidth = UIScale.scale( toolbarFocusWidth );
|
||||
float arc = UIScale.scale( (float) getArc( c ) );
|
||||
Color outlineColor = getOutlineColor( c );
|
||||
|
||||
Insets spacing = UIScale.scale( toolbarSpacingInsets );
|
||||
x += spacing.left;
|
||||
y += spacing.top;
|
||||
width -= spacing.left + spacing.right;
|
||||
height -= spacing.top + spacing.bottom;
|
||||
|
||||
Color color = (outlineColor != null) ? outlineColor : getFocusColor( c );
|
||||
// not using focus border painting of paintOutlinedComponent() here
|
||||
// because its round edges look too "thick"
|
||||
FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height, 0, 0, 0, focusWidth, arc, null, color, null );
|
||||
} finally {
|
||||
g2.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Color getFocusColor( Component c ) {
|
||||
return FlatButtonUI.isDefaultButton( c ) ? defaultFocusColor : super.getFocusColor( c );
|
||||
return (toolbarFocusColor != null && FlatButtonUI.isToolBarButton( c ))
|
||||
? toolbarFocusColor
|
||||
: (FlatButtonUI.isDefaultButton( c ) ? defaultFocusColor : super.getFocusColor( c ));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -135,7 +183,7 @@ public class FlatButtonBorder
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getBorderWidth( Component c ) {
|
||||
protected float getBorderWidth( Component c ) {
|
||||
return FlatButtonUI.isDefaultButton( c ) ? defaultBorderWidth : borderWidth;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,9 @@ import java.awt.Insets;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import javax.swing.AbstractButton;
|
||||
import javax.swing.ButtonModel;
|
||||
import javax.swing.Icon;
|
||||
@@ -39,11 +42,17 @@ import javax.swing.JToggleButton;
|
||||
import javax.swing.JToolBar;
|
||||
import javax.swing.LookAndFeel;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.ButtonUI;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.UIResource;
|
||||
import javax.swing.plaf.basic.BasicButtonListener;
|
||||
import javax.swing.plaf.basic.BasicButtonUI;
|
||||
import com.formdev.flatlaf.FlatLaf;
|
||||
import com.formdev.flatlaf.icons.FlatHelpButtonIcon;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
/**
|
||||
@@ -93,8 +102,9 @@ import com.formdev.flatlaf.util.UIScale;
|
||||
*/
|
||||
public class FlatButtonUI
|
||||
extends BasicButtonUI
|
||||
implements StyleableUI
|
||||
{
|
||||
protected int minimumWidth;
|
||||
@Styleable protected int minimumWidth;
|
||||
protected int iconTextGap;
|
||||
|
||||
protected Color background;
|
||||
@@ -102,42 +112,62 @@ public class FlatButtonUI
|
||||
|
||||
protected Color startBackground;
|
||||
protected Color endBackground;
|
||||
protected Color focusedBackground;
|
||||
protected Color hoverBackground;
|
||||
protected Color pressedBackground;
|
||||
protected Color selectedBackground;
|
||||
protected Color selectedForeground;
|
||||
protected Color disabledBackground;
|
||||
protected Color disabledText;
|
||||
protected Color disabledSelectedBackground;
|
||||
@Styleable protected Color focusedBackground;
|
||||
@Styleable protected Color hoverBackground;
|
||||
@Styleable protected Color pressedBackground;
|
||||
@Styleable protected Color selectedBackground;
|
||||
@Styleable protected Color selectedForeground;
|
||||
@Styleable protected Color disabledBackground;
|
||||
@Styleable protected Color disabledText;
|
||||
@Styleable protected Color disabledSelectedBackground;
|
||||
|
||||
protected Color defaultBackground;
|
||||
@Styleable(dot=true) protected Color defaultBackground;
|
||||
protected Color defaultEndBackground;
|
||||
protected Color defaultForeground;
|
||||
protected Color defaultFocusedBackground;
|
||||
protected Color defaultHoverBackground;
|
||||
protected Color defaultPressedBackground;
|
||||
protected boolean defaultBoldText;
|
||||
@Styleable(dot=true) protected Color defaultForeground;
|
||||
@Styleable(dot=true) protected Color defaultFocusedBackground;
|
||||
@Styleable(dot=true) protected Color defaultHoverBackground;
|
||||
@Styleable(dot=true) protected Color defaultPressedBackground;
|
||||
@Styleable(dot=true) protected boolean defaultBoldText;
|
||||
|
||||
protected int shadowWidth;
|
||||
protected Color shadowColor;
|
||||
protected Color defaultShadowColor;
|
||||
@Styleable protected boolean paintShadow;
|
||||
@Styleable protected int shadowWidth;
|
||||
@Styleable protected Color shadowColor;
|
||||
@Styleable(dot=true) protected Color defaultShadowColor;
|
||||
|
||||
protected Insets toolbarSpacingInsets;
|
||||
protected Color toolbarHoverBackground;
|
||||
protected Color toolbarPressedBackground;
|
||||
protected Color toolbarSelectedBackground;
|
||||
@Styleable(dot=true) protected Color toolbarHoverBackground;
|
||||
@Styleable(dot=true) protected Color toolbarPressedBackground;
|
||||
@Styleable(dot=true) protected Color toolbarSelectedBackground;
|
||||
|
||||
// only used via styling (not in UI defaults, but has likewise client properties)
|
||||
/** @since 2 */ @Styleable protected String buttonType;
|
||||
/** @since 2 */ @Styleable protected boolean squareSize;
|
||||
/** @since 2 */ @Styleable protected int minimumHeight;
|
||||
|
||||
private Icon helpButtonIcon;
|
||||
private Insets defaultMargin;
|
||||
|
||||
private final boolean shared;
|
||||
private boolean helpButtonIconShared = true;
|
||||
private boolean defaults_initialized = false;
|
||||
|
||||
private static ComponentUI instance;
|
||||
private Map<String, Object> oldStyleValues;
|
||||
private AtomicBoolean borderShared;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
if( instance == null )
|
||||
instance = new FlatButtonUI();
|
||||
return instance;
|
||||
return FlatUIUtils.canUseSharedUI( c )
|
||||
? FlatUIUtils.createSharedUI( FlatButtonUI.class, () -> new FlatButtonUI( true ) )
|
||||
: new FlatButtonUI( false );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected FlatButtonUI( boolean shared ) {
|
||||
this.shared = shared;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installUI( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
installStyle( (AbstractButton) c );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -164,16 +194,6 @@ public class FlatButtonUI
|
||||
disabledText = UIManager.getColor( prefix + "disabledText" );
|
||||
disabledSelectedBackground = UIManager.getColor( prefix + "disabledSelectedBackground" );
|
||||
|
||||
if( UIManager.getBoolean( "Button.paintShadow" ) ) {
|
||||
shadowWidth = FlatUIUtils.getUIInt( "Button.shadowWidth", 2 );
|
||||
shadowColor = UIManager.getColor( "Button.shadowColor" );
|
||||
defaultShadowColor = UIManager.getColor( "Button.default.shadowColor" );
|
||||
} else {
|
||||
shadowWidth = 0;
|
||||
shadowColor = null;
|
||||
defaultShadowColor = null;
|
||||
}
|
||||
|
||||
defaultBackground = FlatUIUtils.getUIColor( "Button.default.startBackground", "Button.default.background" );
|
||||
defaultEndBackground = UIManager.getColor( "Button.default.endBackground" );
|
||||
defaultForeground = UIManager.getColor( "Button.default.foreground" );
|
||||
@@ -182,13 +202,19 @@ public class FlatButtonUI
|
||||
defaultPressedBackground = UIManager.getColor( "Button.default.pressedBackground" );
|
||||
defaultBoldText = UIManager.getBoolean( "Button.default.boldText" );
|
||||
|
||||
toolbarSpacingInsets = UIManager.getInsets( "Button.toolbar.spacingInsets" );
|
||||
paintShadow = UIManager.getBoolean( "Button.paintShadow" );
|
||||
shadowWidth = FlatUIUtils.getUIInt( "Button.shadowWidth", 2 );
|
||||
shadowColor = UIManager.getColor( "Button.shadowColor" );
|
||||
defaultShadowColor = UIManager.getColor( "Button.default.shadowColor" );
|
||||
|
||||
toolbarHoverBackground = UIManager.getColor( prefix + "toolbar.hoverBackground" );
|
||||
toolbarPressedBackground = UIManager.getColor( prefix + "toolbar.pressedBackground" );
|
||||
toolbarSelectedBackground = UIManager.getColor( prefix + "toolbar.selectedBackground" );
|
||||
|
||||
helpButtonIcon = UIManager.getIcon( "HelpButton.icon" );
|
||||
defaultMargin = UIManager.getInsets( prefix + "margin" );
|
||||
|
||||
helpButtonIconShared = true;
|
||||
defaults_initialized = true;
|
||||
}
|
||||
|
||||
@@ -208,6 +234,9 @@ public class FlatButtonUI
|
||||
protected void uninstallDefaults( AbstractButton b ) {
|
||||
super.uninstallDefaults( b );
|
||||
|
||||
oldStyleValues = null;
|
||||
borderShared = null;
|
||||
|
||||
MigLayoutVisualPadding.uninstall( b );
|
||||
defaults_initialized = false;
|
||||
}
|
||||
@@ -229,9 +258,70 @@ public class FlatButtonUI
|
||||
b.revalidate();
|
||||
b.repaint();
|
||||
break;
|
||||
|
||||
case STYLE:
|
||||
case STYLE_CLASS:
|
||||
if( shared && FlatStylingSupport.hasStyleProperty( b ) ) {
|
||||
// unshare component UI if necessary
|
||||
// updateUI() invokes applyStyle() from installUI()
|
||||
b.updateUI();
|
||||
} else
|
||||
installStyle( b );
|
||||
b.revalidate();
|
||||
b.repaint();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void installStyle( AbstractButton b ) {
|
||||
try {
|
||||
applyStyle( b, FlatStylingSupport.getResolvedStyle( b, getStyleType() ) );
|
||||
} catch( RuntimeException ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
String getStyleType() {
|
||||
return "Button";
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void applyStyle( AbstractButton b, Object style ) {
|
||||
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style,
|
||||
(key, value) -> applyStyleProperty( b, key, value ) );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected Object applyStyleProperty( AbstractButton b, String key, Object value ) {
|
||||
if( key.startsWith( "help." ) ) {
|
||||
if( !(helpButtonIcon instanceof FlatHelpButtonIcon) )
|
||||
return new UnknownStyleException( key );
|
||||
|
||||
if( helpButtonIconShared ) {
|
||||
helpButtonIcon = FlatStylingSupport.cloneIcon( helpButtonIcon );
|
||||
helpButtonIconShared = false;
|
||||
}
|
||||
|
||||
key = key.substring( "help.".length() );
|
||||
return ((FlatHelpButtonIcon)helpButtonIcon).applyStyleProperty( key, value );
|
||||
}
|
||||
|
||||
if( borderShared == null )
|
||||
borderShared = new AtomicBoolean( true );
|
||||
return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, b, borderShared );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
Map<String, Class<?>> infos = FlatStylingSupport.getAnnotatedStyleableInfos( this, c.getBorder() );
|
||||
if( helpButtonIcon instanceof FlatHelpButtonIcon )
|
||||
FlatStylingSupport.putAllPrefixKey( infos, "help.", ((FlatHelpButtonIcon)helpButtonIcon).getStyleableInfos() );
|
||||
return infos;
|
||||
}
|
||||
|
||||
static boolean isContentAreaFilled( Component c ) {
|
||||
return !(c instanceof AbstractButton) || ((AbstractButton)c).isContentAreaFilled();
|
||||
}
|
||||
@@ -255,27 +345,53 @@ public class FlatButtonUI
|
||||
Icon icon = ((AbstractButton)c).getIcon();
|
||||
String text = ((AbstractButton)c).getText();
|
||||
return (icon != null && (text == null || text.isEmpty())) ||
|
||||
(icon == null && text != null && ("...".equals( text ) || text.length() == 1));
|
||||
(icon == null && text != null &&
|
||||
("...".equals( text ) ||
|
||||
text.length() == 1 ||
|
||||
(text.length() == 2 && Character.isSurrogatePair( text.charAt( 0 ), text.charAt( 1 ) ))));
|
||||
}
|
||||
|
||||
// same indices as in parameters to clientPropertyChoice()
|
||||
static final int TYPE_OTHER = -1;
|
||||
static final int TYPE_SQUARE = 0;
|
||||
static final int TYPE_ROUND_RECT = 1;
|
||||
|
||||
static int getButtonType( Component c ) {
|
||||
return (c instanceof AbstractButton)
|
||||
? clientPropertyChoice( (AbstractButton) c, BUTTON_TYPE, BUTTON_TYPE_SQUARE, BUTTON_TYPE_ROUND_RECT )
|
||||
: TYPE_OTHER;
|
||||
if( !(c instanceof AbstractButton) )
|
||||
return TYPE_OTHER;
|
||||
|
||||
String value = getButtonTypeStr( (AbstractButton) c );
|
||||
if( value == null )
|
||||
return TYPE_OTHER;
|
||||
|
||||
switch( value ) {
|
||||
case BUTTON_TYPE_SQUARE: return TYPE_SQUARE;
|
||||
case BUTTON_TYPE_ROUND_RECT: return TYPE_ROUND_RECT;
|
||||
default: return TYPE_OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isHelpButton( Component c ) {
|
||||
return c instanceof JButton && clientPropertyEquals( (JButton) c, BUTTON_TYPE, BUTTON_TYPE_HELP );
|
||||
return c instanceof JButton && BUTTON_TYPE_HELP.equals( getButtonTypeStr( (JButton) c ) );
|
||||
}
|
||||
|
||||
static boolean isToolBarButton( Component c ) {
|
||||
return c.getParent() instanceof JToolBar ||
|
||||
(c instanceof AbstractButton && clientPropertyEquals( (AbstractButton) c, BUTTON_TYPE, BUTTON_TYPE_TOOLBAR_BUTTON ));
|
||||
(c instanceof AbstractButton && BUTTON_TYPE_TOOLBAR_BUTTON.equals( getButtonTypeStr( (AbstractButton) c ) ));
|
||||
}
|
||||
|
||||
static boolean isBorderlessButton( Component c ) {
|
||||
return c instanceof AbstractButton && BUTTON_TYPE_BORDERLESS.equals( getButtonTypeStr( (AbstractButton) c ) );
|
||||
}
|
||||
|
||||
static String getButtonTypeStr( AbstractButton c ) {
|
||||
// get from client property
|
||||
Object value = c.getClientProperty( BUTTON_TYPE );
|
||||
if( value instanceof String )
|
||||
return (String) value;
|
||||
|
||||
// get from styling property
|
||||
ButtonUI ui = c.getUI();
|
||||
return (ui instanceof FlatButtonUI) ? ((FlatButtonUI)ui).buttonType : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -315,8 +431,8 @@ public class FlatButtonUI
|
||||
int width = c.getWidth();
|
||||
int height = c.getHeight();
|
||||
|
||||
if( isToolBarButton ) {
|
||||
Insets spacing = UIScale.scale( toolbarSpacingInsets );
|
||||
if( isToolBarButton && c.getBorder() instanceof FlatButtonBorder ) {
|
||||
Insets spacing = UIScale.scale( ((FlatButtonBorder)c.getBorder()).toolbarSpacingInsets );
|
||||
x += spacing.left;
|
||||
y += spacing.top;
|
||||
width -= spacing.left + spacing.right;
|
||||
@@ -325,8 +441,10 @@ public class FlatButtonUI
|
||||
|
||||
// paint shadow
|
||||
Color shadowColor = def ? defaultShadowColor : this.shadowColor;
|
||||
if( !isToolBarButton && shadowColor != null && shadowWidth > 0 && focusWidth > 0 &&
|
||||
!(isFocusPainted( c ) && FlatUIUtils.isPermanentFocusOwner( c )) && c.isEnabled() )
|
||||
if( paintShadow &&
|
||||
shadowColor != null && shadowWidth > 0 && focusWidth > 0 && c.isEnabled() &&
|
||||
!isToolBarButton && !isBorderlessButton( c ) &&
|
||||
!(isFocusPainted( c ) && FlatUIUtils.isPermanentFocusOwner( c )) )
|
||||
{
|
||||
g2.setColor( shadowColor );
|
||||
g2.fill( new RoundRectangle2D.Float( focusWidth, focusWidth + UIScale.scale( (float) shadowWidth ),
|
||||
@@ -381,36 +499,35 @@ public class FlatButtonUI
|
||||
}
|
||||
|
||||
protected Color getBackground( JComponent c ) {
|
||||
boolean toolBarButton = isToolBarButton( c ) || isBorderlessButton( c );
|
||||
|
||||
// selected state
|
||||
if( ((AbstractButton)c).isSelected() ) {
|
||||
// in toolbar use same colors for disabled and enabled because
|
||||
// in toolbar use same background colors for disabled and enabled because
|
||||
// we assume that toolbar icon is shown disabled
|
||||
boolean toolBarButton = isToolBarButton( c );
|
||||
return buttonStateColor( c,
|
||||
toolBarButton ? toolbarSelectedBackground : selectedBackground,
|
||||
toolBarButton ? toolbarSelectedBackground : disabledSelectedBackground,
|
||||
null, null,
|
||||
null,
|
||||
null,
|
||||
toolBarButton ? toolbarPressedBackground : pressedBackground );
|
||||
}
|
||||
|
||||
if( !c.isEnabled() )
|
||||
return disabledBackground;
|
||||
|
||||
// toolbar button
|
||||
if( isToolBarButton( c ) ) {
|
||||
ButtonModel model = ((AbstractButton)c).getModel();
|
||||
if( model.isPressed() )
|
||||
return toolbarPressedBackground;
|
||||
if( model.isRollover() )
|
||||
return toolbarHoverBackground;
|
||||
|
||||
// use background of toolbar
|
||||
return c.getParent().getBackground();
|
||||
if( toolBarButton ) {
|
||||
Color bg = c.getBackground();
|
||||
return buttonStateColor( c,
|
||||
isCustomBackground( bg ) ? bg : null,
|
||||
null,
|
||||
null,
|
||||
toolbarHoverBackground,
|
||||
toolbarPressedBackground );
|
||||
}
|
||||
|
||||
boolean def = isDefaultButton( c );
|
||||
return buttonStateColor( c,
|
||||
getBackgroundBase( c, def ),
|
||||
null,
|
||||
disabledBackground,
|
||||
isCustomBackground( c.getBackground() ) ? null : (def ? defaultFocusedBackground : focusedBackground),
|
||||
def ? defaultHoverBackground : hoverBackground,
|
||||
def ? defaultPressedBackground : pressedBackground );
|
||||
@@ -432,16 +549,18 @@ public class FlatButtonUI
|
||||
public static Color buttonStateColor( Component c, Color enabledColor, Color disabledColor,
|
||||
Color focusedColor, Color hoverColor, Color pressedColor )
|
||||
{
|
||||
AbstractButton b = (c instanceof AbstractButton) ? (AbstractButton) c : null;
|
||||
|
||||
if( !c.isEnabled() )
|
||||
return disabledColor;
|
||||
|
||||
if( pressedColor != null && b != null && b.getModel().isPressed() )
|
||||
return pressedColor;
|
||||
if( c instanceof AbstractButton ) {
|
||||
ButtonModel model = ((AbstractButton)c).getModel();
|
||||
|
||||
if( hoverColor != null && b != null && b.getModel().isRollover() )
|
||||
return hoverColor;
|
||||
if( pressedColor != null && model.isPressed() )
|
||||
return pressedColor;
|
||||
|
||||
if( hoverColor != null && model.isRollover() )
|
||||
return hoverColor;
|
||||
}
|
||||
|
||||
if( focusedColor != null && isFocusPainted( c ) && FlatUIUtils.isPermanentFocusOwner( c ) )
|
||||
return focusedColor;
|
||||
@@ -453,7 +572,7 @@ public class FlatButtonUI
|
||||
if( !c.isEnabled() )
|
||||
return disabledText;
|
||||
|
||||
if( ((AbstractButton)c).isSelected() && !isToolBarButton( c ) )
|
||||
if( ((AbstractButton)c).isSelected() && !(isToolBarButton( c ) || isBorderlessButton( c )) )
|
||||
return selectedForeground;
|
||||
|
||||
// use component foreground if explicitly set
|
||||
@@ -480,22 +599,29 @@ public class FlatButtonUI
|
||||
|
||||
// make square or apply minimum width/height
|
||||
boolean isIconOnlyOrSingleCharacter = isIconOnlyOrSingleCharacterButton( c );
|
||||
if( clientPropertyBoolean( c, SQUARE_SIZE, false ) ) {
|
||||
if( clientPropertyBoolean( c, SQUARE_SIZE, squareSize ) ) {
|
||||
// make button square (increase width or height so that they are equal)
|
||||
prefSize.width = prefSize.height = Math.max( prefSize.width, prefSize.height );
|
||||
} else if( isIconOnlyOrSingleCharacter && ((AbstractButton)c).getIcon() == null ) {
|
||||
// make single-character-no-icon button square (increase width)
|
||||
prefSize.width = Math.max( prefSize.width, prefSize.height );
|
||||
} else if( !isIconOnlyOrSingleCharacter && !isToolBarButton( c ) && c.getBorder() instanceof FlatButtonBorder ) {
|
||||
} else if( !isIconOnlyOrSingleCharacter && !isToolBarButton( c ) &&
|
||||
c.getBorder() instanceof FlatButtonBorder && hasDefaultMargins( c ) )
|
||||
{
|
||||
// apply minimum width/height
|
||||
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
|
||||
prefSize.width = Math.max( prefSize.width, scale( FlatUIUtils.minimumWidth( c, minimumWidth ) ) + Math.round( focusWidth * 2 ) );
|
||||
prefSize.height = Math.max( prefSize.height, scale( FlatUIUtils.minimumHeight( c, 0 ) ) + Math.round( focusWidth * 2 ) );
|
||||
int fw = Math.round( FlatUIUtils.getBorderFocusWidth( c ) * 2 );
|
||||
prefSize.width = Math.max( prefSize.width, scale( FlatUIUtils.minimumWidth( c, minimumWidth ) ) + fw );
|
||||
prefSize.height = Math.max( prefSize.height, scale( FlatUIUtils.minimumHeight( c, minimumHeight ) ) + fw );
|
||||
}
|
||||
|
||||
return prefSize;
|
||||
}
|
||||
|
||||
private boolean hasDefaultMargins( JComponent c ) {
|
||||
Insets margin = ((AbstractButton)c).getMargin();
|
||||
return margin instanceof UIResource && Objects.equals( margin, defaultMargin );
|
||||
}
|
||||
|
||||
//---- class FlatButtonListener -------------------------------------------
|
||||
|
||||
protected class FlatButtonListener
|
||||
|
||||
@@ -18,16 +18,27 @@ package com.formdev.flatlaf.ui;
|
||||
|
||||
import static com.formdev.flatlaf.FlatClientProperties.*;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.ActionMap;
|
||||
import javax.swing.JFormattedTextField;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.UIResource;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.DefaultCaret;
|
||||
import javax.swing.text.DefaultEditorKit;
|
||||
import javax.swing.text.Document;
|
||||
import javax.swing.text.JTextComponent;
|
||||
import javax.swing.text.Utilities;
|
||||
|
||||
/**
|
||||
* Caret that can select all text on focus gained.
|
||||
* Also fixes Swing's double-click-and-drag behavior so that dragging after
|
||||
* a double-click extends selection by whole words.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
@@ -35,33 +46,100 @@ public class FlatCaret
|
||||
extends DefaultCaret
|
||||
implements UIResource
|
||||
{
|
||||
private final String selectAllOnFocusPolicy;
|
||||
private static final String KEY_CARET_INFO = "FlatLaf.internal.caretInfo";
|
||||
|
||||
private final String selectAllOnFocusPolicy;
|
||||
private final boolean selectAllOnMouseClick;
|
||||
|
||||
private boolean inInstall;
|
||||
private boolean wasFocused;
|
||||
private boolean wasTemporaryLost;
|
||||
private boolean isMousePressed;
|
||||
private boolean isWordSelection;
|
||||
private boolean isLineSelection;
|
||||
private int dragSelectionStart;
|
||||
private int dragSelectionEnd;
|
||||
|
||||
public FlatCaret( String selectAllOnFocusPolicy ) {
|
||||
public FlatCaret( String selectAllOnFocusPolicy, boolean selectAllOnMouseClick ) {
|
||||
this.selectAllOnFocusPolicy = selectAllOnFocusPolicy;
|
||||
this.selectAllOnMouseClick = selectAllOnMouseClick;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void install( JTextComponent c ) {
|
||||
super.install( c );
|
||||
// get caret info if switched theme
|
||||
long[] ci = (long[]) c.getClientProperty( KEY_CARET_INFO );
|
||||
if( ci != null ) {
|
||||
c.putClientProperty( KEY_CARET_INFO, null );
|
||||
|
||||
// the dot and mark are lost when switching LaF
|
||||
// --> move dot to end of text so that all text may be selected when it gains focus
|
||||
Document doc = c.getDocument();
|
||||
if( doc != null && getDot() == 0 && getMark() == 0 ) {
|
||||
int length = doc.getLength();
|
||||
if( length > 0 )
|
||||
setDot( length );
|
||||
// if caret info is too old assume that switched from FlatLaf
|
||||
// to another Laf and back to FlatLaf
|
||||
if( System.currentTimeMillis() - 500 > ci[3] )
|
||||
ci = null;
|
||||
}
|
||||
if( ci != null ) {
|
||||
// when switching theme, it is necessary to set blink rate before
|
||||
// invoking super.install() otherwise the caret does not blink
|
||||
setBlinkRate( (int) ci[2] );
|
||||
}
|
||||
|
||||
inInstall = true;
|
||||
try {
|
||||
super.install( c );
|
||||
} finally {
|
||||
inInstall = false;
|
||||
}
|
||||
|
||||
if( ci != null ) {
|
||||
// restore selection
|
||||
select( (int) ci[1], (int) ci[0] );
|
||||
|
||||
// if text component is focused, then caret and selection are visible,
|
||||
// but when switching theme, the component does not yet have
|
||||
// an highlighter and the selection is not painted
|
||||
// --> make selection temporary invisible later, then the caret
|
||||
// adds selection highlights to the text component highlighter
|
||||
if( isSelectionVisible() ) {
|
||||
EventQueue.invokeLater( () -> {
|
||||
if( isSelectionVisible() ) {
|
||||
setSelectionVisible( false );
|
||||
setSelectionVisible( true );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deinstall( JTextComponent c ) {
|
||||
// remember dot and mark (the selection) when switching theme
|
||||
c.putClientProperty( KEY_CARET_INFO, new long[] {
|
||||
getDot(),
|
||||
getMark(),
|
||||
getBlinkRate(),
|
||||
System.currentTimeMillis(),
|
||||
} );
|
||||
|
||||
super.deinstall( c );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void adjustVisibility( Rectangle nloc ) {
|
||||
JTextComponent c = getComponent();
|
||||
if( c != null && c.getUI() instanceof FlatTextFieldUI ) {
|
||||
// need to fix x location because JTextField.scrollRectToVisible() uses insets.left
|
||||
// (as BasicTextUI.getVisibleEditorRect() does),
|
||||
// but FlatTextFieldUI.getVisibleEditorRect() may add some padding
|
||||
Rectangle r = ((FlatTextFieldUI)c.getUI()).getVisibleEditorRect();
|
||||
if( r != null )
|
||||
nloc.x -= r.x - c.getInsets().left;
|
||||
}
|
||||
super.adjustVisibility( nloc );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focusGained( FocusEvent e ) {
|
||||
if( !wasTemporaryLost && !isMousePressed )
|
||||
if( !inInstall && !wasTemporaryLost && (!isMousePressed || selectAllOnMouseClick) )
|
||||
selectAllOnFocusGained();
|
||||
wasTemporaryLost = false;
|
||||
wasFocused = true;
|
||||
@@ -79,18 +157,76 @@ public class FlatCaret
|
||||
public void mousePressed( MouseEvent e ) {
|
||||
isMousePressed = true;
|
||||
super.mousePressed( e );
|
||||
|
||||
JTextComponent c = getComponent();
|
||||
|
||||
// left double-click starts word selection
|
||||
isWordSelection = e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton( e ) && !e.isConsumed();
|
||||
|
||||
// left triple-click starts line selection
|
||||
isLineSelection = e.getClickCount() == 3 && SwingUtilities.isLeftMouseButton( e ) && (!e.isConsumed() || c.getDragEnabled());
|
||||
|
||||
// select line
|
||||
// (this is also done in DefaultCaret.mouseClicked(), but this event is
|
||||
// sent when the mouse is released, which is too late for triple-click-and-drag)
|
||||
if( isLineSelection ) {
|
||||
ActionMap actionMap = c.getActionMap();
|
||||
Action selectLineAction = (actionMap != null)
|
||||
? actionMap.get( DefaultEditorKit.selectLineAction )
|
||||
: null;
|
||||
if( selectLineAction != null ) {
|
||||
selectLineAction.actionPerformed( new ActionEvent( c,
|
||||
ActionEvent.ACTION_PERFORMED, null, e.getWhen(), e.getModifiers() ) );
|
||||
}
|
||||
}
|
||||
|
||||
// remember selection where word/line selection starts to keep it always selected while dragging
|
||||
if( isWordSelection || isLineSelection ) {
|
||||
int mark = getMark();
|
||||
int dot = getDot();
|
||||
dragSelectionStart = Math.min( dot, mark );
|
||||
dragSelectionEnd = Math.max( dot, mark );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased( MouseEvent e ) {
|
||||
isMousePressed = false;
|
||||
isWordSelection = false;
|
||||
isLineSelection = false;
|
||||
super.mouseReleased( e );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged( MouseEvent e ) {
|
||||
if( (isWordSelection || isLineSelection) &&
|
||||
!e.isConsumed() && SwingUtilities.isLeftMouseButton( e ) )
|
||||
{
|
||||
// fix Swing's double/triple-click-and-drag behavior so that dragging after
|
||||
// a double/triple-click extends selection by whole words/lines
|
||||
JTextComponent c = getComponent();
|
||||
int pos = c.viewToModel( e.getPoint() );
|
||||
if( pos < 0 )
|
||||
return;
|
||||
|
||||
try {
|
||||
if( pos > dragSelectionEnd )
|
||||
select( dragSelectionStart, isWordSelection ? Utilities.getWordEnd( c, pos ) : Utilities.getRowEnd( c, pos ) );
|
||||
else if( pos < dragSelectionStart )
|
||||
select( dragSelectionEnd, isWordSelection ? Utilities.getWordStart( c, pos ) : Utilities.getRowStart( c, pos ) );
|
||||
else
|
||||
select( dragSelectionStart, dragSelectionEnd );
|
||||
} catch( BadLocationException ex ) {
|
||||
UIManager.getLookAndFeel().provideErrorFeedback( c );
|
||||
}
|
||||
} else
|
||||
super.mouseDragged( e );
|
||||
}
|
||||
|
||||
protected void selectAllOnFocusGained() {
|
||||
JTextComponent c = getComponent();
|
||||
Document doc = c.getDocument();
|
||||
if( doc == null || !c.isEnabled() || !c.isEditable() )
|
||||
if( doc == null || !c.isEnabled() || !c.isEditable() || FlatUIUtils.isCellEditor( c ) )
|
||||
return;
|
||||
|
||||
Object selectAllOnFocusPolicy = c.getClientProperty( SELECT_ALL_ON_FOCUS_POLICY );
|
||||
@@ -117,12 +253,34 @@ public class FlatCaret
|
||||
// select all
|
||||
if( c instanceof JFormattedTextField ) {
|
||||
EventQueue.invokeLater( () -> {
|
||||
setDot( 0 );
|
||||
moveDot( doc.getLength() );
|
||||
select( 0, doc.getLength() );
|
||||
} );
|
||||
} else {
|
||||
setDot( 0 );
|
||||
moveDot( doc.getLength() );
|
||||
select( 0, doc.getLength() );
|
||||
}
|
||||
}
|
||||
|
||||
private void select( int mark, int dot ) {
|
||||
if( mark != getMark() )
|
||||
setDot( mark );
|
||||
if( dot != getDot() )
|
||||
moveDot( dot );
|
||||
}
|
||||
|
||||
/** @since 1.4 */
|
||||
public void scrollCaretToVisible() {
|
||||
JTextComponent c = getComponent();
|
||||
if( c == null || c.getUI() == null )
|
||||
return;
|
||||
|
||||
try {
|
||||
Rectangle loc = c.getUI().modelToView( c, getDot(), getDotBias() );
|
||||
if( loc != null ) {
|
||||
adjustVisibility( loc );
|
||||
damage( loc );
|
||||
}
|
||||
} catch( BadLocationException ex ) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,13 +16,19 @@
|
||||
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Map;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.LookAndFeel;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
|
||||
/**
|
||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JCheckBoxMenuItem}.
|
||||
@@ -54,13 +60,22 @@ import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
|
||||
*/
|
||||
public class FlatCheckBoxMenuItemUI
|
||||
extends BasicCheckBoxMenuItemUI
|
||||
implements StyleableUI
|
||||
{
|
||||
private FlatMenuItemRenderer renderer;
|
||||
private Map<String, Object> oldStyleValues;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatCheckBoxMenuItemUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installUI( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
installStyle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installDefaults() {
|
||||
super.installDefaults();
|
||||
@@ -75,12 +90,59 @@ public class FlatCheckBoxMenuItemUI
|
||||
super.uninstallDefaults();
|
||||
|
||||
renderer = null;
|
||||
oldStyleValues = null;
|
||||
}
|
||||
|
||||
protected FlatMenuItemRenderer createRenderer() {
|
||||
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
||||
return FlatStylingSupport.createPropertyChangeListener( c, this::installStyle, super.createPropertyChangeListener( c ) );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void installStyle() {
|
||||
try {
|
||||
applyStyle( FlatStylingSupport.getResolvedStyle( menuItem, "CheckBoxMenuItem" ) );
|
||||
} catch( RuntimeException ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void applyStyle( Object style ) {
|
||||
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected Object applyStyleProperty( String key, Object value ) {
|
||||
try {
|
||||
return renderer.applyStyleProperty( key, value );
|
||||
} catch ( UnknownStyleException ex ) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
Object oldValue;
|
||||
switch( key ) {
|
||||
// BasicMenuItemUI
|
||||
case "selectionBackground": oldValue = selectionBackground; selectionBackground = (Color) value; return oldValue;
|
||||
case "selectionForeground": oldValue = selectionForeground; selectionForeground = (Color) value; return oldValue;
|
||||
case "disabledForeground": oldValue = disabledForeground; disabledForeground = (Color) value; return oldValue;
|
||||
case "acceleratorForeground": oldValue = acceleratorForeground; acceleratorForeground = (Color) value; return oldValue;
|
||||
case "acceleratorSelectionForeground": oldValue = acceleratorSelectionForeground; acceleratorSelectionForeground = (Color) value; return oldValue;
|
||||
}
|
||||
|
||||
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, menuItem, key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
return FlatMenuItemUI.getStyleableInfos( renderer );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
|
||||
return renderer.getPreferredMenuItemSize();
|
||||
|
||||
@@ -42,16 +42,25 @@ import javax.swing.plaf.ComponentUI;
|
||||
public class FlatCheckBoxUI
|
||||
extends FlatRadioButtonUI
|
||||
{
|
||||
private static ComponentUI instance;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
if( instance == null )
|
||||
instance = new FlatCheckBoxUI();
|
||||
return instance;
|
||||
return FlatUIUtils.canUseSharedUI( c )
|
||||
? FlatUIUtils.createSharedUI( FlatCheckBoxUI.class, () -> new FlatCheckBoxUI( true ) )
|
||||
: new FlatCheckBoxUI( false );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected FlatCheckBoxUI( boolean shared ) {
|
||||
super( shared );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPropertyPrefix() {
|
||||
return "CheckBox.";
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
String getStyleType() {
|
||||
return "CheckBox";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,30 +16,37 @@
|
||||
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import static com.formdev.flatlaf.FlatClientProperties.*;
|
||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
||||
import static com.formdev.flatlaf.util.UIScale.unscale;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.ComponentOrientation;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.GraphicsConfiguration;
|
||||
import java.awt.Insets;
|
||||
import java.awt.LayoutManager;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Shape;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.FocusListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.ComboBoxEditor;
|
||||
import javax.swing.CellRendererPane;
|
||||
import javax.swing.DefaultListCellRenderer;
|
||||
import javax.swing.InputMap;
|
||||
import javax.swing.JButton;
|
||||
@@ -57,13 +64,15 @@ import javax.swing.UIManager;
|
||||
import javax.swing.border.AbstractBorder;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.UIResource;
|
||||
import javax.swing.plaf.basic.BasicComboBoxUI;
|
||||
import javax.swing.plaf.basic.BasicComboPopup;
|
||||
import javax.swing.plaf.basic.ComboPopup;
|
||||
import javax.swing.text.JTextComponent;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
import com.formdev.flatlaf.util.SystemInfo;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
/**
|
||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JComboBox}.
|
||||
@@ -83,62 +92,108 @@ import com.formdev.flatlaf.util.UIScale;
|
||||
* @uiDefault ComboBox.editorColumns int
|
||||
* @uiDefault ComboBox.maximumRowCount int
|
||||
* @uiDefault ComboBox.buttonStyle String auto (default), button or none
|
||||
* @uiDefault Component.arrowType String triangle (default) or chevron
|
||||
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||
* @uiDefault Component.isIntelliJTheme boolean
|
||||
* @uiDefault Component.borderColor Color
|
||||
* @uiDefault Component.disabledBorderColor Color
|
||||
* @uiDefault ComboBox.editableBackground Color optional; defaults to ComboBox.background
|
||||
* @uiDefault ComboBox.focusedBackground Color optional
|
||||
* @uiDefault ComboBox.disabledBackground Color
|
||||
* @uiDefault ComboBox.disabledForeground Color
|
||||
* @uiDefault ComboBox.buttonBackground Color
|
||||
* @uiDefault ComboBox.buttonEditableBackground Color
|
||||
* @uiDefault ComboBox.buttonFocusedBackground Color optional; defaults to ComboBox.focusedBackground
|
||||
* @uiDefault ComboBox.buttonSeparatorWidth int or float optional; defaults to Component.borderWidth
|
||||
* @uiDefault ComboBox.buttonSeparatorColor Color optional
|
||||
* @uiDefault ComboBox.buttonDisabledSeparatorColor Color optional
|
||||
* @uiDefault ComboBox.buttonArrowColor Color
|
||||
* @uiDefault ComboBox.buttonDisabledArrowColor Color
|
||||
* @uiDefault ComboBox.buttonHoverArrowColor Color
|
||||
* @uiDefault ComboBox.buttonPressedArrowColor Color
|
||||
* @uiDefault ComboBox.popupBackground Color optional
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
public class FlatComboBoxUI
|
||||
extends BasicComboBoxUI
|
||||
implements StyleableUI
|
||||
{
|
||||
protected int minimumWidth;
|
||||
protected int editorColumns;
|
||||
protected String buttonStyle;
|
||||
protected String arrowType;
|
||||
@Styleable protected int minimumWidth;
|
||||
@Styleable protected int editorColumns;
|
||||
@Styleable protected String buttonStyle;
|
||||
@Styleable protected String arrowType;
|
||||
protected boolean isIntelliJTheme;
|
||||
protected Color borderColor;
|
||||
protected Color disabledBorderColor;
|
||||
|
||||
protected Color editableBackground;
|
||||
protected Color disabledBackground;
|
||||
protected Color disabledForeground;
|
||||
@Styleable protected Color editableBackground;
|
||||
@Styleable protected Color focusedBackground;
|
||||
@Styleable protected Color disabledBackground;
|
||||
@Styleable protected Color disabledForeground;
|
||||
|
||||
protected Color buttonBackground;
|
||||
protected Color buttonEditableBackground;
|
||||
protected Color buttonArrowColor;
|
||||
protected Color buttonDisabledArrowColor;
|
||||
protected Color buttonHoverArrowColor;
|
||||
@Styleable protected Color buttonBackground;
|
||||
@Styleable protected Color buttonEditableBackground;
|
||||
@Styleable protected Color buttonFocusedBackground;
|
||||
/** @since 2 */ @Styleable protected float buttonSeparatorWidth;
|
||||
/** @since 2 */ @Styleable protected Color buttonSeparatorColor;
|
||||
/** @since 2 */ @Styleable protected Color buttonDisabledSeparatorColor;
|
||||
@Styleable protected Color buttonArrowColor;
|
||||
@Styleable protected Color buttonDisabledArrowColor;
|
||||
@Styleable protected Color buttonHoverArrowColor;
|
||||
@Styleable protected Color buttonPressedArrowColor;
|
||||
|
||||
@Styleable protected Color popupBackground;
|
||||
|
||||
private MouseListener hoverListener;
|
||||
protected boolean hover;
|
||||
protected boolean pressed;
|
||||
|
||||
private WeakReference<Component> lastRendererComponent;
|
||||
private CellPaddingBorder paddingBorder;
|
||||
|
||||
private Map<String, Object> oldStyleValues;
|
||||
private AtomicBoolean borderShared;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatComboBoxUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installUI( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
installStyle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installListeners() {
|
||||
super.installListeners();
|
||||
|
||||
hoverListener = new FlatUIUtils.HoverListener( null, h -> {
|
||||
if( !comboBox.isEditable() ) {
|
||||
hover = h;
|
||||
if( arrowButton != null )
|
||||
hoverListener = new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseEntered( MouseEvent e ) {
|
||||
hover = true;
|
||||
repaintArrowButton();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited( MouseEvent e ) {
|
||||
hover = false;
|
||||
repaintArrowButton();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed( MouseEvent e ) {
|
||||
pressed = true;
|
||||
repaintArrowButton();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased( MouseEvent e ) {
|
||||
pressed = false;
|
||||
repaintArrowButton();
|
||||
}
|
||||
|
||||
private void repaintArrowButton() {
|
||||
if( arrowButton != null && !comboBox.isEditable() )
|
||||
arrowButton.repaint();
|
||||
}
|
||||
} );
|
||||
};
|
||||
comboBox.addMouseListener( hoverListener );
|
||||
}
|
||||
|
||||
@@ -161,26 +216,31 @@ public class FlatComboBoxUI
|
||||
buttonStyle = UIManager.getString( "ComboBox.buttonStyle" );
|
||||
arrowType = UIManager.getString( "Component.arrowType" );
|
||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
||||
borderColor = UIManager.getColor( "Component.borderColor" );
|
||||
disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
|
||||
|
||||
editableBackground = UIManager.getColor( "ComboBox.editableBackground" );
|
||||
focusedBackground = UIManager.getColor( "ComboBox.focusedBackground" );
|
||||
disabledBackground = UIManager.getColor( "ComboBox.disabledBackground" );
|
||||
disabledForeground = UIManager.getColor( "ComboBox.disabledForeground" );
|
||||
|
||||
buttonBackground = UIManager.getColor( "ComboBox.buttonBackground" );
|
||||
buttonFocusedBackground = UIManager.getColor( "ComboBox.buttonFocusedBackground" );
|
||||
buttonEditableBackground = UIManager.getColor( "ComboBox.buttonEditableBackground" );
|
||||
buttonSeparatorWidth = FlatUIUtils.getUIFloat( "ComboBox.buttonSeparatorWidth", FlatUIUtils.getUIFloat( "Component.borderWidth", 1 ) );
|
||||
buttonSeparatorColor = UIManager.getColor( "ComboBox.buttonSeparatorColor" );
|
||||
buttonDisabledSeparatorColor = UIManager.getColor( "ComboBox.buttonDisabledSeparatorColor" );
|
||||
buttonArrowColor = UIManager.getColor( "ComboBox.buttonArrowColor" );
|
||||
buttonDisabledArrowColor = UIManager.getColor( "ComboBox.buttonDisabledArrowColor" );
|
||||
buttonHoverArrowColor = UIManager.getColor( "ComboBox.buttonHoverArrowColor" );
|
||||
buttonPressedArrowColor = UIManager.getColor( "ComboBox.buttonPressedArrowColor" );
|
||||
|
||||
popupBackground = UIManager.getColor( "ComboBox.popupBackground" );
|
||||
|
||||
// set maximumRowCount
|
||||
int maximumRowCount = UIManager.getInt( "ComboBox.maximumRowCount" );
|
||||
if( maximumRowCount > 0 && maximumRowCount != 8 && comboBox.getMaximumRowCount() == 8 )
|
||||
comboBox.setMaximumRowCount( maximumRowCount );
|
||||
|
||||
// scale
|
||||
padding = UIScale.scale( padding );
|
||||
paddingBorder = new CellPaddingBorder( padding );
|
||||
|
||||
MigLayoutVisualPadding.install( comboBox );
|
||||
}
|
||||
@@ -189,18 +249,27 @@ public class FlatComboBoxUI
|
||||
protected void uninstallDefaults() {
|
||||
super.uninstallDefaults();
|
||||
|
||||
borderColor = null;
|
||||
disabledBorderColor = null;
|
||||
|
||||
editableBackground = null;
|
||||
focusedBackground = null;
|
||||
disabledBackground = null;
|
||||
disabledForeground = null;
|
||||
|
||||
buttonBackground = null;
|
||||
buttonEditableBackground = null;
|
||||
buttonFocusedBackground = null;
|
||||
buttonSeparatorColor = null;
|
||||
buttonDisabledSeparatorColor = null;
|
||||
buttonArrowColor = null;
|
||||
buttonDisabledArrowColor = null;
|
||||
buttonHoverArrowColor = null;
|
||||
buttonPressedArrowColor = null;
|
||||
|
||||
popupBackground = null;
|
||||
|
||||
paddingBorder.uninstall();
|
||||
|
||||
oldStyleValues = null;
|
||||
borderShared = null;
|
||||
|
||||
MigLayoutVisualPadding.uninstall( comboBox );
|
||||
}
|
||||
@@ -212,9 +281,25 @@ public class FlatComboBoxUI
|
||||
public void layoutContainer( Container parent ) {
|
||||
super.layoutContainer( parent );
|
||||
|
||||
if ( editor != null && padding != null ) {
|
||||
// fix editor bounds by subtracting padding
|
||||
editor.setBounds( FlatUIUtils.subtractInsets( editor.getBounds(), padding ) );
|
||||
if( arrowButton != null ) {
|
||||
// limit button width to height of a raw combobox (without insets)
|
||||
FontMetrics fm = comboBox.getFontMetrics( comboBox.getFont() );
|
||||
int maxButtonWidth = fm.getHeight() + scale( padding.top ) + scale( padding.bottom );
|
||||
|
||||
Insets insets = getInsets();
|
||||
int buttonWidth = Math.min( parent.getPreferredSize().height - insets.top - insets.bottom, maxButtonWidth );
|
||||
if( buttonWidth != arrowButton.getWidth() ) {
|
||||
// set width of arrow button to preferred height of combobox
|
||||
int xOffset = comboBox.getComponentOrientation().isLeftToRight()
|
||||
? arrowButton.getWidth() - buttonWidth
|
||||
: 0;
|
||||
arrowButton.setBounds( arrowButton.getX() + xOffset, arrowButton.getY(),
|
||||
buttonWidth, arrowButton.getHeight() );
|
||||
|
||||
// update editor bounds
|
||||
if( editor != null )
|
||||
editor.setBounds( rectangleForCurrentValue() );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -242,29 +327,44 @@ public class FlatComboBoxUI
|
||||
|
||||
@Override
|
||||
protected PropertyChangeListener createPropertyChangeListener() {
|
||||
return new BasicComboBoxUI.PropertyChangeHandler() {
|
||||
@Override
|
||||
public void propertyChange( PropertyChangeEvent e ) {
|
||||
super.propertyChange( e );
|
||||
PropertyChangeListener superListener = super.createPropertyChangeListener();
|
||||
return e -> {
|
||||
superListener.propertyChange( e );
|
||||
|
||||
Object source = e.getSource();
|
||||
String propertyName = e.getPropertyName();
|
||||
Object source = e.getSource();
|
||||
String propertyName = e.getPropertyName();
|
||||
|
||||
if( editor != null &&
|
||||
((source == comboBox && propertyName == "foreground") ||
|
||||
(source == editor && propertyName == "enabled")) )
|
||||
{
|
||||
// fix editor component colors
|
||||
updateEditorColors();
|
||||
} else if( editor != null && source == comboBox && propertyName == "componentOrientation" ) {
|
||||
ComponentOrientation o = (ComponentOrientation) e.getNewValue();
|
||||
editor.applyComponentOrientation( o );
|
||||
} else if( editor != null && FlatClientProperties.PLACEHOLDER_TEXT.equals( propertyName ) )
|
||||
editor.repaint();
|
||||
else if( FlatClientProperties.COMPONENT_ROUND_RECT.equals( propertyName ) )
|
||||
comboBox.repaint();
|
||||
else if( FlatClientProperties.MINIMUM_WIDTH.equals( propertyName ) )
|
||||
comboBox.revalidate();
|
||||
if( editor != null &&
|
||||
((source == comboBox && propertyName == "foreground") ||
|
||||
(source == editor && propertyName == "enabled")) )
|
||||
{
|
||||
// fix editor component colors
|
||||
updateEditorColors();
|
||||
} else if( editor != null && source == comboBox && propertyName == "componentOrientation" ) {
|
||||
ComponentOrientation o = (ComponentOrientation) e.getNewValue();
|
||||
editor.applyComponentOrientation( o );
|
||||
} else {
|
||||
switch( propertyName ) {
|
||||
case PLACEHOLDER_TEXT:
|
||||
if( editor != null )
|
||||
editor.repaint();
|
||||
break;
|
||||
|
||||
case COMPONENT_ROUND_RECT:
|
||||
comboBox.repaint();
|
||||
break;
|
||||
|
||||
case MINIMUM_WIDTH:
|
||||
comboBox.revalidate();
|
||||
break;
|
||||
|
||||
case STYLE:
|
||||
case STYLE_CLASS:
|
||||
installStyle();
|
||||
comboBox.revalidate();
|
||||
comboBox.repaint();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -275,35 +375,32 @@ public class FlatComboBoxUI
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ComboBoxEditor createEditor() {
|
||||
ComboBoxEditor comboBoxEditor = super.createEditor();
|
||||
protected void configureEditor() {
|
||||
super.configureEditor();
|
||||
|
||||
Component editor = comboBoxEditor.getEditorComponent();
|
||||
if( editor instanceof JTextField ) {
|
||||
JTextField textField = (JTextField) editor;
|
||||
textField.setColumns( editorColumns );
|
||||
|
||||
// assign a non-null and non-javax.swing.plaf.UIResource border to the text field,
|
||||
// otherwise it is replaced with default text field border when switching LaF
|
||||
// because javax.swing.plaf.basic.BasicComboBoxEditor.BorderlessTextField.setBorder()
|
||||
// uses "border instanceof javax.swing.plaf.basic.BasicComboBoxEditor.UIResource"
|
||||
// instead of "border instanceof javax.swing.plaf.UIResource"
|
||||
textField.setBorder( BorderFactory.createEmptyBorder() );
|
||||
// remove default text field border from editor
|
||||
Border border = textField.getBorder();
|
||||
if( border == null || border instanceof UIResource ) {
|
||||
// assign a non-null and non-javax.swing.plaf.UIResource border to the text field,
|
||||
// otherwise it is replaced with default text field border when switching LaF
|
||||
// because javax.swing.plaf.basic.BasicComboBoxEditor.BorderlessTextField.setBorder()
|
||||
// uses "border instanceof javax.swing.plaf.basic.BasicComboBoxEditor.UIResource"
|
||||
// instead of "border instanceof javax.swing.plaf.UIResource"
|
||||
textField.setBorder( BorderFactory.createEmptyBorder() );
|
||||
}
|
||||
}
|
||||
|
||||
return comboBoxEditor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureEditor() {
|
||||
super.configureEditor();
|
||||
|
||||
// explicitly make non-opaque
|
||||
if( editor instanceof JComponent )
|
||||
((JComponent)editor).setOpaque( false );
|
||||
|
||||
editor.applyComponentOrientation( comboBox.getComponentOrientation() );
|
||||
|
||||
updateEditorPadding();
|
||||
updateEditorColors();
|
||||
|
||||
// macOS
|
||||
@@ -320,6 +417,25 @@ public class FlatComboBoxUI
|
||||
}
|
||||
}
|
||||
|
||||
private void updateEditorPadding() {
|
||||
if( !(editor instanceof JTextField) )
|
||||
return;
|
||||
|
||||
JTextField textField = (JTextField) editor;
|
||||
Insets insets = textField.getInsets();
|
||||
Insets pad = padding;
|
||||
if( insets.top != 0 || insets.left != 0 || insets.bottom != 0 || insets.right != 0 ) {
|
||||
// if text field has custom border, subtract text field insets from padding
|
||||
pad = new Insets(
|
||||
unscale( Math.max( scale( padding.top ) - insets.top, 0 ) ),
|
||||
unscale( Math.max( scale( padding.left ) - insets.left, 0 ) ),
|
||||
unscale( Math.max( scale( padding.bottom ) - insets.bottom, 0 ) ),
|
||||
unscale( Math.max( scale( padding.right ) - insets.right, 0 ) )
|
||||
);
|
||||
}
|
||||
textField.putClientProperty( TEXT_FIELD_PADDING, pad );
|
||||
}
|
||||
|
||||
private void updateEditorColors() {
|
||||
// use non-UIResource colors because when SwingUtilities.updateComponentTreeUI()
|
||||
// is used, then the editor is updated after the combobox and the
|
||||
@@ -336,17 +452,78 @@ public class FlatComboBoxUI
|
||||
return new FlatComboBoxButton();
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void installStyle() {
|
||||
try {
|
||||
applyStyle( FlatStylingSupport.getResolvedStyle( comboBox, "ComboBox" ) );
|
||||
} catch( RuntimeException ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void applyStyle( Object style ) {
|
||||
Insets oldPadding = padding;
|
||||
int oldEditorColumns = editorColumns;
|
||||
|
||||
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||
|
||||
if( !padding.equals( oldPadding ) ) {
|
||||
paddingBorder.padding = padding;
|
||||
updateEditorPadding();
|
||||
}
|
||||
if( arrowButton instanceof FlatComboBoxButton )
|
||||
((FlatComboBoxButton)arrowButton).updateStyle();
|
||||
if( popup instanceof FlatComboPopup )
|
||||
((FlatComboPopup)popup).updateStyle();
|
||||
if( editorColumns != oldEditorColumns && editor instanceof JTextField )
|
||||
((JTextField)editor).setColumns( editorColumns );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected Object applyStyleProperty( String key, Object value ) {
|
||||
// BasicComboBoxUI
|
||||
if( key.equals( "padding" ) ) {
|
||||
Object oldValue = padding;
|
||||
padding = (Insets) value;
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
if( borderShared == null )
|
||||
borderShared = new AtomicBoolean( true );
|
||||
return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, comboBox, borderShared );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
Map<String, Class<?>> infos = new FlatStylingSupport.StyleableInfosMap<>();
|
||||
infos.put( "padding", Insets.class );
|
||||
FlatStylingSupport.collectAnnotatedStyleableInfos( this, infos );
|
||||
FlatStylingSupport.collectStyleableInfos( comboBox.getBorder(), infos );
|
||||
return infos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update( Graphics g, JComponent c ) {
|
||||
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
|
||||
float arc = FlatUIUtils.getBorderArc( c );
|
||||
boolean paintBackground = true;
|
||||
|
||||
// check whether used as cell renderer
|
||||
boolean isCellRenderer = c.getParent() instanceof CellRendererPane;
|
||||
if( isCellRenderer ) {
|
||||
focusWidth = 0;
|
||||
arc = 0;
|
||||
paintBackground = isCellRendererBackgroundChanged();
|
||||
}
|
||||
|
||||
// fill background if opaque to avoid garbage if user sets opaque to true
|
||||
if( c.isOpaque() && (focusWidth > 0 || arc > 0) )
|
||||
FlatUIUtils.paintParentBackground( g, c );
|
||||
|
||||
Graphics2D g2 = (Graphics2D) g;
|
||||
FlatUIUtils.setRenderingHints( g2 );
|
||||
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g2 );
|
||||
|
||||
int width = c.getWidth();
|
||||
int height = c.getHeight();
|
||||
@@ -357,28 +534,40 @@ public class FlatComboBoxUI
|
||||
boolean isLeftToRight = comboBox.getComponentOrientation().isLeftToRight();
|
||||
|
||||
// paint background
|
||||
g2.setColor( getBackground( enabled ) );
|
||||
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
|
||||
|
||||
// paint arrow button background
|
||||
if( enabled ) {
|
||||
g2.setColor( paintButton ? buttonEditableBackground : buttonBackground );
|
||||
Shape oldClip = g2.getClip();
|
||||
if( isLeftToRight )
|
||||
g2.clipRect( arrowX, 0, width - arrowX, height );
|
||||
else
|
||||
g2.clipRect( 0, 0, arrowX + arrowWidth, height );
|
||||
if( paintBackground || c.isOpaque() ) {
|
||||
g2.setColor( getBackground( enabled ) );
|
||||
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
|
||||
g2.setClip( oldClip );
|
||||
|
||||
// paint arrow button background
|
||||
if( enabled && !isCellRenderer ) {
|
||||
g2.setColor( paintButton
|
||||
? buttonEditableBackground
|
||||
: (buttonFocusedBackground != null || focusedBackground != null) && isPermanentFocusOwner( comboBox )
|
||||
? (buttonFocusedBackground != null ? buttonFocusedBackground : focusedBackground)
|
||||
: buttonBackground );
|
||||
Shape oldClip = g2.getClip();
|
||||
if( isLeftToRight )
|
||||
g2.clipRect( arrowX, 0, width - arrowX, height );
|
||||
else
|
||||
g2.clipRect( 0, 0, arrowX + arrowWidth, height );
|
||||
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
|
||||
g2.setClip( oldClip );
|
||||
}
|
||||
|
||||
// paint vertical line between value and arrow button
|
||||
if( paintButton ) {
|
||||
Color separatorColor = enabled ? buttonSeparatorColor : buttonDisabledSeparatorColor;
|
||||
if( separatorColor != null ) {
|
||||
g2.setColor( separatorColor );
|
||||
float lw = scale( buttonSeparatorWidth );
|
||||
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
|
||||
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2)) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// paint vertical line between value and arrow button
|
||||
if( paintButton ) {
|
||||
g2.setColor( enabled ? borderColor : disabledBorderColor );
|
||||
float lw = scale( 1f );
|
||||
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
|
||||
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2)) );
|
||||
}
|
||||
// avoid that the "current value" renderer is invoked with enabled antialiasing
|
||||
FlatUIUtils.resetRenderingHints( g2, oldRenderingHints );
|
||||
|
||||
paint( g, c );
|
||||
}
|
||||
@@ -386,30 +575,33 @@ public class FlatComboBoxUI
|
||||
@Override
|
||||
@SuppressWarnings( "unchecked" )
|
||||
public void paintCurrentValue( Graphics g, Rectangle bounds, boolean hasFocus ) {
|
||||
paddingBorder.uninstall();
|
||||
|
||||
ListCellRenderer<Object> renderer = comboBox.getRenderer();
|
||||
uninstallCellPaddingBorder( renderer );
|
||||
if( renderer == null )
|
||||
renderer = new DefaultListCellRenderer();
|
||||
Component c = renderer.getListCellRendererComponent( listBox, comboBox.getSelectedItem(), -1, false, false );
|
||||
c.setFont( comboBox.getFont() );
|
||||
c.applyComponentOrientation( comboBox.getComponentOrientation() );
|
||||
uninstallCellPaddingBorder( c );
|
||||
|
||||
boolean enabled = comboBox.isEnabled();
|
||||
c.setBackground( getBackground( enabled ) );
|
||||
c.setForeground( getForeground( enabled ) );
|
||||
|
||||
// make renderer component temporary non-opaque to avoid that renderer paints
|
||||
// background outside of border if combobox uses larger arc for edges
|
||||
// (e.g. FlatClientProperties.COMPONENT_ROUND_RECT is true)
|
||||
if( c instanceof JComponent )
|
||||
((JComponent)c).setOpaque( false );
|
||||
|
||||
boolean shouldValidate = (c instanceof JPanel);
|
||||
if( padding != null )
|
||||
bounds = FlatUIUtils.subtractInsets( bounds, padding );
|
||||
|
||||
// increase the size of the rendering area to make sure that the text
|
||||
// is vertically aligned with other component types (e.g. JTextField)
|
||||
Insets rendererInsets = getRendererComponentInsets( c );
|
||||
if( rendererInsets != null )
|
||||
bounds = FlatUIUtils.addInsets( bounds, rendererInsets );
|
||||
|
||||
paddingBorder.install( c );
|
||||
currentValuePane.paintComponent( g, c, comboBox, bounds.x, bounds.y, bounds.width, bounds.height, shouldValidate );
|
||||
paddingBorder.uninstall();
|
||||
|
||||
if( c instanceof JComponent )
|
||||
((JComponent)c).setOpaque( true );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -418,9 +610,20 @@ public class FlatComboBoxUI
|
||||
}
|
||||
|
||||
protected Color getBackground( boolean enabled ) {
|
||||
return enabled
|
||||
? (editableBackground != null && comboBox.isEditable() ? editableBackground : comboBox.getBackground())
|
||||
: (isIntelliJTheme ? FlatUIUtils.getParentBackground( comboBox ) : disabledBackground);
|
||||
if( enabled ) {
|
||||
Color background = comboBox.getBackground();
|
||||
|
||||
// always use explicitly set color
|
||||
if( !(background instanceof UIResource) )
|
||||
return background;
|
||||
|
||||
// focused
|
||||
if( focusedBackground != null && isPermanentFocusOwner( comboBox ) )
|
||||
return focusedBackground;
|
||||
|
||||
return (editableBackground != null && comboBox.isEditable()) ? editableBackground : background;
|
||||
} else
|
||||
return isIntelliJTheme ? FlatUIUtils.getParentBackground( comboBox ) : disabledBackground;
|
||||
}
|
||||
|
||||
protected Color getForeground( boolean enabled ) {
|
||||
@@ -430,75 +633,69 @@ public class FlatComboBoxUI
|
||||
@Override
|
||||
public Dimension getMinimumSize( JComponent c ) {
|
||||
Dimension minimumSize = super.getMinimumSize( c );
|
||||
minimumSize.width = Math.max( minimumSize.width, scale( FlatUIUtils.minimumWidth( c, minimumWidth ) ) );
|
||||
int fw = Math.round( FlatUIUtils.getBorderFocusWidth( c ) * 2 );
|
||||
minimumSize.width = Math.max( minimumSize.width, scale( FlatUIUtils.minimumWidth( c, minimumWidth ) ) + fw );
|
||||
return minimumSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dimension getDefaultSize() {
|
||||
@SuppressWarnings( "unchecked" )
|
||||
ListCellRenderer<Object> renderer = comboBox.getRenderer();
|
||||
uninstallCellPaddingBorder( renderer );
|
||||
|
||||
paddingBorder.uninstall();
|
||||
Dimension size = super.getDefaultSize();
|
||||
|
||||
uninstallCellPaddingBorder( renderer );
|
||||
paddingBorder.uninstall();
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dimension getDisplaySize() {
|
||||
@SuppressWarnings( "unchecked" )
|
||||
ListCellRenderer<Object> renderer = comboBox.getRenderer();
|
||||
uninstallCellPaddingBorder( renderer );
|
||||
|
||||
paddingBorder.uninstall();
|
||||
Dimension displaySize = super.getDisplaySize();
|
||||
paddingBorder.uninstall();
|
||||
|
||||
// remove padding added in super.getDisplaySize()
|
||||
int displayWidth = displaySize.width - padding.left - padding.right;
|
||||
int displayHeight = displaySize.height - padding.top - padding.bottom;
|
||||
|
||||
// recalculate width without hardcoded 100 under special conditions
|
||||
if( displaySize.width == 100 + padding.left + padding.right &&
|
||||
if( displayWidth == 100 &&
|
||||
comboBox.isEditable() &&
|
||||
comboBox.getItemCount() == 0 &&
|
||||
comboBox.getPrototypeDisplayValue() == null )
|
||||
{
|
||||
int width = getDefaultSize().width;
|
||||
width = Math.max( width, editor.getPreferredSize().width );
|
||||
width += padding.left + padding.right;
|
||||
displaySize = new Dimension( width, displaySize.height );
|
||||
displayWidth = Math.max( getDefaultSize().width, editor.getPreferredSize().width );
|
||||
}
|
||||
|
||||
uninstallCellPaddingBorder( renderer );
|
||||
return displaySize;
|
||||
return new Dimension( displayWidth, displayHeight );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dimension getSizeForComponent( Component comp ) {
|
||||
paddingBorder.install( comp );
|
||||
Dimension size = super.getSizeForComponent( comp );
|
||||
|
||||
// remove the renderer border top/bottom insets from the size to make sure that
|
||||
// the combobox gets the same height as other component types (e.g. JTextField)
|
||||
Insets rendererInsets = getRendererComponentInsets( comp );
|
||||
if( rendererInsets != null )
|
||||
size = new Dimension( size.width, size.height - rendererInsets.top - rendererInsets.bottom );
|
||||
|
||||
paddingBorder.uninstall();
|
||||
return size;
|
||||
}
|
||||
|
||||
private Insets getRendererComponentInsets( Component rendererComponent ) {
|
||||
if( rendererComponent instanceof JComponent ) {
|
||||
Border rendererBorder = ((JComponent)rendererComponent).getBorder();
|
||||
if( rendererBorder != null )
|
||||
return rendererBorder.getBorderInsets( rendererComponent );
|
||||
}
|
||||
|
||||
return null;
|
||||
private boolean isCellRenderer() {
|
||||
return comboBox.getParent() instanceof CellRendererPane;
|
||||
}
|
||||
|
||||
private void uninstallCellPaddingBorder( Object o ) {
|
||||
CellPaddingBorder.uninstall( o );
|
||||
if( lastRendererComponent != null ) {
|
||||
CellPaddingBorder.uninstall( lastRendererComponent );
|
||||
lastRendererComponent = null;
|
||||
}
|
||||
private boolean isCellRendererBackgroundChanged() {
|
||||
// parent is a CellRendererPane, parentParent is e.g. a JTable
|
||||
Container parentParent = comboBox.getParent().getParent();
|
||||
return parentParent != null && !comboBox.getBackground().equals( parentParent.getBackground() );
|
||||
}
|
||||
|
||||
/** @since 1.3 */
|
||||
public static boolean isPermanentFocusOwner( JComboBox<?> comboBox ) {
|
||||
if( comboBox.isEditable() ) {
|
||||
if( FlatUIUtils.isPermanentFocusOwner( comboBox ) )
|
||||
return true;
|
||||
|
||||
Component editorComponent = comboBox.getEditor().getEditorComponent();
|
||||
return (editorComponent != null) ? FlatUIUtils.isPermanentFocusOwner( editorComponent ) : false;
|
||||
} else
|
||||
return FlatUIUtils.isPermanentFocusOwner( comboBox );
|
||||
}
|
||||
|
||||
//---- class FlatComboBoxButton -------------------------------------------
|
||||
@@ -507,19 +704,39 @@ public class FlatComboBoxUI
|
||||
extends FlatArrowButton
|
||||
{
|
||||
protected FlatComboBoxButton() {
|
||||
this( SwingConstants.SOUTH, arrowType, buttonArrowColor, buttonDisabledArrowColor, buttonHoverArrowColor, null, null );
|
||||
this( SwingConstants.SOUTH, arrowType, buttonArrowColor, buttonDisabledArrowColor,
|
||||
buttonHoverArrowColor, null, buttonPressedArrowColor, null );
|
||||
}
|
||||
|
||||
protected FlatComboBoxButton( int direction, String type, Color foreground, Color disabledForeground,
|
||||
Color hoverForeground, Color hoverBackground, Color pressedBackground )
|
||||
Color hoverForeground, Color hoverBackground, Color pressedForeground, Color pressedBackground )
|
||||
{
|
||||
super( direction, type, foreground, disabledForeground, hoverForeground, hoverBackground, pressedBackground );
|
||||
super( direction, type, foreground, disabledForeground,
|
||||
hoverForeground, hoverBackground, pressedForeground, pressedBackground );
|
||||
}
|
||||
|
||||
protected void updateStyle() {
|
||||
updateStyle( arrowType, buttonArrowColor, buttonDisabledArrowColor,
|
||||
buttonHoverArrowColor, null, buttonPressedArrowColor, null );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isHover() {
|
||||
return super.isHover() || (!comboBox.isEditable() ? hover : false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isPressed() {
|
||||
return super.isPressed() || (!comboBox.isEditable() ? pressed : false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Color getArrowColor() {
|
||||
if( isCellRenderer() && isCellRendererBackgroundChanged() )
|
||||
return comboBox.getForeground();
|
||||
|
||||
return super.getArrowColor();
|
||||
}
|
||||
}
|
||||
|
||||
//---- class FlatComboPopup -----------------------------------------------
|
||||
@@ -528,13 +745,11 @@ public class FlatComboBoxUI
|
||||
protected class FlatComboPopup
|
||||
extends BasicComboPopup
|
||||
{
|
||||
private CellPaddingBorder paddingBorder;
|
||||
|
||||
protected FlatComboPopup( JComboBox combo ) {
|
||||
super( combo );
|
||||
|
||||
// BasicComboPopup listens to JComboBox.componentOrientation and updates
|
||||
// the component orientation of the list, scroller and popup, but when
|
||||
// the component orientation of the list, scroll pane and popup, but when
|
||||
// switching the LaF and a new combo popup is created, the component
|
||||
// orientation is not applied.
|
||||
ComponentOrientation o = comboBox.getComponentOrientation();
|
||||
@@ -563,6 +778,17 @@ public class FlatComboBoxUI
|
||||
|
||||
// make popup wider if necessary
|
||||
if( displayWidth > pw ) {
|
||||
// limit popup width to screen width
|
||||
GraphicsConfiguration gc = comboBox.getGraphicsConfiguration();
|
||||
if( gc != null ) {
|
||||
Rectangle screenBounds = gc.getBounds();
|
||||
Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets( gc );
|
||||
displayWidth = Math.min( displayWidth, screenBounds.width - screenInsets.left - screenInsets.right );
|
||||
} else {
|
||||
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
|
||||
displayWidth = Math.min( displayWidth, screenSize.width );
|
||||
}
|
||||
|
||||
int diff = displayWidth - pw;
|
||||
pw = displayWidth;
|
||||
|
||||
@@ -577,6 +803,9 @@ public class FlatComboBoxUI
|
||||
protected void configurePopup() {
|
||||
super.configurePopup();
|
||||
|
||||
// make opaque to avoid that background shines thru border (e.g. at 150% scaling)
|
||||
setOpaque( true );
|
||||
|
||||
Border border = UIManager.getBorder( "PopupMenu.border" );
|
||||
if( border != null )
|
||||
setBorder( border );
|
||||
@@ -587,21 +816,53 @@ public class FlatComboBoxUI
|
||||
super.configureList();
|
||||
|
||||
list.setCellRenderer( new PopupListCellRenderer() );
|
||||
updateStyle();
|
||||
}
|
||||
|
||||
void updateStyle() {
|
||||
if( popupBackground != null )
|
||||
list.setBackground( popupBackground );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PropertyChangeListener createPropertyChangeListener() {
|
||||
return new BasicComboPopup.PropertyChangeHandler() {
|
||||
@Override
|
||||
public void propertyChange( PropertyChangeEvent e ) {
|
||||
super.propertyChange( e );
|
||||
PropertyChangeListener superListener = super.createPropertyChangeListener();
|
||||
return e -> {
|
||||
superListener.propertyChange( e );
|
||||
|
||||
if( e.getPropertyName() == "renderer" )
|
||||
list.setCellRenderer( new PopupListCellRenderer() );
|
||||
}
|
||||
if( e.getPropertyName() == "renderer" )
|
||||
list.setCellRenderer( new PopupListCellRenderer() );
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPopupHeightForRowCount( int maxRowCount ) {
|
||||
int height = super.getPopupHeightForRowCount( maxRowCount );
|
||||
paddingBorder.uninstall();
|
||||
return height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show( Component invoker, int x, int y ) {
|
||||
// Java 8: fix y coordinate if popup is shown above the combobox
|
||||
// (already fixed in Java 9+ https://bugs.openjdk.java.net/browse/JDK-7072653)
|
||||
if( y < 0 && !SystemInfo.isJava_9_orLater ) {
|
||||
Border popupBorder = getBorder();
|
||||
if( popupBorder != null ) {
|
||||
Insets insets = popupBorder.getBorderInsets( this );
|
||||
y -= insets.top + insets.bottom;
|
||||
}
|
||||
}
|
||||
|
||||
super.show( invoker, x, y );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintChildren( Graphics g ) {
|
||||
super.paintChildren( g );
|
||||
paddingBorder.uninstall();
|
||||
}
|
||||
|
||||
//---- class PopupListCellRenderer -----
|
||||
|
||||
private class PopupListCellRenderer
|
||||
@@ -611,22 +872,15 @@ public class FlatComboBoxUI
|
||||
public Component getListCellRendererComponent( JList list, Object value,
|
||||
int index, boolean isSelected, boolean cellHasFocus )
|
||||
{
|
||||
ListCellRenderer renderer = comboBox.getRenderer();
|
||||
CellPaddingBorder.uninstall( renderer );
|
||||
CellPaddingBorder.uninstall( lastRendererComponent );
|
||||
paddingBorder.uninstall();
|
||||
|
||||
ListCellRenderer renderer = comboBox.getRenderer();
|
||||
if( renderer == null )
|
||||
renderer = new DefaultListCellRenderer();
|
||||
Component c = renderer.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
|
||||
c.applyComponentOrientation( comboBox.getComponentOrientation() );
|
||||
|
||||
if( c instanceof JComponent ) {
|
||||
if( paddingBorder == null )
|
||||
paddingBorder = new CellPaddingBorder( padding );
|
||||
paddingBorder.install( (JComponent) c );
|
||||
}
|
||||
|
||||
lastRendererComponent = (c != renderer) ? new WeakReference<>( c ) : null;
|
||||
paddingBorder.install( c );
|
||||
|
||||
return c;
|
||||
}
|
||||
@@ -636,49 +890,69 @@ public class FlatComboBoxUI
|
||||
//---- class CellPaddingBorder --------------------------------------------
|
||||
|
||||
/**
|
||||
* Cell padding border used only in popup list.
|
||||
*
|
||||
* Cell padding border used in popup list and for current value if not editable.
|
||||
* <p>
|
||||
* The insets are the union of the cell padding and the renderer border insets,
|
||||
* which vertically aligns text in popup list with text in combobox.
|
||||
*
|
||||
* The renderer border is painted on the outside of this border.
|
||||
* <p>
|
||||
* The renderer border is painted on the outer side of this border.
|
||||
*/
|
||||
private static class CellPaddingBorder
|
||||
extends AbstractBorder
|
||||
{
|
||||
private final Insets padding;
|
||||
private Insets padding;
|
||||
private JComponent rendererComponent;
|
||||
private Border rendererBorder;
|
||||
|
||||
CellPaddingBorder( Insets padding ) {
|
||||
this.padding = padding;
|
||||
}
|
||||
|
||||
void install( JComponent rendererComponent ) {
|
||||
Border oldBorder = rendererComponent.getBorder();
|
||||
if( !(oldBorder instanceof CellPaddingBorder) ) {
|
||||
rendererBorder = oldBorder;
|
||||
rendererComponent.setBorder( this );
|
||||
}
|
||||
}
|
||||
|
||||
static void uninstall( Object o ) {
|
||||
if( o instanceof WeakReference )
|
||||
o = ((WeakReference<?>)o).get();
|
||||
|
||||
if( !(o instanceof JComponent) )
|
||||
void install( Component c ) {
|
||||
if( !(c instanceof JComponent) )
|
||||
return;
|
||||
|
||||
JComponent rendererComponent = (JComponent) o;
|
||||
Border border = rendererComponent.getBorder();
|
||||
if( border instanceof CellPaddingBorder ) {
|
||||
CellPaddingBorder paddingBorder = (CellPaddingBorder) border;
|
||||
rendererComponent.setBorder( paddingBorder.rendererBorder );
|
||||
paddingBorder.rendererBorder = null;
|
||||
}
|
||||
JComponent jc = (JComponent) c;
|
||||
Border oldBorder = jc.getBorder();
|
||||
if( oldBorder == this )
|
||||
return; // already installed
|
||||
|
||||
// component already has a padding border --> uninstall it
|
||||
// (may happen if single renderer instance is used in multiple comboboxes)
|
||||
if( oldBorder instanceof CellPaddingBorder )
|
||||
((CellPaddingBorder)oldBorder).uninstall();
|
||||
|
||||
// this border can be installed only at one component
|
||||
// (may happen if a renderer returns varying components)
|
||||
uninstall();
|
||||
|
||||
// remember component where this border was installed for uninstall
|
||||
rendererComponent = jc;
|
||||
|
||||
// remember old border and replace it
|
||||
rendererBorder = jc.getBorder();
|
||||
jc.setBorder( this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall border from previously installed component.
|
||||
* Because this border is installed in PopupListCellRenderer.getListCellRendererComponent(),
|
||||
* there is no single place to uninstall it.
|
||||
* This is the reason why this method is called from various places.
|
||||
*/
|
||||
void uninstall() {
|
||||
if( rendererComponent == null )
|
||||
return;
|
||||
|
||||
if( rendererComponent.getBorder() == this )
|
||||
rendererComponent.setBorder( rendererBorder );
|
||||
rendererComponent = null;
|
||||
rendererBorder = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||
Insets padding = scale( this.padding );
|
||||
if( rendererBorder != null ) {
|
||||
Insets insideInsets = rendererBorder.getBorderInsets( c );
|
||||
insets.top = Math.max( padding.top, insideInsets.top );
|
||||
|
||||
@@ -16,10 +16,12 @@
|
||||
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Image;
|
||||
import java.awt.Insets;
|
||||
@@ -28,11 +30,13 @@ import java.awt.Point;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyVetoException;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDesktopPane;
|
||||
import javax.swing.event.MouseInputAdapter;
|
||||
import javax.swing.event.MouseInputListener;
|
||||
import javax.swing.JLabel;
|
||||
@@ -45,6 +49,7 @@ import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.basic.BasicDesktopIconUI;
|
||||
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
/**
|
||||
@@ -75,11 +80,21 @@ public class FlatDesktopIconUI
|
||||
private JToolTip titleTip;
|
||||
private ActionListener closeListener;
|
||||
private MouseInputListener mouseInputListener;
|
||||
private PropertyChangeListener ancestorListener;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatDesktopIconUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installUI( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
// update dock icon preview if already iconified
|
||||
if( c.isDisplayable() )
|
||||
updateDockIconPreviewLater();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uninstallUI( JComponent c ) {
|
||||
super.uninstallUI( c );
|
||||
@@ -136,6 +151,17 @@ public class FlatDesktopIconUI
|
||||
};
|
||||
closeButton.addActionListener( closeListener );
|
||||
closeButton.addMouseListener( mouseInputListener );
|
||||
|
||||
ancestorListener = e -> {
|
||||
if( e.getNewValue() != null ) {
|
||||
// update dock icon preview if desktopIcon is added to desktop (internal frame was iconified)
|
||||
updateDockIconPreviewLater();
|
||||
} else {
|
||||
// remove preview icon to release memory
|
||||
dockIcon.setIcon( null );
|
||||
}
|
||||
};
|
||||
desktopIcon.addPropertyChangeListener( "ancestor", ancestorListener );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -146,6 +172,9 @@ public class FlatDesktopIconUI
|
||||
closeButton.removeMouseListener( mouseInputListener );
|
||||
closeListener = null;
|
||||
mouseInputListener = null;
|
||||
|
||||
desktopIcon.removePropertyChangeListener( "ancestor", ancestorListener );
|
||||
ancestorListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -228,15 +257,30 @@ public class FlatDesktopIconUI
|
||||
return getPreferredSize( c );
|
||||
}
|
||||
|
||||
void updateDockIcon() {
|
||||
@Override
|
||||
public void update( Graphics g, JComponent c ) {
|
||||
if( c.isOpaque() ) {
|
||||
// fill background with color derived from desktop pane
|
||||
Color background = c.getBackground();
|
||||
JDesktopPane desktopPane = desktopIcon.getDesktopPane();
|
||||
g.setColor( (desktopPane != null)
|
||||
? FlatUIUtils.deriveColor( background, desktopPane.getBackground() )
|
||||
: background );
|
||||
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
|
||||
}
|
||||
|
||||
paint( g, c );
|
||||
}
|
||||
|
||||
private void updateDockIconPreviewLater() {
|
||||
// use invoke later to make sure that components are updated when switching LaF
|
||||
EventQueue.invokeLater( () -> {
|
||||
if( dockIcon != null )
|
||||
updateDockIconLater();
|
||||
updateDockIconPreview();
|
||||
} );
|
||||
}
|
||||
|
||||
private void updateDockIconLater() {
|
||||
protected void updateDockIconPreview() {
|
||||
// make sure that frame is not selected
|
||||
if( frame.isSelected() ) {
|
||||
try {
|
||||
@@ -246,13 +290,22 @@ public class FlatDesktopIconUI
|
||||
}
|
||||
}
|
||||
|
||||
// layout internal frame title pane, which was recreated when switching Laf
|
||||
// (directly invoke doLayout() because frame.validate() does not work here
|
||||
// because frame is not displayable)
|
||||
if( !frame.isValid() )
|
||||
frame.doLayout();
|
||||
for( Component c : frame.getComponents() ) {
|
||||
if( !c.isValid() )
|
||||
c.doLayout();
|
||||
}
|
||||
|
||||
// paint internal frame to buffered image
|
||||
int frameWidth = Math.max( frame.getWidth(), 1 );
|
||||
int frameHeight = Math.max( frame.getHeight(), 1 );
|
||||
BufferedImage frameImage = new BufferedImage( frameWidth, frameHeight, BufferedImage.TYPE_INT_ARGB );
|
||||
Graphics2D g = frameImage.createGraphics();
|
||||
try {
|
||||
//TODO fix missing internal frame header when switching LaF
|
||||
frame.paint( g );
|
||||
} finally {
|
||||
g.dispose();
|
||||
@@ -270,6 +323,27 @@ public class FlatDesktopIconUI
|
||||
|
||||
// scale preview
|
||||
Image previewImage = frameImage.getScaledInstance( previewWidth, previewHeight, Image.SCALE_SMOOTH );
|
||||
if( MultiResolutionImageSupport.isAvailable() ) {
|
||||
// On HiDPI screens, create preview images for 1x, 2x and current scale factor.
|
||||
// The icon then chooses the best resolution for painting, which is usually
|
||||
// the one for the current scale factor. But if changing scale factor or
|
||||
// moving window to another screen with different scale factor, then another
|
||||
// resolution may be used because the preview icon is not updated.
|
||||
Image previewImage2x = frameImage.getScaledInstance( previewWidth * 2, previewHeight * 2, Image.SCALE_SMOOTH );
|
||||
double scaleFactor = UIScale.getSystemScaleFactor( desktopIcon.getGraphicsConfiguration() );
|
||||
if( scaleFactor != 1 && scaleFactor != 2 ) {
|
||||
Image previewImageCurrent = frameImage.getScaledInstance(
|
||||
(int) Math.round( previewWidth * scaleFactor ),
|
||||
(int) Math.round( previewHeight * scaleFactor ),
|
||||
Image.SCALE_SMOOTH );
|
||||
|
||||
// the images must be ordered by resolution
|
||||
previewImage = (scaleFactor < 2)
|
||||
? MultiResolutionImageSupport.create( 0, previewImage, previewImageCurrent, previewImage2x )
|
||||
: MultiResolutionImageSupport.create( 0, previewImage, previewImage2x, previewImageCurrent );
|
||||
} else
|
||||
previewImage = MultiResolutionImageSupport.create( 0, previewImage, previewImage2x );
|
||||
}
|
||||
dockIcon.setIcon( new ImageIcon( previewImage ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,16 @@
|
||||
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import javax.swing.DefaultDesktopManager;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.event.ComponentAdapter;
|
||||
import java.awt.event.ComponentEvent;
|
||||
import java.awt.event.ContainerEvent;
|
||||
import java.awt.event.ContainerListener;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JInternalFrame;
|
||||
import javax.swing.JInternalFrame.JDesktopIcon;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.UIResource;
|
||||
import javax.swing.plaf.basic.BasicDesktopPaneUI;
|
||||
|
||||
/**
|
||||
@@ -36,30 +41,96 @@ import javax.swing.plaf.basic.BasicDesktopPaneUI;
|
||||
public class FlatDesktopPaneUI
|
||||
extends BasicDesktopPaneUI
|
||||
{
|
||||
private LayoutDockListener layoutDockListener;
|
||||
private boolean layoutDockPending;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatDesktopPaneUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installDesktopManager() {
|
||||
desktopManager = desktop.getDesktopManager();
|
||||
if( desktopManager == null ) {
|
||||
desktopManager = new FlatDesktopManager();
|
||||
desktop.setDesktopManager( desktopManager );
|
||||
public void installUI( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
layoutDockLaterOnce();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installListeners() {
|
||||
super.installListeners();
|
||||
|
||||
layoutDockListener = new LayoutDockListener();
|
||||
desktop.addContainerListener( layoutDockListener );
|
||||
desktop.addComponentListener( layoutDockListener );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void uninstallListeners() {
|
||||
super.uninstallListeners();
|
||||
|
||||
desktop.removeContainerListener( layoutDockListener );
|
||||
desktop.removeComponentListener( layoutDockListener );
|
||||
layoutDockListener = null;
|
||||
}
|
||||
|
||||
private void layoutDockLaterOnce() {
|
||||
if( layoutDockPending )
|
||||
return;
|
||||
layoutDockPending = true;
|
||||
|
||||
EventQueue.invokeLater( () -> {
|
||||
layoutDockPending = false;
|
||||
if( desktop != null )
|
||||
layoutDock();
|
||||
} );
|
||||
}
|
||||
|
||||
protected void layoutDock() {
|
||||
Dimension desktopSize = desktop.getSize();
|
||||
int x = 0;
|
||||
int y = desktopSize.height;
|
||||
int rowHeight = 0;
|
||||
|
||||
for( Component c : desktop.getComponents() ) {
|
||||
if( !(c instanceof JDesktopIcon) )
|
||||
continue;
|
||||
|
||||
JDesktopIcon icon = (JDesktopIcon) c;
|
||||
Dimension iconSize = icon.getPreferredSize();
|
||||
|
||||
if( x + iconSize.width > desktopSize.width ) {
|
||||
// new row
|
||||
x = 0;
|
||||
y -= rowHeight;
|
||||
rowHeight = 0;
|
||||
}
|
||||
|
||||
icon.setLocation( x, y - iconSize.height );
|
||||
|
||||
x += iconSize.width;
|
||||
rowHeight = Math.max( iconSize.height, rowHeight );
|
||||
}
|
||||
}
|
||||
|
||||
//---- class FlatDesktopManager -------------------------------------------
|
||||
//---- class LayoutDockListener -------------------------------------------
|
||||
|
||||
private class FlatDesktopManager
|
||||
extends DefaultDesktopManager
|
||||
implements UIResource
|
||||
private class LayoutDockListener
|
||||
extends ComponentAdapter
|
||||
implements ContainerListener
|
||||
{
|
||||
@Override
|
||||
public void iconifyFrame( JInternalFrame f ) {
|
||||
super.iconifyFrame( f );
|
||||
public void componentAdded( ContainerEvent e ) {
|
||||
layoutDockLaterOnce();
|
||||
}
|
||||
|
||||
((FlatDesktopIconUI)f.getDesktopIcon().getUI()).updateDockIcon();
|
||||
@Override
|
||||
public void componentRemoved( ContainerEvent e ) {
|
||||
layoutDockLaterOnce();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentResized( ComponentEvent e ) {
|
||||
layoutDockLaterOnce();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,9 @@ import java.awt.Image;
|
||||
import java.awt.Insets;
|
||||
import java.awt.RadialGradientPaint;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.Map;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableBorder;
|
||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
@@ -40,14 +43,17 @@ import com.formdev.flatlaf.util.UIScale;
|
||||
*/
|
||||
public class FlatDropShadowBorder
|
||||
extends FlatEmptyBorder
|
||||
implements StyleableBorder
|
||||
{
|
||||
private final Color shadowColor;
|
||||
private final Insets shadowInsets;
|
||||
private final float shadowOpacity;
|
||||
@Styleable protected Color shadowColor;
|
||||
@Styleable protected Insets shadowInsets;
|
||||
@Styleable protected float shadowOpacity;
|
||||
|
||||
private final int shadowSize;
|
||||
private int shadowSize;
|
||||
private Image shadowImage;
|
||||
private Color lastShadowColor;
|
||||
private float lastShadowOpacity;
|
||||
private int lastShadowSize;
|
||||
private double lastSystemScaleFactor;
|
||||
private float lastUserScaleFactor;
|
||||
|
||||
@@ -64,17 +70,43 @@ public class FlatDropShadowBorder
|
||||
}
|
||||
|
||||
public FlatDropShadowBorder( Color shadowColor, Insets shadowInsets, float shadowOpacity ) {
|
||||
super( Math.max( shadowInsets.top, 0 ), Math.max( shadowInsets.left, 0 ),
|
||||
Math.max( shadowInsets.bottom, 0 ), Math.max( shadowInsets.right, 0 ) );
|
||||
super( nonNegativeInsets( shadowInsets ) );
|
||||
|
||||
this.shadowColor = shadowColor;
|
||||
this.shadowInsets = shadowInsets;
|
||||
this.shadowOpacity = shadowOpacity;
|
||||
|
||||
shadowSize = Math.max(
|
||||
shadowSize = maxInset( shadowInsets );
|
||||
}
|
||||
|
||||
private static Insets nonNegativeInsets( Insets shadowInsets ) {
|
||||
return new Insets( Math.max( shadowInsets.top, 0 ), Math.max( shadowInsets.left, 0 ),
|
||||
Math.max( shadowInsets.bottom, 0 ), Math.max( shadowInsets.right, 0 ) );
|
||||
}
|
||||
|
||||
private int maxInset( Insets shadowInsets ) {
|
||||
return Math.max(
|
||||
Math.max( shadowInsets.left, shadowInsets.right ),
|
||||
Math.max( shadowInsets.top, shadowInsets.bottom ) );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Object applyStyleProperty( String key, Object value ) {
|
||||
Object oldValue = FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||
if( key.equals( "shadowInsets" ) ) {
|
||||
applyStyleProperty( nonNegativeInsets( shadowInsets ) );
|
||||
shadowSize = maxInset( shadowInsets );
|
||||
}
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos() {
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||
if( shadowSize <= 0 )
|
||||
@@ -91,12 +123,16 @@ public class FlatDropShadowBorder
|
||||
float userScaleFactor = UIScale.getUserScaleFactor();
|
||||
if( shadowImage == null ||
|
||||
!shadowColor.equals( lastShadowColor ) ||
|
||||
lastShadowOpacity != shadowOpacity ||
|
||||
lastShadowSize != shadowSize ||
|
||||
lastSystemScaleFactor != scaleFactor ||
|
||||
lastUserScaleFactor != userScaleFactor )
|
||||
{
|
||||
shadowImage = createShadowImage( shadowColor, shadowSize, shadowOpacity,
|
||||
(float) (scaleFactor * userScaleFactor) );
|
||||
lastShadowColor = shadowColor;
|
||||
lastShadowOpacity = shadowOpacity;
|
||||
lastShadowSize = shadowSize;
|
||||
lastSystemScaleFactor = scaleFactor;
|
||||
lastUserScaleFactor = userScaleFactor;
|
||||
}
|
||||
|
||||
@@ -17,19 +17,26 @@
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Insets;
|
||||
import java.awt.event.FocusListener;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.util.Map;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JEditorPane;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.UIResource;
|
||||
import javax.swing.plaf.basic.BasicEditorPaneUI;
|
||||
import javax.swing.text.Caret;
|
||||
import javax.swing.text.JTextComponent;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
|
||||
/**
|
||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JEditorPane}.
|
||||
@@ -53,27 +60,54 @@ import com.formdev.flatlaf.util.HiDPIUtils;
|
||||
*
|
||||
* @uiDefault Component.minimumWidth int
|
||||
* @uiDefault Component.isIntelliJTheme boolean
|
||||
* @uiDefault EditorPane.focusedBackground Color optional
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
public class FlatEditorPaneUI
|
||||
extends BasicEditorPaneUI
|
||||
implements StyleableUI
|
||||
{
|
||||
protected int minimumWidth;
|
||||
@Styleable protected int minimumWidth;
|
||||
protected boolean isIntelliJTheme;
|
||||
private Color background;
|
||||
@Styleable protected Color disabledBackground;
|
||||
@Styleable protected Color inactiveBackground;
|
||||
@Styleable protected Color focusedBackground;
|
||||
|
||||
private Color oldDisabledBackground;
|
||||
private Color oldInactiveBackground;
|
||||
|
||||
private Insets defaultMargin;
|
||||
|
||||
private Object oldHonorDisplayProperties;
|
||||
private FocusListener focusListener;
|
||||
private Map<String, Object> oldStyleValues;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatEditorPaneUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installUI( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
installStyle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installDefaults() {
|
||||
super.installDefaults();
|
||||
|
||||
String prefix = getPropertyPrefix();
|
||||
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
||||
background = UIManager.getColor( prefix + ".background" );
|
||||
disabledBackground = UIManager.getColor( prefix + ".disabledBackground" );
|
||||
inactiveBackground = UIManager.getColor( prefix + ".inactiveBackground" );
|
||||
focusedBackground = UIManager.getColor( prefix + ".focusedBackground" );
|
||||
|
||||
defaultMargin = UIManager.getInsets( prefix + ".margin" );
|
||||
|
||||
// use component font and foreground for HTML text
|
||||
oldHonorDisplayProperties = getComponent().getClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES );
|
||||
@@ -84,34 +118,118 @@ public class FlatEditorPaneUI
|
||||
protected void uninstallDefaults() {
|
||||
super.uninstallDefaults();
|
||||
|
||||
background = null;
|
||||
disabledBackground = null;
|
||||
inactiveBackground = null;
|
||||
focusedBackground = null;
|
||||
|
||||
oldDisabledBackground = null;
|
||||
oldInactiveBackground = null;
|
||||
|
||||
oldStyleValues = null;
|
||||
|
||||
getComponent().putClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES, oldHonorDisplayProperties );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void propertyChange( PropertyChangeEvent e ) {
|
||||
super.propertyChange( e );
|
||||
propertyChange( getComponent(), e );
|
||||
protected void installListeners() {
|
||||
super.installListeners();
|
||||
|
||||
// necessary to update focus background
|
||||
focusListener = new FlatUIUtils.RepaintFocusListener( getComponent(), c -> focusedBackground != null );
|
||||
getComponent().addFocusListener( focusListener );
|
||||
}
|
||||
|
||||
static void propertyChange( JTextComponent c, PropertyChangeEvent e ) {
|
||||
@Override
|
||||
protected void uninstallListeners() {
|
||||
super.uninstallListeners();
|
||||
|
||||
getComponent().removeFocusListener( focusListener );
|
||||
focusListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Caret createCaret() {
|
||||
return new FlatCaret( null, false );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void propertyChange( PropertyChangeEvent e ) {
|
||||
// invoke updateBackground() before super.propertyChange()
|
||||
String propertyName = e.getPropertyName();
|
||||
if( "editable".equals( propertyName ) || "enabled".equals( propertyName ) )
|
||||
updateBackground();
|
||||
|
||||
super.propertyChange( e );
|
||||
propertyChange( getComponent(), e, this::installStyle );
|
||||
}
|
||||
|
||||
static void propertyChange( JTextComponent c, PropertyChangeEvent e, Runnable installStyle ) {
|
||||
switch( e.getPropertyName() ) {
|
||||
case FlatClientProperties.MINIMUM_WIDTH:
|
||||
c.revalidate();
|
||||
break;
|
||||
|
||||
case FlatClientProperties.STYLE:
|
||||
case FlatClientProperties.STYLE_CLASS:
|
||||
installStyle.run();
|
||||
c.revalidate();
|
||||
c.repaint();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void installStyle() {
|
||||
try {
|
||||
applyStyle( FlatStylingSupport.getResolvedStyle( getComponent(), "EditorPane" ) );
|
||||
} catch( RuntimeException ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void applyStyle( Object style ) {
|
||||
oldDisabledBackground = disabledBackground;
|
||||
oldInactiveBackground = inactiveBackground;
|
||||
|
||||
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||
|
||||
updateBackground();
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected Object applyStyleProperty( String key, Object value ) {
|
||||
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, getComponent(), key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
private void updateBackground() {
|
||||
FlatTextFieldUI.updateBackground( getComponent(), background,
|
||||
disabledBackground, inactiveBackground,
|
||||
oldDisabledBackground, oldInactiveBackground );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize( JComponent c ) {
|
||||
return applyMinimumWidth( c, super.getPreferredSize( c ), minimumWidth );
|
||||
return applyMinimumWidth( c, super.getPreferredSize( c ), minimumWidth, defaultMargin );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getMinimumSize( JComponent c ) {
|
||||
return applyMinimumWidth( c, super.getMinimumSize( c ), minimumWidth );
|
||||
return applyMinimumWidth( c, super.getMinimumSize( c ), minimumWidth, defaultMargin );
|
||||
}
|
||||
|
||||
static Dimension applyMinimumWidth( JComponent c, Dimension size, int minimumWidth ) {
|
||||
static Dimension applyMinimumWidth( JComponent c, Dimension size, int minimumWidth, Insets defaultMargin ) {
|
||||
// do not apply minimum width if JTextComponent.margin is set
|
||||
if( !FlatTextFieldUI.hasDefaultMargins( c, defaultMargin ) )
|
||||
return size;
|
||||
|
||||
// Assume that text area is in a scroll pane (that displays the border)
|
||||
// and subtract 1px border line width.
|
||||
// Using "(scale( 1 ) * 2)" instead of "scale( 2 )" to deal with rounding
|
||||
@@ -128,14 +246,11 @@ public class FlatEditorPaneUI
|
||||
|
||||
@Override
|
||||
protected void paintBackground( Graphics g ) {
|
||||
JTextComponent c = getComponent();
|
||||
paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground );
|
||||
}
|
||||
|
||||
// for compatibility with IntelliJ themes
|
||||
if( isIntelliJTheme && (!c.isEnabled() || !c.isEditable()) && (c.getBackground() instanceof UIResource) ) {
|
||||
FlatUIUtils.paintParentBackground( g, c );
|
||||
return;
|
||||
}
|
||||
|
||||
super.paintBackground( g );
|
||||
static void paintBackground( Graphics g, JTextComponent c, boolean isIntelliJTheme, Color focusedBackground ) {
|
||||
g.setColor( FlatTextFieldUI.getBackground( c, isIntelliJTheme, focusedBackground ) );
|
||||
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,12 @@ public class FlatEmptyBorder
|
||||
|
||||
@Override
|
||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||
return scaleInsets( c, insets, top, left, bottom, right );
|
||||
}
|
||||
|
||||
protected static Insets scaleInsets( Component c, Insets insets,
|
||||
int top, int left, int bottom, int right )
|
||||
{
|
||||
boolean leftToRight = left == right || c.getComponentOrientation().isLeftToRight();
|
||||
insets.left = scale( leftToRight ? left : right );
|
||||
insets.top = scale( top );
|
||||
@@ -61,4 +67,13 @@ public class FlatEmptyBorder
|
||||
public Insets getUnscaledBorderInsets() {
|
||||
return super.getBorderInsets();
|
||||
}
|
||||
|
||||
public Object applyStyleProperty( Insets insets ) {
|
||||
Insets oldInsets = getUnscaledBorderInsets();
|
||||
top = insets.top;
|
||||
left = insets.left;
|
||||
bottom = insets.bottom;
|
||||
right = insets.right;
|
||||
return oldInsets;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,13 +31,17 @@ import javax.swing.JComboBox;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JToggleButton;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.filechooser.FileView;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.metal.MetalFileChooserUI;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
import com.formdev.flatlaf.util.ScaledImageIcon;
|
||||
import com.formdev.flatlaf.util.SystemInfo;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
/**
|
||||
@@ -190,6 +194,62 @@ public class FlatFileChooserUI
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JPanel createDetailsView( JFileChooser fc ) {
|
||||
JPanel p = super.createDetailsView( fc );
|
||||
|
||||
if( !SystemInfo.isWindows )
|
||||
return p;
|
||||
|
||||
// find scroll pane
|
||||
JScrollPane scrollPane = null;
|
||||
for( Component c : p.getComponents() ) {
|
||||
if( c instanceof JScrollPane ) {
|
||||
scrollPane = (JScrollPane) c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( scrollPane == null )
|
||||
return p;
|
||||
|
||||
// get scroll view, which should be a table
|
||||
Component view = scrollPane.getViewport().getView();
|
||||
if( !(view instanceof JTable) )
|
||||
return p;
|
||||
|
||||
JTable table = (JTable) view;
|
||||
|
||||
// on Windows 10, the date may contain left-to-right (0x200e) and right-to-left (0x200f)
|
||||
// mark characters (see https://en.wikipedia.org/wiki/Left-to-right_mark)
|
||||
// when the "current user" item is selected in the "look in" combobox
|
||||
// --> remove them
|
||||
TableCellRenderer defaultRenderer = table.getDefaultRenderer( Object.class );
|
||||
table.setDefaultRenderer( Object.class, new TableCellRenderer() {
|
||||
@Override
|
||||
public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected,
|
||||
boolean hasFocus, int row, int column )
|
||||
{
|
||||
// remove left-to-right and right-to-left mark characters
|
||||
if( value instanceof String && ((String)value).startsWith( "\u200e" ) ) {
|
||||
String str = (String) value;
|
||||
char[] buf = new char[str.length()];
|
||||
int j = 0;
|
||||
for( int i = 0; i < buf.length; i++ ) {
|
||||
char ch = str.charAt( i );
|
||||
if( ch != '\u200e' && ch != '\u200f' )
|
||||
buf[j++] = ch;
|
||||
}
|
||||
value = new String( buf, 0, j );
|
||||
}
|
||||
|
||||
return defaultRenderer.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column );
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize( JComponent c ) {
|
||||
return UIScale.scale( super.getPreferredSize( c ) );
|
||||
@@ -202,12 +262,20 @@ public class FlatFileChooserUI
|
||||
|
||||
@Override
|
||||
public FileView getFileView( JFileChooser fc ) {
|
||||
return fileView;
|
||||
return doNotUseSystemIcons() ? super.getFileView( fc ) : fileView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearIconCache() {
|
||||
fileView.clearIconCache();
|
||||
if( doNotUseSystemIcons() )
|
||||
super.clearIconCache();
|
||||
else
|
||||
fileView.clearIconCache();
|
||||
}
|
||||
|
||||
private boolean doNotUseSystemIcons() {
|
||||
// Java 17 32bit craches on Windows when using system icons
|
||||
return SystemInfo.isWindows && SystemInfo.isJava_17_orLater && !SystemInfo.isX86_64;
|
||||
}
|
||||
|
||||
//---- class FlatFileView -------------------------------------------------
|
||||
|
||||
@@ -39,12 +39,13 @@ import javax.swing.plaf.ComponentUI;
|
||||
*
|
||||
* <!-- FlatTextFieldUI -->
|
||||
*
|
||||
* @uiDefault TextComponent.arc int
|
||||
* @uiDefault Component.focusWidth int
|
||||
* @uiDefault Component.minimumWidth int
|
||||
* @uiDefault Component.isIntelliJTheme boolean
|
||||
* @uiDefault FormattedTextField.placeholderForeground Color
|
||||
* @uiDefault FormattedTextField.focusedBackground Color optional
|
||||
* @uiDefault FormattedTextField.iconTextGap int optional, default is 4
|
||||
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
|
||||
* @uiDefault TextComponent.selectAllOnMouseClick boolean
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
@@ -59,4 +60,10 @@ public class FlatFormattedTextFieldUI
|
||||
protected String getPropertyPrefix() {
|
||||
return "FormattedTextField";
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
String getStyleType() {
|
||||
return "FormattedTextField";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.LayoutManager;
|
||||
import java.awt.Rectangle;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import javax.swing.BorderFactory;
|
||||
@@ -146,6 +147,19 @@ public class FlatInternalFrameTitlePane
|
||||
closeButton.setVisible( frame.isClosable() );
|
||||
}
|
||||
|
||||
Rectangle getFrameIconBounds() {
|
||||
Icon icon = titleLabel.getIcon();
|
||||
if( icon == null )
|
||||
return null;
|
||||
|
||||
int iconWidth = icon.getIconWidth();
|
||||
int iconHeight = icon.getIconHeight();
|
||||
boolean leftToRight = titleLabel.getComponentOrientation().isLeftToRight();
|
||||
int x = titleLabel.getX() + (leftToRight ? 0 : (titleLabel.getWidth() - iconWidth));
|
||||
int y = titleLabel.getY() + ((titleLabel.getHeight() - iconHeight) / 2);
|
||||
return new Rectangle( x, y, iconWidth, iconHeight );
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing because FlatLaf internal frames do not have system menus.
|
||||
*/
|
||||
|
||||
@@ -22,12 +22,22 @@ import java.awt.Component;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JInternalFrame;
|
||||
import javax.swing.LookAndFeel;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.event.MouseInputAdapter;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.basic.BasicInternalFrameUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableBorder;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
|
||||
/**
|
||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JInternalFrame}.
|
||||
@@ -83,9 +93,13 @@ import javax.swing.plaf.basic.BasicInternalFrameUI;
|
||||
*/
|
||||
public class FlatInternalFrameUI
|
||||
extends BasicInternalFrameUI
|
||||
implements StyleableUI
|
||||
{
|
||||
protected FlatWindowResizer windowResizer;
|
||||
|
||||
private Map<String, Object> oldStyleValues;
|
||||
private AtomicBoolean borderShared;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatInternalFrameUI( (JInternalFrame) c );
|
||||
}
|
||||
@@ -101,6 +115,8 @@ public class FlatInternalFrameUI
|
||||
LookAndFeel.installProperty( frame, "opaque", false );
|
||||
|
||||
windowResizer = createWindowResizer();
|
||||
|
||||
installStyle();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -111,6 +127,9 @@ public class FlatInternalFrameUI
|
||||
windowResizer.uninstall();
|
||||
windowResizer = null;
|
||||
}
|
||||
|
||||
oldStyleValues = null;
|
||||
borderShared = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -122,15 +141,54 @@ public class FlatInternalFrameUI
|
||||
return new FlatWindowResizer.InternalFrameResizer( frame, this::getDesktopManager );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MouseInputAdapter createBorderListener( JInternalFrame w ) {
|
||||
return new FlatBorderListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PropertyChangeListener createPropertyChangeListener() {
|
||||
return FlatStylingSupport.createPropertyChangeListener( frame, this::installStyle,
|
||||
super.createPropertyChangeListener() );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void installStyle() {
|
||||
try {
|
||||
applyStyle( FlatStylingSupport.getResolvedStyle( frame, "InternalFrame" ) );
|
||||
} catch( RuntimeException ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void applyStyle( Object style ) {
|
||||
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected Object applyStyleProperty( String key, Object value ) {
|
||||
if( borderShared == null )
|
||||
borderShared = new AtomicBoolean( true );
|
||||
return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, frame, borderShared );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this, frame.getBorder() );
|
||||
}
|
||||
|
||||
//---- class FlatInternalFrameBorder --------------------------------------
|
||||
|
||||
public static class FlatInternalFrameBorder
|
||||
extends FlatEmptyBorder
|
||||
implements StyleableBorder
|
||||
{
|
||||
private final Color activeBorderColor = UIManager.getColor( "InternalFrame.activeBorderColor" );
|
||||
private final Color inactiveBorderColor = UIManager.getColor( "InternalFrame.inactiveBorderColor" );
|
||||
private final int borderLineWidth = FlatUIUtils.getUIInt( "InternalFrame.borderLineWidth", 1 );
|
||||
private final boolean dropShadowPainted = UIManager.getBoolean( "InternalFrame.dropShadowPainted" );
|
||||
@Styleable protected Color activeBorderColor = UIManager.getColor( "InternalFrame.activeBorderColor" );
|
||||
@Styleable protected Color inactiveBorderColor = UIManager.getColor( "InternalFrame.inactiveBorderColor" );
|
||||
@Styleable protected int borderLineWidth = FlatUIUtils.getUIInt( "InternalFrame.borderLineWidth", 1 );
|
||||
@Styleable protected boolean dropShadowPainted = UIManager.getBoolean( "InternalFrame.dropShadowPainted" );
|
||||
|
||||
private final FlatDropShadowBorder activeDropShadowBorder = new FlatDropShadowBorder(
|
||||
UIManager.getColor( "InternalFrame.activeDropShadowColor" ),
|
||||
@@ -145,6 +203,36 @@ public class FlatInternalFrameUI
|
||||
super( UIManager.getInsets( "InternalFrame.borderMargins" ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object applyStyleProperty( String key, Object value ) {
|
||||
switch( key ) {
|
||||
case "borderMargins": return applyStyleProperty( (Insets) value );
|
||||
|
||||
case "activeDropShadowColor": return activeDropShadowBorder.applyStyleProperty( "shadowColor", value );
|
||||
case "activeDropShadowInsets": return activeDropShadowBorder.applyStyleProperty( "shadowInsets", value );
|
||||
case "activeDropShadowOpacity": return activeDropShadowBorder.applyStyleProperty( "shadowOpacity", value );
|
||||
case "inactiveDropShadowColor": return inactiveDropShadowBorder.applyStyleProperty( "shadowColor", value );
|
||||
case "inactiveDropShadowInsets": return inactiveDropShadowBorder.applyStyleProperty( "shadowInsets", value );
|
||||
case "inactiveDropShadowOpacity": return inactiveDropShadowBorder.applyStyleProperty( "shadowOpacity", value );
|
||||
}
|
||||
|
||||
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos() {
|
||||
Map<String, Class<?>> infos = new FlatStylingSupport.StyleableInfosMap<>();
|
||||
FlatStylingSupport.collectAnnotatedStyleableInfos( this, infos );
|
||||
infos.put( "borderMargins", Insets.class );
|
||||
infos.put( "activeDropShadowColor", Color.class );
|
||||
infos.put( "activeDropShadowInsets", Insets.class );
|
||||
infos.put( "activeDropShadowOpacity", float.class );
|
||||
infos.put( "inactiveDropShadowColor", Color.class );
|
||||
infos.put( "inactiveDropShadowInsets", Insets.class );
|
||||
infos.put( "inactiveDropShadowOpacity", float.class );
|
||||
return infos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||
if( c instanceof JInternalFrame && ((JInternalFrame)c).isMaximum() ) {
|
||||
@@ -195,4 +283,27 @@ public class FlatInternalFrameUI
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---- class FlatBorderListener -------------------------------------------
|
||||
|
||||
/** @since 1.6 */
|
||||
protected class FlatBorderListener
|
||||
extends BorderListener
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent e ) {
|
||||
if( e.getClickCount() == 2 && !frame.isIcon() &&
|
||||
e.getSource() instanceof FlatInternalFrameTitlePane )
|
||||
{
|
||||
Rectangle iconBounds = ((FlatInternalFrameTitlePane)e.getSource()).getFrameIconBounds();
|
||||
if( iconBounds != null && iconBounds.contains( e.getX(), e.getY() ) ) {
|
||||
if( frame.isClosable() )
|
||||
frame.doDefaultCloseAction();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
super.mouseClicked( e );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,10 @@ import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
@@ -30,8 +34,12 @@ import javax.swing.UIManager;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.basic.BasicHTML;
|
||||
import javax.swing.plaf.basic.BasicLabelUI;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
import com.formdev.flatlaf.FlatLaf;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
/**
|
||||
@@ -51,17 +59,30 @@ import com.formdev.flatlaf.util.UIScale;
|
||||
*/
|
||||
public class FlatLabelUI
|
||||
extends BasicLabelUI
|
||||
implements StyleableUI
|
||||
{
|
||||
private Color disabledForeground;
|
||||
@Styleable protected Color disabledForeground;
|
||||
|
||||
private final boolean shared;
|
||||
private boolean defaults_initialized = false;
|
||||
|
||||
private static ComponentUI instance;
|
||||
private Map<String, Object> oldStyleValues;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
if( instance == null )
|
||||
instance = new FlatLabelUI();
|
||||
return instance;
|
||||
return FlatUIUtils.canUseSharedUI( c )
|
||||
? FlatUIUtils.createSharedUI( FlatLabelUI.class, () -> new FlatLabelUI( true ) )
|
||||
: new FlatLabelUI( false );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected FlatLabelUI( boolean shared ) {
|
||||
this.shared = shared;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installUI( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
installStyle( (JLabel) c );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,7 +99,9 @@ public class FlatLabelUI
|
||||
@Override
|
||||
protected void uninstallDefaults( JLabel c ) {
|
||||
super.uninstallDefaults( c );
|
||||
|
||||
defaults_initialized = false;
|
||||
oldStyleValues = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,28 +118,78 @@ public class FlatLabelUI
|
||||
if( name == "text" || name == "font" || name == "foreground" ) {
|
||||
JLabel label = (JLabel) e.getSource();
|
||||
updateHTMLRenderer( label, label.getText(), true );
|
||||
} else if( name.equals( FlatClientProperties.STYLE ) || name.equals( FlatClientProperties.STYLE_CLASS ) ) {
|
||||
JLabel label = (JLabel) e.getSource();
|
||||
if( shared && FlatStylingSupport.hasStyleProperty( label ) ) {
|
||||
// unshare component UI if necessary
|
||||
// updateUI() invokes applyStyle() from installUI()
|
||||
label.updateUI();
|
||||
} else
|
||||
installStyle( label );
|
||||
label.revalidate();
|
||||
label.repaint();
|
||||
} else
|
||||
super.propertyChange( e );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void installStyle( JLabel c ) {
|
||||
try {
|
||||
applyStyle( c, FlatStylingSupport.getResolvedStyle( c, "Label" ) );
|
||||
} catch( RuntimeException ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void applyStyle( JLabel c, Object style ) {
|
||||
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style,
|
||||
(key, value) -> applyStyleProperty( c, key, value ) );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected Object applyStyleProperty( JLabel c, String key, Object value ) {
|
||||
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, c, key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether text contains HTML headings and adds a special CSS rule to
|
||||
* re-calculate heading font sizes based on current component font size.
|
||||
* Checks whether text contains HTML tags that use "absolute-size" keywords
|
||||
* (e.g. "x-large") for font-size in default style sheet
|
||||
* (see javax/swing/text/html/default.css).
|
||||
* If yes, adds a special CSS rule (BASE_SIZE) to the HTML text, which
|
||||
* re-calculates font sizes based on current component font size.
|
||||
*/
|
||||
static void updateHTMLRenderer( JComponent c, String text, boolean always ) {
|
||||
if( BasicHTML.isHTMLString( text ) &&
|
||||
c.getClientProperty( "html.disable" ) != Boolean.TRUE &&
|
||||
text.contains( "<h" ) &&
|
||||
(text.contains( "<h1" ) || text.contains( "<h2" ) || text.contains( "<h3" ) ||
|
||||
text.contains( "<h4" ) || text.contains( "<h5" ) || text.contains( "<h6" )) )
|
||||
needsFontBaseSize( text ) )
|
||||
{
|
||||
int headIndex = text.indexOf( "<head>" );
|
||||
|
||||
// BASE_SIZE rule is parsed in javax.swing.text.html.StyleSheet.addRule()
|
||||
String style = "<style>BASE_SIZE " + c.getFont().getSize() + "</style>";
|
||||
if( headIndex < 0 )
|
||||
style = "<head>" + style + "</head>";
|
||||
|
||||
int insertIndex = headIndex >= 0 ? (headIndex + "<head>".length()) : "<html>".length();
|
||||
String lowerText = text.toLowerCase();
|
||||
int headIndex;
|
||||
int styleIndex;
|
||||
|
||||
int insertIndex;
|
||||
if( (headIndex = lowerText.indexOf( "<head>" )) >= 0 ) {
|
||||
// there is a <head> tag --> insert after <head> tag
|
||||
insertIndex = headIndex + "<head>".length();
|
||||
} else if( (styleIndex = lowerText.indexOf( "<style>" )) >= 0 ) {
|
||||
// there is a <style> tag --> insert before <style> tag
|
||||
insertIndex = styleIndex;
|
||||
} else {
|
||||
// no <head> or <style> tag --> insert <head> tag after <html> tag
|
||||
style = "<head>" + style + "</head>";
|
||||
insertIndex = "<html>".length();
|
||||
}
|
||||
|
||||
text = text.substring( 0, insertIndex )
|
||||
+ style
|
||||
+ text.substring( insertIndex );
|
||||
@@ -126,6 +199,44 @@ public class FlatLabelUI
|
||||
BasicHTML.updateRenderer( c, text );
|
||||
}
|
||||
|
||||
private static Set<String> tagsUseFontSizeSet;
|
||||
|
||||
private static boolean needsFontBaseSize( String text ) {
|
||||
if( tagsUseFontSizeSet == null ) {
|
||||
// tags that use font-size in javax/swing/text/html/default.css
|
||||
tagsUseFontSizeSet = new HashSet<>( Arrays.asList(
|
||||
"h1", "h2", "h3", "h4", "h5", "h6", "code", "kbd", "big", "small", "samp" ) );
|
||||
}
|
||||
|
||||
// search for tags in HTML text
|
||||
int textLength = text.length();
|
||||
for( int i = 6; i < textLength - 1; i++ ) {
|
||||
if( text.charAt( i ) == '<' ) {
|
||||
switch( text.charAt( i + 1 ) ) {
|
||||
// first letters of tags in tagsUseFontSizeSet
|
||||
case 'b': case 'B':
|
||||
case 'c': case 'C':
|
||||
case 'h': case 'H':
|
||||
case 'k': case 'K':
|
||||
case 's': case 'S':
|
||||
int tagBegin = i + 1;
|
||||
for( i += 2; i < textLength; i++ ) {
|
||||
if( !Character.isLetterOrDigit( text.charAt( i ) ) ) {
|
||||
String tag = text.substring( tagBegin, i ).toLowerCase();
|
||||
if( tagsUseFontSizeSet.contains( tag ) )
|
||||
return true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static Graphics createGraphicsHTMLTextYCorrection( Graphics g, JComponent c ) {
|
||||
return (c.getClientProperty( BasicHTML.propertyKey ) != null)
|
||||
? HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g )
|
||||
|
||||
@@ -61,8 +61,8 @@ public class FlatLineBorder
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
try {
|
||||
FlatUIUtils.setRenderingHints( g2 );
|
||||
g2.setColor( lineColor );
|
||||
FlatUIUtils.paintComponentBorder( g2, x, y, width, height, 0f, scale( lineThickness ), 0f );
|
||||
FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height,
|
||||
0, 0, 0, scale( getLineThickness() ), 0, null, getLineColor(), null );
|
||||
} finally {
|
||||
g2.dispose();
|
||||
}
|
||||
|
||||
@@ -16,11 +16,15 @@
|
||||
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Insets;
|
||||
import java.util.function.Function;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.ListUI;
|
||||
|
||||
/**
|
||||
* Cell border for {@link javax.swing.DefaultListCellRenderer}
|
||||
@@ -33,12 +37,54 @@ import javax.swing.UIManager;
|
||||
public class FlatListCellBorder
|
||||
extends FlatLineBorder
|
||||
{
|
||||
final boolean showCellFocusIndicator = UIManager.getBoolean( "List.showCellFocusIndicator" );
|
||||
protected boolean showCellFocusIndicator = UIManager.getBoolean( "List.showCellFocusIndicator" );
|
||||
|
||||
private Component c;
|
||||
|
||||
protected FlatListCellBorder() {
|
||||
super( UIManager.getInsets( "List.cellMargins" ), UIManager.getColor( "List.cellFocusColor" ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||
Insets m = getStyleFromListUI( c, ui -> ui.cellMargins );
|
||||
if( m != null )
|
||||
return scaleInsets( c, insets, m.top, m.left, m.bottom, m.right );
|
||||
|
||||
return super.getBorderInsets( c, insets );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getLineColor() {
|
||||
if( c != null ) {
|
||||
Color color = getStyleFromListUI( c, ui -> ui.cellFocusColor );
|
||||
if( color != null )
|
||||
return color;
|
||||
}
|
||||
return super.getLineColor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||
this.c = c;
|
||||
super.paintBorder( c, g, x, y, width, height );
|
||||
this.c = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Because this borders are always shared for all lists,
|
||||
* get border specific style from FlatListUI.
|
||||
*/
|
||||
static <T> T getStyleFromListUI( Component c, Function<FlatListUI, T> f ) {
|
||||
JList<?> list = (JList<?>) SwingUtilities.getAncestorOfClass( JList.class, c );
|
||||
if( list != null ) {
|
||||
ListUI ui = list.getUI();
|
||||
if( ui instanceof FlatListUI )
|
||||
return f.apply( (FlatListUI) ui );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//---- class Default ------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -74,6 +120,8 @@ public class FlatListCellBorder
|
||||
{
|
||||
@Override
|
||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||
Boolean b = getStyleFromListUI( c, ui -> ui.showCellFocusIndicator );
|
||||
boolean showCellFocusIndicator = (b != null) ? b : this.showCellFocusIndicator;
|
||||
if( !showCellFocusIndicator )
|
||||
return;
|
||||
|
||||
|
||||
@@ -18,12 +18,19 @@ package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.Insets;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.FocusListener;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Map;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.basic.BasicListUI;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
|
||||
/**
|
||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JList}.
|
||||
@@ -63,16 +70,31 @@ import javax.swing.plaf.basic.BasicListUI;
|
||||
*/
|
||||
public class FlatListUI
|
||||
extends BasicListUI
|
||||
implements StyleableUI
|
||||
{
|
||||
protected Color selectionBackground;
|
||||
protected Color selectionForeground;
|
||||
protected Color selectionInactiveBackground;
|
||||
protected Color selectionInactiveForeground;
|
||||
@Styleable protected Color selectionBackground;
|
||||
@Styleable protected Color selectionForeground;
|
||||
@Styleable protected Color selectionInactiveBackground;
|
||||
@Styleable protected Color selectionInactiveForeground;
|
||||
|
||||
// for FlatListCellBorder
|
||||
@Styleable protected Insets cellMargins;
|
||||
@Styleable protected Color cellFocusColor;
|
||||
@Styleable protected boolean showCellFocusIndicator;
|
||||
|
||||
private Map<String, Object> oldStyleValues;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatListUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installUI( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
installStyle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installDefaults() {
|
||||
super.installDefaults();
|
||||
@@ -93,6 +115,8 @@ public class FlatListUI
|
||||
selectionForeground = null;
|
||||
selectionInactiveBackground = null;
|
||||
selectionInactiveForeground = null;
|
||||
|
||||
oldStyleValues = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -116,6 +140,75 @@ public class FlatListUI
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PropertyChangeListener createPropertyChangeListener() {
|
||||
PropertyChangeListener superListener = super.createPropertyChangeListener();
|
||||
return e -> {
|
||||
superListener.propertyChange( e );
|
||||
|
||||
switch( e.getPropertyName() ) {
|
||||
case FlatClientProperties.COMPONENT_FOCUS_OWNER:
|
||||
toggleSelectionColors();
|
||||
break;
|
||||
|
||||
case FlatClientProperties.STYLE:
|
||||
case FlatClientProperties.STYLE_CLASS:
|
||||
installStyle();
|
||||
list.revalidate();
|
||||
list.repaint();
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void installStyle() {
|
||||
try {
|
||||
applyStyle( FlatStylingSupport.getResolvedStyle( list, "List" ) );
|
||||
} catch( RuntimeException ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void applyStyle( Object style ) {
|
||||
Color oldSelectionBackground = selectionBackground;
|
||||
Color oldSelectionForeground = selectionForeground;
|
||||
Color oldSelectionInactiveBackground = selectionInactiveBackground;
|
||||
Color oldSelectionInactiveForeground = selectionInactiveForeground;
|
||||
|
||||
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||
|
||||
// update selection background
|
||||
if( selectionBackground != oldSelectionBackground ) {
|
||||
Color selBg = list.getSelectionBackground();
|
||||
if( selBg == oldSelectionBackground )
|
||||
list.setSelectionBackground( selectionBackground );
|
||||
else if( selBg == oldSelectionInactiveBackground )
|
||||
list.setSelectionBackground( selectionInactiveBackground );
|
||||
}
|
||||
|
||||
// update selection foreground
|
||||
if( selectionForeground != oldSelectionForeground ) {
|
||||
Color selFg = list.getSelectionForeground();
|
||||
if( selFg == oldSelectionForeground )
|
||||
list.setSelectionForeground( selectionForeground );
|
||||
else if( selFg == oldSelectionInactiveForeground )
|
||||
list.setSelectionForeground( selectionInactiveForeground );
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected Object applyStyleProperty( String key, Object value ) {
|
||||
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, list, key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle selection colors from focused to inactive and vice versa.
|
||||
*
|
||||
|
||||
@@ -29,7 +29,7 @@ import javax.swing.plaf.basic.BasicBorders;
|
||||
public class FlatMarginBorder
|
||||
extends BasicBorders.MarginBorder
|
||||
{
|
||||
private final int left, right, top, bottom;
|
||||
protected int left, right, top, bottom;
|
||||
|
||||
public FlatMarginBorder() {
|
||||
left = right = top = bottom = 0;
|
||||
|
||||
@@ -20,11 +20,12 @@ import static com.formdev.flatlaf.util.UIScale.scale;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Insets;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.Map;
|
||||
import javax.swing.JMenuBar;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableBorder;
|
||||
|
||||
/**
|
||||
* Border for {@link javax.swing.JMenuBar}.
|
||||
@@ -35,21 +36,28 @@ import javax.swing.UIManager;
|
||||
*/
|
||||
public class FlatMenuBarBorder
|
||||
extends FlatMarginBorder
|
||||
implements StyleableBorder
|
||||
{
|
||||
private final Color borderColor = UIManager.getColor( "MenuBar.borderColor" );
|
||||
@Styleable protected Color borderColor = UIManager.getColor( "MenuBar.borderColor" );
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Object applyStyleProperty( String key, Object value ) {
|
||||
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos() {
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
try {
|
||||
float lineHeight = scale( (float) 1 );
|
||||
if( !showBottomSeparator( c ) )
|
||||
return;
|
||||
|
||||
FlatUIUtils.setRenderingHints( g2 );
|
||||
g2.setColor( borderColor );
|
||||
g2.fill( new Rectangle2D.Float( x, y + height - lineHeight, width, lineHeight ) );
|
||||
} finally {
|
||||
g2.dispose();
|
||||
}
|
||||
float lineHeight = scale( (float) 1 );
|
||||
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,4 +71,9 @@ public class FlatMenuBarBorder
|
||||
insets.right = scale( margin.right );
|
||||
return insets;
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected boolean showBottomSeparator( Component c ) {
|
||||
return !FlatMenuBarUI.useUnifiedBackground( c );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,19 +16,34 @@
|
||||
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.ActionMap;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuBar;
|
||||
import javax.swing.JRootPane;
|
||||
import javax.swing.LookAndFeel;
|
||||
import javax.swing.MenuElement;
|
||||
import javax.swing.MenuSelectionManager;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.ActionMapUIResource;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.UIResource;
|
||||
import javax.swing.plaf.basic.BasicMenuBarUI;
|
||||
import com.formdev.flatlaf.FlatLaf;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
import com.formdev.flatlaf.util.SystemInfo;
|
||||
|
||||
/**
|
||||
@@ -41,11 +56,29 @@ import com.formdev.flatlaf.util.SystemInfo;
|
||||
* @uiDefault MenuBar.foreground Color
|
||||
* @uiDefault MenuBar.border Border
|
||||
*
|
||||
* <!-- FlatMenuBarUI -->
|
||||
*
|
||||
* @uiDefault TitlePane.unifiedBackground boolean
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
public class FlatMenuBarUI
|
||||
extends BasicMenuBarUI
|
||||
implements StyleableUI
|
||||
{
|
||||
// used in FlatMenuItemBorder
|
||||
/** @since 2 */ @Styleable protected Insets itemMargins;
|
||||
|
||||
// used in FlatMenuUI
|
||||
/** @since 2 */ @Styleable protected Color hoverBackground;
|
||||
/** @since 2 */ @Styleable protected Color underlineSelectionBackground;
|
||||
/** @since 2 */ @Styleable protected Color underlineSelectionColor;
|
||||
/** @since 2 */ @Styleable protected int underlineSelectionHeight = -1;
|
||||
|
||||
private PropertyChangeListener propertyChangeListener;
|
||||
private Map<String, Object> oldStyleValues;
|
||||
private AtomicBoolean borderShared;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatMenuBarUI();
|
||||
}
|
||||
@@ -55,6 +88,44 @@ public class FlatMenuBarUI
|
||||
* Do not add any functionality here.
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void installUI( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
installStyle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installDefaults() {
|
||||
super.installDefaults();
|
||||
|
||||
LookAndFeel.installProperty( menuBar, "opaque", false );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void uninstallDefaults() {
|
||||
super.uninstallDefaults();
|
||||
|
||||
oldStyleValues = null;
|
||||
borderShared = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installListeners() {
|
||||
super.installListeners();
|
||||
|
||||
propertyChangeListener = FlatStylingSupport.createPropertyChangeListener( menuBar, this::installStyle, null );
|
||||
menuBar.addPropertyChangeListener( propertyChangeListener );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void uninstallListeners() {
|
||||
super.uninstallListeners();
|
||||
|
||||
menuBar.removePropertyChangeListener( propertyChangeListener );
|
||||
propertyChangeListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installKeyboardActions() {
|
||||
super.installKeyboardActions();
|
||||
@@ -67,6 +138,85 @@ public class FlatMenuBarUI
|
||||
map.put( "takeFocus", new TakeFocus() );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void installStyle() {
|
||||
try {
|
||||
applyStyle( FlatStylingSupport.getResolvedStyle( menuBar, "MenuBar" ) );
|
||||
} catch( RuntimeException ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void applyStyle( Object style ) {
|
||||
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected Object applyStyleProperty( String key, Object value ) {
|
||||
if( borderShared == null )
|
||||
borderShared = new AtomicBoolean( true );
|
||||
return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, menuBar, borderShared );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this, menuBar.getBorder() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update( Graphics g, JComponent c ) {
|
||||
// paint background
|
||||
Color background = getBackground( c );
|
||||
if( background != null ) {
|
||||
g.setColor( background );
|
||||
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
|
||||
}
|
||||
|
||||
paint( g, c );
|
||||
}
|
||||
|
||||
protected Color getBackground( JComponent c ) {
|
||||
Color background = c.getBackground();
|
||||
|
||||
// paint background if opaque or if having custom background color
|
||||
if( c.isOpaque() || !(background instanceof UIResource) )
|
||||
return background;
|
||||
|
||||
// paint background if menu bar is not the "main" menu bar
|
||||
JRootPane rootPane = SwingUtilities.getRootPane( c );
|
||||
if( rootPane == null || !(rootPane.getParent() instanceof Window) || rootPane.getJMenuBar() != c )
|
||||
return background;
|
||||
|
||||
// use parent background for unified title pane
|
||||
if( useUnifiedBackground( c ) )
|
||||
background = FlatUIUtils.getParentBackground( c );
|
||||
|
||||
// paint background in full screen mode
|
||||
if( FlatUIUtils.isFullScreen( rootPane ) )
|
||||
return background;
|
||||
|
||||
// do not paint background if menu bar is embedded into title pane
|
||||
return FlatRootPaneUI.isMenuBarEmbedded( rootPane ) ? null : background;
|
||||
}
|
||||
|
||||
/**@since 2 */
|
||||
static boolean useUnifiedBackground( Component c ) {
|
||||
// check whether:
|
||||
// - TitlePane.unifiedBackground is true and
|
||||
// - menu bar is the "main" menu bar and
|
||||
// - window has custom decorations enabled
|
||||
|
||||
JRootPane rootPane;
|
||||
// (not storing value of "TitlePane.unifiedBackground" in class to allow changing at runtime)
|
||||
return UIManager.getBoolean( "TitlePane.unifiedBackground" ) &&
|
||||
(rootPane = SwingUtilities.getRootPane( c )) != null &&
|
||||
rootPane.getParent() instanceof Window &&
|
||||
rootPane.getJMenuBar() == c &&
|
||||
FlatNativeWindowBorder.hasCustomDecoration( (Window) rootPane.getParent() );
|
||||
}
|
||||
|
||||
//---- class TakeFocus ----------------------------------------------------
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,9 +18,11 @@ package com.formdev.flatlaf.ui;
|
||||
|
||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Insets;
|
||||
import javax.swing.JMenuBar;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.MenuBarUI;
|
||||
|
||||
/**
|
||||
* Border for {@link javax.swing.JMenu}, {@link javax.swing.JMenuItem},
|
||||
@@ -33,15 +35,22 @@ import javax.swing.UIManager;
|
||||
public class FlatMenuItemBorder
|
||||
extends FlatMarginBorder
|
||||
{
|
||||
// only used if parent menubar is not a instance of FlatMenuBarUI
|
||||
private final Insets menuBarItemMargins = UIManager.getInsets( "MenuBar.itemMargins" );
|
||||
|
||||
@Override
|
||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||
if( c.getParent() instanceof JMenuBar ) {
|
||||
insets.top = scale( menuBarItemMargins.top );
|
||||
insets.left = scale( menuBarItemMargins.left );
|
||||
insets.bottom = scale( menuBarItemMargins.bottom );
|
||||
insets.right = scale( menuBarItemMargins.right );
|
||||
Container parent = c.getParent();
|
||||
if( parent instanceof JMenuBar ) {
|
||||
// get margins from FlatMenuBarUI to allow styling
|
||||
MenuBarUI ui = ((JMenuBar)parent).getUI();
|
||||
Insets margins = (ui instanceof FlatMenuBarUI && ((FlatMenuBarUI)ui).itemMargins != null)
|
||||
? ((FlatMenuBarUI)ui).itemMargins
|
||||
: this.menuBarItemMargins;
|
||||
insets.top = scale( margins.top );
|
||||
insets.left = scale( margins.left );
|
||||
insets.bottom = scale( margins.bottom );
|
||||
insets.right = scale( margins.right );
|
||||
return insets;
|
||||
} else
|
||||
return super.getBorderInsets( c, insets );
|
||||
|
||||
@@ -30,6 +30,7 @@ import java.awt.Rectangle;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.text.AttributedCharacterIterator;
|
||||
import java.util.Map;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuItem;
|
||||
@@ -39,6 +40,11 @@ import javax.swing.UIManager;
|
||||
import javax.swing.plaf.basic.BasicHTML;
|
||||
import javax.swing.text.View;
|
||||
import com.formdev.flatlaf.FlatLaf;
|
||||
import com.formdev.flatlaf.icons.FlatCheckBoxMenuItemIcon;
|
||||
import com.formdev.flatlaf.icons.FlatMenuArrowIcon;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||
import com.formdev.flatlaf.util.DerivedColor;
|
||||
import com.formdev.flatlaf.util.Graphics2DProxy;
|
||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||
import com.formdev.flatlaf.util.SystemInfo;
|
||||
@@ -55,31 +61,33 @@ import com.formdev.flatlaf.util.SystemInfo;
|
||||
* @uiDefault MenuItem.underlineSelectionBackground Color
|
||||
* @uiDefault MenuItem.underlineSelectionCheckBackground Color
|
||||
* @uiDefault MenuItem.underlineSelectionColor Color
|
||||
* @uiDefault MenuItem.underlineSelectionHeight Color
|
||||
* @uiDefault MenuItem.underlineSelectionHeight int
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
public class FlatMenuItemRenderer
|
||||
{
|
||||
protected final JMenuItem menuItem;
|
||||
protected final Icon checkIcon;
|
||||
protected final Icon arrowIcon;
|
||||
protected Icon checkIcon;
|
||||
protected Icon arrowIcon;
|
||||
protected final Font acceleratorFont;
|
||||
protected final String acceleratorDelimiter;
|
||||
|
||||
protected final int minimumWidth = UIManager.getInt( "MenuItem.minimumWidth" );
|
||||
protected final Dimension minimumIconSize;
|
||||
protected final int textAcceleratorGap = FlatUIUtils.getUIInt( "MenuItem.textAcceleratorGap", 28 );
|
||||
protected final int textNoAcceleratorGap = FlatUIUtils.getUIInt( "MenuItem.textNoAcceleratorGap", 6 );
|
||||
protected final int acceleratorArrowGap = FlatUIUtils.getUIInt( "MenuItem.acceleratorArrowGap", 2 );
|
||||
@Styleable protected int minimumWidth = UIManager.getInt( "MenuItem.minimumWidth" );
|
||||
@Styleable protected Dimension minimumIconSize;
|
||||
@Styleable protected int textAcceleratorGap = FlatUIUtils.getUIInt( "MenuItem.textAcceleratorGap", 28 );
|
||||
@Styleable protected int textNoAcceleratorGap = FlatUIUtils.getUIInt( "MenuItem.textNoAcceleratorGap", 6 );
|
||||
@Styleable protected int acceleratorArrowGap = FlatUIUtils.getUIInt( "MenuItem.acceleratorArrowGap", 2 );
|
||||
|
||||
protected final Color checkBackground = UIManager.getColor( "MenuItem.checkBackground" );
|
||||
protected final Insets checkMargins = UIManager.getInsets( "MenuItem.checkMargins" );
|
||||
@Styleable protected Color checkBackground = UIManager.getColor( "MenuItem.checkBackground" );
|
||||
@Styleable protected Insets checkMargins = UIManager.getInsets( "MenuItem.checkMargins" );
|
||||
|
||||
protected final Color underlineSelectionBackground = UIManager.getColor( "MenuItem.underlineSelectionBackground" );
|
||||
protected final Color underlineSelectionCheckBackground = UIManager.getColor( "MenuItem.underlineSelectionCheckBackground" );
|
||||
protected final Color underlineSelectionColor = UIManager.getColor( "MenuItem.underlineSelectionColor" );
|
||||
protected final int underlineSelectionHeight = UIManager.getInt( "MenuItem.underlineSelectionHeight" );
|
||||
@Styleable protected Color underlineSelectionBackground = UIManager.getColor( "MenuItem.underlineSelectionBackground" );
|
||||
@Styleable protected Color underlineSelectionCheckBackground = UIManager.getColor( "MenuItem.underlineSelectionCheckBackground" );
|
||||
@Styleable protected Color underlineSelectionColor = UIManager.getColor( "MenuItem.underlineSelectionColor" );
|
||||
@Styleable protected int underlineSelectionHeight = UIManager.getInt( "MenuItem.underlineSelectionHeight" );
|
||||
|
||||
private boolean iconsShared = true;
|
||||
|
||||
protected FlatMenuItemRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
|
||||
Font acceleratorFont, String acceleratorDelimiter )
|
||||
@@ -94,6 +102,64 @@ public class FlatMenuItemRenderer
|
||||
this.minimumIconSize = (minimumIconSize != null) ? minimumIconSize : new Dimension( 16, 16 );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected Object applyStyleProperty( String key, Object value ) {
|
||||
// style icon
|
||||
if( key.startsWith( "icon." ) || key.equals( "selectionForeground" ) ) {
|
||||
if( iconsShared ) {
|
||||
if( checkIcon instanceof FlatCheckBoxMenuItemIcon )
|
||||
checkIcon = FlatStylingSupport.cloneIcon( checkIcon );
|
||||
if( arrowIcon instanceof FlatMenuArrowIcon )
|
||||
arrowIcon = FlatStylingSupport.cloneIcon( arrowIcon );
|
||||
iconsShared = false;
|
||||
}
|
||||
|
||||
if( key.startsWith( "icon." ) ) {
|
||||
String key2 = key.substring( "icon.".length() );
|
||||
|
||||
try {
|
||||
if( checkIcon instanceof FlatCheckBoxMenuItemIcon )
|
||||
return ((FlatCheckBoxMenuItemIcon)checkIcon).applyStyleProperty( key2, value );
|
||||
} catch ( UnknownStyleException ex ) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
if( arrowIcon instanceof FlatMenuArrowIcon )
|
||||
return ((FlatMenuArrowIcon)arrowIcon).applyStyleProperty( key2, value );
|
||||
} catch ( UnknownStyleException ex ) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// keys with prefix "icon." are only for icons
|
||||
throw new UnknownStyleException( key );
|
||||
} else if( key.equals( "selectionForeground" ) ) {
|
||||
// special case: same key is used in icons and in menuitem
|
||||
if( checkIcon instanceof FlatCheckBoxMenuItemIcon )
|
||||
((FlatCheckBoxMenuItemIcon)checkIcon).applyStyleProperty( key, value );
|
||||
if( arrowIcon instanceof FlatMenuArrowIcon )
|
||||
((FlatMenuArrowIcon)arrowIcon).applyStyleProperty( key, value );
|
||||
|
||||
// throw exception because the caller should also apply this key
|
||||
throw new UnknownStyleException( key );
|
||||
}
|
||||
}
|
||||
|
||||
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
public Map<String, Class<?>> getStyleableInfos() {
|
||||
Map<String, Class<?>> infos = FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
if( checkIcon instanceof FlatCheckBoxMenuItemIcon )
|
||||
FlatStylingSupport.putAllPrefixKey( infos, "icon.", ((FlatCheckBoxMenuItemIcon)checkIcon).getStyleableInfos() );
|
||||
infos.remove( "icon.selectionForeground" );
|
||||
if( arrowIcon instanceof FlatMenuArrowIcon )
|
||||
FlatStylingSupport.putAllPrefixKey( infos, "icon.", ((FlatMenuArrowIcon)arrowIcon).getStyleableInfos() );
|
||||
infos.remove( "icon.selectionForeground" );
|
||||
return infos;
|
||||
}
|
||||
|
||||
protected Dimension getPreferredMenuItemSize() {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
@@ -246,8 +312,11 @@ public class FlatMenuItemRenderer
|
||||
g.setColor( Color.orange ); g.drawRect( arrowRect.x, arrowRect.y, arrowRect.width - 1, arrowRect.height - 1 );
|
||||
debug*/
|
||||
|
||||
paintBackground( g, selectionBackground );
|
||||
paintIcon( g, iconRect, getIconForPainting() );
|
||||
boolean underlineSelection = isUnderlineSelection();
|
||||
paintBackground( g, underlineSelection ? underlineSelectionBackground : selectionBackground );
|
||||
if( underlineSelection && isArmedOrSelected( menuItem ) )
|
||||
paintUnderlineSelection( g, underlineSelectionColor, underlineSelectionHeight );
|
||||
paintIcon( g, iconRect, getIconForPainting(), underlineSelection ? underlineSelectionCheckBackground : checkBackground, selectionBackground );
|
||||
paintText( g, textRect, menuItem.getText(), selectionForeground, disabledForeground );
|
||||
paintAccelerator( g, accelRect, getAcceleratorText(), acceleratorForeground, acceleratorSelectionForeground, disabledForeground );
|
||||
if( !isTopLevelMenu( menuItem ) )
|
||||
@@ -257,36 +326,36 @@ debug*/
|
||||
protected void paintBackground( Graphics g, Color selectionBackground ) {
|
||||
boolean armedOrSelected = isArmedOrSelected( menuItem );
|
||||
if( menuItem.isOpaque() || armedOrSelected ) {
|
||||
int width = menuItem.getWidth();
|
||||
int height = menuItem.getHeight();
|
||||
|
||||
// paint background
|
||||
g.setColor( armedOrSelected
|
||||
? (isUnderlineSelection()
|
||||
? deriveBackground( underlineSelectionBackground )
|
||||
: selectionBackground)
|
||||
? deriveBackground( selectionBackground )
|
||||
: menuItem.getBackground() );
|
||||
g.fillRect( 0, 0, width, height );
|
||||
g.fillRect( 0, 0, menuItem.getWidth(), menuItem.getHeight() );
|
||||
}
|
||||
}
|
||||
|
||||
// paint underline
|
||||
if( armedOrSelected && isUnderlineSelection() ) {
|
||||
int underlineHeight = scale( underlineSelectionHeight );
|
||||
g.setColor( underlineSelectionColor );
|
||||
if( isTopLevelMenu( menuItem ) ) {
|
||||
// paint underline at bottom
|
||||
g.fillRect( 0, height - underlineHeight, width, underlineHeight );
|
||||
} else if( menuItem.getComponentOrientation().isLeftToRight() ) {
|
||||
// paint underline at left side
|
||||
g.fillRect( 0, 0, underlineHeight, height );
|
||||
} else {
|
||||
// paint underline at right side
|
||||
g.fillRect( width - underlineHeight, 0, underlineHeight, height );
|
||||
}
|
||||
}
|
||||
protected void paintUnderlineSelection( Graphics g, Color underlineSelectionColor, int underlineSelectionHeight ) {
|
||||
int width = menuItem.getWidth();
|
||||
int height = menuItem.getHeight();
|
||||
|
||||
int underlineHeight = scale( underlineSelectionHeight );
|
||||
g.setColor( underlineSelectionColor );
|
||||
if( isTopLevelMenu( menuItem ) ) {
|
||||
// paint underline at bottom
|
||||
g.fillRect( 0, height - underlineHeight, width, underlineHeight );
|
||||
} else if( menuItem.getComponentOrientation().isLeftToRight() ) {
|
||||
// paint underline at left side
|
||||
g.fillRect( 0, 0, underlineHeight, height );
|
||||
} else {
|
||||
// paint underline at right side
|
||||
g.fillRect( width - underlineHeight, 0, underlineHeight, height );
|
||||
}
|
||||
}
|
||||
|
||||
protected Color deriveBackground( Color background ) {
|
||||
if( !(background instanceof DerivedColor) )
|
||||
return background;
|
||||
|
||||
Color baseColor = menuItem.isOpaque()
|
||||
? menuItem.getBackground()
|
||||
: FlatUIUtils.getParentBackground( menuItem );
|
||||
@@ -294,12 +363,12 @@ debug*/
|
||||
return FlatUIUtils.deriveColor( background, baseColor );
|
||||
}
|
||||
|
||||
protected void paintIcon( Graphics g, Rectangle iconRect, Icon icon ) {
|
||||
protected void paintIcon( Graphics g, Rectangle iconRect, Icon icon, Color checkBackground, Color selectionBackground ) {
|
||||
// if checkbox/radiobutton menu item is selected and also has a custom icon,
|
||||
// then use filled icon background to indicate selection (instead of using checkIcon)
|
||||
if( menuItem.isSelected() && checkIcon != null && icon != checkIcon ) {
|
||||
Rectangle r = FlatUIUtils.addInsets( iconRect, scale( checkMargins ) );
|
||||
g.setColor( deriveBackground( isUnderlineSelection() ? underlineSelectionCheckBackground : checkBackground ) );
|
||||
g.setColor( FlatUIUtils.deriveColor( checkBackground, selectionBackground ) );
|
||||
g.fillRect( r.x, r.y, r.width, r.height );
|
||||
}
|
||||
|
||||
|
||||
@@ -16,13 +16,19 @@
|
||||
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Map;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.LookAndFeel;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.basic.BasicMenuItemUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
|
||||
/**
|
||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenuItem}.
|
||||
@@ -54,13 +60,22 @@ import javax.swing.plaf.basic.BasicMenuItemUI;
|
||||
*/
|
||||
public class FlatMenuItemUI
|
||||
extends BasicMenuItemUI
|
||||
implements StyleableUI
|
||||
{
|
||||
private FlatMenuItemRenderer renderer;
|
||||
private Map<String, Object> oldStyleValues;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatMenuItemUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installUI( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
installStyle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installDefaults() {
|
||||
super.installDefaults();
|
||||
@@ -75,12 +90,70 @@ public class FlatMenuItemUI
|
||||
super.uninstallDefaults();
|
||||
|
||||
renderer = null;
|
||||
oldStyleValues = null;
|
||||
}
|
||||
|
||||
protected FlatMenuItemRenderer createRenderer() {
|
||||
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
||||
return FlatStylingSupport.createPropertyChangeListener( c, this::installStyle, super.createPropertyChangeListener( c ) );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void installStyle() {
|
||||
try {
|
||||
applyStyle( FlatStylingSupport.getResolvedStyle( menuItem, "MenuItem" ) );
|
||||
} catch( RuntimeException ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void applyStyle( Object style ) {
|
||||
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected Object applyStyleProperty( String key, Object value ) {
|
||||
try {
|
||||
return renderer.applyStyleProperty( key, value );
|
||||
} catch ( UnknownStyleException ex ) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
Object oldValue;
|
||||
switch( key ) {
|
||||
// BasicMenuItemUI
|
||||
case "selectionBackground": oldValue = selectionBackground; selectionBackground = (Color) value; return oldValue;
|
||||
case "selectionForeground": oldValue = selectionForeground; selectionForeground = (Color) value; return oldValue;
|
||||
case "disabledForeground": oldValue = disabledForeground; disabledForeground = (Color) value; return oldValue;
|
||||
case "acceleratorForeground": oldValue = acceleratorForeground; acceleratorForeground = (Color) value; return oldValue;
|
||||
case "acceleratorSelectionForeground": oldValue = acceleratorSelectionForeground; acceleratorSelectionForeground = (Color) value; return oldValue;
|
||||
}
|
||||
|
||||
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, menuItem, key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
return getStyleableInfos( renderer );
|
||||
}
|
||||
|
||||
static Map<String, Class<?>> getStyleableInfos( FlatMenuItemRenderer renderer ) {
|
||||
Map<String, Class<?>> infos = new FlatStylingSupport.StyleableInfosMap<>();
|
||||
infos.put( "selectionBackground", Color.class );
|
||||
infos.put( "selectionForeground", Color.class );
|
||||
infos.put( "disabledForeground", Color.class );
|
||||
infos.put( "acceleratorForeground", Color.class );
|
||||
infos.put( "acceleratorSelectionForeground", Color.class );
|
||||
infos.putAll( renderer.getStyleableInfos() );
|
||||
return infos;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
|
||||
return renderer.getPreferredMenuItemSize();
|
||||
|
||||
@@ -21,16 +21,24 @@ import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import javax.swing.ButtonModel;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuBar;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.LookAndFeel;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.event.MouseInputListener;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.MenuBarUI;
|
||||
import javax.swing.plaf.basic.BasicMenuUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
|
||||
/**
|
||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenu}.
|
||||
@@ -60,20 +68,34 @@ import javax.swing.plaf.basic.BasicMenuUI;
|
||||
* <!-- FlatMenuUI -->
|
||||
*
|
||||
* @uiDefault MenuItem.iconTextGap int
|
||||
*
|
||||
* <!-- FlatMenuRenderer -->
|
||||
*
|
||||
* @uiDefault MenuBar.hoverBackground Color
|
||||
* @uiDefault MenuBar.underlineSelectionBackground Color
|
||||
* @uiDefault MenuBar.underlineSelectionColor Color
|
||||
* @uiDefault MenuBar.underlineSelectionHeight int
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
public class FlatMenuUI
|
||||
extends BasicMenuUI
|
||||
implements StyleableUI
|
||||
{
|
||||
private Color hoverBackground;
|
||||
private FlatMenuItemRenderer renderer;
|
||||
private Map<String, Object> oldStyleValues;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatMenuUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installUI( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
installStyle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installDefaults() {
|
||||
super.installDefaults();
|
||||
@@ -82,7 +104,6 @@ public class FlatMenuUI
|
||||
|
||||
menuItem.setRolloverEnabled( true );
|
||||
|
||||
hoverBackground = UIManager.getColor( "MenuBar.hoverBackground" );
|
||||
renderer = createRenderer();
|
||||
}
|
||||
|
||||
@@ -90,8 +111,8 @@ public class FlatMenuUI
|
||||
protected void uninstallDefaults() {
|
||||
super.uninstallDefaults();
|
||||
|
||||
hoverBackground = null;
|
||||
renderer = null;
|
||||
oldStyleValues = null;
|
||||
}
|
||||
|
||||
protected FlatMenuItemRenderer createRenderer() {
|
||||
@@ -123,6 +144,52 @@ public class FlatMenuUI
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
||||
return FlatStylingSupport.createPropertyChangeListener( c, this::installStyle, super.createPropertyChangeListener( c ) );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void installStyle() {
|
||||
try {
|
||||
applyStyle( FlatStylingSupport.getResolvedStyle( menuItem, "Menu" ) );
|
||||
} catch( RuntimeException ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void applyStyle( Object style ) {
|
||||
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected Object applyStyleProperty( String key, Object value ) {
|
||||
try {
|
||||
return renderer.applyStyleProperty( key, value );
|
||||
} catch ( UnknownStyleException ex ) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
Object oldValue;
|
||||
switch( key ) {
|
||||
// BasicMenuItemUI
|
||||
case "selectionBackground": oldValue = selectionBackground; selectionBackground = (Color) value; return oldValue;
|
||||
case "selectionForeground": oldValue = selectionForeground; selectionForeground = (Color) value; return oldValue;
|
||||
case "disabledForeground": oldValue = disabledForeground; disabledForeground = (Color) value; return oldValue;
|
||||
case "acceleratorForeground": oldValue = acceleratorForeground; acceleratorForeground = (Color) value; return oldValue;
|
||||
case "acceleratorSelectionForeground": oldValue = acceleratorSelectionForeground; acceleratorSelectionForeground = (Color) value; return oldValue;
|
||||
}
|
||||
|
||||
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, menuItem, key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
return FlatMenuItemUI.getStyleableInfos( renderer );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getMinimumSize( JComponent c ) {
|
||||
// avoid that top-level menus (in menu bar) are made smaller if horizontal space is rare
|
||||
@@ -147,6 +214,11 @@ public class FlatMenuUI
|
||||
protected class FlatMenuRenderer
|
||||
extends FlatMenuItemRenderer
|
||||
{
|
||||
protected Color hoverBackground = UIManager.getColor( "MenuBar.hoverBackground" );
|
||||
protected Color menuBarUnderlineSelectionBackground = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionBackground", underlineSelectionBackground );
|
||||
protected Color menuBarUnderlineSelectionColor = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionColor", underlineSelectionColor );
|
||||
protected int menuBarUnderlineSelectionHeight = FlatUIUtils.getUIInt( "MenuBar.underlineSelectionHeight", underlineSelectionHeight );
|
||||
|
||||
protected FlatMenuRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
|
||||
Font acceleratorFont, String acceleratorDelimiter )
|
||||
{
|
||||
@@ -155,14 +227,39 @@ public class FlatMenuUI
|
||||
|
||||
@Override
|
||||
protected void paintBackground( Graphics g, Color selectionBackground ) {
|
||||
ButtonModel model = menuItem.getModel();
|
||||
if( model.isRollover() && !model.isArmed() && !model.isSelected() &&
|
||||
model.isEnabled() && ((JMenu)menuItem).isTopLevelMenu() )
|
||||
{
|
||||
g.setColor( deriveBackground( hoverBackground ) );
|
||||
g.fillRect( 0, 0, menuItem.getWidth(), menuItem.getHeight() );
|
||||
} else
|
||||
super.paintBackground( g, selectionBackground );
|
||||
if( ((JMenu)menuItem).isTopLevelMenu() ) {
|
||||
if( isUnderlineSelection() )
|
||||
selectionBackground = getStyleFromMenuBarUI( ui -> ui.underlineSelectionBackground, menuBarUnderlineSelectionBackground );
|
||||
|
||||
ButtonModel model = menuItem.getModel();
|
||||
if( model.isRollover() && !model.isArmed() && !model.isSelected() && model.isEnabled() ) {
|
||||
g.setColor( deriveBackground( getStyleFromMenuBarUI( ui -> ui.hoverBackground, hoverBackground ) ) );
|
||||
g.fillRect( 0, 0, menuItem.getWidth(), menuItem.getHeight() );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
super.paintBackground( g, selectionBackground );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintUnderlineSelection( Graphics g, Color underlineSelectionColor, int underlineSelectionHeight ) {
|
||||
if( ((JMenu)menuItem).isTopLevelMenu() ) {
|
||||
underlineSelectionColor = getStyleFromMenuBarUI( ui -> ui.underlineSelectionColor, menuBarUnderlineSelectionColor );
|
||||
underlineSelectionHeight = getStyleFromMenuBarUI( ui -> (ui.underlineSelectionHeight != -1)
|
||||
? ui.underlineSelectionHeight : null, menuBarUnderlineSelectionHeight );
|
||||
}
|
||||
|
||||
super.paintUnderlineSelection( g, underlineSelectionColor, underlineSelectionHeight );
|
||||
}
|
||||
|
||||
private <T> T getStyleFromMenuBarUI( Function<FlatMenuBarUI, T> f, T defaultValue ) {
|
||||
MenuBarUI ui = ((JMenuBar)menuItem.getParent()).getUI();
|
||||
if( !(ui instanceof FlatMenuBarUI) )
|
||||
return defaultValue;
|
||||
|
||||
T value = f.apply( (FlatMenuBarUI) ui );
|
||||
return (value != null) ? value : defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
* Copyright 2021 FormDev Software GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Container;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Window;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.List;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JRootPane;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
import com.formdev.flatlaf.FlatLaf;
|
||||
import com.formdev.flatlaf.FlatSystemProperties;
|
||||
import com.formdev.flatlaf.ui.JBRCustomDecorations.JBRWindowTopBorder;
|
||||
import com.formdev.flatlaf.util.SystemInfo;
|
||||
|
||||
/**
|
||||
* Support for custom window decorations with native window border.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
* @since 1.1
|
||||
*/
|
||||
public class FlatNativeWindowBorder
|
||||
{
|
||||
// can use window decorations if:
|
||||
// - on Windows 10
|
||||
// - not when running in JetBrains Projector, Webswing or WinPE
|
||||
// - not disabled via system property
|
||||
private static final boolean canUseWindowDecorations =
|
||||
SystemInfo.isWindows_10_orLater &&
|
||||
!SystemInfo.isProjector &&
|
||||
!SystemInfo.isWebswing &&
|
||||
!SystemInfo.isWinPE &&
|
||||
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_WINDOW_DECORATIONS, true );
|
||||
|
||||
// check this field before using class JBRCustomDecorations to avoid unnecessary loading of that class
|
||||
private static final boolean canUseJBRCustomDecorations =
|
||||
canUseWindowDecorations &&
|
||||
SystemInfo.isJetBrainsJVM_11_orLater &&
|
||||
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, true );
|
||||
|
||||
private static Boolean supported;
|
||||
private static Provider nativeProvider;
|
||||
|
||||
public static boolean isSupported() {
|
||||
if( canUseJBRCustomDecorations )
|
||||
return JBRCustomDecorations.isSupported();
|
||||
|
||||
initialize();
|
||||
return supported;
|
||||
}
|
||||
|
||||
static Object install( JRootPane rootPane ) {
|
||||
if( canUseJBRCustomDecorations )
|
||||
return JBRCustomDecorations.install( rootPane );
|
||||
|
||||
if( !isSupported() )
|
||||
return null;
|
||||
|
||||
// do nothing if root pane has a parent that is not a window (e.g. a JInternalFrame)
|
||||
Container parent = rootPane.getParent();
|
||||
if( parent != null && !(parent instanceof Window) )
|
||||
return null;
|
||||
|
||||
// Check whether root pane already has a window, which is the case when
|
||||
// switching from another LaF to FlatLaf.
|
||||
// Also check whether the window is displayable, which is required to install
|
||||
// FlatLaf native window border.
|
||||
// If the window is not displayable, then it was probably closed/disposed but not yet removed
|
||||
// from the list of windows that AWT maintains and returns with Window.getWindows().
|
||||
// It could be also be a window that is currently hidden, but may be shown later.
|
||||
if( parent instanceof Window && parent.isDisplayable() )
|
||||
install( (Window) parent );
|
||||
|
||||
// Install FlatLaf native window border, which must be done late,
|
||||
// when the native window is already created, because it needs access to the window.
|
||||
// Uninstall FlatLaf native window border when window is disposed (or root pane removed).
|
||||
// "ancestor" property change event is fired from JComponent.addNotify() and removeNotify().
|
||||
PropertyChangeListener ancestorListener = e -> {
|
||||
Object newValue = e.getNewValue();
|
||||
if( newValue instanceof Window )
|
||||
install( (Window) newValue );
|
||||
else if( newValue == null && e.getOldValue() instanceof Window )
|
||||
uninstall( (Window) e.getOldValue() );
|
||||
};
|
||||
rootPane.addPropertyChangeListener( "ancestor", ancestorListener );
|
||||
return ancestorListener;
|
||||
}
|
||||
|
||||
static void install( Window window ) {
|
||||
if( hasCustomDecoration( window ) )
|
||||
return;
|
||||
|
||||
// do not enable native window border if LaF provides decorations
|
||||
if( UIManager.getLookAndFeel().getSupportsWindowDecorations() )
|
||||
return;
|
||||
|
||||
if( window instanceof JFrame ) {
|
||||
JFrame frame = (JFrame) window;
|
||||
JRootPane rootPane = frame.getRootPane();
|
||||
|
||||
// check whether disabled via system property, client property or UI default
|
||||
if( !useWindowDecorations( rootPane ) )
|
||||
return;
|
||||
|
||||
// do not enable native window border if frame is undecorated
|
||||
if( frame.isUndecorated() )
|
||||
return;
|
||||
|
||||
// enable native window border for window
|
||||
setHasCustomDecoration( frame, true );
|
||||
|
||||
// avoid double window title bar if enabling native window border failed
|
||||
if( !hasCustomDecoration( frame ) )
|
||||
return;
|
||||
|
||||
// enable Swing window decoration
|
||||
rootPane.setWindowDecorationStyle( JRootPane.FRAME );
|
||||
|
||||
} else if( window instanceof JDialog ) {
|
||||
JDialog dialog = (JDialog) window;
|
||||
JRootPane rootPane = dialog.getRootPane();
|
||||
|
||||
// check whether disabled via system property, client property or UI default
|
||||
if( !useWindowDecorations( rootPane ) )
|
||||
return;
|
||||
|
||||
// do not enable native window border if dialog is undecorated
|
||||
if( dialog.isUndecorated() )
|
||||
return;
|
||||
|
||||
// enable native window border for window
|
||||
setHasCustomDecoration( dialog, true );
|
||||
|
||||
// avoid double window title bar if enabling native window border failed
|
||||
if( !hasCustomDecoration( dialog ) )
|
||||
return;
|
||||
|
||||
// enable Swing window decoration
|
||||
rootPane.setWindowDecorationStyle( JRootPane.PLAIN_DIALOG );
|
||||
}
|
||||
}
|
||||
|
||||
static void uninstall( JRootPane rootPane, Object data ) {
|
||||
if( canUseJBRCustomDecorations ) {
|
||||
JBRCustomDecorations.uninstall( rootPane, data );
|
||||
return;
|
||||
}
|
||||
|
||||
if( !isSupported() )
|
||||
return;
|
||||
|
||||
// remove listener
|
||||
if( data instanceof PropertyChangeListener )
|
||||
rootPane.removePropertyChangeListener( "ancestor", (PropertyChangeListener) data );
|
||||
|
||||
// do not uninstall when switching to another FlatLaf theme and if still enabled
|
||||
if( UIManager.getLookAndFeel() instanceof FlatLaf && useWindowDecorations( rootPane ) )
|
||||
return;
|
||||
|
||||
// uninstall native window border
|
||||
Container parent = rootPane.getParent();
|
||||
if( parent instanceof Window )
|
||||
uninstall( (Window) parent );
|
||||
}
|
||||
|
||||
private static void uninstall( Window window ) {
|
||||
if( !hasCustomDecoration( window ) )
|
||||
return;
|
||||
|
||||
// disable native window border for window
|
||||
setHasCustomDecoration( window, false );
|
||||
|
||||
if( window instanceof JFrame ) {
|
||||
JFrame frame = (JFrame) window;
|
||||
|
||||
// disable Swing window decoration
|
||||
frame.getRootPane().setWindowDecorationStyle( JRootPane.NONE );
|
||||
|
||||
} else if( window instanceof JDialog ) {
|
||||
JDialog dialog = (JDialog) window;
|
||||
|
||||
// disable Swing window decoration
|
||||
dialog.getRootPane().setWindowDecorationStyle( JRootPane.NONE );
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean useWindowDecorations( JRootPane rootPane ) {
|
||||
return FlatUIUtils.getBoolean( rootPane,
|
||||
FlatSystemProperties.USE_WINDOW_DECORATIONS,
|
||||
FlatClientProperties.USE_WINDOW_DECORATIONS,
|
||||
"TitlePane.useWindowDecorations",
|
||||
false );
|
||||
}
|
||||
|
||||
public static boolean hasCustomDecoration( Window window ) {
|
||||
if( canUseJBRCustomDecorations )
|
||||
return JBRCustomDecorations.hasCustomDecoration( window );
|
||||
|
||||
if( !isSupported() )
|
||||
return false;
|
||||
|
||||
return nativeProvider.hasCustomDecoration( window );
|
||||
}
|
||||
|
||||
public static void setHasCustomDecoration( Window window, boolean hasCustomDecoration ) {
|
||||
if( canUseJBRCustomDecorations ) {
|
||||
JBRCustomDecorations.setHasCustomDecoration( window, hasCustomDecoration );
|
||||
return;
|
||||
}
|
||||
|
||||
if( !isSupported() )
|
||||
return;
|
||||
|
||||
nativeProvider.setHasCustomDecoration( window, hasCustomDecoration );
|
||||
}
|
||||
|
||||
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight,
|
||||
List<Rectangle> hitTestSpots, Rectangle appIconBounds, Rectangle minimizeButtonBounds,
|
||||
Rectangle maximizeButtonBounds, Rectangle closeButtonBounds )
|
||||
{
|
||||
if( canUseJBRCustomDecorations ) {
|
||||
JBRCustomDecorations.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots );
|
||||
return;
|
||||
}
|
||||
|
||||
if( !isSupported() )
|
||||
return;
|
||||
|
||||
nativeProvider.updateTitleBarInfo( window, titleBarHeight, hitTestSpots,
|
||||
appIconBounds, minimizeButtonBounds, maximizeButtonBounds, closeButtonBounds );
|
||||
}
|
||||
|
||||
static boolean showWindow( Window window, int cmd ) {
|
||||
if( canUseJBRCustomDecorations || !isSupported() )
|
||||
return false;
|
||||
|
||||
return nativeProvider.showWindow( window, cmd );
|
||||
}
|
||||
|
||||
private static void initialize() {
|
||||
if( supported != null )
|
||||
return;
|
||||
supported = false;
|
||||
|
||||
if( !canUseWindowDecorations )
|
||||
return;
|
||||
|
||||
try {
|
||||
/*
|
||||
Class<?> cls = Class.forName( "com.formdev.flatlaf.natives.jna.windows.FlatWindowsNativeWindowBorder" );
|
||||
java.lang.reflect.Method m = cls.getMethod( "getInstance" );
|
||||
setNativeProvider( (Provider) m.invoke( null ) );
|
||||
*/
|
||||
setNativeProvider( FlatWindowsNativeWindowBorder.getInstance() );
|
||||
} catch( Exception ex ) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 1.1.1 */
|
||||
public static void setNativeProvider( Provider provider ) {
|
||||
if( nativeProvider != null )
|
||||
throw new IllegalStateException();
|
||||
|
||||
nativeProvider = provider;
|
||||
supported = (nativeProvider != null);
|
||||
}
|
||||
|
||||
//---- interface Provider -------------------------------------------------
|
||||
|
||||
public interface Provider
|
||||
{
|
||||
boolean hasCustomDecoration( Window window );
|
||||
void setHasCustomDecoration( Window window, boolean hasCustomDecoration );
|
||||
void updateTitleBarInfo( Window window, int titleBarHeight, List<Rectangle> hitTestSpots,
|
||||
Rectangle appIconBounds, Rectangle minimizeButtonBounds, Rectangle maximizeButtonBounds,
|
||||
Rectangle closeButtonBounds );
|
||||
|
||||
// commands for showWindow(); values must match Win32 API
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
|
||||
int SW_MAXIMIZE = 3;
|
||||
int SW_MINIMIZE = 6;
|
||||
int SW_RESTORE = 9;
|
||||
boolean showWindow( Window window, int cmd );
|
||||
|
||||
boolean isColorizationColorAffectsBorders();
|
||||
Color getColorizationColor();
|
||||
int getColorizationColorBalance();
|
||||
|
||||
void addChangeListener( ChangeListener l );
|
||||
void removeChangeListener( ChangeListener l );
|
||||
}
|
||||
|
||||
//---- class WindowTopBorder -------------------------------------------
|
||||
|
||||
static class WindowTopBorder
|
||||
extends JBRCustomDecorations.JBRWindowTopBorder
|
||||
{
|
||||
private static WindowTopBorder instance;
|
||||
|
||||
static JBRWindowTopBorder getInstance() {
|
||||
if( canUseJBRCustomDecorations )
|
||||
return JBRWindowTopBorder.getInstance();
|
||||
|
||||
if( instance == null )
|
||||
instance = new WindowTopBorder();
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
void installListeners() {
|
||||
nativeProvider.addChangeListener( e -> {
|
||||
update();
|
||||
|
||||
// repaint top borders of all windows
|
||||
for( Window window : Window.getWindows() ) {
|
||||
if( window.isDisplayable() )
|
||||
window.repaint( 0, 0, window.getWidth(), 1 );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isColorizationColorAffectsBorders() {
|
||||
return nativeProvider.isColorizationColorAffectsBorders();
|
||||
}
|
||||
|
||||
@Override
|
||||
Color getColorizationColor() {
|
||||
return nativeProvider.getColorizationColor();
|
||||
}
|
||||
|
||||
@Override
|
||||
int getColorizationColorBalance() {
|
||||
return nativeProvider.getColorizationColorBalance();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,14 +22,22 @@ import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.Insets;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JRootPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.UIResource;
|
||||
import javax.swing.plaf.basic.BasicHTML;
|
||||
import javax.swing.plaf.basic.BasicOptionPaneUI;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
/**
|
||||
@@ -75,6 +83,7 @@ import com.formdev.flatlaf.util.UIScale;
|
||||
*
|
||||
* <!-- FlatOptionPaneUI -->
|
||||
*
|
||||
* @uiDefault OptionPane.showIcon boolean
|
||||
* @uiDefault OptionPane.iconMessageGap int
|
||||
* @uiDefault OptionPane.messagePadding int
|
||||
* @uiDefault OptionPane.maxCharactersPerLine int
|
||||
@@ -84,10 +93,12 @@ import com.formdev.flatlaf.util.UIScale;
|
||||
public class FlatOptionPaneUI
|
||||
extends BasicOptionPaneUI
|
||||
{
|
||||
/** @since 2 */ protected boolean showIcon;
|
||||
protected int iconMessageGap;
|
||||
protected int messagePadding;
|
||||
protected int maxCharactersPerLine;
|
||||
private int focusWidth;
|
||||
private boolean sameSizeButtons;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatOptionPaneUI();
|
||||
@@ -97,10 +108,12 @@ public class FlatOptionPaneUI
|
||||
protected void installDefaults() {
|
||||
super.installDefaults();
|
||||
|
||||
showIcon = UIManager.getBoolean( "OptionPane.showIcon" );
|
||||
iconMessageGap = UIManager.getInt( "OptionPane.iconMessageGap" );
|
||||
messagePadding = UIManager.getInt( "OptionPane.messagePadding" );
|
||||
maxCharactersPerLine = UIManager.getInt( "OptionPane.maxCharactersPerLine" );
|
||||
focusWidth = UIManager.getInt( "Component.focusWidth" );
|
||||
sameSizeButtons = FlatUIUtils.getUIBoolean( "OptionPane.sameSizeButtons", true );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -110,6 +123,24 @@ public class FlatOptionPaneUI
|
||||
updateChildPanels( optionPane );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PropertyChangeListener createPropertyChangeListener() {
|
||||
PropertyChangeListener superListener = super.createPropertyChangeListener();
|
||||
return e -> {
|
||||
superListener.propertyChange( e );
|
||||
|
||||
// hide window title bar icon
|
||||
// (only if showIcon is false, otherwise the default behavior is used)
|
||||
if( !showIcon && "ancestor".equals( e.getPropertyName() ) && e.getNewValue() != null ) {
|
||||
JRootPane rootPane = SwingUtilities.getRootPane( optionPane );
|
||||
if( rootPane != null &&
|
||||
rootPane.getContentPane().getComponentCount() > 0 &&
|
||||
rootPane.getContentPane().getComponent( 0 ) == optionPane )
|
||||
rootPane.putClientProperty( FlatClientProperties.TITLE_BAR_SHOW_ICON, false );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getMinimumOptionPaneSize() {
|
||||
return UIScale.scale( super.getMinimumOptionPaneSize() );
|
||||
@@ -157,15 +188,40 @@ public class FlatOptionPaneUI
|
||||
cons.insets.bottom = UIScale.scale( messagePadding );
|
||||
|
||||
// disable line wrapping for HTML
|
||||
if( msg instanceof String && BasicHTML.isHTMLString( (String) msg ) )
|
||||
maxll = Integer.MAX_VALUE;
|
||||
if( msg != null &&
|
||||
!(msg instanceof Component) &&
|
||||
!(msg instanceof Object[]) &&
|
||||
!(msg instanceof Icon) )
|
||||
{
|
||||
msg = msg.toString();
|
||||
if( BasicHTML.isHTMLString( (String) msg ) )
|
||||
maxll = Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
// fix right-to-left alignment if super.addMessageComponents() breaks longer lines
|
||||
// into multiple labels and puts them into a box that aligns them to the left
|
||||
if( msg instanceof Box ) {
|
||||
Box box = (Box) msg;
|
||||
if( "OptionPane.verticalBox".equals( box.getName() ) &&
|
||||
box.getLayout() instanceof BoxLayout &&
|
||||
((BoxLayout)box.getLayout()).getAxis() == BoxLayout.Y_AXIS )
|
||||
{
|
||||
box.addPropertyChangeListener( "componentOrientation", e -> {
|
||||
float alignX = box.getComponentOrientation().isLeftToRight() ? 0 : 1;
|
||||
for( Component c : box.getComponents() ) {
|
||||
if( c instanceof JLabel && "OptionPane.label".equals( c.getName() ) )
|
||||
((JLabel)c).setAlignmentX( alignX );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
super.addMessageComponents( container, cons, msg, maxll, internallyCreated );
|
||||
}
|
||||
|
||||
private void updateChildPanels( Container c ) {
|
||||
for( Component child : c.getComponents() ) {
|
||||
if( child instanceof JPanel ) {
|
||||
if( child.getClass() == JPanel.class ) {
|
||||
JPanel panel = (JPanel)child;
|
||||
|
||||
// make sub-panel non-opaque for OptionPane.background
|
||||
@@ -177,9 +233,8 @@ public class FlatOptionPaneUI
|
||||
panel.setBorder( new NonUIResourceBorder( border ) );
|
||||
}
|
||||
|
||||
if( child instanceof Container ) {
|
||||
if( child instanceof Container )
|
||||
updateChildPanels( (Container) child );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,6 +252,11 @@ public class FlatOptionPaneUI
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean getSizeButtonsToSameWidth() {
|
||||
return sameSizeButtons;
|
||||
}
|
||||
|
||||
//---- class NonUIResourceBorder ------------------------------------------
|
||||
|
||||
private static class NonUIResourceBorder
|
||||
|
||||
@@ -35,11 +35,7 @@ import javax.swing.plaf.basic.BasicPanelUI;
|
||||
public class FlatPanelUI
|
||||
extends BasicPanelUI
|
||||
{
|
||||
private static ComponentUI instance;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
if( instance == null )
|
||||
instance = new FlatPanelUI();
|
||||
return instance;
|
||||
return FlatUIUtils.createSharedUI( FlatPanelUI.class, FlatPanelUI::new );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,29 +17,34 @@
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.event.FocusListener;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.KeyListener;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.util.Map;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.ActionMap;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.LookAndFeel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.basic.BasicPasswordFieldUI;
|
||||
import javax.swing.text.Caret;
|
||||
import javax.swing.text.DefaultEditorKit;
|
||||
import javax.swing.text.Element;
|
||||
import javax.swing.text.JTextComponent;
|
||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||
import javax.swing.text.PasswordView;
|
||||
import javax.swing.text.View;
|
||||
import com.formdev.flatlaf.icons.FlatCapsLockIcon;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
/**
|
||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JPasswordField}.
|
||||
*
|
||||
* <!-- BasicPasswordFieldUI -->
|
||||
* <!-- BasicTextFieldUI -->
|
||||
*
|
||||
* @uiDefault PasswordField.font Font
|
||||
* @uiDefault PasswordField.background Color
|
||||
@@ -52,67 +57,70 @@ import com.formdev.flatlaf.util.HiDPIUtils;
|
||||
* @uiDefault PasswordField.inactiveForeground Color used if not enabled (yes, this is confusing; this should be named disabledForeground)
|
||||
* @uiDefault PasswordField.border Border
|
||||
* @uiDefault PasswordField.margin Insets
|
||||
* @uiDefault PasswordField.echoChar character
|
||||
* @uiDefault PasswordField.caretBlinkRate int default is 500 milliseconds
|
||||
*
|
||||
* <!-- FlatPasswordFieldUI -->
|
||||
* <!-- FlatTextFieldUI -->
|
||||
*
|
||||
* @uiDefault Component.minimumWidth int
|
||||
* @uiDefault Component.isIntelliJTheme boolean
|
||||
* @uiDefault PasswordField.placeholderForeground Color
|
||||
* @uiDefault PasswordField.focusedBackground Color optional
|
||||
* @uiDefault PasswordField.iconTextGap int optional, default is 4
|
||||
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
|
||||
* @uiDefault TextComponent.selectAllOnMouseClick boolean
|
||||
*
|
||||
* <!-- FlatPasswordFieldUI -->
|
||||
*
|
||||
* @uiDefault PasswordField.echoChar character
|
||||
* @uiDefault PasswordField.showCapsLock boolean
|
||||
* @uiDefault PasswordField.capsLockIcon Icon
|
||||
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
public class FlatPasswordFieldUI
|
||||
extends BasicPasswordFieldUI
|
||||
extends FlatTextFieldUI
|
||||
{
|
||||
protected int minimumWidth;
|
||||
protected boolean isIntelliJTheme;
|
||||
protected Color placeholderForeground;
|
||||
protected boolean showCapsLock;
|
||||
@Styleable protected boolean showCapsLock;
|
||||
protected Icon capsLockIcon;
|
||||
|
||||
private FocusListener focusListener;
|
||||
private KeyListener capsLockListener;
|
||||
private boolean capsLockIconShared = true;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatPasswordFieldUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getPropertyPrefix() {
|
||||
return "PasswordField";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installDefaults() {
|
||||
super.installDefaults();
|
||||
|
||||
String prefix = getPropertyPrefix();
|
||||
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
||||
placeholderForeground = UIManager.getColor( prefix + ".placeholderForeground" );
|
||||
Character echoChar = (Character) UIManager.get( prefix + ".echoChar" );
|
||||
if( echoChar != null )
|
||||
LookAndFeel.installProperty( getComponent(), "echoChar", echoChar );
|
||||
|
||||
showCapsLock = UIManager.getBoolean( "PasswordField.showCapsLock" );
|
||||
capsLockIcon = UIManager.getIcon( "PasswordField.capsLockIcon" );
|
||||
|
||||
LookAndFeel.installProperty( getComponent(), "opaque", false );
|
||||
|
||||
MigLayoutVisualPadding.install( getComponent() );
|
||||
capsLockIconShared = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void uninstallDefaults() {
|
||||
super.uninstallDefaults();
|
||||
|
||||
placeholderForeground = null;
|
||||
capsLockIcon = null;
|
||||
|
||||
MigLayoutVisualPadding.uninstall( getComponent() );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installListeners() {
|
||||
super.installListeners();
|
||||
|
||||
focusListener = new FlatUIUtils.RepaintFocusListener( getComponent() );
|
||||
// update caps lock indicator
|
||||
capsLockListener = new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed( KeyEvent e ) {
|
||||
@@ -123,12 +131,13 @@ public class FlatPasswordFieldUI
|
||||
repaint( e );
|
||||
}
|
||||
private void repaint( KeyEvent e ) {
|
||||
if( e.getKeyCode() == KeyEvent.VK_CAPS_LOCK )
|
||||
if( e.getKeyCode() == KeyEvent.VK_CAPS_LOCK ) {
|
||||
e.getComponent().repaint();
|
||||
scrollCaretToVisible();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
getComponent().addFocusListener( focusListener );
|
||||
getComponent().addKeyListener( capsLockListener );
|
||||
}
|
||||
|
||||
@@ -136,58 +145,95 @@ public class FlatPasswordFieldUI
|
||||
protected void uninstallListeners() {
|
||||
super.uninstallListeners();
|
||||
|
||||
getComponent().removeFocusListener( focusListener );
|
||||
getComponent().removeKeyListener( capsLockListener );
|
||||
focusListener = null;
|
||||
capsLockListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Caret createCaret() {
|
||||
return new FlatCaret( UIManager.getString( "TextComponent.selectAllOnFocusPolicy" ) );
|
||||
protected void installKeyboardActions() {
|
||||
super.installKeyboardActions();
|
||||
|
||||
// map "select-word" action (double-click) to "select-line" action
|
||||
ActionMap map = SwingUtilities.getUIActionMap( getComponent() );
|
||||
if( map != null && map.get( DefaultEditorKit.selectWordAction ) != null ) {
|
||||
Action selectLineAction = map.get( DefaultEditorKit.selectLineAction );
|
||||
if( selectLineAction != null )
|
||||
map.put( DefaultEditorKit.selectWordAction, selectLineAction );
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
String getStyleType() {
|
||||
return "PasswordField";
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
protected Object applyStyleProperty( String key, Object value ) {
|
||||
if( key.equals( "capsLockIconColor" ) && capsLockIcon instanceof FlatCapsLockIcon ) {
|
||||
if( capsLockIconShared ) {
|
||||
capsLockIcon = FlatStylingSupport.cloneIcon( capsLockIcon );
|
||||
capsLockIconShared = false;
|
||||
}
|
||||
return ((FlatCapsLockIcon)capsLockIcon).applyStyleProperty( key, value );
|
||||
}
|
||||
|
||||
return super.applyStyleProperty( key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
Map<String, Class<?>> infos = super.getStyleableInfos( c );
|
||||
infos.put( "capsLockIconColor", Color.class );
|
||||
return infos;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void propertyChange( PropertyChangeEvent e ) {
|
||||
super.propertyChange( e );
|
||||
FlatTextFieldUI.propertyChange( getComponent(), e );
|
||||
public View create( Element elem ) {
|
||||
return new PasswordView( elem );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
protected void paintSafely( Graphics g ) {
|
||||
FlatTextFieldUI.paintBackground( g, getComponent(), isIntelliJTheme );
|
||||
FlatTextFieldUI.paintPlaceholder( g, getComponent(), placeholderForeground );
|
||||
paintCapsLock( g );
|
||||
protected void paintIcons( Graphics g, Rectangle r ) {
|
||||
super.paintIcons( g, r );
|
||||
|
||||
super.paintSafely( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ) );
|
||||
if( isCapsLockVisible() )
|
||||
paintCapsLock( g, r );
|
||||
}
|
||||
|
||||
protected void paintCapsLock( Graphics g ) {
|
||||
if( !showCapsLock )
|
||||
return;
|
||||
|
||||
/** @since 2 */
|
||||
protected void paintCapsLock( Graphics g, Rectangle r ) {
|
||||
JTextComponent c = getComponent();
|
||||
if( !FlatUIUtils.isPermanentFocusOwner( c ) ||
|
||||
!Toolkit.getDefaultToolkit().getLockingKeyState( KeyEvent.VK_CAPS_LOCK ) )
|
||||
return;
|
||||
|
||||
int y = (c.getHeight() - capsLockIcon.getIconHeight()) / 2;
|
||||
int x = c.getWidth() - capsLockIcon.getIconWidth() - y;
|
||||
int x = c.getComponentOrientation().isLeftToRight()
|
||||
? r.x + r.width - capsLockIcon.getIconWidth()
|
||||
: r.x;
|
||||
int y = r.y + Math.round( (r.height - capsLockIcon.getIconHeight()) / 2f );
|
||||
capsLockIcon.paintIcon( c, g, x, y );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
protected void paintBackground( Graphics g ) {
|
||||
// background is painted elsewhere
|
||||
protected boolean hasTrailingIcon() {
|
||||
return super.hasTrailingIcon() || isCapsLockVisible();
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Dimension getPreferredSize( JComponent c ) {
|
||||
return FlatTextFieldUI.applyMinimumWidth( c, super.getPreferredSize( c ), minimumWidth );
|
||||
protected int getTrailingIconWidth() {
|
||||
return super.getTrailingIconWidth()
|
||||
+ (isCapsLockVisible() ? capsLockIcon.getIconWidth() + UIScale.scale( iconTextGap ) : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getMinimumSize( JComponent c ) {
|
||||
return FlatTextFieldUI.applyMinimumWidth( c, super.getMinimumSize( c ), minimumWidth );
|
||||
/** @since 1.4 */
|
||||
protected boolean isCapsLockVisible() {
|
||||
if( !showCapsLock )
|
||||
return false;
|
||||
|
||||
JTextComponent c = getComponent();
|
||||
return FlatUIUtils.isPermanentFocusOwner( c ) &&
|
||||
Toolkit.getDefaultToolkit().getLockingKeyState( KeyEvent.VK_CAPS_LOCK );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,11 +20,16 @@ import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.GraphicsConfiguration;
|
||||
import java.awt.GraphicsDevice;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.Insets;
|
||||
import java.awt.MouseInfo;
|
||||
import java.awt.Panel;
|
||||
import java.awt.Point;
|
||||
import java.awt.PointerInfo;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ComponentEvent;
|
||||
import java.awt.event.ComponentListener;
|
||||
@@ -39,6 +44,7 @@ import javax.swing.Popup;
|
||||
import javax.swing.PopupFactory;
|
||||
import javax.swing.RootPaneContainer;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.ToolTipManager;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.border.Border;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
@@ -68,19 +74,17 @@ public class FlatPopupFactory
|
||||
y = pt.y;
|
||||
}
|
||||
|
||||
if( !isDropShadowPainted( owner, contents ) )
|
||||
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, false ), contents );
|
||||
boolean forceHeavyWeight = isOptionEnabled( owner, contents, FlatClientProperties.POPUP_FORCE_HEAVY_WEIGHT, "Popup.forceHeavyWeight" );
|
||||
|
||||
if( !isOptionEnabled( owner, contents, FlatClientProperties.POPUP_DROP_SHADOW_PAINTED, "Popup.dropShadowPainted" ) || SystemInfo.isProjector || SystemInfo.isWebswing )
|
||||
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), contents );
|
||||
|
||||
// macOS and Linux adds drop shadow to heavy weight popups
|
||||
if( SystemInfo.isMacOS || SystemInfo.isLinux ) {
|
||||
Popup popup = getPopupForScreenOfOwner( owner, contents, x, y, true );
|
||||
if( popup == null )
|
||||
popup = getPopupForScreenOfOwner( owner, contents, x, y, false );
|
||||
return new NonFlashingPopup( popup, contents );
|
||||
}
|
||||
if( SystemInfo.isMacOS || SystemInfo.isLinux )
|
||||
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), contents );
|
||||
|
||||
// create drop shadow popup
|
||||
return new DropShadowPopup( getPopupForScreenOfOwner( owner, contents, x, y, false ), owner, contents );
|
||||
return new DropShadowPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), owner, contents );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,6 +117,7 @@ public class FlatPopupFactory
|
||||
|
||||
// check whether heavy weight popup window is on same screen as owner component
|
||||
if( popupWindow == null ||
|
||||
owner == null ||
|
||||
popupWindow.getGraphicsConfiguration() == owner.getGraphicsConfiguration() )
|
||||
return popup;
|
||||
|
||||
@@ -155,24 +160,20 @@ public class FlatPopupFactory
|
||||
popup.show();
|
||||
}
|
||||
|
||||
private boolean isDropShadowPainted( Component owner, Component contents ) {
|
||||
Boolean b = isDropShadowPainted( owner );
|
||||
if( b != null )
|
||||
return b;
|
||||
private boolean isOptionEnabled( Component owner, Component contents, String clientKey, String uiKey ) {
|
||||
if( owner instanceof JComponent ) {
|
||||
Boolean b = FlatClientProperties.clientPropertyBooleanStrict( (JComponent) owner, clientKey, null );
|
||||
if( b != null )
|
||||
return b;
|
||||
}
|
||||
|
||||
b = isDropShadowPainted( contents );
|
||||
if( b != null )
|
||||
return b;
|
||||
if( contents instanceof JComponent ) {
|
||||
Boolean b = FlatClientProperties.clientPropertyBooleanStrict( (JComponent) contents, clientKey, null );
|
||||
if( b != null )
|
||||
return b;
|
||||
}
|
||||
|
||||
return UIManager.getBoolean( "Popup.dropShadowPainted" );
|
||||
}
|
||||
|
||||
private Boolean isDropShadowPainted( Component c ) {
|
||||
if( !(c instanceof JComponent) )
|
||||
return null;
|
||||
|
||||
Object value = ((JComponent)c).getClientProperty( FlatClientProperties.POPUP_DROP_SHADOW_PAINTED );
|
||||
return (value instanceof Boolean ) ? (Boolean) value : null;
|
||||
return UIManager.getBoolean( uiKey );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -211,17 +212,21 @@ public class FlatPopupFactory
|
||||
/**
|
||||
* Usually ToolTipManager places a tooltip at (mouseLocation.x, mouseLocation.y + 20).
|
||||
* In case that the tooltip would be partly outside of the screen,
|
||||
* ToolTipManagerthe changes the location so that the entire tooltip fits on screen.
|
||||
* the ToolTipManager changes the location so that the entire tooltip fits on screen.
|
||||
* But this can place the tooltip under the mouse location and hide the owner component.
|
||||
* <p>
|
||||
* This method checks whether the current mouse location is within tooltip bounds
|
||||
* and corrects the y-location so that the tooltip is placed above the mouse location.
|
||||
*/
|
||||
private Point fixToolTipLocation( Component owner, Component contents, int x, int y ) {
|
||||
if( !(contents instanceof JToolTip) )
|
||||
if( !(contents instanceof JToolTip) || !wasInvokedFromToolTipManager() )
|
||||
return null;
|
||||
|
||||
Point mouseLocation = MouseInfo.getPointerInfo().getLocation();
|
||||
PointerInfo pointerInfo = MouseInfo.getPointerInfo();
|
||||
if( pointerInfo == null )
|
||||
return null;
|
||||
|
||||
Point mouseLocation = pointerInfo.getLocation();
|
||||
Dimension tipSize = contents.getPreferredSize();
|
||||
|
||||
// check whether mouse location is within tooltip bounds
|
||||
@@ -229,8 +234,34 @@ public class FlatPopupFactory
|
||||
if( !tipBounds.contains( mouseLocation ) )
|
||||
return null;
|
||||
|
||||
// place tooltip above mouse location
|
||||
return new Point( x, mouseLocation.y - tipSize.height - UIScale.scale( 20 ) );
|
||||
// find GraphicsConfiguration at mouse location (similar to ToolTipManager.getDrawingGC())
|
||||
GraphicsConfiguration gc = null;
|
||||
for( GraphicsDevice device : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices() ) {
|
||||
GraphicsConfiguration dgc = device.getDefaultConfiguration();
|
||||
if( dgc.getBounds().contains( mouseLocation ) ) {
|
||||
gc = dgc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( gc == null )
|
||||
gc = owner.getGraphicsConfiguration();
|
||||
if( gc == null )
|
||||
return null;
|
||||
|
||||
Rectangle screenBounds = gc.getBounds();
|
||||
Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets( gc );
|
||||
int screenTop = screenBounds.y + screenInsets.top;
|
||||
|
||||
// place tooltip above mouse location if there is enough space
|
||||
int newY = mouseLocation.y - tipSize.height - UIScale.scale( 20 );
|
||||
if( newY < screenTop )
|
||||
return null;
|
||||
|
||||
return new Point( x, newY );
|
||||
}
|
||||
|
||||
private boolean wasInvokedFromToolTipManager() {
|
||||
return StackUtils.wasInvokedFrom( ToolTipManager.class.getName(), "showTipWindow", 8 );
|
||||
}
|
||||
|
||||
//---- class NonFlashingPopup ---------------------------------------------
|
||||
@@ -267,16 +298,17 @@ public class FlatPopupFactory
|
||||
|
||||
// increase tooltip size if necessary because it may be too small on HiDPI screens
|
||||
// https://bugs.openjdk.java.net/browse/JDK-8213535
|
||||
if( contents instanceof JToolTip ) {
|
||||
if( contents instanceof JToolTip && popupWindow == null ) {
|
||||
Container parent = contents.getParent();
|
||||
if( parent instanceof JPanel ) {
|
||||
Dimension prefSize = parent.getPreferredSize();
|
||||
if( !prefSize.equals( parent.getSize() ) ) {
|
||||
Container panel = SwingUtilities.getAncestorOfClass( Panel.class, parent );
|
||||
if( panel != null )
|
||||
panel.setSize( prefSize ); // for medium weight popup
|
||||
else
|
||||
parent.setSize( prefSize ); // for light weight popup
|
||||
Container mediumWeightPanel = SwingUtilities.getAncestorOfClass( Panel.class, parent );
|
||||
Container c = (mediumWeightPanel != null)
|
||||
? mediumWeightPanel // medium weight popup
|
||||
: parent; // light weight popup
|
||||
c.setSize( prefSize );
|
||||
c.validate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -445,10 +477,10 @@ public class FlatPopupFactory
|
||||
|
||||
mediumWeightShown = true;
|
||||
|
||||
Window window = SwingUtilities.windowForComponent( owner );
|
||||
if( window == null )
|
||||
if( owner == null )
|
||||
return;
|
||||
|
||||
Window window = SwingUtilities.windowForComponent( owner );
|
||||
if( !(window instanceof RootPaneContainer) )
|
||||
return;
|
||||
|
||||
@@ -457,6 +489,9 @@ public class FlatPopupFactory
|
||||
JLayeredPane layeredPane = ((RootPaneContainer)window).getLayeredPane();
|
||||
layeredPane.add( dropShadowPanel, JLayeredPane.POPUP_LAYER, 0 );
|
||||
|
||||
moveMediumWeightDropShadow();
|
||||
resizeMediumWeightDropShadow();
|
||||
|
||||
mediumPanelListener = new ComponentListener() {
|
||||
@Override
|
||||
public void componentShown( ComponentEvent e ) {
|
||||
@@ -472,17 +507,12 @@ public class FlatPopupFactory
|
||||
|
||||
@Override
|
||||
public void componentMoved( ComponentEvent e ) {
|
||||
if( dropShadowPanel != null && mediumWeightPanel != null ) {
|
||||
Point location = mediumWeightPanel.getLocation();
|
||||
Insets insets = dropShadowPanel.getInsets();
|
||||
dropShadowPanel.setLocation( location.x - insets.left, location.y - insets.top );
|
||||
}
|
||||
moveMediumWeightDropShadow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentResized( ComponentEvent e ) {
|
||||
if( dropShadowPanel != null )
|
||||
dropShadowPanel.setSize( FlatUIUtils.addInsets( mediumWeightPanel.getSize(), dropShadowPanel.getInsets() ) );
|
||||
resizeMediumWeightDropShadow();
|
||||
}
|
||||
};
|
||||
mediumWeightPanel.addComponentListener( mediumPanelListener );
|
||||
@@ -498,5 +528,18 @@ public class FlatPopupFactory
|
||||
parent.repaint( bounds.x, bounds.y, bounds.width, bounds.height );
|
||||
}
|
||||
}
|
||||
|
||||
private void moveMediumWeightDropShadow() {
|
||||
if( dropShadowPanel != null && mediumWeightPanel != null ) {
|
||||
Point location = mediumWeightPanel.getLocation();
|
||||
Insets insets = dropShadowPanel.getInsets();
|
||||
dropShadowPanel.setLocation( location.x - insets.left, location.y - insets.top );
|
||||
}
|
||||
}
|
||||
|
||||
private void resizeMediumWeightDropShadow() {
|
||||
if( dropShadowPanel != null && mediumWeightPanel != null )
|
||||
dropShadowPanel.setSize( FlatUIUtils.addInsets( mediumWeightPanel.getSize(), dropShadowPanel.getInsets() ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,15 @@
|
||||
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Insets;
|
||||
import java.util.Map;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableBorder;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
/**
|
||||
@@ -33,12 +37,40 @@ import com.formdev.flatlaf.util.UIScale;
|
||||
*/
|
||||
public class FlatPopupMenuBorder
|
||||
extends FlatLineBorder
|
||||
implements StyleableBorder
|
||||
{
|
||||
private Color borderColor;
|
||||
|
||||
public FlatPopupMenuBorder() {
|
||||
super( UIManager.getInsets( "PopupMenu.borderInsets" ),
|
||||
UIManager.getColor( "PopupMenu.borderColor" ) );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Object applyStyleProperty( String key, Object value ) {
|
||||
Object oldValue;
|
||||
switch( key ) {
|
||||
case "borderInsets": return applyStyleProperty( (Insets) value );
|
||||
case "borderColor": oldValue = getLineColor(); borderColor = (Color) value; return oldValue;
|
||||
}
|
||||
throw new UnknownStyleException( key );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos() {
|
||||
Map<String, Class<?>> infos = new FlatStylingSupport.StyleableInfosMap<>();
|
||||
infos.put( "borderInsets", Insets.class );
|
||||
infos.put( "borderColor", Color.class );
|
||||
return infos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getLineColor() {
|
||||
return (borderColor != null) ? borderColor : super.getLineColor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||
if( c instanceof Container &&
|
||||
|
||||
@@ -24,8 +24,8 @@ import javax.swing.plaf.ComponentUI;
|
||||
*
|
||||
* <!-- BasicSeparatorUI -->
|
||||
*
|
||||
* @uiDefault PopupMenuSeparator.background Color unused
|
||||
* @uiDefault PopupMenuSeparator.foreground Color
|
||||
* @uiDefault Separator.background Color unused
|
||||
* @uiDefault Separator.foreground Color
|
||||
*
|
||||
* <!-- FlatSeparatorUI -->
|
||||
*
|
||||
@@ -38,16 +38,25 @@ import javax.swing.plaf.ComponentUI;
|
||||
public class FlatPopupMenuSeparatorUI
|
||||
extends FlatSeparatorUI
|
||||
{
|
||||
private static ComponentUI instance;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
if( instance == null )
|
||||
instance = new FlatPopupMenuSeparatorUI();
|
||||
return instance;
|
||||
return FlatUIUtils.canUseSharedUI( c )
|
||||
? FlatUIUtils.createSharedUI( FlatPopupMenuSeparatorUI.class, () -> new FlatPopupMenuSeparatorUI( true ) )
|
||||
: new FlatPopupMenuSeparatorUI( false );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected FlatPopupMenuSeparatorUI( boolean shared ) {
|
||||
super( shared );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getPropertyPrefix() {
|
||||
return "PopupMenuSeparator";
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
String getStyleType() {
|
||||
return "PopupMenuSeparator";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,14 @@
|
||||
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.basic.BasicPopupMenuUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
|
||||
/**
|
||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JPopupMenu}.
|
||||
@@ -34,8 +39,71 @@ import javax.swing.plaf.basic.BasicPopupMenuUI;
|
||||
*/
|
||||
public class FlatPopupMenuUI
|
||||
extends BasicPopupMenuUI
|
||||
implements StyleableUI
|
||||
{
|
||||
private PropertyChangeListener propertyChangeListener;
|
||||
private Map<String, Object> oldStyleValues;
|
||||
private AtomicBoolean borderShared;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatPopupMenuUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installUI( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
installStyle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uninstallUI( JComponent c ) {
|
||||
super.uninstallUI( c );
|
||||
|
||||
oldStyleValues = null;
|
||||
borderShared = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installListeners() {
|
||||
super.installListeners();
|
||||
|
||||
propertyChangeListener = FlatStylingSupport.createPropertyChangeListener( popupMenu, this::installStyle, null );
|
||||
popupMenu.addPropertyChangeListener( propertyChangeListener );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void uninstallListeners() {
|
||||
super.uninstallListeners();
|
||||
|
||||
popupMenu.removePropertyChangeListener( propertyChangeListener );
|
||||
propertyChangeListener = null;
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void installStyle() {
|
||||
try {
|
||||
applyStyle( FlatStylingSupport.getResolvedStyle( popupMenu, "PopupMenu" ) );
|
||||
} catch( RuntimeException ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void applyStyle( Object style ) {
|
||||
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected Object applyStyleProperty( String key, Object value ) {
|
||||
if( borderShared == null )
|
||||
borderShared = new AtomicBoolean( true );
|
||||
return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, popupMenu, borderShared );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this, popupMenu.getBorder() );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,13 +25,17 @@ import java.awt.Insets;
|
||||
import java.awt.geom.Area;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Map;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JProgressBar;
|
||||
import javax.swing.LookAndFeel;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.basic.BasicProgressBarUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
/**
|
||||
@@ -58,17 +62,30 @@ import com.formdev.flatlaf.util.UIScale;
|
||||
*/
|
||||
public class FlatProgressBarUI
|
||||
extends BasicProgressBarUI
|
||||
implements StyleableUI
|
||||
{
|
||||
protected int arc;
|
||||
protected Dimension horizontalSize;
|
||||
protected Dimension verticalSize;
|
||||
@Styleable protected int arc;
|
||||
@Styleable protected Dimension horizontalSize;
|
||||
@Styleable protected Dimension verticalSize;
|
||||
|
||||
// only used via styling (not in UI defaults, but has likewise client properties)
|
||||
/** @since 2 */ @Styleable protected boolean largeHeight;
|
||||
/** @since 2 */ @Styleable protected boolean square;
|
||||
|
||||
private PropertyChangeListener propertyChangeListener;
|
||||
private Map<String, Object> oldStyleValues;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatProgressBarUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installUI( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
installStyle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installDefaults() {
|
||||
super.installDefaults();
|
||||
@@ -80,6 +97,13 @@ public class FlatProgressBarUI
|
||||
verticalSize = UIManager.getDimension( "ProgressBar.verticalSize" );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void uninstallDefaults() {
|
||||
super.uninstallDefaults();
|
||||
|
||||
oldStyleValues = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installListeners() {
|
||||
super.installListeners();
|
||||
@@ -91,6 +115,13 @@ public class FlatProgressBarUI
|
||||
progressBar.revalidate();
|
||||
progressBar.repaint();
|
||||
break;
|
||||
|
||||
case STYLE:
|
||||
case STYLE_CLASS:
|
||||
installStyle();
|
||||
progressBar.revalidate();
|
||||
progressBar.repaint();
|
||||
break;
|
||||
}
|
||||
};
|
||||
progressBar.addPropertyChangeListener( propertyChangeListener );
|
||||
@@ -104,11 +135,36 @@ public class FlatProgressBarUI
|
||||
propertyChangeListener = null;
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void installStyle() {
|
||||
try {
|
||||
applyStyle( FlatStylingSupport.getResolvedStyle( progressBar, "ProgressBar" ) );
|
||||
} catch( RuntimeException ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void applyStyle( Object style ) {
|
||||
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected Object applyStyleProperty( String key, Object value ) {
|
||||
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, progressBar, key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize( JComponent c ) {
|
||||
Dimension size = super.getPreferredSize( c );
|
||||
|
||||
if( progressBar.isStringPainted() || clientPropertyBoolean( c, PROGRESS_BAR_LARGE_HEIGHT, false ) ) {
|
||||
if( progressBar.isStringPainted() || clientPropertyBoolean( c, PROGRESS_BAR_LARGE_HEIGHT, largeHeight ) ) {
|
||||
// recalculate progress height/width to make it smaller
|
||||
Insets insets = progressBar.getInsets();
|
||||
FontMetrics fm = progressBar.getFontMetrics( progressBar.getFont() );
|
||||
@@ -151,11 +207,11 @@ public class FlatProgressBarUI
|
||||
return;
|
||||
|
||||
boolean horizontal = (progressBar.getOrientation() == JProgressBar.HORIZONTAL);
|
||||
int arc = clientPropertyBoolean( c, PROGRESS_BAR_SQUARE, false )
|
||||
int arc = clientPropertyBoolean( c, PROGRESS_BAR_SQUARE, square )
|
||||
? 0
|
||||
: Math.min( UIScale.scale( this.arc ), horizontal ? height : width );
|
||||
|
||||
FlatUIUtils.setRenderingHints( (Graphics2D) g );
|
||||
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
||||
|
||||
// paint track
|
||||
RoundRectangle2D.Float trackShape = new RoundRectangle2D.Float( x, y, width, height, arc, arc );
|
||||
@@ -163,6 +219,7 @@ public class FlatProgressBarUI
|
||||
((Graphics2D)g).fill( trackShape );
|
||||
|
||||
// paint progress
|
||||
int amountFull = 0;
|
||||
if( progressBar.isIndeterminate() ) {
|
||||
boxRect = getBox( boxRect );
|
||||
if( boxRect != null ) {
|
||||
@@ -170,11 +227,8 @@ public class FlatProgressBarUI
|
||||
((Graphics2D)g).fill( new RoundRectangle2D.Float( boxRect.x, boxRect.y,
|
||||
boxRect.width, boxRect.height, arc, arc ) );
|
||||
}
|
||||
|
||||
if( progressBar.isStringPainted() )
|
||||
paintString( g, x, y, width, height, 0, insets );
|
||||
} else {
|
||||
int amountFull = getAmountFull( insets, width, height );
|
||||
amountFull = getAmountFull( insets, width, height );
|
||||
|
||||
RoundRectangle2D.Float progressShape = horizontal
|
||||
? new RoundRectangle2D.Float( c.getComponentOrientation().isLeftToRight() ? x : x + (width - amountFull),
|
||||
@@ -189,10 +243,12 @@ public class FlatProgressBarUI
|
||||
((Graphics2D)g).fill( area );
|
||||
} else
|
||||
((Graphics2D)g).fill( progressShape );
|
||||
|
||||
if( progressBar.isStringPainted() )
|
||||
paintString( g, x, y, width, height, amountFull, insets );
|
||||
}
|
||||
|
||||
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||
|
||||
if( progressBar.isStringPainted() )
|
||||
paintString( g, x, y, width, height, amountFull, insets );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,13 +16,19 @@
|
||||
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Map;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.LookAndFeel;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
|
||||
/**
|
||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JRadioButtonMenuItem}.
|
||||
@@ -54,13 +60,22 @@ import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI;
|
||||
*/
|
||||
public class FlatRadioButtonMenuItemUI
|
||||
extends BasicRadioButtonMenuItemUI
|
||||
implements StyleableUI
|
||||
{
|
||||
private FlatMenuItemRenderer renderer;
|
||||
private Map<String, Object> oldStyleValues;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatRadioButtonMenuItemUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installUI( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
installStyle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installDefaults() {
|
||||
super.installDefaults();
|
||||
@@ -75,12 +90,59 @@ public class FlatRadioButtonMenuItemUI
|
||||
super.uninstallDefaults();
|
||||
|
||||
renderer = null;
|
||||
oldStyleValues = null;
|
||||
}
|
||||
|
||||
protected FlatMenuItemRenderer createRenderer() {
|
||||
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
||||
return FlatStylingSupport.createPropertyChangeListener( c, this::installStyle, super.createPropertyChangeListener( c ) );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void installStyle() {
|
||||
try {
|
||||
applyStyle( FlatStylingSupport.getResolvedStyle( menuItem, "RadioButtonMenuItem" ) );
|
||||
} catch( RuntimeException ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void applyStyle( Object style ) {
|
||||
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected Object applyStyleProperty( String key, Object value ) {
|
||||
try {
|
||||
return renderer.applyStyleProperty( key, value );
|
||||
} catch ( UnknownStyleException ex ) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
Object oldValue;
|
||||
switch( key ) {
|
||||
// BasicMenuItemUI
|
||||
case "selectionBackground": oldValue = selectionBackground; selectionBackground = (Color) value; return oldValue;
|
||||
case "selectionForeground": oldValue = selectionForeground; selectionForeground = (Color) value; return oldValue;
|
||||
case "disabledForeground": oldValue = disabledForeground; disabledForeground = (Color) value; return oldValue;
|
||||
case "acceleratorForeground": oldValue = acceleratorForeground; acceleratorForeground = (Color) value; return oldValue;
|
||||
case "acceleratorSelectionForeground": oldValue = acceleratorSelectionForeground; acceleratorSelectionForeground = (Color) value; return oldValue;
|
||||
}
|
||||
|
||||
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, menuItem, key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
return FlatMenuItemUI.getStyleableInfos( renderer );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
|
||||
return renderer.getPreferredMenuItemSize();
|
||||
|
||||
@@ -18,18 +18,29 @@ package com.formdev.flatlaf.ui;
|
||||
|
||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
||||
import java.awt.Color;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Rectangle;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import javax.swing.AbstractButton;
|
||||
import javax.swing.CellRendererPane;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.LookAndFeel;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.UIResource;
|
||||
import javax.swing.plaf.basic.BasicButtonListener;
|
||||
import javax.swing.plaf.basic.BasicRadioButtonUI;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
import com.formdev.flatlaf.icons.FlatCheckBoxIcon;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
/**
|
||||
@@ -54,18 +65,34 @@ import com.formdev.flatlaf.util.UIScale;
|
||||
*/
|
||||
public class FlatRadioButtonUI
|
||||
extends BasicRadioButtonUI
|
||||
implements StyleableUI
|
||||
{
|
||||
protected int iconTextGap;
|
||||
protected Color disabledText;
|
||||
@Styleable protected Color disabledText;
|
||||
|
||||
private Color defaultBackground;
|
||||
|
||||
private final boolean shared;
|
||||
private boolean iconShared = true;
|
||||
private boolean defaults_initialized = false;
|
||||
|
||||
private static ComponentUI instance;
|
||||
private Map<String, Object> oldStyleValues;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
if( instance == null )
|
||||
instance = new FlatRadioButtonUI();
|
||||
return instance;
|
||||
return FlatUIUtils.canUseSharedUI( c )
|
||||
? FlatUIUtils.createSharedUI( FlatRadioButtonUI.class, () -> new FlatRadioButtonUI( true ) )
|
||||
: new FlatRadioButtonUI( false );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected FlatRadioButtonUI( boolean shared ) {
|
||||
this.shared = shared;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installUI( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
installStyle( (AbstractButton) c );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,6 +105,9 @@ public class FlatRadioButtonUI
|
||||
iconTextGap = FlatUIUtils.getUIInt( prefix + "iconTextGap", 4 );
|
||||
disabledText = UIManager.getColor( prefix + "disabledText" );
|
||||
|
||||
defaultBackground = UIManager.getColor( prefix + "background" );
|
||||
|
||||
iconShared = true;
|
||||
defaults_initialized = true;
|
||||
}
|
||||
|
||||
@@ -91,10 +121,84 @@ public class FlatRadioButtonUI
|
||||
protected void uninstallDefaults( AbstractButton b ) {
|
||||
super.uninstallDefaults( b );
|
||||
|
||||
oldStyleValues = null;
|
||||
|
||||
MigLayoutVisualPadding.uninstall( b );
|
||||
defaults_initialized = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BasicButtonListener createButtonListener( AbstractButton b ) {
|
||||
return new FlatRadioButtonListener( b );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void propertyChange( AbstractButton b, PropertyChangeEvent e ) {
|
||||
switch( e.getPropertyName() ) {
|
||||
case FlatClientProperties.STYLE:
|
||||
case FlatClientProperties.STYLE_CLASS:
|
||||
if( shared && FlatStylingSupport.hasStyleProperty( b ) ) {
|
||||
// unshare component UI if necessary
|
||||
// updateUI() invokes applyStyle() from installUI()
|
||||
b.updateUI();
|
||||
} else
|
||||
installStyle( b );
|
||||
b.revalidate();
|
||||
b.repaint();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void installStyle( AbstractButton b ) {
|
||||
try {
|
||||
applyStyle( b, FlatStylingSupport.getResolvedStyle( b, getStyleType() ) );
|
||||
} catch( RuntimeException ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
String getStyleType() {
|
||||
return "RadioButton";
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void applyStyle( AbstractButton b, Object style ) {
|
||||
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style,
|
||||
(key, value) -> applyStyleProperty( b, key, value ) );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected Object applyStyleProperty( AbstractButton b, String key, Object value ) {
|
||||
// style icon
|
||||
if( key.startsWith( "icon." ) ) {
|
||||
if( !(icon instanceof FlatCheckBoxIcon) )
|
||||
return new UnknownStyleException( key );
|
||||
|
||||
if( iconShared ) {
|
||||
icon = FlatStylingSupport.cloneIcon( icon );
|
||||
iconShared = false;
|
||||
}
|
||||
|
||||
key = key.substring( "icon.".length() );
|
||||
return ((FlatCheckBoxIcon)icon).applyStyleProperty( key, value );
|
||||
}
|
||||
|
||||
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, b, key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
Map<String, Class<?>> infos = FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
if( icon instanceof FlatCheckBoxIcon ) {
|
||||
for( Map.Entry<String, Class<?>> e : ((FlatCheckBoxIcon)icon).getStyleableInfos().entrySet() )
|
||||
infos.put( "icon.".concat( e.getKey() ), e.getValue() );
|
||||
}
|
||||
return infos;
|
||||
}
|
||||
|
||||
private static Insets tempInsets = new Insets( 0, 0, 0, 0 );
|
||||
|
||||
@Override
|
||||
@@ -121,10 +225,11 @@ public class FlatRadioButtonUI
|
||||
public void paint( Graphics g, JComponent c ) {
|
||||
// fill background even if not opaque if
|
||||
// - contentAreaFilled is true and
|
||||
// - if background was explicitly set to a non-UIResource color
|
||||
// - if background color is different to default background color
|
||||
// (this paints selection if using the component as cell renderer)
|
||||
if( !c.isOpaque() &&
|
||||
((AbstractButton)c).isContentAreaFilled() &&
|
||||
!(c.getBackground() instanceof UIResource) )
|
||||
!Objects.equals( c.getBackground(), getDefaultBackground( c ) ) )
|
||||
{
|
||||
g.setColor( c.getBackground() );
|
||||
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
|
||||
@@ -161,10 +266,46 @@ public class FlatRadioButtonUI
|
||||
FlatButtonUI.paintText( g, b, textRect, text, b.isEnabled() ? b.getForeground() : disabledText );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default background color of the component.
|
||||
* If the component is used as cell renderer (e.g. in JTable),
|
||||
* then the background color of the renderer container is returned.
|
||||
*/
|
||||
private Color getDefaultBackground( JComponent c ) {
|
||||
Container parent = c.getParent();
|
||||
return (parent instanceof CellRendererPane && parent.getParent() != null)
|
||||
? parent.getParent().getBackground()
|
||||
: defaultBackground;
|
||||
}
|
||||
|
||||
private int getIconFocusWidth( JComponent c ) {
|
||||
AbstractButton b = (AbstractButton) c;
|
||||
return (b.getIcon() == null && getDefaultIcon() instanceof FlatCheckBoxIcon)
|
||||
? UIScale.scale( ((FlatCheckBoxIcon)getDefaultIcon()).focusWidth )
|
||||
Icon icon = b.getIcon();
|
||||
if( icon == null )
|
||||
icon = getDefaultIcon();
|
||||
|
||||
return (icon instanceof FlatCheckBoxIcon)
|
||||
? Math.round( UIScale.scale( ((FlatCheckBoxIcon)icon).getFocusWidth() ) )
|
||||
: 0;
|
||||
}
|
||||
|
||||
//---- class FlatRadioButtonListener --------------------------------------
|
||||
|
||||
/** @since 2 */
|
||||
protected class FlatRadioButtonListener
|
||||
extends BasicButtonListener
|
||||
{
|
||||
private final AbstractButton b;
|
||||
|
||||
protected FlatRadioButtonListener( AbstractButton b ) {
|
||||
super( b );
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertyChange( PropertyChangeEvent e ) {
|
||||
super.propertyChange( e );
|
||||
FlatRadioButtonUI.this.propertyChange( b, e );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,11 @@ import java.awt.Insets;
|
||||
import java.awt.LayoutManager;
|
||||
import java.awt.LayoutManager2;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ComponentAdapter;
|
||||
import java.awt.event.ComponentEvent;
|
||||
import java.awt.event.ComponentListener;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.function.Function;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
@@ -36,15 +40,19 @@ import javax.swing.JLayeredPane;
|
||||
import javax.swing.JMenuBar;
|
||||
import javax.swing.JRootPane;
|
||||
import javax.swing.LookAndFeel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.plaf.BorderUIResource;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.RootPaneUI;
|
||||
import javax.swing.plaf.UIResource;
|
||||
import javax.swing.plaf.basic.BasicRootPaneUI;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
import com.formdev.flatlaf.FlatLaf;
|
||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||
import com.formdev.flatlaf.util.SystemInfo;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
/**
|
||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JRootPane}.
|
||||
@@ -54,9 +62,13 @@ import com.formdev.flatlaf.util.SystemInfo;
|
||||
* @uiDefault RootPane.border Border
|
||||
* @uiDefault RootPane.activeBorderColor Color
|
||||
* @uiDefault RootPane.inactiveBorderColor Color
|
||||
* @uiDefault TitlePane.borderColor Color optional
|
||||
*
|
||||
* <!-- FlatWindowResizer -->
|
||||
*
|
||||
* @uiDefault RootPane.font Font unused
|
||||
* @uiDefault RootPane.background Color
|
||||
* @uiDefault RootPane.foreground Color unused
|
||||
* @uiDefault RootPane.borderDragThickness int
|
||||
* @uiDefault RootPane.cornerDragWidth int
|
||||
* @uiDefault RootPane.honorFrameMinimumSizeOnResize boolean
|
||||
@@ -67,15 +79,16 @@ import com.formdev.flatlaf.util.SystemInfo;
|
||||
public class FlatRootPaneUI
|
||||
extends BasicRootPaneUI
|
||||
{
|
||||
// check this field before using class JBRCustomDecorations to avoid unnecessary loading of that class
|
||||
static final boolean canUseJBRCustomDecorations
|
||||
= SystemInfo.isJetBrainsJVM_11_orLater && SystemInfo.isWindows_10_orLater;
|
||||
protected final Color borderColor = UIManager.getColor( "TitlePane.borderColor" );
|
||||
|
||||
protected JRootPane rootPane;
|
||||
protected FlatTitlePane titlePane;
|
||||
protected FlatWindowResizer windowResizer;
|
||||
|
||||
private Object nativeWindowBorderData;
|
||||
private LayoutManager oldLayout;
|
||||
private PropertyChangeListener ancestorListener;
|
||||
private ComponentListener componentListener;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatRootPaneUI();
|
||||
@@ -89,15 +102,25 @@ public class FlatRootPaneUI
|
||||
|
||||
if( rootPane.getWindowDecorationStyle() != JRootPane.NONE )
|
||||
installClientDecorations();
|
||||
else
|
||||
installBorder();
|
||||
|
||||
if( canUseJBRCustomDecorations )
|
||||
JBRCustomDecorations.install( rootPane );
|
||||
installNativeWindowBorder();
|
||||
}
|
||||
|
||||
protected void installBorder() {
|
||||
if( borderColor != null ) {
|
||||
Border b = rootPane.getBorder();
|
||||
if( b == null || b instanceof UIResource )
|
||||
rootPane.setBorder( new FlatWindowTitleBorder( borderColor ) );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uninstallUI( JComponent c ) {
|
||||
super.uninstallUI( c );
|
||||
|
||||
uninstallNativeWindowBorder();
|
||||
uninstallClientDecorations();
|
||||
rootPane = null;
|
||||
}
|
||||
@@ -106,8 +129,23 @@ public class FlatRootPaneUI
|
||||
protected void installDefaults( JRootPane c ) {
|
||||
super.installDefaults( c );
|
||||
|
||||
// Give the root pane useful background, foreground and font.
|
||||
// Background is used for title bar and menu bar if native window decorations
|
||||
// and unified background are enabled.
|
||||
// Foreground and font are usually not used, but set for completeness.
|
||||
// Not using LookAndFeel.installColorsAndFont() here because it will not work
|
||||
// because the properties are null by default but inherit non-null values from parent.
|
||||
if( !c.isBackgroundSet() || c.getBackground() instanceof UIResource )
|
||||
c.setBackground( UIManager.getColor( "RootPane.background" ) );
|
||||
if( !c.isForegroundSet() || c.getForeground() instanceof UIResource )
|
||||
c.setForeground( UIManager.getColor( "RootPane.foreground" ) );
|
||||
if( !c.isFontSet() || c.getFont() instanceof UIResource )
|
||||
c.setFont( UIManager.getFont( "RootPane.font" ) );
|
||||
|
||||
// Update background color of JFrame or JDialog parent to avoid bad border
|
||||
// on HiDPI screens when switching from light to dark Laf.
|
||||
// Window background color is also used in native window decorations
|
||||
// to fill background when window is initially shown or when resizing window.
|
||||
// The background of JFrame is initialized in JFrame.frameInit() and
|
||||
// the background of JDialog in JDialog.dialogInit(),
|
||||
// but it was not updated when switching Laf.
|
||||
@@ -119,18 +157,92 @@ public class FlatRootPaneUI
|
||||
}
|
||||
|
||||
// enable dark window appearance on macOS when running in JetBrains Runtime
|
||||
if( SystemInfo.isJetBrainsJVM && SystemInfo.isMacOS_10_14_Mojave_orLater ) {
|
||||
LookAndFeel laf = UIManager.getLookAndFeel();
|
||||
boolean isDark = laf instanceof FlatLaf && ((FlatLaf)laf).isDark();
|
||||
c.putClientProperty( "jetbrains.awt.windowDarkAppearance", isDark );
|
||||
if( SystemInfo.isJetBrainsJVM && SystemInfo.isMacOS_10_14_Mojave_orLater )
|
||||
c.putClientProperty( "jetbrains.awt.windowDarkAppearance", FlatLaf.isLafDark() );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installListeners( JRootPane root ) {
|
||||
super.installListeners( root );
|
||||
|
||||
if( SystemInfo.isJava_9_orLater ) {
|
||||
// On HiDPI screens, where scaling is used, there may be white lines at the
|
||||
// bottom and at the right side of the window when it is initially shown.
|
||||
// This is very disturbing in dark themes, but hard to notice in light themes.
|
||||
// Seems to be a rounding issue when Swing adds dirty region of window
|
||||
// using RepaintManager.nativeAddDirtyRegion().
|
||||
//
|
||||
// Note: Not using a HierarchyListener here, which would be much easier,
|
||||
// because this causes problems with mouse clicks in heavy-weight popups.
|
||||
// Instead, add a listener to the root pane that waits until it is added
|
||||
// to a window, then add a component listener to the window.
|
||||
// See: https://github.com/JFormDesigner/FlatLaf/issues/371
|
||||
ancestorListener = e -> {
|
||||
Object oldValue = e.getOldValue();
|
||||
Object newValue = e.getNewValue();
|
||||
if( newValue instanceof Window ) {
|
||||
if( componentListener == null ) {
|
||||
componentListener = new ComponentAdapter() {
|
||||
@Override
|
||||
public void componentShown( ComponentEvent e ) {
|
||||
// add whole root pane to dirty regions when window is initially shown
|
||||
root.getParent().repaint( root.getX(), root.getY(), root.getWidth(), root.getHeight() );
|
||||
}
|
||||
};
|
||||
}
|
||||
((Window)newValue).addComponentListener( componentListener );
|
||||
} else if( newValue == null && oldValue instanceof Window ) {
|
||||
if( componentListener != null )
|
||||
((Window)oldValue).removeComponentListener( componentListener );
|
||||
}
|
||||
};
|
||||
root.addPropertyChangeListener( "ancestor", ancestorListener );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void uninstallListeners( JRootPane root ) {
|
||||
super.uninstallListeners( root );
|
||||
|
||||
if( SystemInfo.isJava_9_orLater ) {
|
||||
if( componentListener != null ) {
|
||||
Window window = SwingUtilities.windowForComponent( root );
|
||||
if( window != null )
|
||||
window.removeComponentListener( componentListener );
|
||||
componentListener = null;
|
||||
}
|
||||
root.removePropertyChangeListener( "ancestor", ancestorListener );
|
||||
ancestorListener = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 1.1.2 */
|
||||
protected void installNativeWindowBorder() {
|
||||
nativeWindowBorderData = FlatNativeWindowBorder.install( rootPane );
|
||||
}
|
||||
|
||||
/** @since 1.1.2 */
|
||||
protected void uninstallNativeWindowBorder() {
|
||||
FlatNativeWindowBorder.uninstall( rootPane, nativeWindowBorderData );
|
||||
nativeWindowBorderData = null;
|
||||
}
|
||||
|
||||
/** @since 1.1.2 */
|
||||
public static void updateNativeWindowBorder( JRootPane rootPane ) {
|
||||
RootPaneUI rui = rootPane.getUI();
|
||||
if( !(rui instanceof FlatRootPaneUI) )
|
||||
return;
|
||||
|
||||
FlatRootPaneUI ui = (FlatRootPaneUI) rui;
|
||||
ui.uninstallNativeWindowBorder();
|
||||
ui.installNativeWindowBorder();
|
||||
}
|
||||
|
||||
protected void installClientDecorations() {
|
||||
boolean isJBRSupported = canUseJBRCustomDecorations && JBRCustomDecorations.isSupported();
|
||||
boolean isNativeWindowBorderSupported = FlatNativeWindowBorder.isSupported();
|
||||
|
||||
// install border
|
||||
if( rootPane.getWindowDecorationStyle() != JRootPane.NONE && !isJBRSupported )
|
||||
if( rootPane.getWindowDecorationStyle() != JRootPane.NONE && !isNativeWindowBorderSupported )
|
||||
LookAndFeel.installBorder( rootPane, "RootPane.border" );
|
||||
else
|
||||
LookAndFeel.uninstallBorder( rootPane );
|
||||
@@ -143,7 +255,7 @@ public class FlatRootPaneUI
|
||||
rootPane.setLayout( createRootLayout() );
|
||||
|
||||
// install window resizer
|
||||
if( !isJBRSupported )
|
||||
if( !isNativeWindowBorderSupported )
|
||||
windowResizer = createWindowResizer();
|
||||
}
|
||||
|
||||
@@ -203,6 +315,12 @@ public class FlatRootPaneUI
|
||||
uninstallClientDecorations();
|
||||
if( rootPane.getWindowDecorationStyle() != JRootPane.NONE )
|
||||
installClientDecorations();
|
||||
else
|
||||
installBorder();
|
||||
break;
|
||||
|
||||
case FlatClientProperties.USE_WINDOW_DECORATIONS:
|
||||
updateNativeWindowBorder( rootPane );
|
||||
break;
|
||||
|
||||
case FlatClientProperties.MENU_BAR_EMBEDDED:
|
||||
@@ -212,9 +330,27 @@ public class FlatRootPaneUI
|
||||
rootPane.repaint();
|
||||
}
|
||||
break;
|
||||
|
||||
case FlatClientProperties.TITLE_BAR_SHOW_ICON:
|
||||
if( titlePane != null )
|
||||
titlePane.updateIcon();
|
||||
break;
|
||||
|
||||
case FlatClientProperties.TITLE_BAR_BACKGROUND:
|
||||
case FlatClientProperties.TITLE_BAR_FOREGROUND:
|
||||
if( titlePane != null )
|
||||
titlePane.titleBarColorsChanged();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected static boolean isMenuBarEmbedded( JRootPane rootPane ) {
|
||||
RootPaneUI ui = rootPane.getUI();
|
||||
return ui instanceof FlatRootPaneUI &&
|
||||
((FlatRootPaneUI)ui).titlePane != null &&
|
||||
((FlatRootPaneUI)ui).titlePane.isMenuBarEmbedded();
|
||||
}
|
||||
|
||||
//---- class FlatRootLayout -----------------------------------------------
|
||||
|
||||
protected class FlatRootLayout
|
||||
@@ -249,11 +385,12 @@ public class FlatRootPaneUI
|
||||
? getSizeFunc.apply( rootPane.getContentPane() )
|
||||
: rootPane.getSize();
|
||||
|
||||
int width = Math.max( titlePaneSize.width, contentSize.width );
|
||||
int width = contentSize.width; // title pane width is not considered here
|
||||
int height = titlePaneSize.height + contentSize.height;
|
||||
if( titlePane == null || !titlePane.isMenuBarEmbedded() ) {
|
||||
Dimension menuBarSize = (rootPane.getJMenuBar() != null)
|
||||
? getSizeFunc.apply( rootPane.getJMenuBar() )
|
||||
JMenuBar menuBar = rootPane.getJMenuBar();
|
||||
Dimension menuBarSize = (menuBar != null && menuBar.isVisible())
|
||||
? getSizeFunc.apply( menuBar )
|
||||
: new Dimension();
|
||||
|
||||
width = Math.max( width, menuBarSize.width );
|
||||
@@ -270,6 +407,7 @@ public class FlatRootPaneUI
|
||||
@Override
|
||||
public void layoutContainer( Container parent ) {
|
||||
JRootPane rootPane = (JRootPane) parent;
|
||||
boolean isFullScreen = FlatUIUtils.isFullScreen( rootPane );
|
||||
|
||||
Insets insets = rootPane.getInsets();
|
||||
int x = insets.left;
|
||||
@@ -284,14 +422,15 @@ public class FlatRootPaneUI
|
||||
|
||||
int nextY = 0;
|
||||
if( titlePane != null ) {
|
||||
Dimension prefSize = titlePane.getPreferredSize();
|
||||
titlePane.setBounds( 0, 0, width, prefSize.height );
|
||||
nextY += prefSize.height;
|
||||
int prefHeight = !isFullScreen ? titlePane.getPreferredSize().height : 0;
|
||||
titlePane.setBounds( 0, 0, width, prefHeight );
|
||||
nextY += prefHeight;
|
||||
}
|
||||
|
||||
JMenuBar menuBar = rootPane.getJMenuBar();
|
||||
if( menuBar != null ) {
|
||||
if( titlePane != null && titlePane.isMenuBarEmbedded() ) {
|
||||
if( menuBar != null && menuBar.isVisible() ) {
|
||||
boolean embedded = !isFullScreen && titlePane != null && titlePane.isMenuBarEmbedded();
|
||||
if( embedded ) {
|
||||
titlePane.validate();
|
||||
menuBar.setBounds( titlePane.getMenuBarBounds() );
|
||||
} else {
|
||||
@@ -328,6 +467,9 @@ public class FlatRootPaneUI
|
||||
|
||||
//---- class FlatWindowBorder ---------------------------------------------
|
||||
|
||||
/**
|
||||
* Window border used for non-native window decorations.
|
||||
*/
|
||||
public static class FlatWindowBorder
|
||||
extends BorderUIResource.EmptyBorderUIResource
|
||||
{
|
||||
@@ -341,8 +483,8 @@ public class FlatRootPaneUI
|
||||
|
||||
@Override
|
||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||
if( isWindowMaximized( c ) ) {
|
||||
// hide border if window is maximized
|
||||
if( isWindowMaximized( c ) || FlatUIUtils.isFullScreen( c ) ) {
|
||||
// hide border if window is maximized or full screen
|
||||
insets.top = insets.left = insets.bottom = insets.right = 0;
|
||||
return insets;
|
||||
} else
|
||||
@@ -351,7 +493,7 @@ public class FlatRootPaneUI
|
||||
|
||||
@Override
|
||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||
if( isWindowMaximized( c ) )
|
||||
if( isWindowMaximized( c ) || FlatUIUtils.isFullScreen( c ) )
|
||||
return;
|
||||
|
||||
Container parent = c.getParent();
|
||||
@@ -372,4 +514,42 @@ public class FlatRootPaneUI
|
||||
: false;
|
||||
}
|
||||
}
|
||||
|
||||
//---- class FlatWindowTitleBorder ----------------------------------------
|
||||
|
||||
private static class FlatWindowTitleBorder
|
||||
extends BorderUIResource.EmptyBorderUIResource
|
||||
{
|
||||
private final Color borderColor;
|
||||
|
||||
FlatWindowTitleBorder( Color borderColor ) {
|
||||
super( 0, 0, 0, 0 );
|
||||
this.borderColor = borderColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||
if( showBorder( c ) ) {
|
||||
float lineHeight = UIScale.scale( (float) 1 );
|
||||
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y, width, lineHeight );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||
insets.set( showBorder( c ) ? 1 : 0, 0, 0, 0 );
|
||||
return insets;
|
||||
}
|
||||
|
||||
private boolean showBorder( Component c ) {
|
||||
Container parent = c.getParent();
|
||||
return
|
||||
(parent instanceof JFrame &&
|
||||
(((JFrame)parent).getJMenuBar() == null ||
|
||||
!((JFrame)parent).getJMenuBar().isVisible())) ||
|
||||
(parent instanceof JDialog &&
|
||||
(((JDialog)parent).getJMenuBar() == null ||
|
||||
!((JDialog)parent).getJMenuBar().isVisible()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Component;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
|
||||
/**
|
||||
* Border for various components (e.g. {@link javax.swing.JComboBox}).
|
||||
@@ -29,7 +30,10 @@ import javax.swing.UIManager;
|
||||
public class FlatRoundBorder
|
||||
extends FlatBorder
|
||||
{
|
||||
protected final int arc = UIManager.getInt( "Component.arc" );
|
||||
@Styleable protected int arc = UIManager.getInt( "Component.arc" );
|
||||
|
||||
// only used via styling (not in UI defaults, but has likewise client properties)
|
||||
/** @since 2 */ @Styleable protected Boolean roundRect;
|
||||
|
||||
@Override
|
||||
protected int getArc( Component c ) {
|
||||
@@ -37,6 +41,8 @@ public class FlatRoundBorder
|
||||
return 0;
|
||||
|
||||
Boolean roundRect = FlatUIUtils.isRoundRect( c );
|
||||
if( roundRect == null )
|
||||
roundRect = this.roundRect;
|
||||
return roundRect != null ? (roundRect ? Short.MAX_VALUE : 0) : arc;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,13 +19,12 @@ package com.formdev.flatlaf.ui;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import javax.swing.InputMap;
|
||||
import javax.swing.JButton;
|
||||
@@ -37,6 +36,9 @@ import javax.swing.UIManager;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.basic.BasicScrollBarUI;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
/**
|
||||
@@ -65,7 +67,7 @@ import com.formdev.flatlaf.util.UIScale;
|
||||
* @uiDefault ScrollBar.pressedTrackColor Color optional
|
||||
* @uiDefault ScrollBar.pressedThumbColor Color optional
|
||||
* @uiDefault ScrollBar.pressedThumbWithTrack boolean
|
||||
* @uiDefault Component.arrowType String triangle (default) or chevron
|
||||
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||
* @uiDefault ScrollBar.showButtons boolean
|
||||
* @uiDefault ScrollBar.buttonArrowColor Color
|
||||
* @uiDefault ScrollBar.buttonDisabledArrowColor Color
|
||||
@@ -76,33 +78,46 @@ import com.formdev.flatlaf.util.UIScale;
|
||||
*/
|
||||
public class FlatScrollBarUI
|
||||
extends BasicScrollBarUI
|
||||
implements StyleableUI
|
||||
{
|
||||
protected Insets trackInsets;
|
||||
protected Insets thumbInsets;
|
||||
protected int trackArc;
|
||||
protected int thumbArc;
|
||||
protected Color hoverTrackColor;
|
||||
protected Color hoverThumbColor;
|
||||
protected boolean hoverThumbWithTrack;
|
||||
protected Color pressedTrackColor;
|
||||
protected Color pressedThumbColor;
|
||||
protected boolean pressedThumbWithTrack;
|
||||
// overrides BasicScrollBarUI.supportsAbsolutePositioning (which is private)
|
||||
@Styleable protected boolean allowsAbsolutePositioning;
|
||||
|
||||
protected boolean showButtons;
|
||||
protected String arrowType;
|
||||
protected Color buttonArrowColor;
|
||||
protected Color buttonDisabledArrowColor;
|
||||
protected Color hoverButtonBackground;
|
||||
protected Color pressedButtonBackground;
|
||||
@Styleable protected Insets trackInsets;
|
||||
@Styleable protected Insets thumbInsets;
|
||||
@Styleable protected int trackArc;
|
||||
@Styleable protected int thumbArc;
|
||||
@Styleable protected Color hoverTrackColor;
|
||||
@Styleable protected Color hoverThumbColor;
|
||||
@Styleable protected boolean hoverThumbWithTrack;
|
||||
@Styleable protected Color pressedTrackColor;
|
||||
@Styleable protected Color pressedThumbColor;
|
||||
@Styleable protected boolean pressedThumbWithTrack;
|
||||
|
||||
@Styleable protected boolean showButtons;
|
||||
@Styleable protected String arrowType;
|
||||
@Styleable protected Color buttonArrowColor;
|
||||
@Styleable protected Color buttonDisabledArrowColor;
|
||||
@Styleable protected Color hoverButtonBackground;
|
||||
@Styleable protected Color pressedButtonBackground;
|
||||
|
||||
private MouseAdapter hoverListener;
|
||||
protected boolean hoverTrack;
|
||||
protected boolean hoverThumb;
|
||||
|
||||
private Map<String, Object> oldStyleValues;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatScrollBarUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installUI( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
installStyle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installListeners() {
|
||||
super.installListeners();
|
||||
@@ -125,6 +140,8 @@ public class FlatScrollBarUI
|
||||
protected void installDefaults() {
|
||||
super.installDefaults();
|
||||
|
||||
allowsAbsolutePositioning = super.getSupportsAbsolutePositioning();
|
||||
|
||||
trackInsets = UIManager.getInsets( "ScrollBar.trackInsets" );
|
||||
thumbInsets = UIManager.getInsets( "ScrollBar.thumbInsets" );
|
||||
trackArc = UIManager.getInt( "ScrollBar.trackArc" );
|
||||
@@ -142,6 +159,12 @@ public class FlatScrollBarUI
|
||||
buttonDisabledArrowColor = UIManager.getColor( "ScrollBar.buttonDisabledArrowColor" );
|
||||
hoverButtonBackground = UIManager.getColor( "ScrollBar.hoverButtonBackground" );
|
||||
pressedButtonBackground = UIManager.getColor( "ScrollBar.pressedButtonBackground" );
|
||||
|
||||
// fallback (e.g. when used in NetBeans GUI builder)
|
||||
if( trackInsets == null )
|
||||
trackInsets = new Insets( 0, 0, 0, 0 );
|
||||
if( thumbInsets == null )
|
||||
thumbInsets = new Insets( 0, 0, 0, 0 );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -159,38 +182,92 @@ public class FlatScrollBarUI
|
||||
buttonDisabledArrowColor = null;
|
||||
hoverButtonBackground = null;
|
||||
pressedButtonBackground = null;
|
||||
|
||||
oldStyleValues = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PropertyChangeListener createPropertyChangeListener() {
|
||||
return new BasicScrollBarUI.PropertyChangeHandler() {
|
||||
@Override
|
||||
public void propertyChange( PropertyChangeEvent e ) {
|
||||
super.propertyChange( e );
|
||||
PropertyChangeListener superListener = super.createPropertyChangeListener();
|
||||
return e -> {
|
||||
superListener.propertyChange( e );
|
||||
|
||||
switch( e.getPropertyName() ) {
|
||||
case FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS:
|
||||
scrollbar.revalidate();
|
||||
scrollbar.repaint();
|
||||
break;
|
||||
switch( e.getPropertyName() ) {
|
||||
case FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS:
|
||||
scrollbar.revalidate();
|
||||
scrollbar.repaint();
|
||||
break;
|
||||
|
||||
case "componentOrientation":
|
||||
// this is missing in BasicScrollBarUI.Handler.propertyChange()
|
||||
InputMap inputMap = (InputMap) UIManager.get( "ScrollBar.ancestorInputMap" );
|
||||
if( !scrollbar.getComponentOrientation().isLeftToRight() ) {
|
||||
InputMap rtlInputMap = (InputMap) UIManager.get( "ScrollBar.ancestorInputMap.RightToLeft" );
|
||||
if( rtlInputMap != null ) {
|
||||
rtlInputMap.setParent( inputMap );
|
||||
inputMap = rtlInputMap;
|
||||
}
|
||||
case FlatClientProperties.STYLE:
|
||||
case FlatClientProperties.STYLE_CLASS:
|
||||
installStyle();
|
||||
scrollbar.revalidate();
|
||||
scrollbar.repaint();
|
||||
break;
|
||||
|
||||
case "componentOrientation":
|
||||
// this is missing in BasicScrollBarUI.Handler.propertyChange()
|
||||
InputMap inputMap = (InputMap) UIManager.get( "ScrollBar.ancestorInputMap" );
|
||||
if( !scrollbar.getComponentOrientation().isLeftToRight() ) {
|
||||
InputMap rtlInputMap = (InputMap) UIManager.get( "ScrollBar.ancestorInputMap.RightToLeft" );
|
||||
if( rtlInputMap != null ) {
|
||||
rtlInputMap.setParent( inputMap );
|
||||
inputMap = rtlInputMap;
|
||||
}
|
||||
SwingUtilities.replaceUIInputMap( scrollbar, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap );
|
||||
break;
|
||||
}
|
||||
}
|
||||
SwingUtilities.replaceUIInputMap( scrollbar, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap );
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void installStyle() {
|
||||
try {
|
||||
applyStyle( FlatStylingSupport.getResolvedStyle( scrollbar, "ScrollBar" ) );
|
||||
} catch( RuntimeException ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void applyStyle( Object style ) {
|
||||
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||
|
||||
if( incrButton instanceof FlatScrollBarButton )
|
||||
((FlatScrollBarButton)incrButton).updateStyle();
|
||||
if( decrButton instanceof FlatScrollBarButton )
|
||||
((FlatScrollBarButton)decrButton).updateStyle();
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected Object applyStyleProperty( String key, Object value ) {
|
||||
Object oldValue;
|
||||
switch( key ) {
|
||||
// BasicScrollBarUI
|
||||
case "track": oldValue = trackColor; trackColor = (Color) value; return oldValue;
|
||||
case "thumb": oldValue = thumbColor; thumbColor = (Color) value; return oldValue;
|
||||
case "width": oldValue = scrollBarWidth; scrollBarWidth = (int) value; return oldValue;
|
||||
case "minimumThumbSize": oldValue = minimumThumbSize; minimumThumbSize = (Dimension) value; return oldValue;
|
||||
case "maximumThumbSize": oldValue = maximumThumbSize; maximumThumbSize = (Dimension) value; return oldValue;
|
||||
}
|
||||
|
||||
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, scrollbar, key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
Map<String, Class<?>> infos = new FlatStylingSupport.StyleableInfosMap<>();
|
||||
infos.put( "track", Color.class );
|
||||
infos.put( "thumb", Color.class );
|
||||
infos.put( "width", int.class );
|
||||
infos.put( "minimumThumbSize", Dimension.class );
|
||||
infos.put( "maximumThumbSize", Dimension.class );
|
||||
FlatStylingSupport.collectAnnotatedStyleableInfos( this, infos );
|
||||
return infos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize( JComponent c ) {
|
||||
return UIScale.scale( super.getPreferredSize( c ) );
|
||||
@@ -207,16 +284,25 @@ public class FlatScrollBarUI
|
||||
}
|
||||
|
||||
protected boolean isShowButtons() {
|
||||
// check client property on scroll bar
|
||||
Object showButtons = scrollbar.getClientProperty( FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS );
|
||||
if( showButtons == null && scrollbar.getParent() instanceof JScrollPane )
|
||||
showButtons = ((JScrollPane)scrollbar.getParent()).getClientProperty( FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS );
|
||||
if( showButtons == null && scrollbar.getParent() instanceof JScrollPane ) {
|
||||
JScrollPane scrollPane = (JScrollPane) scrollbar.getParent();
|
||||
// check client property on scroll pane
|
||||
showButtons = scrollPane.getClientProperty( FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS );
|
||||
if( showButtons == null && scrollPane.getUI() instanceof FlatScrollPaneUI ) {
|
||||
// check styling property on scroll pane
|
||||
showButtons = ((FlatScrollPaneUI)scrollPane.getUI()).showButtons;
|
||||
}
|
||||
}
|
||||
return (showButtons != null) ? Objects.equals( showButtons, true ) : this.showButtons;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint( Graphics g, JComponent c ) {
|
||||
FlatUIUtils.setRenderingHints( (Graphics2D) g );
|
||||
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
||||
super.paint( g, c );
|
||||
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -292,6 +378,11 @@ public class FlatScrollBarUI
|
||||
return UIScale.scale( FlatUIUtils.addInsets( super.getMaximumThumbSize(), thumbInsets ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getSupportsAbsolutePositioning() {
|
||||
return allowsAbsolutePositioning;
|
||||
}
|
||||
|
||||
//---- class ScrollBarHoverListener ---------------------------------------
|
||||
|
||||
// using static field to disabling hover for other scroll bars
|
||||
@@ -351,19 +442,25 @@ public class FlatScrollBarUI
|
||||
{
|
||||
protected FlatScrollBarButton( int direction ) {
|
||||
this( direction, arrowType, buttonArrowColor, buttonDisabledArrowColor,
|
||||
null, hoverButtonBackground, pressedButtonBackground );
|
||||
null, hoverButtonBackground, null, pressedButtonBackground );
|
||||
}
|
||||
|
||||
protected FlatScrollBarButton( int direction, String type, Color foreground, Color disabledForeground,
|
||||
Color hoverForeground, Color hoverBackground, Color pressedBackground )
|
||||
Color hoverForeground, Color hoverBackground, Color pressedForeground, Color pressedBackground )
|
||||
{
|
||||
super( direction, type, foreground, disabledForeground, hoverForeground, hoverBackground, pressedBackground );
|
||||
super( direction, type, foreground, disabledForeground,
|
||||
hoverForeground, hoverBackground, pressedForeground, pressedBackground );
|
||||
|
||||
setArrowWidth( FlatArrowButton.DEFAULT_ARROW_WIDTH - 2 );
|
||||
setFocusable( false );
|
||||
setRequestFocusEnabled( false );
|
||||
}
|
||||
|
||||
protected void updateStyle() {
|
||||
updateStyle( arrowType, buttonArrowColor, buttonDisabledArrowColor,
|
||||
null, hoverButtonBackground, null, pressedButtonBackground );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Color deriveBackground( Color background ) {
|
||||
return FlatUIUtils.deriveColor( background, scrollbar.getBackground() );
|
||||
|
||||
@@ -14,17 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Smooth scrolling code partly based on code from IntelliJ IDEA Community Edition,
|
||||
* which is licensed under the Apache 2.0 license. Copyright 2000-2016 JetBrains s.r.o.
|
||||
* See: https://github.com/JetBrains/intellij-community/blob/31e1b5a8e43219b9571951bab6457cfb3012e3ef/platform/platform-api/src/com/intellij/ui/components/SmoothScrollPane.java#L141-L185
|
||||
*
|
||||
*/
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Insets;
|
||||
import java.awt.KeyboardFocusManager;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.ContainerEvent;
|
||||
import java.awt.event.ContainerListener;
|
||||
@@ -34,21 +29,28 @@ import java.awt.event.MouseWheelEvent;
|
||||
import java.awt.event.MouseWheelListener;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JScrollBar;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.JViewport;
|
||||
import javax.swing.LookAndFeel;
|
||||
import javax.swing.ScrollPaneConstants;
|
||||
import javax.swing.Scrollable;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.basic.BasicScrollPaneUI;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
|
||||
/**
|
||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JScrollPane}.
|
||||
@@ -69,9 +71,16 @@ import com.formdev.flatlaf.FlatClientProperties;
|
||||
*/
|
||||
public class FlatScrollPaneUI
|
||||
extends BasicScrollPaneUI
|
||||
implements StyleableUI
|
||||
{
|
||||
// only used via styling (not in UI defaults, but has likewise client properties)
|
||||
/** @since 2 */ @Styleable protected Boolean showButtons;
|
||||
|
||||
private Handler handler;
|
||||
|
||||
private Map<String, Object> oldStyleValues;
|
||||
private AtomicBoolean borderShared;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatScrollPaneUI();
|
||||
}
|
||||
@@ -83,6 +92,8 @@ public class FlatScrollPaneUI
|
||||
int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
||||
LookAndFeel.installProperty( c, "opaque", focusWidth == 0 );
|
||||
|
||||
installStyle();
|
||||
|
||||
MigLayoutVisualPadding.install( scrollpane );
|
||||
}
|
||||
|
||||
@@ -91,6 +102,9 @@ public class FlatScrollPaneUI
|
||||
MigLayoutVisualPadding.uninstall( scrollpane );
|
||||
|
||||
super.uninstallUI( c );
|
||||
|
||||
oldStyleValues = null;
|
||||
borderShared = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -111,19 +125,17 @@ public class FlatScrollPaneUI
|
||||
|
||||
@Override
|
||||
protected MouseWheelListener createMouseWheelListener() {
|
||||
return new BasicScrollPaneUI.MouseWheelHandler() {
|
||||
@Override
|
||||
public void mouseWheelMoved( MouseWheelEvent e ) {
|
||||
if( isSmoothScrollingEnabled() &&
|
||||
scrollpane.isWheelScrollingEnabled() &&
|
||||
e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL &&
|
||||
e.getPreciseWheelRotation() != 0 &&
|
||||
e.getPreciseWheelRotation() != e.getWheelRotation() )
|
||||
{
|
||||
mouseWheelMovedSmooth( e );
|
||||
} else
|
||||
super.mouseWheelMoved( e );
|
||||
}
|
||||
MouseWheelListener superListener = super.createMouseWheelListener();
|
||||
return e -> {
|
||||
if( isSmoothScrollingEnabled() &&
|
||||
scrollpane.isWheelScrollingEnabled() &&
|
||||
e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL &&
|
||||
e.getPreciseWheelRotation() != 0 &&
|
||||
e.getPreciseWheelRotation() != e.getWheelRotation() )
|
||||
{
|
||||
mouseWheelMovedSmooth( e );
|
||||
} else
|
||||
superListener.mouseWheelMoved( e );
|
||||
};
|
||||
}
|
||||
|
||||
@@ -138,8 +150,6 @@ public class FlatScrollPaneUI
|
||||
return UIManager.getBoolean( "ScrollPane.smoothScrolling" );
|
||||
}
|
||||
|
||||
private static final double EPSILON = 1e-5d;
|
||||
|
||||
private void mouseWheelMovedSmooth( MouseWheelEvent e ) {
|
||||
// return if there is no viewport
|
||||
JViewport viewport = scrollpane.getViewport();
|
||||
@@ -160,24 +170,22 @@ public class FlatScrollPaneUI
|
||||
// get precise wheel rotation
|
||||
double rotation = e.getPreciseWheelRotation();
|
||||
|
||||
// get unit and block increment
|
||||
// get unit increment
|
||||
int unitIncrement;
|
||||
int blockIncrement;
|
||||
int orientation = scrollbar.getOrientation();
|
||||
Component view = viewport.getView();
|
||||
if( view instanceof Scrollable ) {
|
||||
Scrollable scrollable = (Scrollable) view;
|
||||
|
||||
// Use (0, 0) view position to obtain constant unit increment of first item
|
||||
// (which might otherwise be variable on smaller-than-unit scrolling).
|
||||
// Use (0, 0) view position to obtain a constant unit increment of first item.
|
||||
// Unit increment may be different for each item.
|
||||
Rectangle visibleRect = new Rectangle( viewport.getViewSize() );
|
||||
unitIncrement = scrollable.getScrollableUnitIncrement( visibleRect, orientation, 1 );
|
||||
blockIncrement = scrollable.getScrollableBlockIncrement( visibleRect, orientation, 1 );
|
||||
|
||||
if( unitIncrement > 0 ) {
|
||||
// For the case that the first item (e.g. in a list) is larger
|
||||
// than the other items, get the unit increment of the second item
|
||||
// and use the smaller one.
|
||||
// than the other items (e.g. themes list in FlatLaf Demo),
|
||||
// get the unit increment of the second item and use the smaller one.
|
||||
if( orientation == SwingConstants.VERTICAL ) {
|
||||
visibleRect.y += unitIncrement;
|
||||
visibleRect.height -= unitIncrement;
|
||||
@@ -192,92 +200,103 @@ public class FlatScrollPaneUI
|
||||
} else {
|
||||
int direction = rotation < 0 ? -1 : 1;
|
||||
unitIncrement = scrollbar.getUnitIncrement( direction );
|
||||
blockIncrement = scrollbar.getBlockIncrement( direction );
|
||||
}
|
||||
|
||||
// limit scroll amount (number of units to scroll) for small viewports
|
||||
// (e.g. vertical scrolling in file chooser)
|
||||
int scrollAmount = e.getScrollAmount();
|
||||
// get viewport width/height (the visible width/height)
|
||||
int viewportWH = (orientation == SwingConstants.VERTICAL)
|
||||
? viewport.getHeight()
|
||||
: viewport.getWidth();
|
||||
if( unitIncrement * scrollAmount > viewportWH )
|
||||
scrollAmount = Math.max( viewportWH / unitIncrement, 1 );
|
||||
|
||||
// limit scroll increment to viewport width/height
|
||||
// - if scroll amount is set to a large value in OS settings
|
||||
// - for large unit increments in small viewports (e.g. horizontal scrolling in file chooser)
|
||||
int scrollIncrement = Math.min( unitIncrement * e.getScrollAmount(), viewportWH );
|
||||
|
||||
// compute relative delta
|
||||
double delta = rotation * scrollAmount * unitIncrement;
|
||||
boolean adjustDelta = Math.abs( rotation ) < (1.0 + EPSILON);
|
||||
double adjustedDelta = adjustDelta
|
||||
? Math.max( -blockIncrement, Math.min( delta, blockIncrement ) )
|
||||
: delta;
|
||||
double delta = rotation * scrollIncrement;
|
||||
int idelta = (int) Math.round( delta );
|
||||
|
||||
// scroll at least one pixel to avoid "hanging"
|
||||
// - for "super-low-speed" scrolling (move fingers very slowly on trackpad)
|
||||
// - if unit increment is very small (e.g. 1 if scroll view does not implement
|
||||
// javax.swing.Scrollable interface)
|
||||
if( idelta == 0 ) {
|
||||
if( rotation > 0 )
|
||||
idelta = 1;
|
||||
else if( rotation < 0 )
|
||||
idelta = -1;
|
||||
}
|
||||
|
||||
// compute new value
|
||||
int value = scrollbar.getValue();
|
||||
double minDelta = scrollbar.getMinimum() - value;
|
||||
double maxDelta = scrollbar.getMaximum() - scrollbar.getModel().getExtent() - value;
|
||||
double boundedDelta = Math.max( minDelta, Math.min( adjustedDelta, maxDelta ) );
|
||||
int newValue = value + (int) Math.round( boundedDelta );
|
||||
int minValue = scrollbar.getMinimum();
|
||||
int maxValue = scrollbar.getMaximum() - scrollbar.getModel().getExtent();
|
||||
int newValue = Math.max( minValue, Math.min( value + idelta, maxValue ) );
|
||||
|
||||
// set new value
|
||||
if( newValue != value )
|
||||
scrollbar.setValue( newValue );
|
||||
|
||||
/*debug
|
||||
System.out.println( String.format( "%4d %9f / %4d %4d / %12f %5s %12f / %4d %4d %4d / %12f %12f %12f / %4d",
|
||||
System.out.println( String.format( "%s %4d %9f / %3d * %d = %3d [%3d] / %8.2f %5d / %4d --> %4d [%d, %d]",
|
||||
(orientation == SwingConstants.VERTICAL) ? "V" : "H",
|
||||
e.getWheelRotation(),
|
||||
e.getPreciseWheelRotation(),
|
||||
unitIncrement,
|
||||
blockIncrement,
|
||||
e.getScrollAmount(),
|
||||
scrollIncrement,
|
||||
viewportWH,
|
||||
delta,
|
||||
adjustDelta,
|
||||
adjustedDelta,
|
||||
idelta,
|
||||
value,
|
||||
scrollbar.getMinimum(),
|
||||
scrollbar.getMaximum(),
|
||||
minDelta,
|
||||
maxDelta,
|
||||
boundedDelta,
|
||||
newValue ) );
|
||||
newValue,
|
||||
minValue,
|
||||
maxValue ) );
|
||||
*/
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PropertyChangeListener createPropertyChangeListener() {
|
||||
return new BasicScrollPaneUI.PropertyChangeHandler() {
|
||||
@Override
|
||||
public void propertyChange( PropertyChangeEvent e ) {
|
||||
super.propertyChange( e );
|
||||
PropertyChangeListener superListener = super.createPropertyChangeListener();
|
||||
return e -> {
|
||||
superListener.propertyChange( e );
|
||||
|
||||
switch( e.getPropertyName() ) {
|
||||
case FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS:
|
||||
JScrollBar vsb = scrollpane.getVerticalScrollBar();
|
||||
JScrollBar hsb = scrollpane.getHorizontalScrollBar();
|
||||
if( vsb != null ) {
|
||||
vsb.revalidate();
|
||||
vsb.repaint();
|
||||
}
|
||||
if( hsb != null ) {
|
||||
hsb.revalidate();
|
||||
hsb.repaint();
|
||||
}
|
||||
break;
|
||||
|
||||
case ScrollPaneConstants.LOWER_LEFT_CORNER:
|
||||
case ScrollPaneConstants.LOWER_RIGHT_CORNER:
|
||||
case ScrollPaneConstants.UPPER_LEFT_CORNER:
|
||||
case ScrollPaneConstants.UPPER_RIGHT_CORNER:
|
||||
// remove border from buttons added to corners
|
||||
Object corner = e.getNewValue();
|
||||
if( corner instanceof JButton &&
|
||||
((JButton)corner).getBorder() instanceof FlatButtonBorder &&
|
||||
scrollpane.getViewport() != null &&
|
||||
scrollpane.getViewport().getView() instanceof JTable )
|
||||
{
|
||||
((JButton)corner).setBorder( BorderFactory.createEmptyBorder() );
|
||||
((JButton)corner).setFocusable( false );
|
||||
}
|
||||
switch( e.getPropertyName() ) {
|
||||
case FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS:
|
||||
JScrollBar vsb = scrollpane.getVerticalScrollBar();
|
||||
JScrollBar hsb = scrollpane.getHorizontalScrollBar();
|
||||
if( vsb != null ) {
|
||||
vsb.revalidate();
|
||||
vsb.repaint();
|
||||
}
|
||||
if( hsb != null ) {
|
||||
hsb.revalidate();
|
||||
hsb.repaint();
|
||||
}
|
||||
break;
|
||||
|
||||
case ScrollPaneConstants.LOWER_LEFT_CORNER:
|
||||
case ScrollPaneConstants.LOWER_RIGHT_CORNER:
|
||||
case ScrollPaneConstants.UPPER_LEFT_CORNER:
|
||||
case ScrollPaneConstants.UPPER_RIGHT_CORNER:
|
||||
// remove border from buttons added to corners
|
||||
Object corner = e.getNewValue();
|
||||
if( corner instanceof JButton &&
|
||||
((JButton)corner).getBorder() instanceof FlatButtonBorder &&
|
||||
scrollpane.getViewport() != null &&
|
||||
scrollpane.getViewport().getView() instanceof JTable )
|
||||
{
|
||||
((JButton)corner).setBorder( BorderFactory.createEmptyBorder() );
|
||||
((JButton)corner).setFocusable( false );
|
||||
}
|
||||
break;
|
||||
|
||||
case FlatClientProperties.STYLE:
|
||||
case FlatClientProperties.STYLE_CLASS:
|
||||
installStyle();
|
||||
scrollpane.revalidate();
|
||||
scrollpane.repaint();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -288,6 +307,38 @@ public class FlatScrollPaneUI
|
||||
return handler;
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void installStyle() {
|
||||
try {
|
||||
applyStyle( FlatStylingSupport.getResolvedStyle( scrollpane, "ScrollPane" ) );
|
||||
} catch( RuntimeException ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void applyStyle( Object style ) {
|
||||
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected Object applyStyleProperty( String key, Object value ) {
|
||||
if( key.equals( "focusWidth" ) ) {
|
||||
int focusWidth = (value instanceof Integer) ? (int) value : UIManager.getInt( "Component.focusWidth" );
|
||||
LookAndFeel.installProperty( scrollpane, "opaque", focusWidth == 0 );
|
||||
}
|
||||
|
||||
if( borderShared == null )
|
||||
borderShared = new AtomicBoolean( true );
|
||||
return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, scrollpane, borderShared );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this, scrollpane.getBorder() );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateViewport( PropertyChangeEvent e ) {
|
||||
super.updateViewport( e );
|
||||
@@ -337,6 +388,29 @@ public class FlatScrollPaneUI
|
||||
paint( g, c );
|
||||
}
|
||||
|
||||
/** @since 1.3 */
|
||||
public static boolean isPermanentFocusOwner( JScrollPane scrollPane ) {
|
||||
JViewport viewport = scrollPane.getViewport();
|
||||
Component view = (viewport != null) ? viewport.getView() : null;
|
||||
if( view == null )
|
||||
return false;
|
||||
|
||||
// check whether view is focus owner
|
||||
if( FlatUIUtils.isPermanentFocusOwner( view ) )
|
||||
return true;
|
||||
|
||||
// check whether editor component in JTable or JTree is focus owner
|
||||
if( (view instanceof JTable && ((JTable)view).isEditing()) ||
|
||||
(view instanceof JTree && ((JTree)view).isEditing()) )
|
||||
{
|
||||
Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
|
||||
if( focusOwner != null )
|
||||
return SwingUtilities.isDescendingFrom( focusOwner, view );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//---- class Handler ------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -358,11 +432,13 @@ public class FlatScrollPaneUI
|
||||
|
||||
@Override
|
||||
public void focusGained( FocusEvent e ) {
|
||||
// necessary to update focus border
|
||||
scrollpane.repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focusLost( FocusEvent e ) {
|
||||
// necessary to update focus border
|
||||
scrollpane.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,11 +21,16 @@ import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Map;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JSeparator;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.basic.BasicSeparatorUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
|
||||
/**
|
||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JSeparator}.
|
||||
@@ -45,19 +50,37 @@ import javax.swing.plaf.basic.BasicSeparatorUI;
|
||||
*/
|
||||
public class FlatSeparatorUI
|
||||
extends BasicSeparatorUI
|
||||
implements StyleableUI
|
||||
{
|
||||
protected int height;
|
||||
protected int stripeWidth;
|
||||
protected int stripeIndent;
|
||||
@Styleable protected int height;
|
||||
@Styleable protected int stripeWidth;
|
||||
@Styleable protected int stripeIndent;
|
||||
|
||||
private final boolean shared;
|
||||
private boolean defaults_initialized = false;
|
||||
|
||||
private static ComponentUI instance;
|
||||
private PropertyChangeListener propertyChangeListener;
|
||||
private Map<String, Object> oldStyleValues;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
if( instance == null )
|
||||
instance = new FlatSeparatorUI();
|
||||
return instance;
|
||||
return FlatUIUtils.canUseSharedUI( c )
|
||||
? FlatUIUtils.createSharedUI( FlatSeparatorUI.class, () -> new FlatSeparatorUI( true ) )
|
||||
: new FlatSeparatorUI( false );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected FlatSeparatorUI( boolean shared ) {
|
||||
this.shared = shared;
|
||||
}
|
||||
|
||||
protected String getPropertyPrefix() {
|
||||
return "Separator";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installUI( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
installStyle( (JSeparator) c );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -77,13 +100,70 @@ public class FlatSeparatorUI
|
||||
@Override
|
||||
protected void uninstallDefaults( JSeparator s ) {
|
||||
super.uninstallDefaults( s );
|
||||
|
||||
defaults_initialized = false;
|
||||
oldStyleValues = null;
|
||||
}
|
||||
|
||||
protected String getPropertyPrefix() {
|
||||
@Override
|
||||
protected void installListeners( JSeparator s ) {
|
||||
super.installListeners( s );
|
||||
|
||||
propertyChangeListener = FlatStylingSupport.createPropertyChangeListener(
|
||||
s, () -> stylePropertyChange( s ), null );
|
||||
s.addPropertyChangeListener( propertyChangeListener );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void uninstallListeners( JSeparator s ) {
|
||||
super.uninstallListeners( s );
|
||||
|
||||
s.removePropertyChangeListener( propertyChangeListener );
|
||||
propertyChangeListener = null;
|
||||
}
|
||||
|
||||
private void stylePropertyChange( JSeparator s ) {
|
||||
if( shared && FlatStylingSupport.hasStyleProperty( s ) ) {
|
||||
// unshare component UI if necessary
|
||||
// updateUI() invokes applyStyle() from installUI()
|
||||
s.updateUI();
|
||||
} else
|
||||
installStyle( s );
|
||||
s.revalidate();
|
||||
s.repaint();
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void installStyle( JSeparator s ) {
|
||||
try {
|
||||
applyStyle( s, FlatStylingSupport.getResolvedStyle( s, getStyleType() ) );
|
||||
} catch( RuntimeException ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
String getStyleType() {
|
||||
return "Separator";
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void applyStyle( JSeparator s, Object style ) {
|
||||
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style,
|
||||
(key, value) -> applyStyleProperty( s, key, value ) );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected Object applyStyleProperty( JSeparator s, String key, Object value ) {
|
||||
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, s, key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint( Graphics g, JComponent c ) {
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
|
||||
@@ -18,17 +18,31 @@ package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Shape;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Map;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JSlider;
|
||||
import javax.swing.LookAndFeel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.basic.BasicSliderUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.util.Graphics2DProxy;
|
||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
/**
|
||||
@@ -49,29 +63,55 @@ import com.formdev.flatlaf.util.UIScale;
|
||||
* <!-- FlatSliderUI -->
|
||||
*
|
||||
* @uiDefault Slider.trackWidth int
|
||||
* @uiDefault Slider.thumbWidth int
|
||||
* @uiDefault Slider.thumbSize Dimension
|
||||
* @uiDefault Slider.focusWidth int
|
||||
* @uiDefault Slider.thumbBorderWidth int or float
|
||||
*
|
||||
* @uiDefault Slider.trackValueColor Color optional; defaults to Slider.thumbColor
|
||||
* @uiDefault Slider.trackColor Color
|
||||
* @uiDefault Slider.thumbColor Color
|
||||
* @uiDefault Slider.thumbBorderColor Color optional; if null, no border is painted
|
||||
* @uiDefault Slider.focusedColor Color optional; defaults to Component.focusColor
|
||||
* @uiDefault Slider.hoverColor Color optional; defaults to Slider.focusedColor
|
||||
* @uiDefault Slider.disabledForeground Color used for track and thumb is disabled
|
||||
* @uiDefault Slider.focusedThumbBorderColor Color optional; defaults to Component.focusedBorderColor
|
||||
* @uiDefault Slider.hoverThumbColor Color optional
|
||||
* @uiDefault Slider.pressedThumbColor Color optional
|
||||
* @uiDefault Slider.disabledTrackColor Color
|
||||
* @uiDefault Slider.disabledThumbColor Color
|
||||
* @uiDefault Slider.disabledThumbBorderColor Color optional; defaults to Component.disabledBorderColor
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
public class FlatSliderUI
|
||||
extends BasicSliderUI
|
||||
implements StyleableUI
|
||||
{
|
||||
private int trackWidth;
|
||||
private int thumbWidth;
|
||||
@Styleable protected int trackWidth;
|
||||
@Styleable protected Dimension thumbSize;
|
||||
@Styleable protected int focusWidth;
|
||||
/** @since 2 */ @Styleable protected float thumbBorderWidth;
|
||||
|
||||
private Color trackColor;
|
||||
private Color thumbColor;
|
||||
private Color focusColor;
|
||||
private Color hoverColor;
|
||||
private Color disabledForeground;
|
||||
@Styleable protected Color trackValueColor;
|
||||
@Styleable protected Color trackColor;
|
||||
@Styleable protected Color thumbColor;
|
||||
@Styleable protected Color thumbBorderColor;
|
||||
protected Color focusBaseColor;
|
||||
@Styleable protected Color focusedColor;
|
||||
@Styleable protected Color focusedThumbBorderColor;
|
||||
@Styleable protected Color hoverThumbColor;
|
||||
@Styleable protected Color pressedThumbColor;
|
||||
@Styleable protected Color disabledTrackColor;
|
||||
@Styleable protected Color disabledThumbColor;
|
||||
@Styleable protected Color disabledThumbBorderColor;
|
||||
@Styleable protected Color tickColor;
|
||||
|
||||
private MouseListener hoverListener;
|
||||
private boolean hover;
|
||||
private Color defaultBackground;
|
||||
private Color defaultForeground;
|
||||
|
||||
protected boolean thumbHover;
|
||||
protected boolean thumbPressed;
|
||||
|
||||
private Object[] oldRenderingHints;
|
||||
private Map<String, Object> oldStyleValues;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatSliderUI();
|
||||
@@ -82,21 +122,10 @@ public class FlatSliderUI
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installListeners( JSlider slider ) {
|
||||
super.installListeners( slider );
|
||||
public void installUI( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
hoverListener = new FlatUIUtils.HoverListener( slider, h -> {
|
||||
hover = h;
|
||||
} );
|
||||
slider.addMouseListener( hoverListener );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void uninstallListeners( JSlider slider ) {
|
||||
super.uninstallListeners( slider );
|
||||
|
||||
slider.removeMouseListener( hoverListener );
|
||||
hoverListener = null;
|
||||
installStyle();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -106,24 +135,125 @@ public class FlatSliderUI
|
||||
LookAndFeel.installProperty( slider, "opaque", false );
|
||||
|
||||
trackWidth = UIManager.getInt( "Slider.trackWidth" );
|
||||
thumbWidth = UIManager.getInt( "Slider.thumbWidth" );
|
||||
thumbSize = UIManager.getDimension( "Slider.thumbSize" );
|
||||
if( thumbSize == null ) {
|
||||
// fallback for compatibility with old versions
|
||||
int thumbWidth = UIManager.getInt( "Slider.thumbWidth" );
|
||||
thumbSize = new Dimension( thumbWidth, thumbWidth );
|
||||
}
|
||||
focusWidth = FlatUIUtils.getUIInt( "Slider.focusWidth", 4 );
|
||||
thumbBorderWidth = FlatUIUtils.getUIFloat( "Slider.thumbBorderWidth", 1 );
|
||||
|
||||
trackValueColor = FlatUIUtils.getUIColor( "Slider.trackValueColor", "Slider.thumbColor" );
|
||||
trackColor = UIManager.getColor( "Slider.trackColor" );
|
||||
thumbColor = UIManager.getColor( "Slider.thumbColor" );
|
||||
focusColor = FlatUIUtils.getUIColor( "Slider.focusedColor", "Component.focusColor" );
|
||||
hoverColor = FlatUIUtils.getUIColor( "Slider.hoverColor", focusColor );
|
||||
disabledForeground = UIManager.getColor( "Slider.disabledForeground" );
|
||||
thumbBorderColor = UIManager.getColor( "Slider.thumbBorderColor" );
|
||||
focusBaseColor = UIManager.getColor( "Component.focusColor" );
|
||||
focusedColor = FlatUIUtils.getUIColor( "Slider.focusedColor", focusBaseColor );
|
||||
focusedThumbBorderColor = FlatUIUtils.getUIColor( "Slider.focusedThumbBorderColor", "Component.focusedBorderColor" );
|
||||
hoverThumbColor = UIManager.getColor( "Slider.hoverThumbColor" );
|
||||
pressedThumbColor = UIManager.getColor( "Slider.pressedThumbColor" );
|
||||
disabledTrackColor = UIManager.getColor( "Slider.disabledTrackColor" );
|
||||
disabledThumbColor = UIManager.getColor( "Slider.disabledThumbColor" );
|
||||
disabledThumbBorderColor = FlatUIUtils.getUIColor( "Slider.disabledThumbBorderColor", "Component.disabledBorderColor" );
|
||||
tickColor = FlatUIUtils.getUIColor( "Slider.tickColor", Color.BLACK ); // see BasicSliderUI.paintTicks()
|
||||
|
||||
defaultBackground = UIManager.getColor( "Slider.background" );
|
||||
defaultForeground = UIManager.getColor( "Slider.foreground" );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void uninstallDefaults( JSlider slider ) {
|
||||
super.uninstallDefaults( slider );
|
||||
|
||||
trackValueColor = null;
|
||||
trackColor = null;
|
||||
thumbColor = null;
|
||||
focusColor = null;
|
||||
hoverColor = null;
|
||||
disabledForeground = null;
|
||||
thumbBorderColor = null;
|
||||
focusBaseColor = null;
|
||||
focusedColor = null;
|
||||
focusedThumbBorderColor = null;
|
||||
hoverThumbColor = null;
|
||||
pressedThumbColor = null;
|
||||
disabledTrackColor = null;
|
||||
disabledThumbColor = null;
|
||||
disabledThumbBorderColor = null;
|
||||
tickColor = null;
|
||||
|
||||
defaultBackground = null;
|
||||
defaultForeground = null;
|
||||
|
||||
oldStyleValues = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TrackListener createTrackListener( JSlider slider ) {
|
||||
return new FlatTrackListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PropertyChangeListener createPropertyChangeListener( JSlider slider ) {
|
||||
return FlatStylingSupport.createPropertyChangeListener( slider, this::installStyle,
|
||||
super.createPropertyChangeListener( slider ) );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void installStyle() {
|
||||
try {
|
||||
applyStyle( FlatStylingSupport.getResolvedStyle( slider, "Slider" ) );
|
||||
} catch( RuntimeException ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void applyStyle( Object style ) {
|
||||
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected Object applyStyleProperty( String key, Object value ) {
|
||||
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, slider, key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBaseline( JComponent c, int width, int height ) {
|
||||
if( c == null )
|
||||
throw new NullPointerException();
|
||||
if( width < 0 || height < 0 )
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
// no baseline for vertical orientation
|
||||
if( slider.getOrientation() == JSlider.VERTICAL )
|
||||
return -1;
|
||||
|
||||
// use default font (instead of slider font) because the slider font size
|
||||
// may be different to label font size, but we want align the track/thumb with labels
|
||||
Font font = UIManager.getFont( "defaultFont" );
|
||||
if( font == null )
|
||||
font = slider.getFont();
|
||||
FontMetrics fm = slider.getFontMetrics( font );
|
||||
|
||||
// calculate track y coordinate and height
|
||||
// (not using field trackRect here because slider size may be [0,0]
|
||||
// and field trackRect may have invalid values in this case)
|
||||
Insets insets = slider.getInsets();
|
||||
int thumbHeight = getThumbSize().height;
|
||||
int contentHeight = height - insets.top - insets.bottom - focusInsets.top - focusInsets.bottom;
|
||||
int centerSpacing = thumbHeight
|
||||
+ (slider.getPaintTicks() ? getTickLength() : 0)
|
||||
+ (slider.getPaintLabels() ? getHeightOfTallestLabel() : 0);
|
||||
int trackY = insets.top + focusInsets.top + (contentHeight - centerSpacing - 1) / 2;
|
||||
int trackHeight = thumbHeight;
|
||||
|
||||
// compute a baseline so that the track is vertically centered
|
||||
return trackY + Math.round( (trackHeight - fm.getHeight()) / 2f ) + fm.getAscent() - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -153,14 +283,50 @@ public class FlatSliderUI
|
||||
|
||||
@Override
|
||||
protected Dimension getThumbSize() {
|
||||
return new Dimension( UIScale.scale( thumbWidth ), UIScale.scale( thumbWidth ) );
|
||||
return calcThumbSize( slider, thumbSize, focusWidth );
|
||||
}
|
||||
|
||||
public static Dimension calcThumbSize( JSlider slider, Dimension thumbSize, int focusWidth ) {
|
||||
int fw = UIScale.scale( focusWidth );
|
||||
int w = UIScale.scale( thumbSize.width ) + fw + fw;
|
||||
int h = UIScale.scale( thumbSize.height ) + fw + fw;
|
||||
return (slider.getOrientation() == JSlider.HORIZONTAL)
|
||||
? new Dimension( w, h )
|
||||
: new Dimension( h, w );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint( Graphics g, JComponent c ) {
|
||||
FlatUIUtils.setRenderingHints( (Graphics2D) g );
|
||||
oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
||||
|
||||
/*debug
|
||||
g.setColor( Color.gray );
|
||||
g.drawRect( 0, 0, c.getWidth() - 1, c.getHeight() - 1 );
|
||||
g.setColor( Color.orange );
|
||||
g.drawRect( focusRect.x, focusRect.y, focusRect.width - 1, focusRect.height - 1 );
|
||||
g.setColor( Color.magenta );
|
||||
g.drawRect( contentRect.x, contentRect.y, contentRect.width - 1, contentRect.height - 1 );
|
||||
g.setColor( Color.blue );
|
||||
g.drawRect( trackRect.x, trackRect.y, trackRect.width - 1, trackRect.height - 1 );
|
||||
g.setColor( Color.red );
|
||||
g.drawRect( thumbRect.x, thumbRect.y, thumbRect.width - 1, thumbRect.height - 1 );
|
||||
g.setColor( Color.green );
|
||||
g.drawRect( tickRect.x, tickRect.y, tickRect.width - 1, tickRect.height - 1 );
|
||||
g.setColor( Color.red );
|
||||
g.drawRect( labelRect.x, labelRect.y, labelRect.width - 1, labelRect.height - 1 );
|
||||
debug*/
|
||||
|
||||
super.paint( g, c );
|
||||
|
||||
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||
oldRenderingHints = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintLabels( Graphics g ) {
|
||||
FlatUIUtils.runWithoutRenderingHints( g, oldRenderingHints, () -> {
|
||||
super.paintLabels( g );
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -201,50 +367,341 @@ public class FlatSliderUI
|
||||
}
|
||||
|
||||
if( coloredTrack != null ) {
|
||||
g.setColor( FlatUIUtils.deriveColor( FlatUIUtils.isPermanentFocusOwner( slider ) ? focusColor : (hover ? hoverColor : thumbColor), thumbColor ) );
|
||||
if( slider.getInverted() ) {
|
||||
RoundRectangle2D temp = track;
|
||||
track = coloredTrack;
|
||||
coloredTrack = temp;
|
||||
}
|
||||
|
||||
g.setColor( getTrackValueColor() );
|
||||
((Graphics2D)g).fill( coloredTrack );
|
||||
}
|
||||
|
||||
g.setColor( enabled ? trackColor : disabledForeground );
|
||||
g.setColor( enabled ? getTrackColor() : disabledTrackColor );
|
||||
((Graphics2D)g).fill( track );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintTicks( Graphics g ) {
|
||||
// because BasicSliderUI.paintTicks() always uses
|
||||
// g.setColor( UIManager.getColor("Slider.tickColor") )
|
||||
// we override this method and use our tickColor field to allow styling
|
||||
super.paintTicks( new Graphics2DProxy( (Graphics2D) g ) {
|
||||
@Override
|
||||
public void setColor( Color c ) {
|
||||
super.setColor( tickColor );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintThumb( Graphics g ) {
|
||||
g.setColor( FlatUIUtils.deriveColor( slider.isEnabled()
|
||||
? (FlatUIUtils.isPermanentFocusOwner( slider ) ? focusColor : (hover ? hoverColor : thumbColor))
|
||||
: disabledForeground,
|
||||
thumbColor ) );
|
||||
Color thumbColor = getThumbColor();
|
||||
Color color = stateColor( slider, thumbHover, thumbPressed,
|
||||
thumbColor, disabledThumbColor, null, hoverThumbColor, pressedThumbColor );
|
||||
color = FlatUIUtils.deriveColor( color, thumbColor );
|
||||
|
||||
if( isRoundThumb() )
|
||||
g.fillOval( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height );
|
||||
else {
|
||||
double w = thumbRect.width;
|
||||
double h = thumbRect.height;
|
||||
double wh = w / 2;
|
||||
Color foreground = slider.getForeground();
|
||||
Color borderColor = (thumbBorderColor != null && foreground == defaultForeground)
|
||||
? stateColor( slider, false, false, thumbBorderColor, disabledThumbBorderColor, focusedThumbBorderColor, null, null )
|
||||
: null;
|
||||
|
||||
Path2D thumb = FlatUIUtils.createPath( 0,0, w,0, w,(h - wh), wh,h, 0,(h - wh) );
|
||||
Color focusedColor = FlatUIUtils.deriveColor( this.focusedColor,
|
||||
(foreground != defaultForeground) ? foreground : focusBaseColor );
|
||||
|
||||
paintThumb( g, slider, thumbRect, isRoundThumb(), color, borderColor, focusedColor, thumbBorderWidth, focusWidth );
|
||||
}
|
||||
|
||||
public static void paintThumb( Graphics g, JSlider slider, Rectangle thumbRect, boolean roundThumb,
|
||||
Color thumbColor, Color thumbBorderColor, Color focusedColor, float thumbBorderWidth, int focusWidth )
|
||||
{
|
||||
double systemScaleFactor = UIScale.getSystemScaleFactor( (Graphics2D) g );
|
||||
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
|
||||
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
|
||||
HiDPIUtils.paintAtScale1x( (Graphics2D) g, thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height,
|
||||
(g2d, x2, y2, width2, height2, scaleFactor) -> {
|
||||
paintThumbImpl( g, slider, x2, y2, width2, height2,
|
||||
roundThumb, thumbColor, thumbBorderColor, focusedColor,
|
||||
(float) (thumbBorderWidth * scaleFactor),
|
||||
(float) (focusWidth * scaleFactor) );
|
||||
} );
|
||||
return;
|
||||
}
|
||||
|
||||
paintThumbImpl( g, slider, thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height,
|
||||
roundThumb, thumbColor, thumbBorderColor, focusedColor, thumbBorderWidth, focusWidth );
|
||||
|
||||
}
|
||||
|
||||
private static void paintThumbImpl( Graphics g, JSlider slider, int x, int y, int width, int height,
|
||||
boolean roundThumb, Color thumbColor, Color thumbBorderColor, Color focusedColor,
|
||||
float thumbBorderWidth, float focusWidth )
|
||||
{
|
||||
int fw = Math.round( UIScale.scale( focusWidth ) );
|
||||
int tx = x + fw;
|
||||
int ty = y + fw;
|
||||
int tw = width - fw - fw;
|
||||
int th = height - fw - fw;
|
||||
boolean focused = FlatUIUtils.isPermanentFocusOwner( slider );
|
||||
|
||||
if( roundThumb ) {
|
||||
// paint thumb focus border
|
||||
if( focused ) {
|
||||
g.setColor( focusedColor );
|
||||
((Graphics2D)g).fill( createRoundThumbShape( x, y, width, height ) );
|
||||
}
|
||||
|
||||
if( thumbBorderColor != null ) {
|
||||
// paint thumb border
|
||||
g.setColor( thumbBorderColor );
|
||||
((Graphics2D)g).fill( createRoundThumbShape( tx, ty, tw, th ) );
|
||||
|
||||
// paint thumb background
|
||||
float lw = UIScale.scale( thumbBorderWidth );
|
||||
g.setColor( thumbColor );
|
||||
((Graphics2D)g).fill( createRoundThumbShape( tx + lw, ty + lw,
|
||||
tw - lw - lw, th - lw - lw ) );
|
||||
} else {
|
||||
// paint thumb background
|
||||
g.setColor( thumbColor );
|
||||
((Graphics2D)g).fill( createRoundThumbShape( tx, ty, tw, th ) );
|
||||
}
|
||||
} else {
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
try {
|
||||
g2.translate( thumbRect.x, thumbRect.y );
|
||||
g2.translate( x, y );
|
||||
if( slider.getOrientation() == JSlider.VERTICAL ) {
|
||||
if( slider.getComponentOrientation().isLeftToRight() ) {
|
||||
g2.translate( 0, thumbRect.height );
|
||||
g2.translate( 0, height );
|
||||
g2.rotate( Math.toRadians( 270 ) );
|
||||
} else {
|
||||
g2.translate( thumbRect.width, 0 );
|
||||
g2.translate( width, 0 );
|
||||
g2.rotate( Math.toRadians( 90 ) );
|
||||
}
|
||||
|
||||
// rotate thumb width/height
|
||||
int temp = tw;
|
||||
tw = th;
|
||||
th = temp;
|
||||
}
|
||||
|
||||
// paint thumb focus border
|
||||
if( focused ) {
|
||||
g2.setColor( focusedColor );
|
||||
g2.fill( createDirectionalThumbShape( 0, 0,
|
||||
tw + fw + fw, th + fw + fw + (fw * 0.4142f), fw ) );
|
||||
}
|
||||
|
||||
if( thumbBorderColor != null ) {
|
||||
// paint thumb border
|
||||
g2.setColor( thumbBorderColor );
|
||||
g2.fill( createDirectionalThumbShape( fw, fw, tw, th, 0 ) );
|
||||
|
||||
// paint thumb background
|
||||
float lw = UIScale.scale( thumbBorderWidth );
|
||||
g2.setColor( thumbColor );
|
||||
g2.fill( createDirectionalThumbShape( fw + lw, fw + lw,
|
||||
tw - lw - lw, th - lw - lw - (lw * 0.4142f), 0 ) );
|
||||
} else {
|
||||
// paint thumb background
|
||||
g2.setColor( thumbColor );
|
||||
g2.fill( createDirectionalThumbShape( fw, fw, tw, th, 0 ) );
|
||||
}
|
||||
g2.fill( thumb );
|
||||
} finally {
|
||||
g2.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRoundThumb() {
|
||||
public static Shape createRoundThumbShape( float x, float y, float w, float h ) {
|
||||
if( w == h )
|
||||
return new Ellipse2D.Float( x, y, w, h );
|
||||
else {
|
||||
float arc = Math.min( w, h );
|
||||
return new RoundRectangle2D.Float( x, y, w, h, arc, arc );
|
||||
}
|
||||
}
|
||||
|
||||
public static Shape createDirectionalThumbShape( float x, float y, float w, float h, float arc ) {
|
||||
float wh = w / 2;
|
||||
|
||||
Path2D path = new Path2D.Float();
|
||||
path.moveTo( x + wh, y + h );
|
||||
path.lineTo( x, y + (h - wh) );
|
||||
path.lineTo( x, y + arc );
|
||||
path.quadTo( x, y, x + arc, y );
|
||||
path.lineTo( x + (w - arc), y );
|
||||
path.quadTo( x + w, y, x + w, y + arc );
|
||||
path.lineTo( x + w, y + (h - wh) );
|
||||
path.closePath();
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
protected Color getTrackValueColor() {
|
||||
Color foreground = slider.getForeground();
|
||||
return (foreground != defaultForeground) ? foreground : trackValueColor;
|
||||
}
|
||||
|
||||
protected Color getTrackColor() {
|
||||
Color backround = slider.getBackground();
|
||||
return (backround != defaultBackground) ? backround : trackColor;
|
||||
}
|
||||
|
||||
protected Color getThumbColor() {
|
||||
Color foreground = slider.getForeground();
|
||||
return (foreground != defaultForeground) ? foreground : thumbColor;
|
||||
}
|
||||
|
||||
public static Color stateColor( JSlider slider, boolean hover, boolean pressed,
|
||||
Color enabledColor, Color disabledColor, Color focusedColor, Color hoverColor, Color pressedColor )
|
||||
{
|
||||
if( disabledColor != null && !slider.isEnabled() )
|
||||
return disabledColor;
|
||||
if( pressedColor != null && pressed )
|
||||
return pressedColor;
|
||||
if( hoverColor != null && hover )
|
||||
return hoverColor;
|
||||
if( focusedColor != null && FlatUIUtils.isPermanentFocusOwner( slider ) )
|
||||
return focusedColor;
|
||||
return enabledColor;
|
||||
}
|
||||
|
||||
protected boolean isRoundThumb() {
|
||||
return !slider.getPaintTicks() && !slider.getPaintLabels();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setThumbLocation( int x, int y ) {
|
||||
if( !isRoundThumb() ) {
|
||||
// the needle of the directional thumb is painted outside of thumbRect
|
||||
// --> must increase repaint rectangle
|
||||
|
||||
// set new thumb location and compute union of old and new thumb bounds
|
||||
Rectangle r = new Rectangle( thumbRect );
|
||||
thumbRect.setLocation( x, y );
|
||||
SwingUtilities.computeUnion( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, r );
|
||||
|
||||
// increase union rectangle for repaint
|
||||
int extra = (int) Math.ceil( UIScale.scale( focusWidth ) * 0.4142f );
|
||||
if( slider.getOrientation() == JSlider.HORIZONTAL )
|
||||
r.height += extra;
|
||||
else {
|
||||
r.width += extra;
|
||||
if( !slider.getComponentOrientation().isLeftToRight() )
|
||||
r.x -= extra;
|
||||
}
|
||||
|
||||
slider.repaint( r );
|
||||
} else
|
||||
super.setThumbLocation( x, y );
|
||||
}
|
||||
|
||||
//---- class FlatTrackListener --------------------------------------------
|
||||
|
||||
protected class FlatTrackListener
|
||||
extends TrackListener
|
||||
{
|
||||
@Override
|
||||
public void mouseEntered( MouseEvent e ) {
|
||||
setThumbHover( isOverThumb( e ) );
|
||||
super.mouseEntered( e );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited( MouseEvent e ) {
|
||||
setThumbHover( false );
|
||||
super.mouseExited( e );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseMoved( MouseEvent e ) {
|
||||
setThumbHover( isOverThumb( e ) );
|
||||
super.mouseMoved( e );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed( MouseEvent e ) {
|
||||
setThumbPressed( isOverThumb( e ) );
|
||||
|
||||
if( !slider.isEnabled() )
|
||||
return;
|
||||
|
||||
// use "old" behavior when clicking on track
|
||||
if( UIManager.getBoolean( "Slider.scrollOnTrackClick" ) ) {
|
||||
super.mousePressed( e );
|
||||
return;
|
||||
}
|
||||
|
||||
// "new" behavior set thumb to mouse location when clicking on track
|
||||
|
||||
int x = e.getX();
|
||||
int y = e.getY();
|
||||
|
||||
// clicked on thumb --> let super class do the work
|
||||
calculateGeometry();
|
||||
if( thumbRect.contains( x, y ) ) {
|
||||
super.mousePressed( e );
|
||||
return;
|
||||
}
|
||||
|
||||
if( UIManager.getBoolean( "Slider.onlyLeftMouseButtonDrag" ) &&
|
||||
!SwingUtilities.isLeftMouseButton( e ) )
|
||||
return;
|
||||
|
||||
// move the mouse event coordinates to the center of the thumb
|
||||
int tx = thumbRect.x + (thumbRect.width / 2) - x;
|
||||
int ty = thumbRect.y + (thumbRect.height / 2) - y;
|
||||
e.translatePoint( tx, ty );
|
||||
|
||||
// invoke super mousePressed() to start dragging thumb
|
||||
super.mousePressed( e );
|
||||
|
||||
// move the mouse event coordinates back to current mouse location
|
||||
e.translatePoint( -tx, -ty );
|
||||
|
||||
// invoke mouseDragged() to update thumb location
|
||||
mouseDragged( e );
|
||||
|
||||
setThumbPressed( true );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased( MouseEvent e ) {
|
||||
setThumbPressed( false );
|
||||
super.mouseReleased( e );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged( MouseEvent e ) {
|
||||
super.mouseDragged( e );
|
||||
|
||||
if( isDragging() &&
|
||||
slider.getSnapToTicks() &&
|
||||
slider.isEnabled() &&
|
||||
!UIManager.getBoolean( "Slider.snapToTicksOnReleased" ) )
|
||||
{
|
||||
calculateThumbLocation();
|
||||
slider.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
protected void setThumbHover( boolean hover ) {
|
||||
if( hover != thumbHover ) {
|
||||
thumbHover = hover;
|
||||
slider.repaint( thumbRect );
|
||||
}
|
||||
}
|
||||
|
||||
protected void setThumbPressed( boolean pressed ) {
|
||||
if( pressed != thumbPressed ) {
|
||||
thumbPressed = pressed;
|
||||
slider.repaint( thumbRect );
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isOverThumb( MouseEvent e ) {
|
||||
return e != null && slider.isEnabled() && thumbRect.contains( e.getX(), e.getY() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Insets;
|
||||
@@ -32,6 +33,8 @@ import java.awt.event.FocusListener;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JSpinner;
|
||||
import javax.swing.JTextField;
|
||||
@@ -39,8 +42,12 @@ import javax.swing.LookAndFeel;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.UIResource;
|
||||
import javax.swing.plaf.basic.BasicSpinnerUI;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
|
||||
/**
|
||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JSpinner}.
|
||||
@@ -59,43 +66,60 @@ import com.formdev.flatlaf.FlatClientProperties;
|
||||
*
|
||||
* @uiDefault Component.minimumWidth int
|
||||
* @uiDefault Spinner.buttonStyle String button (default) or none
|
||||
* @uiDefault Component.arrowType String triangle (default) or chevron
|
||||
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||
* @uiDefault Component.isIntelliJTheme boolean
|
||||
* @uiDefault Component.borderColor Color
|
||||
* @uiDefault Component.disabledBorderColor Color
|
||||
* @uiDefault Spinner.disabledBackground Color
|
||||
* @uiDefault Spinner.disabledForeground Color
|
||||
* @uiDefault Spinner.focusedBackground Color optional
|
||||
* @uiDefault Spinner.buttonBackground Color
|
||||
* @uiDefault Spinner.buttonSeparatorWidth int or float optional; defaults to Component.borderWidth
|
||||
* @uiDefault Spinner.buttonSeparatorColor Color optional
|
||||
* @uiDefault Spinner.buttonDisabledSeparatorColor Color optional
|
||||
* @uiDefault Spinner.buttonArrowColor Color
|
||||
* @uiDefault Spinner.buttonDisabledArrowColor Color
|
||||
* @uiDefault Spinner.buttonHoverArrowColor Color
|
||||
* @uiDefault Spinner.buttonPressedArrowColor Color
|
||||
* @uiDefault Spinner.padding Insets
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
public class FlatSpinnerUI
|
||||
extends BasicSpinnerUI
|
||||
implements StyleableUI
|
||||
{
|
||||
private Handler handler;
|
||||
|
||||
protected int minimumWidth;
|
||||
protected String buttonStyle;
|
||||
protected String arrowType;
|
||||
@Styleable protected int minimumWidth;
|
||||
@Styleable protected String buttonStyle;
|
||||
@Styleable protected String arrowType;
|
||||
protected boolean isIntelliJTheme;
|
||||
protected Color borderColor;
|
||||
protected Color disabledBorderColor;
|
||||
protected Color disabledBackground;
|
||||
protected Color disabledForeground;
|
||||
protected Color buttonBackground;
|
||||
protected Color buttonArrowColor;
|
||||
protected Color buttonDisabledArrowColor;
|
||||
protected Color buttonHoverArrowColor;
|
||||
protected Insets padding;
|
||||
@Styleable protected Color disabledBackground;
|
||||
@Styleable protected Color disabledForeground;
|
||||
@Styleable protected Color focusedBackground;
|
||||
@Styleable protected Color buttonBackground;
|
||||
/** @since 2 */ @Styleable protected float buttonSeparatorWidth;
|
||||
/** @since 2 */ @Styleable protected Color buttonSeparatorColor;
|
||||
/** @since 2 */ @Styleable protected Color buttonDisabledSeparatorColor;
|
||||
@Styleable protected Color buttonArrowColor;
|
||||
@Styleable protected Color buttonDisabledArrowColor;
|
||||
@Styleable protected Color buttonHoverArrowColor;
|
||||
@Styleable protected Color buttonPressedArrowColor;
|
||||
@Styleable protected Insets padding;
|
||||
|
||||
private Map<String, Object> oldStyleValues;
|
||||
private AtomicBoolean borderShared;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatSpinnerUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installUI( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
installStyle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installDefaults() {
|
||||
super.installDefaults();
|
||||
@@ -106,19 +130,19 @@ public class FlatSpinnerUI
|
||||
buttonStyle = UIManager.getString( "Spinner.buttonStyle" );
|
||||
arrowType = UIManager.getString( "Component.arrowType" );
|
||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
||||
borderColor = UIManager.getColor( "Component.borderColor" );
|
||||
disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
|
||||
disabledBackground = UIManager.getColor( "Spinner.disabledBackground" );
|
||||
disabledForeground = UIManager.getColor( "Spinner.disabledForeground" );
|
||||
focusedBackground = UIManager.getColor( "Spinner.focusedBackground" );
|
||||
buttonBackground = UIManager.getColor( "Spinner.buttonBackground" );
|
||||
buttonSeparatorWidth = FlatUIUtils.getUIFloat( "Spinner.buttonSeparatorWidth", FlatUIUtils.getUIFloat( "Component.borderWidth", 1 ) );
|
||||
buttonSeparatorColor = UIManager.getColor( "Spinner.buttonSeparatorColor" );
|
||||
buttonDisabledSeparatorColor = UIManager.getColor( "Spinner.buttonDisabledSeparatorColor" );
|
||||
buttonArrowColor = UIManager.getColor( "Spinner.buttonArrowColor" );
|
||||
buttonDisabledArrowColor = UIManager.getColor( "Spinner.buttonDisabledArrowColor" );
|
||||
buttonHoverArrowColor = UIManager.getColor( "Spinner.buttonHoverArrowColor" );
|
||||
buttonPressedArrowColor = UIManager.getColor( "Spinner.buttonPressedArrowColor" );
|
||||
padding = UIManager.getInsets( "Spinner.padding" );
|
||||
|
||||
// scale
|
||||
padding = scale( padding );
|
||||
|
||||
MigLayoutVisualPadding.install( spinner );
|
||||
}
|
||||
|
||||
@@ -126,16 +150,21 @@ public class FlatSpinnerUI
|
||||
protected void uninstallDefaults() {
|
||||
super.uninstallDefaults();
|
||||
|
||||
borderColor = null;
|
||||
disabledBorderColor = null;
|
||||
disabledBackground = null;
|
||||
disabledForeground = null;
|
||||
focusedBackground = null;
|
||||
buttonBackground = null;
|
||||
buttonSeparatorColor = null;
|
||||
buttonDisabledSeparatorColor = null;
|
||||
buttonArrowColor = null;
|
||||
buttonDisabledArrowColor = null;
|
||||
buttonHoverArrowColor = null;
|
||||
buttonPressedArrowColor = null;
|
||||
padding = null;
|
||||
|
||||
oldStyleValues = null;
|
||||
borderShared = null;
|
||||
|
||||
MigLayoutVisualPadding.uninstall( spinner );
|
||||
}
|
||||
|
||||
@@ -165,17 +194,39 @@ public class FlatSpinnerUI
|
||||
return handler;
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void installStyle() {
|
||||
try {
|
||||
applyStyle( FlatStylingSupport.getResolvedStyle( spinner, "Spinner" ) );
|
||||
} catch( RuntimeException ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void applyStyle( Object style ) {
|
||||
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||
updateEditorPadding();
|
||||
updateArrowButtonsStyle();
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected Object applyStyleProperty( String key, Object value ) {
|
||||
if( borderShared == null )
|
||||
borderShared = new AtomicBoolean( true );
|
||||
return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, spinner, borderShared );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this, spinner.getBorder() );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent createEditor() {
|
||||
JComponent editor = super.createEditor();
|
||||
|
||||
// explicitly make non-opaque
|
||||
editor.setOpaque( false );
|
||||
JTextField textField = getEditorTextField( editor );
|
||||
if( textField != null )
|
||||
textField.setOpaque( false );
|
||||
|
||||
updateEditorColors();
|
||||
configureEditor( editor );
|
||||
return editor;
|
||||
}
|
||||
|
||||
@@ -183,8 +234,21 @@ public class FlatSpinnerUI
|
||||
protected void replaceEditor( JComponent oldEditor, JComponent newEditor ) {
|
||||
super.replaceEditor( oldEditor, newEditor );
|
||||
|
||||
configureEditor( newEditor );
|
||||
|
||||
removeEditorFocusListener( oldEditor );
|
||||
addEditorFocusListener( newEditor );
|
||||
}
|
||||
|
||||
/** @since 1.6 */
|
||||
protected void configureEditor( JComponent editor ) {
|
||||
// explicitly make non-opaque
|
||||
editor.setOpaque( false );
|
||||
JTextField textField = getEditorTextField( editor );
|
||||
if( textField != null )
|
||||
textField.setOpaque( false );
|
||||
|
||||
updateEditorPadding();
|
||||
updateEditorColors();
|
||||
}
|
||||
|
||||
@@ -200,6 +264,12 @@ public class FlatSpinnerUI
|
||||
textField.removeFocusListener( getHandler() );
|
||||
}
|
||||
|
||||
private void updateEditorPadding() {
|
||||
JTextField textField = getEditorTextField( spinner.getEditor() );
|
||||
if( textField != null )
|
||||
textField.putClientProperty( FlatClientProperties.TEXT_FIELD_PADDING, padding );
|
||||
}
|
||||
|
||||
private void updateEditorColors() {
|
||||
JTextField textField = getEditorTextField( spinner.getEditor() );
|
||||
if( textField != null ) {
|
||||
@@ -217,10 +287,32 @@ public class FlatSpinnerUI
|
||||
: null;
|
||||
}
|
||||
|
||||
/** @since 1.3 */
|
||||
public static boolean isPermanentFocusOwner( JSpinner spinner ) {
|
||||
if( FlatUIUtils.isPermanentFocusOwner( spinner ) )
|
||||
return true;
|
||||
|
||||
JTextField textField = getEditorTextField( spinner.getEditor() );
|
||||
return (textField != null)
|
||||
? FlatUIUtils.isPermanentFocusOwner( textField )
|
||||
: false;
|
||||
}
|
||||
|
||||
protected Color getBackground( boolean enabled ) {
|
||||
return enabled
|
||||
? spinner.getBackground()
|
||||
: (isIntelliJTheme ? FlatUIUtils.getParentBackground( spinner ) : disabledBackground);
|
||||
if( enabled ) {
|
||||
Color background = spinner.getBackground();
|
||||
|
||||
// always use explicitly set color
|
||||
if( !(background instanceof UIResource) )
|
||||
return background;
|
||||
|
||||
// focused
|
||||
if( focusedBackground != null && isPermanentFocusOwner( spinner ) )
|
||||
return focusedBackground;
|
||||
|
||||
return background;
|
||||
} else
|
||||
return isIntelliJTheme ? FlatUIUtils.getParentBackground( spinner ) : disabledBackground;
|
||||
}
|
||||
|
||||
protected Color getForeground( boolean enabled ) {
|
||||
@@ -244,9 +336,9 @@ public class FlatSpinnerUI
|
||||
|
||||
private Component createArrowButton( int direction, String name ) {
|
||||
FlatArrowButton button = new FlatArrowButton( direction, arrowType, buttonArrowColor,
|
||||
buttonDisabledArrowColor, buttonHoverArrowColor, null );
|
||||
buttonDisabledArrowColor, buttonHoverArrowColor, null, buttonPressedArrowColor, null );
|
||||
button.setName( name );
|
||||
button.setYOffset( (direction == SwingConstants.NORTH) ? 1 : -1 );
|
||||
button.setYOffset( (direction == SwingConstants.NORTH) ? 1.25f : -1.25f );
|
||||
if( direction == SwingConstants.NORTH )
|
||||
installNextButtonListeners( button );
|
||||
else
|
||||
@@ -254,6 +346,15 @@ public class FlatSpinnerUI
|
||||
return button;
|
||||
}
|
||||
|
||||
private void updateArrowButtonsStyle() {
|
||||
for( Component c : spinner.getComponents() ) {
|
||||
if( c instanceof FlatArrowButton ) {
|
||||
((FlatArrowButton)c).updateStyle( arrowType, buttonArrowColor,
|
||||
buttonDisabledArrowColor, buttonHoverArrowColor, null, buttonPressedArrowColor, null );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update( Graphics g, JComponent c ) {
|
||||
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
|
||||
@@ -264,42 +365,50 @@ public class FlatSpinnerUI
|
||||
FlatUIUtils.paintParentBackground( g, c );
|
||||
|
||||
Graphics2D g2 = (Graphics2D) g;
|
||||
FlatUIUtils.setRenderingHints( g2 );
|
||||
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g2 );
|
||||
|
||||
int width = c.getWidth();
|
||||
int height = c.getHeight();
|
||||
Component nextButton = getHandler().nextButton;
|
||||
int arrowX = nextButton.getX();
|
||||
int arrowWidth = nextButton.getWidth();
|
||||
boolean paintButton = !"none".equals( buttonStyle );
|
||||
boolean enabled = spinner.isEnabled();
|
||||
boolean isLeftToRight = spinner.getComponentOrientation().isLeftToRight();
|
||||
|
||||
// paint background
|
||||
g2.setColor( getBackground( enabled ) );
|
||||
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
|
||||
|
||||
// paint arrow buttons background
|
||||
if( paintButton && enabled ) {
|
||||
g2.setColor( buttonBackground );
|
||||
Shape oldClip = g2.getClip();
|
||||
if( isLeftToRight )
|
||||
g2.clipRect( arrowX, 0, width - arrowX, height );
|
||||
else
|
||||
g2.clipRect( 0, 0, arrowX + arrowWidth, height );
|
||||
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
|
||||
g2.setClip( oldClip );
|
||||
}
|
||||
// paint button background and separator
|
||||
boolean paintButton = !"none".equals( buttonStyle );
|
||||
Handler handler = getHandler();
|
||||
if( paintButton && (handler.nextButton != null || handler.previousButton != null) ) {
|
||||
Component button = (handler.nextButton != null) ? handler.nextButton : handler.previousButton;
|
||||
int arrowX = button.getX();
|
||||
int arrowWidth = button.getWidth();
|
||||
boolean isLeftToRight = spinner.getComponentOrientation().isLeftToRight();
|
||||
|
||||
// paint vertical line between value and arrow buttons
|
||||
if( paintButton ) {
|
||||
g2.setColor( enabled ? borderColor : disabledBorderColor );
|
||||
float lw = scale( 1f );
|
||||
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
|
||||
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2) ) );
|
||||
// paint arrow buttons background
|
||||
if( enabled ) {
|
||||
g2.setColor( buttonBackground );
|
||||
Shape oldClip = g2.getClip();
|
||||
if( isLeftToRight )
|
||||
g2.clipRect( arrowX, 0, width - arrowX, height );
|
||||
else
|
||||
g2.clipRect( 0, 0, arrowX + arrowWidth, height );
|
||||
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
|
||||
g2.setClip( oldClip );
|
||||
}
|
||||
|
||||
// paint vertical line between value and arrow buttons
|
||||
Color separatorColor = enabled ? buttonSeparatorColor : buttonDisabledSeparatorColor;
|
||||
if( separatorColor != null ) {
|
||||
g2.setColor( separatorColor );
|
||||
float lw = scale( buttonSeparatorWidth );
|
||||
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
|
||||
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2) ) );
|
||||
}
|
||||
}
|
||||
|
||||
paint( g, c );
|
||||
|
||||
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||
}
|
||||
|
||||
//---- class Handler ------------------------------------------------------
|
||||
@@ -335,6 +444,7 @@ public class FlatSpinnerUI
|
||||
@Override
|
||||
public Dimension preferredLayoutSize( Container parent ) {
|
||||
Insets insets = parent.getInsets();
|
||||
Insets padding = scale( FlatSpinnerUI.this.padding );
|
||||
Dimension editorSize = (editor != null) ? editor.getPreferredSize() : new Dimension( 0, 0 );
|
||||
|
||||
// the arrows width is the same as the inner height so that the arrows area is square
|
||||
@@ -366,8 +476,12 @@ public class FlatSpinnerUI
|
||||
Rectangle editorRect = new Rectangle( r );
|
||||
Rectangle buttonsRect = new Rectangle( r );
|
||||
|
||||
// make button area square
|
||||
int buttonsWidth = r.height;
|
||||
// limit buttons width to height of a raw spinner (without insets)
|
||||
FontMetrics fm = spinner.getFontMetrics( spinner.getFont() );
|
||||
int maxButtonWidth = fm.getHeight() + scale( padding.top ) + scale( padding.bottom );
|
||||
|
||||
// make button area square (if spinner has preferred height)
|
||||
int buttonsWidth = Math.min( parent.getPreferredSize().height - insets.top - insets.bottom, maxButtonWidth );
|
||||
buttonsRect.width = buttonsWidth;
|
||||
|
||||
if( parent.getComponentOrientation().isLeftToRight() ) {
|
||||
@@ -379,7 +493,7 @@ public class FlatSpinnerUI
|
||||
}
|
||||
|
||||
if( editor != null )
|
||||
editor.setBounds( FlatUIUtils.subtractInsets( editorRect, padding ) );
|
||||
editor.setBounds( editorRect );
|
||||
|
||||
int nextHeight = (buttonsRect.height / 2) + (buttonsRect.height % 2); // round up
|
||||
if( nextButton != null )
|
||||
@@ -396,6 +510,7 @@ public class FlatSpinnerUI
|
||||
|
||||
@Override
|
||||
public void focusGained( FocusEvent e ) {
|
||||
// necessary to update focus border
|
||||
spinner.repaint();
|
||||
|
||||
// if spinner gained focus, transfer it to the editor text field
|
||||
@@ -408,6 +523,7 @@ public class FlatSpinnerUI
|
||||
|
||||
@Override
|
||||
public void focusLost( FocusEvent e ) {
|
||||
// necessary to update focus border
|
||||
spinner.repaint();
|
||||
}
|
||||
|
||||
@@ -428,6 +544,13 @@ public class FlatSpinnerUI
|
||||
case FlatClientProperties.MINIMUM_WIDTH:
|
||||
spinner.revalidate();
|
||||
break;
|
||||
|
||||
case FlatClientProperties.STYLE:
|
||||
case FlatClientProperties.STYLE_CLASS:
|
||||
installStyle();
|
||||
spinner.revalidate();
|
||||
spinner.repaint();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,15 +17,27 @@
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Container;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Insets;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Map;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.ToolTipManager;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.basic.BasicSplitPaneDivider;
|
||||
import javax.swing.plaf.basic.BasicSplitPaneUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
/**
|
||||
@@ -40,27 +52,47 @@ import com.formdev.flatlaf.util.UIScale;
|
||||
* @uiDefault SplitPaneDivider.border Border
|
||||
* @uiDefault SplitPaneDivider.draggingColor Color only used if continuousLayout is false
|
||||
*
|
||||
* <!-- JSplitPane -->
|
||||
*
|
||||
* @uiDefault SplitPane.continuousLayout boolean
|
||||
*
|
||||
* <!-- FlatSplitPaneUI -->
|
||||
*
|
||||
* @uiDefault Component.arrowType String triangle (default) or chevron
|
||||
* @uiDefault SplitPane.continuousLayout boolean
|
||||
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||
* @uiDefault SplitPaneDivider.oneTouchArrowColor Color
|
||||
* @uiDefault SplitPaneDivider.oneTouchHoverArrowColor Color
|
||||
* @uiDefault SplitPaneDivider.oneTouchPressedArrowColor Color
|
||||
* @uiDefault SplitPaneDivider.style String grip (default) or plain
|
||||
* @uiDefault SplitPaneDivider.gripColor Color
|
||||
* @uiDefault SplitPaneDivider.gripDotCount int
|
||||
* @uiDefault SplitPaneDivider.gripDotSize int
|
||||
* @uiDefault SplitPaneDivider.gripGap int
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
public class FlatSplitPaneUI
|
||||
extends BasicSplitPaneUI
|
||||
implements StyleableUI
|
||||
{
|
||||
protected String arrowType;
|
||||
private Boolean continuousLayout;
|
||||
private Color oneTouchArrowColor;
|
||||
private Color oneTouchHoverArrowColor;
|
||||
@Styleable protected String arrowType;
|
||||
@Styleable protected Color oneTouchArrowColor;
|
||||
@Styleable protected Color oneTouchHoverArrowColor;
|
||||
@Styleable protected Color oneTouchPressedArrowColor;
|
||||
|
||||
private PropertyChangeListener propertyChangeListener;
|
||||
private Map<String, Object> oldStyleValues;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatSplitPaneUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installUI( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
installStyle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installDefaults() {
|
||||
arrowType = UIManager.getString( "Component.arrowType" );
|
||||
@@ -69,15 +101,36 @@ public class FlatSplitPaneUI
|
||||
// used in there on LaF switching
|
||||
oneTouchArrowColor = UIManager.getColor( "SplitPaneDivider.oneTouchArrowColor" );
|
||||
oneTouchHoverArrowColor = UIManager.getColor( "SplitPaneDivider.oneTouchHoverArrowColor" );
|
||||
oneTouchPressedArrowColor = UIManager.getColor( "SplitPaneDivider.oneTouchPressedArrowColor" );
|
||||
|
||||
super.installDefaults();
|
||||
|
||||
continuousLayout = (Boolean) UIManager.get( "SplitPane.continuousLayout" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isContinuousLayout() {
|
||||
return super.isContinuousLayout() || (continuousLayout != null && Boolean.TRUE.equals( continuousLayout ));
|
||||
protected void uninstallDefaults() {
|
||||
super.uninstallDefaults();
|
||||
|
||||
oneTouchArrowColor = null;
|
||||
oneTouchHoverArrowColor = null;
|
||||
oneTouchPressedArrowColor = null;
|
||||
|
||||
oldStyleValues = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installListeners() {
|
||||
super.installListeners();
|
||||
|
||||
propertyChangeListener = FlatStylingSupport.createPropertyChangeListener( splitPane, this::installStyle, null );
|
||||
splitPane.addPropertyChangeListener( propertyChangeListener );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void uninstallListeners() {
|
||||
super.uninstallListeners();
|
||||
|
||||
splitPane.removePropertyChangeListener( propertyChangeListener );
|
||||
propertyChangeListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -85,13 +138,79 @@ public class FlatSplitPaneUI
|
||||
return new FlatSplitPaneDivider( this );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void installStyle() {
|
||||
try {
|
||||
applyStyle( FlatStylingSupport.getResolvedStyle( splitPane, "SplitPane" ) );
|
||||
} catch( RuntimeException ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void applyStyle( Object style ) {
|
||||
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||
|
||||
if( divider instanceof FlatSplitPaneDivider )
|
||||
((FlatSplitPaneDivider)divider).updateStyle();
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected Object applyStyleProperty( String key, Object value ) {
|
||||
try {
|
||||
if( divider instanceof FlatSplitPaneDivider )
|
||||
return ((FlatSplitPaneDivider)divider).applyStyleProperty( key, value );
|
||||
} catch( UnknownStyleException ex ) {
|
||||
// ignore
|
||||
}
|
||||
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, splitPane, key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
Map<String, Class<?>> infos = FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
if( divider instanceof FlatSplitPaneDivider )
|
||||
infos.putAll( ((FlatSplitPaneDivider)divider).getStyleableInfos() );
|
||||
return infos;
|
||||
}
|
||||
|
||||
//---- class FlatSplitPaneDivider -----------------------------------------
|
||||
|
||||
protected class FlatSplitPaneDivider
|
||||
extends BasicSplitPaneDivider
|
||||
{
|
||||
@Styleable protected String style = UIManager.getString( "SplitPaneDivider.style" );
|
||||
@Styleable protected Color gripColor = UIManager.getColor( "SplitPaneDivider.gripColor" );
|
||||
@Styleable protected int gripDotCount = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotCount", 3 );
|
||||
@Styleable protected int gripDotSize = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotSize", 3 );
|
||||
@Styleable protected int gripGap = FlatUIUtils.getUIInt( "SplitPaneDivider.gripGap", 2 );
|
||||
|
||||
protected FlatSplitPaneDivider( BasicSplitPaneUI ui ) {
|
||||
super( ui );
|
||||
|
||||
setLayout( new FlatDividerLayout() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2
|
||||
*/
|
||||
protected Object applyStyleProperty( String key, Object value ) {
|
||||
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2
|
||||
*/
|
||||
public Map<String, Class<?>> getStyleableInfos() {
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
void updateStyle() {
|
||||
if( leftButton instanceof FlatOneTouchButton )
|
||||
((FlatOneTouchButton)leftButton).updateStyle();
|
||||
if( rightButton instanceof FlatOneTouchButton )
|
||||
((FlatOneTouchButton)rightButton).updateStyle();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,27 +228,143 @@ public class FlatSplitPaneUI
|
||||
return new FlatOneTouchButton( false );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertyChange( PropertyChangeEvent e ) {
|
||||
super.propertyChange( e );
|
||||
|
||||
switch( e.getPropertyName() ) {
|
||||
case JSplitPane.DIVIDER_LOCATION_PROPERTY:
|
||||
// necessary to show/hide one-touch buttons on expand/collapse
|
||||
revalidate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint( Graphics g ) {
|
||||
super.paint( g );
|
||||
|
||||
if( "plain".equals( style ) )
|
||||
return;
|
||||
|
||||
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
||||
|
||||
g.setColor( gripColor );
|
||||
paintGrip( g, 0, 0, getWidth(), getHeight() );
|
||||
|
||||
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||
}
|
||||
|
||||
protected void paintGrip( Graphics g, int x, int y, int width, int height ) {
|
||||
FlatUIUtils.paintGrip( g, x, y, width, height,
|
||||
splitPane.getOrientation() == JSplitPane.VERTICAL_SPLIT,
|
||||
gripDotCount, gripDotSize, gripGap, true );
|
||||
}
|
||||
|
||||
protected boolean isLeftCollapsed() {
|
||||
int location = splitPane.getDividerLocation();
|
||||
Insets insets = splitPane.getInsets();
|
||||
return (orientation == JSplitPane.VERTICAL_SPLIT)
|
||||
? location == insets.top
|
||||
: location == insets.left;
|
||||
}
|
||||
|
||||
protected boolean isRightCollapsed() {
|
||||
int location = splitPane.getDividerLocation();
|
||||
Insets insets = splitPane.getInsets();
|
||||
return (orientation == JSplitPane.VERTICAL_SPLIT)
|
||||
? location == (splitPane.getHeight() - getHeight() - insets.bottom)
|
||||
: location == (splitPane.getWidth() - getWidth() - insets.right);
|
||||
}
|
||||
|
||||
//---- class FlatOneTouchButton ---------------------------------------
|
||||
|
||||
private class FlatOneTouchButton
|
||||
protected class FlatOneTouchButton
|
||||
extends FlatArrowButton
|
||||
{
|
||||
private final boolean left;
|
||||
protected final boolean left;
|
||||
|
||||
public FlatOneTouchButton( boolean left ) {
|
||||
super( SwingConstants.NORTH, arrowType, oneTouchArrowColor, null, oneTouchHoverArrowColor, null );
|
||||
protected FlatOneTouchButton( boolean left ) {
|
||||
super( SwingConstants.NORTH, arrowType, oneTouchArrowColor, null,
|
||||
oneTouchHoverArrowColor, null, oneTouchPressedArrowColor, null );
|
||||
setCursor( Cursor.getPredefinedCursor( Cursor.DEFAULT_CURSOR ) );
|
||||
ToolTipManager.sharedInstance().registerComponent( this );
|
||||
|
||||
this.left = left;
|
||||
}
|
||||
|
||||
protected void updateStyle() {
|
||||
updateStyle( arrowType, oneTouchArrowColor, null,
|
||||
oneTouchHoverArrowColor, null, oneTouchPressedArrowColor, null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDirection() {
|
||||
return (orientation == JSplitPane.VERTICAL_SPLIT)
|
||||
? (left ? SwingConstants.NORTH : SwingConstants.SOUTH)
|
||||
: (left ? SwingConstants.WEST : SwingConstants.EAST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToolTipText( MouseEvent e ) {
|
||||
String key = (orientation == JSplitPane.VERTICAL_SPLIT)
|
||||
? (left
|
||||
? (isRightCollapsed()
|
||||
? "SplitPaneDivider.expandBottomToolTipText"
|
||||
: "SplitPaneDivider.collapseTopToolTipText")
|
||||
: (isLeftCollapsed()
|
||||
? "SplitPaneDivider.expandTopToolTipText"
|
||||
: "SplitPaneDivider.collapseBottomToolTipText"))
|
||||
: (left
|
||||
? (isRightCollapsed()
|
||||
? "SplitPaneDivider.expandRightToolTipText"
|
||||
: "SplitPaneDivider.collapseLeftToolTipText")
|
||||
: (isLeftCollapsed()
|
||||
? "SplitPaneDivider.expandLeftToolTipText"
|
||||
: "SplitPaneDivider.collapseRightToolTipText"));
|
||||
|
||||
// get text from client property
|
||||
Object value = splitPane.getClientProperty( key );
|
||||
if( value instanceof String )
|
||||
return (String) value;
|
||||
|
||||
// get text from bundle
|
||||
return UIManager.getString( key, getLocale() );
|
||||
}
|
||||
}
|
||||
|
||||
//---- class FlatDividerLayout ----------------------------------------
|
||||
|
||||
protected class FlatDividerLayout
|
||||
extends DividerLayout
|
||||
{
|
||||
@Override
|
||||
public void layoutContainer( Container c ) {
|
||||
super.layoutContainer( c );
|
||||
|
||||
if( leftButton == null || rightButton == null || !splitPane.isOneTouchExpandable() )
|
||||
return;
|
||||
|
||||
// increase side of buttons, which makes them easier to hit by the user
|
||||
// and avoids cut arrows at small divider sizes
|
||||
int extraSize = UIScale.scale( 4 );
|
||||
if( orientation == JSplitPane.VERTICAL_SPLIT ) {
|
||||
leftButton.setSize( leftButton.getWidth() + extraSize, leftButton.getHeight() );
|
||||
rightButton.setBounds( leftButton.getX() + leftButton.getWidth(), rightButton.getY(),
|
||||
rightButton.getWidth() + extraSize, rightButton.getHeight() );
|
||||
} else {
|
||||
leftButton.setSize( leftButton.getWidth(), leftButton.getHeight() + extraSize );
|
||||
rightButton.setBounds( rightButton.getX(), leftButton.getY() + leftButton.getHeight(),
|
||||
rightButton.getWidth(), rightButton.getHeight() + extraSize );
|
||||
}
|
||||
|
||||
// hide buttons if not applicable
|
||||
boolean leftCollapsed = isLeftCollapsed();
|
||||
if( leftCollapsed )
|
||||
rightButton.setLocation( leftButton.getLocation() );
|
||||
leftButton.setVisible( !leftCollapsed );
|
||||
rightButton.setVisible( !isRightCollapsed() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,723 @@
|
||||
/*
|
||||
* Copyright 2021 FormDev Software GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Predicate;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.border.Border;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
import com.formdev.flatlaf.FlatLaf;
|
||||
import com.formdev.flatlaf.util.StringUtils;
|
||||
import com.formdev.flatlaf.util.SystemInfo;
|
||||
|
||||
/**
|
||||
* Support for styling components in CSS syntax.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
* @since 2
|
||||
*/
|
||||
public class FlatStylingSupport
|
||||
{
|
||||
/**
|
||||
* Indicates that a field is intended to be used by FlatLaf styling support.
|
||||
* <p>
|
||||
* <strong>Do not rename fields annotated with this annotation.</strong>
|
||||
*
|
||||
* @since 2
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Styleable {
|
||||
boolean dot() default false;
|
||||
Class<?> type() default Void.class;
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
public interface StyleableUI {
|
||||
Map<String, Class<?>> getStyleableInfos( JComponent c );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
public interface StyleableBorder {
|
||||
Object applyStyleProperty( String key, Object value );
|
||||
Map<String, Class<?>> getStyleableInfos();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the style specified in client property {@link FlatClientProperties#STYLE}.
|
||||
*/
|
||||
public static Object getStyle( JComponent c ) {
|
||||
return c.getClientProperty( FlatClientProperties.STYLE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the style class(es) specified in client property {@link FlatClientProperties#STYLE_CLASS}.
|
||||
*/
|
||||
public static Object getStyleClass( JComponent c ) {
|
||||
return c.getClientProperty( FlatClientProperties.STYLE_CLASS );
|
||||
}
|
||||
|
||||
static boolean hasStyleProperty( JComponent c ) {
|
||||
return getStyle( c ) != null || getStyleClass( c ) != null;
|
||||
}
|
||||
|
||||
public static Object getResolvedStyle( JComponent c, String type ) {
|
||||
Object style = getStyle( c );
|
||||
Object styleClass = getStyleClass( c );
|
||||
Object styleForClasses = getStyleForClasses( styleClass, type );
|
||||
return joinStyles( styleForClasses, style );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the styles for the given style class(es) and the given type.
|
||||
* <p>
|
||||
* The style rules must be defined in UI defaults either as strings (in CSS syntax)
|
||||
* or as {@link java.util.Map}<String, Object> (with binary values).
|
||||
* The key must be in syntax: {@code [style]type.styleClass}, where the type is optional.
|
||||
* E.g. in FlatLaf properties file:
|
||||
* <pre>{@code
|
||||
* [style]Button.primary = borderColor: #08f; background: #08f; foreground: #fff
|
||||
* [style].secondary = borderColor: #0f8; background: #0f8
|
||||
* }</pre>
|
||||
* or in Java code:
|
||||
* <pre>{@code
|
||||
* UIManager.put( "[style]Button.primary", "borderColor: #08f; background: #08f; foreground: #fff" );
|
||||
* UIManager.put( "[style].secondary", "borderColor: #0f8; background: #0f8" );
|
||||
* }</pre>
|
||||
* The rule "Button.primary" can be applied to buttons only.
|
||||
* The rule ".secondary" can be applied to any component.
|
||||
* <p>
|
||||
* To have similar behavior as in CSS, this method first gets the rule without type,
|
||||
* then the rule with type and concatenates both rules.
|
||||
* E.g. invoking this method with parameters styleClass="foo" and type="Button" does following:
|
||||
* <pre>{@code
|
||||
* return joinStyles(
|
||||
* UIManager.get( "[style].foo" ),
|
||||
* UIManager.get( "[style]Button.foo" ) );
|
||||
* }</pre>
|
||||
*
|
||||
* @param styleClass the style class(es) either as string (single class or multiple classes separated by space characters)
|
||||
* or as {@code String[]} or {@link java.util.List}<String> (multiple classes)
|
||||
* @param type the type of the component
|
||||
* @return the styles
|
||||
*/
|
||||
public static Object getStyleForClasses( Object styleClass, String type ) {
|
||||
if( styleClass == null )
|
||||
return null;
|
||||
|
||||
if( styleClass instanceof String && ((String)styleClass).indexOf( ' ' ) >= 0 )
|
||||
styleClass = StringUtils.split( (String) styleClass, ' ', true, true );
|
||||
|
||||
if( styleClass instanceof String )
|
||||
return getStyleForClass( ((String)styleClass).trim(), type );
|
||||
else if( styleClass instanceof String[] ) {
|
||||
Object style = null;
|
||||
for( String cls : (String[]) styleClass )
|
||||
style = joinStyles( style, getStyleForClass( cls, type ) );
|
||||
return style;
|
||||
} else if( styleClass instanceof List<?> ) {
|
||||
Object style = null;
|
||||
for( Object cls : (List<?>) styleClass )
|
||||
style = joinStyles( style, getStyleForClass( (String) cls, type ) );
|
||||
return style;
|
||||
} else
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Object getStyleForClass( String styleClass, String type ) {
|
||||
return joinStyles(
|
||||
UIManager.get( "[style]." + styleClass ),
|
||||
UIManager.get( "[style]" + type + '.' + styleClass ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins two styles. They can be either strings (in CSS syntax)
|
||||
* or {@link java.util.Map}<String, Object> (with binary values).
|
||||
* <p>
|
||||
* If both styles are strings, then a joined string is returned.
|
||||
* If both styles are maps, then a joined map is returned.
|
||||
* If one style is a map and the other style a string, then the string
|
||||
* is parsed (using {@link #parse(String)}) to a map and a joined map is returned.
|
||||
*
|
||||
* @param style1 first style as string or map, or {@code null}
|
||||
* @param style2 second style as string or map, or {@code null}
|
||||
* @return new joined style
|
||||
*/
|
||||
@SuppressWarnings( "unchecked" )
|
||||
public static Object joinStyles( Object style1, Object style2 ) {
|
||||
if( style1 == null )
|
||||
return style2;
|
||||
if( style2 == null )
|
||||
return style1;
|
||||
|
||||
// join two strings
|
||||
if( style1 instanceof String && style2 instanceof String )
|
||||
return style1 + "; " + style2;
|
||||
|
||||
// convert first style to map
|
||||
Map<String, Object> map1 = (style1 instanceof String)
|
||||
? parse( (String) style1 )
|
||||
: (Map<String, Object>) style1;
|
||||
if( map1 == null )
|
||||
return style2;
|
||||
|
||||
// convert second style to map
|
||||
Map<String, Object> map2 = (style2 instanceof String)
|
||||
? parse( (String) style2 )
|
||||
: (Map<String, Object>) style2;
|
||||
if( map2 == null )
|
||||
return style1;
|
||||
|
||||
// join two maps
|
||||
Map<String, Object> map = new HashMap<>( map1 );
|
||||
map.putAll( map2 );
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates two styles in CSS syntax.
|
||||
*
|
||||
* @param style1 first style, or {@code null}
|
||||
* @param style2 second style, or {@code null}
|
||||
* @return concatenation of the two styles separated by a semicolon
|
||||
*/
|
||||
public static String concatStyles( String style1, String style2 ) {
|
||||
if( style1 == null )
|
||||
return style2;
|
||||
if( style2 == null )
|
||||
return style1;
|
||||
return style1 + "; " + style2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses styles in CSS syntax ("key1: value1; key2: value2; ..."),
|
||||
* converts the value strings into binary and invokes the given function
|
||||
* to apply the properties.
|
||||
*
|
||||
* @param oldStyleValues map of old values modified by the previous invocation, or {@code null}
|
||||
* @param style the style in CSS syntax as string, or a Map, or {@code null}
|
||||
* @param applyProperty function that is invoked to apply the properties;
|
||||
* first parameter is the key, second the binary value;
|
||||
* the function must return the old value
|
||||
* @return map of old values modified by the given style, or {@code null}
|
||||
* @throws UnknownStyleException on unknown style keys
|
||||
* @throws IllegalArgumentException on syntax errors
|
||||
* @throws ClassCastException if value type does not fit to expected type
|
||||
*/
|
||||
public static Map<String, Object> parseAndApply( Map<String, Object> oldStyleValues,
|
||||
Object style, BiFunction<String, Object, Object> applyProperty )
|
||||
throws UnknownStyleException, IllegalArgumentException
|
||||
{
|
||||
// restore previous values
|
||||
if( oldStyleValues != null ) {
|
||||
for( Map.Entry<String, Object> e : oldStyleValues.entrySet() )
|
||||
applyProperty.apply( e.getKey(), e.getValue() );
|
||||
}
|
||||
|
||||
// ignore empty style
|
||||
if( style == null )
|
||||
return null;
|
||||
|
||||
if( style instanceof String ) {
|
||||
// handle style in CSS syntax
|
||||
String str = (String) style;
|
||||
if( StringUtils.isTrimmedEmpty( str ) )
|
||||
return null;
|
||||
|
||||
return applyStyle( parse( str ), applyProperty );
|
||||
} else if( style instanceof Map ) {
|
||||
// handle style of type Map
|
||||
@SuppressWarnings( "unchecked" )
|
||||
Map<String, Object> map = (Map<String, Object>) style;
|
||||
return applyStyle( map, applyProperty );
|
||||
} else
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Map<String, Object> applyStyle( Map<String, Object> style,
|
||||
BiFunction<String, Object, Object> applyProperty )
|
||||
{
|
||||
if( style.isEmpty() )
|
||||
return null;
|
||||
|
||||
Map<String, Object> oldValues = new HashMap<>();
|
||||
for( Map.Entry<String, Object> e : style.entrySet() ) {
|
||||
String key = e.getKey();
|
||||
Object newValue = e.getValue();
|
||||
|
||||
// handle key prefix
|
||||
if( key.startsWith( "[" ) ) {
|
||||
if( (SystemInfo.isWindows && key.startsWith( "[win]" )) ||
|
||||
(SystemInfo.isMacOS && key.startsWith( "[mac]" )) ||
|
||||
(SystemInfo.isLinux && key.startsWith( "[linux]" )) ||
|
||||
(key.startsWith( "[light]" ) && !FlatLaf.isLafDark()) ||
|
||||
(key.startsWith( "[dark]" ) && FlatLaf.isLafDark()) )
|
||||
{
|
||||
// prefix is known and enabled --> remove prefix
|
||||
key = key.substring( key.indexOf( ']' ) + 1 );
|
||||
} else
|
||||
continue;
|
||||
}
|
||||
|
||||
Object oldValue = applyProperty.apply( key, newValue );
|
||||
oldValues.put( key, oldValue );
|
||||
}
|
||||
return oldValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses styles in CSS syntax ("key1: value1; key2: value2; ..."),
|
||||
* converts the value strings into binary and returns all key/value pairs as map.
|
||||
*
|
||||
* @param style the style in CSS syntax, or {@code null}
|
||||
* @return map of parsed styles, or {@code null}
|
||||
* @throws IllegalArgumentException on syntax errors
|
||||
*/
|
||||
public static Map<String, Object> parse( String style )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
if( style == null || StringUtils.isTrimmedEmpty( style ) )
|
||||
return null;
|
||||
|
||||
Map<String, Object> map = null;
|
||||
|
||||
// split style into parts and process them
|
||||
for( String part : StringUtils.split( style, ';', true, true ) ) {
|
||||
// find separator colon
|
||||
int sepIndex = part.indexOf( ':' );
|
||||
if( sepIndex < 0 )
|
||||
throw new IllegalArgumentException( "missing colon in '" + part + "'" );
|
||||
|
||||
// split into key and value
|
||||
String key = StringUtils.substringTrimmed( part, 0, sepIndex );
|
||||
String value = StringUtils.substringTrimmed( part, sepIndex + 1 );
|
||||
if( key.isEmpty() )
|
||||
throw new IllegalArgumentException( "missing key in '" + part + "'" );
|
||||
if( value.isEmpty() )
|
||||
throw new IllegalArgumentException( "missing value in '" + part + "'" );
|
||||
|
||||
// parse value string and convert it into binary value
|
||||
if( map == null )
|
||||
map = new LinkedHashMap<>();
|
||||
map.put( key, parseValue( key, value ) );
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private static Object parseValue( String key, String value ) {
|
||||
// simple reference
|
||||
if( value.startsWith( "$" ) )
|
||||
return UIManager.get( value.substring( 1 ) );
|
||||
|
||||
// remove key prefix for correct value type detection
|
||||
// (e.g. "[light]padding" would not parse to Insets)
|
||||
if( key.startsWith( "[" ) )
|
||||
key = key.substring( key.indexOf( ']' ) + 1 );
|
||||
|
||||
// parse string
|
||||
return FlatLaf.parseDefaultsValue( key, value, null );
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the given value to an annotated field of the given object.
|
||||
* The field must be annotated with {@link Styleable}.
|
||||
*
|
||||
* @param obj the object
|
||||
* @param key the name of the field
|
||||
* @param value the new value
|
||||
* @return the old value of the field
|
||||
* @throws UnknownStyleException if object does not have a annotated field with given name
|
||||
* @throws IllegalArgumentException if value type does not fit to expected type
|
||||
*/
|
||||
public static Object applyToAnnotatedObject( Object obj, String key, Object value )
|
||||
throws UnknownStyleException, IllegalArgumentException
|
||||
{
|
||||
String fieldName = key;
|
||||
int dotIndex = key.indexOf( '.' );
|
||||
if( dotIndex >= 0 ) {
|
||||
// remove first dot in key and change subsequent character to uppercase
|
||||
fieldName = key.substring( 0, dotIndex )
|
||||
+ Character.toUpperCase( key.charAt( dotIndex + 1 ) )
|
||||
+ key.substring( dotIndex + 2 );
|
||||
}
|
||||
|
||||
return applyToField( obj, fieldName, key, value, field -> {
|
||||
Styleable styleable = field.getAnnotation( Styleable.class );
|
||||
return styleable != null && styleable.dot() == (dotIndex >= 0);
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the given value to a field of the given object.
|
||||
*
|
||||
* @param obj the object
|
||||
* @param fieldName the name of the field
|
||||
* @param key the key (only used for error reporting)
|
||||
* @param value the new value
|
||||
* @return the old value of the field
|
||||
* @throws UnknownStyleException if object does not have a field with given name
|
||||
* @throws IllegalArgumentException if value type does not fit to expected type
|
||||
*/
|
||||
static Object applyToField( Object obj, String fieldName, String key, Object value )
|
||||
throws UnknownStyleException, IllegalArgumentException
|
||||
{
|
||||
return applyToField( obj, fieldName, key, value, null );
|
||||
}
|
||||
|
||||
private static Object applyToField( Object obj, String fieldName, String key, Object value, Predicate<Field> predicate )
|
||||
throws UnknownStyleException, IllegalArgumentException
|
||||
{
|
||||
Class<?> cls = obj.getClass();
|
||||
|
||||
for(;;) {
|
||||
try {
|
||||
Field f = cls.getDeclaredField( fieldName );
|
||||
if( predicate == null || predicate.test( f ) ) {
|
||||
if( !isValidField( f ) )
|
||||
throw new IllegalArgumentException( "field '" + cls.getName() + "." + fieldName + "' is final or static" );
|
||||
|
||||
try {
|
||||
// necessary to access protected fields in other packages
|
||||
f.setAccessible( true );
|
||||
|
||||
// get old value and set new value
|
||||
Object oldValue = f.get( obj );
|
||||
f.set( obj, convertToEnum( value, f.getType() ) );
|
||||
return oldValue;
|
||||
} catch( IllegalAccessException ex ) {
|
||||
throw new IllegalArgumentException( "failed to access field '" + cls.getName() + "." + fieldName + "'", ex );
|
||||
}
|
||||
}
|
||||
} catch( NoSuchFieldException ex ) {
|
||||
// field not found in class --> try superclass
|
||||
}
|
||||
|
||||
cls = cls.getSuperclass();
|
||||
if( cls == null )
|
||||
throw new UnknownStyleException( key );
|
||||
|
||||
if( predicate != null ) {
|
||||
String superclassName = cls.getName();
|
||||
if( superclassName.startsWith( "java." ) || superclassName.startsWith( "javax." ) )
|
||||
throw new UnknownStyleException( key );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isValidField( Field f ) {
|
||||
int modifiers = f.getModifiers();
|
||||
return (modifiers & (Modifier.FINAL|Modifier.STATIC)) == 0 && !f.isSynthetic();
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the given value to a property of the given object.
|
||||
* Works only for properties that have public getter and setter methods.
|
||||
* First the property getter is invoked to get the old value,
|
||||
* then the property setter is invoked to set the new value.
|
||||
*
|
||||
* @param obj the object
|
||||
* @param name the name of the property
|
||||
* @param value the new value
|
||||
* @return the old value of the property
|
||||
* @throws UnknownStyleException if object does not have a property with given name
|
||||
* @throws IllegalArgumentException if value type does not fit to expected type
|
||||
*/
|
||||
private static Object applyToProperty( Object obj, String name, Object value )
|
||||
throws UnknownStyleException, IllegalArgumentException
|
||||
{
|
||||
Class<?> cls = obj.getClass();
|
||||
String getterName = buildMethodName( "get", name );
|
||||
String setterName = buildMethodName( "set", name );
|
||||
|
||||
try {
|
||||
Method getter;
|
||||
try {
|
||||
getter = cls.getMethod( getterName );
|
||||
} catch( NoSuchMethodException ex ) {
|
||||
getter = cls.getMethod( buildMethodName( "is", name ) );
|
||||
}
|
||||
Method setter = cls.getMethod( setterName, getter.getReturnType() );
|
||||
Object oldValue = getter.invoke( obj );
|
||||
setter.invoke( obj, convertToEnum( value, getter.getReturnType() ) );
|
||||
return oldValue;
|
||||
} catch( NoSuchMethodException ex ) {
|
||||
throw new UnknownStyleException( name );
|
||||
} catch( Exception ex ) {
|
||||
throw new IllegalArgumentException( "failed to invoke property methods '" + cls.getName() + "."
|
||||
+ getterName + "()' or '" + setterName + "(...)'", ex );
|
||||
}
|
||||
}
|
||||
|
||||
private static String buildMethodName( String prefix, String name ) {
|
||||
int prefixLength = prefix.length();
|
||||
int nameLength = name.length();
|
||||
char[] chars = new char[prefixLength + nameLength];
|
||||
prefix.getChars( 0, prefixLength, chars, 0 );
|
||||
name.getChars( 0, nameLength, chars, prefixLength );
|
||||
chars[prefixLength] = Character.toUpperCase( chars[prefixLength] );
|
||||
return new String( chars );
|
||||
}
|
||||
|
||||
@SuppressWarnings( { "unchecked", "rawtypes" } )
|
||||
private static Object convertToEnum( Object value, Class<?> type )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
// if type is an enum, convert string to enum value
|
||||
if( Enum.class.isAssignableFrom( type ) && value instanceof String ) {
|
||||
try {
|
||||
value = Enum.valueOf( (Class<? extends Enum>) type, (String) value );
|
||||
} catch( IllegalArgumentException ex ) {
|
||||
throw new IllegalArgumentException( "unknown enum value '" + value + "' in enum '" + type.getName() + "'", ex );
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the given value to an annotated field of the given object
|
||||
* or to a property of the given component.
|
||||
* The field must be annotated with {@link Styleable}.
|
||||
* The component property must have public getter and setter methods.
|
||||
*
|
||||
* @param obj the object
|
||||
* @param comp the component, or {@code null}
|
||||
* @param key the name of the field
|
||||
* @param value the new value
|
||||
* @return the old value of the field
|
||||
* @throws UnknownStyleException if object does not have a annotated field with given name
|
||||
* @throws IllegalArgumentException if value type does not fit to expected type
|
||||
*/
|
||||
public static Object applyToAnnotatedObjectOrComponent( Object obj, Object comp, String key, Object value )
|
||||
throws UnknownStyleException, IllegalArgumentException
|
||||
{
|
||||
try {
|
||||
return applyToAnnotatedObject( obj, key, value );
|
||||
} catch( UnknownStyleException ex ) {
|
||||
try {
|
||||
if( comp != null )
|
||||
return applyToProperty( comp, key, value );
|
||||
} catch( UnknownStyleException ex2 ) {
|
||||
// ignore
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
static Object applyToAnnotatedObjectOrBorder( Object obj, String key, Object value,
|
||||
JComponent c, AtomicBoolean borderShared )
|
||||
{
|
||||
try {
|
||||
return applyToAnnotatedObject( obj, key, value );
|
||||
} catch( UnknownStyleException ex ) {
|
||||
// apply to border
|
||||
Border border = c.getBorder();
|
||||
if( border instanceof StyleableBorder ) {
|
||||
if( borderShared.get() ) {
|
||||
border = cloneBorder( border );
|
||||
c.setBorder( border );
|
||||
borderShared.set( false );
|
||||
}
|
||||
|
||||
try {
|
||||
return ((StyleableBorder)border).applyStyleProperty( key, value );
|
||||
} catch( UnknownStyleException ex2 ) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
// apply to component property
|
||||
try {
|
||||
return applyToProperty( c, key, value );
|
||||
} catch( UnknownStyleException ex2 ) {
|
||||
// ignore
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
static PropertyChangeListener createPropertyChangeListener( JComponent c,
|
||||
Runnable installStyle, PropertyChangeListener superListener )
|
||||
{
|
||||
return e -> {
|
||||
if( superListener != null )
|
||||
superListener.propertyChange( e );
|
||||
|
||||
switch( e.getPropertyName() ) {
|
||||
case FlatClientProperties.STYLE:
|
||||
case FlatClientProperties.STYLE_CLASS:
|
||||
installStyle.run();
|
||||
c.revalidate();
|
||||
c.repaint();
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static Border cloneBorder( Border border ) {
|
||||
Class<? extends Border> borderClass = border.getClass();
|
||||
try {
|
||||
return borderClass.getDeclaredConstructor().newInstance();
|
||||
} catch( Exception ex ) {
|
||||
throw new IllegalArgumentException( "failed to clone border '" + borderClass.getName() + "'", ex );
|
||||
}
|
||||
}
|
||||
|
||||
static Icon cloneIcon( Icon icon ) {
|
||||
Class<? extends Icon> iconClass = icon.getClass();
|
||||
try {
|
||||
return iconClass.getDeclaredConstructor().newInstance();
|
||||
} catch( Exception ex ) {
|
||||
throw new IllegalArgumentException( "failed to clone icon '" + iconClass.getName() + "'", ex );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of all fields annotated with {@link Styleable}.
|
||||
* The key is the name of the field and the value the type of the field.
|
||||
*/
|
||||
public static Map<String, Class<?>> getAnnotatedStyleableInfos( Object obj ) {
|
||||
return getAnnotatedStyleableInfos( obj, null );
|
||||
}
|
||||
|
||||
public static Map<String, Class<?>> getAnnotatedStyleableInfos( Object obj, Border border ) {
|
||||
Map<String, Class<?>> infos = new StyleableInfosMap<>();
|
||||
collectAnnotatedStyleableInfos( obj, infos );
|
||||
collectStyleableInfos( border, infos );
|
||||
return infos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for all fields annotated with {@link Styleable} and add them to the given map.
|
||||
* The key is the name of the field and the value the type of the field.
|
||||
*/
|
||||
public static void collectAnnotatedStyleableInfos( Object obj, Map<String, Class<?>> infos ) {
|
||||
HashSet<String> processedFields = new HashSet<>();
|
||||
Class<?> cls = obj.getClass();
|
||||
|
||||
for(;;) {
|
||||
for( Field f : cls.getDeclaredFields() ) {
|
||||
if( !isValidField( f ) )
|
||||
continue;
|
||||
|
||||
Styleable styleable = f.getAnnotation( Styleable.class );
|
||||
if( styleable == null )
|
||||
continue;
|
||||
|
||||
String name = f.getName();
|
||||
Class<?> type = f.getType();
|
||||
|
||||
// for the case that the same field name is used in a class and in
|
||||
// one of its superclasses (e.g. field 'borderColor' in FlatButtonBorder
|
||||
// and in FlatBorder), do not process field in superclass
|
||||
if( processedFields.contains( name ) )
|
||||
continue;
|
||||
processedFields.add( name );
|
||||
|
||||
// handle "dot" keys (e.g. change field name "iconArrowType" to style key "icon.arrowType")
|
||||
if( styleable.dot() ) {
|
||||
int len = name.length();
|
||||
for( int i = 0; i < len; i++ ) {
|
||||
if( Character.isUpperCase( name.charAt( i ) ) ) {
|
||||
name = name.substring( 0, i ) + '.'
|
||||
+ Character.toLowerCase( name.charAt( i ) )
|
||||
+ name.substring( i + 1 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// field has a different type
|
||||
if( styleable.type() != Void.class )
|
||||
type = styleable.type();
|
||||
|
||||
infos.put( name, type );
|
||||
}
|
||||
|
||||
cls = cls.getSuperclass();
|
||||
if( cls == null )
|
||||
return;
|
||||
|
||||
String superclassName = cls.getName();
|
||||
if( superclassName.startsWith( "java." ) || superclassName.startsWith( "javax." ) )
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public static void collectStyleableInfos( Border border, Map<String, Class<?>> infos ) {
|
||||
if( border instanceof StyleableBorder )
|
||||
infos.putAll( ((StyleableBorder)border).getStyleableInfos() );
|
||||
}
|
||||
|
||||
public static void putAllPrefixKey( Map<String, Class<?>> infos, String keyPrefix, Map<String, Class<?>> infos2 ) {
|
||||
for( Map.Entry<String, Class<?>> e : infos2.entrySet() )
|
||||
infos.put( keyPrefix.concat( e.getKey() ), e.getValue() );
|
||||
}
|
||||
|
||||
//---- class UnknownStyleException ----------------------------------------
|
||||
|
||||
public static class UnknownStyleException
|
||||
extends IllegalArgumentException
|
||||
{
|
||||
public UnknownStyleException( String key ) {
|
||||
super( key );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "unknown style '" + super.getMessage() + "'";
|
||||
}
|
||||
}
|
||||
|
||||
//---- class StyleableInfosMap --------------------------------------------
|
||||
|
||||
static class StyleableInfosMap<K,V>
|
||||
extends LinkedHashMap<K,V>
|
||||
{
|
||||
@Override
|
||||
public V put( K key, V value ) {
|
||||
V oldValue = super.put( key, value );
|
||||
if( oldValue != null )
|
||||
throw new IllegalArgumentException( "duplicate key '" + key + "'" );
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll( Map<? extends K, ? extends V> m ) {
|
||||
for( Map.Entry<? extends K, ? extends V> e : m.entrySet() )
|
||||
put( e.getKey(), e.getValue() );
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,11 +16,15 @@
|
||||
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Insets;
|
||||
import java.util.function.Function;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.TableUI;
|
||||
|
||||
/**
|
||||
* Cell border for {@link javax.swing.table.DefaultTableCellRenderer}
|
||||
@@ -33,12 +37,54 @@ import javax.swing.UIManager;
|
||||
public class FlatTableCellBorder
|
||||
extends FlatLineBorder
|
||||
{
|
||||
final boolean showCellFocusIndicator = UIManager.getBoolean( "Table.showCellFocusIndicator" );
|
||||
protected boolean showCellFocusIndicator = UIManager.getBoolean( "Table.showCellFocusIndicator" );
|
||||
|
||||
private Component c;
|
||||
|
||||
protected FlatTableCellBorder() {
|
||||
super( UIManager.getInsets( "Table.cellMargins" ), UIManager.getColor( "Table.cellFocusColor" ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||
Insets m = getStyleFromTableUI( c, ui -> ui.cellMargins );
|
||||
if( m != null )
|
||||
return scaleInsets( c, insets, m.top, m.left, m.bottom, m.right );
|
||||
|
||||
return super.getBorderInsets( c, insets );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getLineColor() {
|
||||
if( c != null ) {
|
||||
Color color = getStyleFromTableUI( c, ui -> ui.cellFocusColor );
|
||||
if( color != null )
|
||||
return color;
|
||||
}
|
||||
return super.getLineColor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||
this.c = c;
|
||||
super.paintBorder( c, g, x, y, width, height );
|
||||
this.c = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Because this borders are always shared for all tables,
|
||||
* get border specific style from FlatTableUI.
|
||||
*/
|
||||
static <T> T getStyleFromTableUI( Component c, Function<FlatTableUI, T> f ) {
|
||||
JTable table = (JTable) SwingUtilities.getAncestorOfClass( JTable.class, c );
|
||||
if( table != null ) {
|
||||
TableUI ui = table.getUI();
|
||||
if( ui instanceof FlatTableUI )
|
||||
return f.apply( (FlatTableUI) ui );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//---- class Default ------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -74,6 +120,9 @@ public class FlatTableCellBorder
|
||||
{
|
||||
@Override
|
||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||
Boolean b = getStyleFromTableUI( c, ui -> ui.showCellFocusIndicator );
|
||||
boolean showCellFocusIndicator = (b != null) ? b : this.showCellFocusIndicator;
|
||||
|
||||
if( !showCellFocusIndicator ) {
|
||||
JTable table = (JTable) SwingUtilities.getAncestorOfClass( JTable.class, c );
|
||||
if( table != null && !isSelectionEditable( table ) )
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright 2021 FormDev Software GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Insets;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import javax.swing.JScrollBar;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JViewport;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.table.JTableHeader;
|
||||
import javax.swing.table.TableColumn;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
/**
|
||||
* Cell border for {@code sun.swing.table.DefaultTableCellHeaderRenderer}
|
||||
* (used by {@link javax.swing.table.JTableHeader}).
|
||||
* <p>
|
||||
* Uses separate cell margins from UI defaults to allow easy customizing.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
* @since 1.2
|
||||
*/
|
||||
public class FlatTableHeaderBorder
|
||||
extends FlatEmptyBorder
|
||||
{
|
||||
protected Color separatorColor = UIManager.getColor( "TableHeader.separatorColor" );
|
||||
protected Color bottomSeparatorColor = UIManager.getColor( "TableHeader.bottomSeparatorColor" );
|
||||
/** @since 1.6 */ protected boolean showTrailingVerticalLine = UIManager.getBoolean( "TableHeader.showTrailingVerticalLine" );
|
||||
|
||||
public FlatTableHeaderBorder() {
|
||||
super( UIManager.getInsets( "TableHeader.cellMargins" ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||
JTableHeader header = (JTableHeader) SwingUtilities.getAncestorOfClass( JTableHeader.class, c );
|
||||
if( header != null ) {
|
||||
if( header.getUI() instanceof FlatTableHeaderUI ) {
|
||||
FlatTableHeaderUI ui = (FlatTableHeaderUI) header.getUI();
|
||||
if( ui.cellMargins != null ) {
|
||||
Insets m = ui.cellMargins;
|
||||
return scaleInsets( c, insets, m.top, m.left, m.bottom, m.right );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.getBorderInsets( c, insets );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||
JTableHeader header = (JTableHeader) SwingUtilities.getAncestorOfClass( JTableHeader.class, c );
|
||||
boolean leftToRight = (header != null ? header : c).getComponentOrientation().isLeftToRight();
|
||||
boolean paintLeft = !leftToRight;
|
||||
boolean paintRight = leftToRight;
|
||||
Color separatorColor = this.separatorColor;
|
||||
Color bottomSeparatorColor = this.bottomSeparatorColor;
|
||||
|
||||
if( header != null ) {
|
||||
int hx = SwingUtilities.convertPoint( c, x, y, header ).x;
|
||||
if( isDraggedColumn( header, hx ) )
|
||||
paintLeft = paintRight = true;
|
||||
else {
|
||||
if( hx <= 0 && !leftToRight && hideTrailingVerticalLine( header ) )
|
||||
paintLeft = false;
|
||||
if( hx + width >= header.getWidth() && leftToRight && hideTrailingVerticalLine( header ) )
|
||||
paintRight = false;
|
||||
}
|
||||
|
||||
// Because this border is always shared for all table headers,
|
||||
// get border specific style from FlatTableHeaderUI.
|
||||
if( header.getUI() instanceof FlatTableHeaderUI ) {
|
||||
FlatTableHeaderUI ui = (FlatTableHeaderUI) header.getUI();
|
||||
if( ui.separatorColor != null )
|
||||
separatorColor = ui.separatorColor;
|
||||
if( ui.bottomSeparatorColor != null )
|
||||
bottomSeparatorColor = ui.bottomSeparatorColor;
|
||||
}
|
||||
}
|
||||
|
||||
float lineWidth = UIScale.scale( 1f );
|
||||
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
try {
|
||||
FlatUIUtils.setRenderingHints( g2 );
|
||||
|
||||
// paint column separator lines
|
||||
g2.setColor( separatorColor );
|
||||
if( paintLeft )
|
||||
g2.fill( new Rectangle2D.Float( x, y, lineWidth, height - lineWidth ) );
|
||||
if( paintRight )
|
||||
g2.fill( new Rectangle2D.Float( x + width - lineWidth, y, lineWidth, height - lineWidth ) );
|
||||
|
||||
// paint bottom line
|
||||
g2.setColor( bottomSeparatorColor );
|
||||
g2.fill( new Rectangle2D.Float( x, y + height - lineWidth, width, lineWidth ) );
|
||||
} finally {
|
||||
g2.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isDraggedColumn( JTableHeader header, int x ) {
|
||||
TableColumn draggedColumn = header.getDraggedColumn();
|
||||
if( draggedColumn == null )
|
||||
return false;
|
||||
|
||||
int draggedDistance = header.getDraggedDistance();
|
||||
if( draggedDistance == 0 )
|
||||
return false;
|
||||
|
||||
int columnCount = header.getColumnModel().getColumnCount();
|
||||
for( int i = 0; i < columnCount; i++ ) {
|
||||
if( header.getHeaderRect( i ).x + draggedDistance == x )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean hideTrailingVerticalLine( JTableHeader header ) {
|
||||
if( header.getUI() instanceof FlatTableHeaderUI ) {
|
||||
FlatTableHeaderUI ui = (FlatTableHeaderUI) header.getUI();
|
||||
if( ui.showTrailingVerticalLine )
|
||||
return false;
|
||||
}
|
||||
|
||||
if( showTrailingVerticalLine )
|
||||
return false;
|
||||
|
||||
// do not hide if table header is not a child of a scroll pane
|
||||
Container viewport = header.getParent();
|
||||
Container viewportParent = (viewport != null) ? viewport.getParent() : null;
|
||||
if( !(viewportParent instanceof JScrollPane) )
|
||||
return false;
|
||||
|
||||
// do not hide if table header is not the column header of the scroll pane
|
||||
JScrollPane scrollPane = (JScrollPane) viewportParent;
|
||||
JViewport columnHeader = scrollPane.getColumnHeader();
|
||||
if( viewport != columnHeader )
|
||||
return false;
|
||||
|
||||
// hide if vertical scroll bar is not shown
|
||||
JScrollBar vsb = scrollPane.getVerticalScrollBar();
|
||||
if( vsb == null || !vsb.isVisible() )
|
||||
return true;
|
||||
|
||||
// if "ScrollPane.fillUpperCorner" is true, then javax.swing.ScrollPaneLayout
|
||||
// extends the vertical scrollbar into the upper right/left corner
|
||||
return vsb.getY() == viewport.getY();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user