mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2026-02-17 00:37:39 -06:00
Compare commits
621 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f5a3e8d8b | ||
|
|
614ac956de | ||
|
|
c228362c01 | ||
|
|
f6c5db07f2 | ||
|
|
78e7839213 | ||
|
|
86a4f306c6 | ||
|
|
0e523f1193 | ||
|
|
9041a16b22 | ||
|
|
596ff3382d | ||
|
|
cbd80252ed | ||
|
|
bcd7a7e3dd | ||
|
|
9c98f1a553 | ||
|
|
c3d214aa23 | ||
|
|
d301f6e104 | ||
|
|
eb9fa585f7 | ||
|
|
16c6ffb032 | ||
|
|
7858e42e37 | ||
|
|
30132aa6b0 | ||
|
|
bf4d4cc2c5 | ||
|
|
9f0554c883 | ||
|
|
218ea6ce47 | ||
|
|
0baae7da8b | ||
|
|
fb4576fc1b | ||
|
|
16f3f9e6ff | ||
|
|
fee7cf6265 | ||
|
|
2dd75c4a64 | ||
|
|
d2f46cd0b5 | ||
|
|
10914083e6 | ||
|
|
5d167da55e | ||
|
|
b381e20e57 | ||
|
|
475cc9a9a5 | ||
|
|
264d6fbd6d | ||
|
|
2826cf379b | ||
|
|
d28745df29 | ||
|
|
94f9e4a1be | ||
|
|
ec547e1d65 | ||
|
|
61d4eb649b | ||
|
|
52ad15e375 | ||
|
|
ff00a6c0f0 | ||
|
|
9b1ebd658d | ||
|
|
f842530537 | ||
|
|
63077bbb19 | ||
|
|
4dad337377 | ||
|
|
10a965d765 | ||
|
|
3e9c9c9066 | ||
|
|
8b5a738e65 | ||
|
|
2c041dce3a | ||
|
|
ef151c68f4 | ||
|
|
52feaac92a | ||
|
|
cddbb3d7d4 | ||
|
|
42764550e6 | ||
|
|
6ee737b314 | ||
|
|
f460ef7685 | ||
|
|
9977bcb468 | ||
|
|
7437d984c7 | ||
|
|
5cd0b2403c | ||
|
|
a372da22f3 | ||
|
|
8b10d3ba5a | ||
|
|
a8b15c6a12 | ||
|
|
23bac7e5fd | ||
|
|
b82ee2ef61 | ||
|
|
b7761f4b71 | ||
|
|
f9a4f9771c | ||
|
|
d2acb2c98a | ||
|
|
d60bd5df14 | ||
|
|
73b6ca3762 | ||
|
|
6c18431a30 | ||
|
|
a49d20249f | ||
|
|
ad384acd57 | ||
|
|
69851b7f3a | ||
|
|
92b53bf0df | ||
|
|
93e0496fd2 | ||
|
|
5151951f46 | ||
|
|
58dbccec2d | ||
|
|
90de14d013 | ||
|
|
5f961618bf | ||
|
|
37c375e2fa | ||
|
|
1758c175ed | ||
|
|
96f2a02cfa | ||
|
|
96d4bda6c8 | ||
|
|
02cf6050a1 | ||
|
|
38cf32a2e9 | ||
|
|
2ae7589d14 | ||
|
|
bcb2e1f0a1 | ||
|
|
14932d3f07 | ||
|
|
c3b9dc397d | ||
|
|
58b653f55d | ||
|
|
1dcdc42dde | ||
|
|
58a0a16985 | ||
|
|
a117243f14 | ||
|
|
22411060be | ||
|
|
045263ae58 | ||
|
|
024b6daaf6 | ||
|
|
bd5512c121 | ||
|
|
9afce83a02 | ||
|
|
07a8bd9486 | ||
|
|
bcdc0a8fce | ||
|
|
b295809432 | ||
|
|
52763ab932 | ||
|
|
99666265c9 | ||
|
|
af3e280d74 | ||
|
|
b57e4c0565 | ||
|
|
aca9931560 | ||
|
|
d09e166e4a | ||
|
|
68a7a60ff2 | ||
|
|
f21261914b | ||
|
|
7b11339fdc | ||
|
|
081fd43d98 | ||
|
|
ef2eedfc7c | ||
|
|
0dba9265be | ||
|
|
301aae9b8e | ||
|
|
c63f4e9662 | ||
|
|
47508dc6ac | ||
|
|
3a8879608a | ||
|
|
b221889549 | ||
|
|
c00d99b85f | ||
|
|
0bf87b753d | ||
|
|
53f2730064 | ||
|
|
d487c3b005 | ||
|
|
fef6ae7ff7 | ||
|
|
f6b42754de | ||
|
|
2ae9bb381d | ||
|
|
53bde84710 | ||
|
|
d006ac27ff | ||
|
|
c478d28b71 | ||
|
|
99f7b9ad84 | ||
|
|
ab58101ce3 | ||
|
|
d8f3682dc0 | ||
|
|
1fec7ba553 | ||
|
|
418f55f34e | ||
|
|
05d795b2ae | ||
|
|
a365b750d9 | ||
|
|
0aecfb565f | ||
|
|
0cf4edd9e5 | ||
|
|
dd7b7c6aef | ||
|
|
0bd677c46b | ||
|
|
1a131d5206 | ||
|
|
016e515ae2 | ||
|
|
456ceb3c58 | ||
|
|
2169be1b45 | ||
|
|
49eb0b0201 | ||
|
|
2e222bcdea | ||
|
|
c7fa475128 | ||
|
|
4174b065f3 | ||
|
|
df6256d989 | ||
|
|
c27db56321 | ||
|
|
97bed8554a | ||
|
|
751c0e16e9 | ||
|
|
936de60700 | ||
|
|
f6b64d48ec | ||
|
|
b043da7d4c | ||
|
|
7e47cc2443 | ||
|
|
b8b45f9442 | ||
|
|
66337f9af6 | ||
|
|
54646706a0 | ||
|
|
e8ee037d09 | ||
|
|
6d705e568a | ||
|
|
e768791eba | ||
|
|
2aff7c97f9 | ||
|
|
ca6fc7773e | ||
|
|
a1395a5490 | ||
|
|
61dd4d71d6 | ||
|
|
6beda53238 | ||
|
|
941441d7e1 | ||
|
|
d10ea41b47 | ||
|
|
9458870f70 | ||
|
|
095794bbd1 | ||
|
|
c7fc0aa936 | ||
|
|
a8d98ced61 | ||
|
|
831b3d851a | ||
|
|
8c891c7016 | ||
|
|
5c4706cbc9 | ||
|
|
db66a6c4f0 | ||
|
|
0517e4fc02 | ||
|
|
dd7fa4a87d | ||
|
|
e5956900ea | ||
|
|
3755593c14 | ||
|
|
8ddd3b6d68 | ||
|
|
840083940d | ||
|
|
0cdfd29ecf | ||
|
|
bb32c727b6 | ||
|
|
f978c04750 | ||
|
|
b6a504e121 | ||
|
|
5fae367fab | ||
|
|
6e807f44b2 | ||
|
|
53ebed7f89 | ||
|
|
1c10c41808 | ||
|
|
01170b669b | ||
|
|
b56215e5e3 | ||
|
|
221e801561 | ||
|
|
90edbe23d7 | ||
|
|
5b16a814c8 | ||
|
|
ef01721464 | ||
|
|
efd8cf8236 | ||
|
|
8dbfc6d5d6 | ||
|
|
ef343397d4 | ||
|
|
cae02d31db | ||
|
|
96c78cbc16 | ||
|
|
f8c769644d | ||
|
|
0bd1e413b0 | ||
|
|
07c9ad484a | ||
|
|
5fd5b1206e | ||
|
|
8e107647bd | ||
|
|
12b7389376 | ||
|
|
45332c8126 | ||
|
|
02a9d4e31d | ||
|
|
a4377e81cb | ||
|
|
8d2ed3faf6 | ||
|
|
e7dacb8fef | ||
|
|
60e2ffac5f | ||
|
|
73c37b2018 | ||
|
|
1b3cc223da | ||
|
|
51be7ad832 | ||
|
|
f93d035e4e | ||
|
|
a3885d7a48 | ||
|
|
bbf2331766 | ||
|
|
2164bd363b | ||
|
|
6205e18c45 | ||
|
|
959b3e46fa | ||
|
|
09d8d09aad | ||
|
|
70336e31c7 | ||
|
|
600e0f3d67 | ||
|
|
023e356057 | ||
|
|
27786ec00a | ||
|
|
e52e72c5a8 | ||
|
|
802dd08ce7 | ||
|
|
568ec5a1a2 | ||
|
|
035d196392 | ||
|
|
dd3ffc64b9 | ||
|
|
c9a38f0a13 | ||
|
|
78461a9d5a | ||
|
|
79b8fb910a | ||
|
|
405e3df1f0 | ||
|
|
f7126d154f | ||
|
|
d8df8c9631 | ||
|
|
37b35f9063 | ||
|
|
f61a7288eb | ||
|
|
47a1122f04 | ||
|
|
e1bfabbce5 | ||
|
|
9708fec0e0 | ||
|
|
7f4efaf0a3 | ||
|
|
269eb0ba29 | ||
|
|
428c6b7813 | ||
|
|
db2452a4ec | ||
|
|
7dac3825d7 | ||
|
|
7c99872278 | ||
|
|
64c7318cfc | ||
|
|
a9d6483829 | ||
|
|
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 | ||
|
|
6b880af447 | ||
|
|
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 | ||
|
|
2b1c55ee67 | ||
|
|
925ddaa63a | ||
|
|
2b60b18d47 | ||
|
|
d502406fa2 | ||
|
|
afdbf711f7 | ||
|
|
b4f7b1d71d | ||
|
|
69061cd41c | ||
|
|
8ba7f7f961 | ||
|
|
5e5aa17e14 | ||
|
|
551f5fc929 | ||
|
|
4e7b0d11d0 | ||
|
|
06bc53692a | ||
|
|
007ee38cb4 | ||
|
|
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 |
97
.github/workflows/ci.yml
vendored
97
.github/workflows/ci.yml
vendored
@@ -19,45 +19,32 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
# test against
|
# test against
|
||||||
# - Java 1.8 (minimum requirement)
|
# - Java 8 (minimum requirement)
|
||||||
# - Java 9 (first version with JPMS)
|
|
||||||
# - Java LTS versions (11, 17, ...)
|
# - Java LTS versions (11, 17, ...)
|
||||||
# - lastest Java version(s)
|
# - lastest Java version(s)
|
||||||
java:
|
java:
|
||||||
- 1.8
|
- 8
|
||||||
- 9
|
|
||||||
- 11 # LTS
|
- 11 # LTS
|
||||||
- 14
|
- 17 # LTS
|
||||||
- 15
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: gradle/wrapper-validation-action@v1
|
- uses: gradle/wrapper-validation-action@v1
|
||||||
|
if: matrix.java == '8'
|
||||||
|
|
||||||
- name: Setup Java ${{ matrix.java }}
|
- name: Setup Java ${{ matrix.java }}
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
java-version: ${{ matrix.java }}
|
java-version: ${{ matrix.java }}
|
||||||
|
distribution: adopt # Java 8 and 11 are pre-installed on ubuntu-latest
|
||||||
- name: Cache Gradle wrapper
|
cache: gradle
|
||||||
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
|
- name: Build with Gradle
|
||||||
run: ./gradlew build
|
run: ./gradlew build
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
if: matrix.java == '11'
|
if: matrix.java == '11'
|
||||||
with:
|
with:
|
||||||
name: FlatLaf-build-artifacts
|
name: FlatLaf-build-artifacts
|
||||||
@@ -72,36 +59,36 @@ jobs:
|
|||||||
needs: build
|
needs: build
|
||||||
if: |
|
if: |
|
||||||
github.event_name == 'push' &&
|
github.event_name == 'push' &&
|
||||||
github.ref == 'refs/heads/main' &&
|
(github.ref == 'refs/heads/main' || startsWith( github.ref, 'refs/heads/develop-' )) &&
|
||||||
github.repository == 'JFormDesigner/FlatLaf'
|
github.repository == 'JFormDesigner/FlatLaf'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Java 11
|
- name: Setup Java 11
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 11
|
||||||
|
distribution: adopt # pre-installed on ubuntu-latest
|
||||||
- name: Cache Gradle wrapper
|
cache: gradle
|
||||||
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
|
- name: Publish snapshot to oss.sonatype.org
|
||||||
run: ./gradlew publish -Dorg.gradle.internal.publish.checksums.insecure=true
|
run: ./gradlew publish :flatlaf-theme-editor:build -Dorg.gradle.internal.publish.checksums.insecure=true
|
||||||
env:
|
env:
|
||||||
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
|
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
|
||||||
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
|
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:
|
release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -112,28 +99,17 @@ jobs:
|
|||||||
github.repository == 'JFormDesigner/FlatLaf'
|
github.repository == 'JFormDesigner/FlatLaf'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Java 11
|
- name: Setup Java 11
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 11
|
||||||
|
distribution: adopt # pre-installed on ubuntu-latest
|
||||||
- name: Cache Gradle wrapper
|
cache: gradle
|
||||||
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
|
- name: Release a new stable version to Maven Central
|
||||||
run: ./gradlew publish :flatlaf-demo:build -Drelease=true
|
run: ./gradlew publish :flatlaf-demo:build :flatlaf-theme-editor:build -Drelease=true
|
||||||
env:
|
env:
|
||||||
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
|
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
|
||||||
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
|
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
|
||||||
@@ -150,3 +126,14 @@ jobs:
|
|||||||
localDir: "flatlaf-demo/build/libs"
|
localDir: "flatlaf-demo/build/libs"
|
||||||
remoteDir: "."
|
remoteDir: "."
|
||||||
options: "--only-newer --no-recursion --verbose=1"
|
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"
|
||||||
|
|||||||
52
.github/workflows/natives.yml
vendored
52
.github/workflows/natives.yml
vendored
@@ -9,50 +9,48 @@ on:
|
|||||||
tags:
|
tags:
|
||||||
- '[0-9]*'
|
- '[0-9]*'
|
||||||
paths:
|
paths:
|
||||||
- 'flatlaf-natives/flatlaf-natives-windows/**'
|
- 'flatlaf-natives/**'
|
||||||
- '.github/workflows/natives.yml'
|
- '.github/workflows/natives.yml'
|
||||||
|
- 'gradle/wrapper/gradle-wrapper.properties'
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- '*'
|
- '*'
|
||||||
paths:
|
paths:
|
||||||
- 'flatlaf-natives/flatlaf-natives-windows/**'
|
- 'flatlaf-natives/**'
|
||||||
- '.github/workflows/natives.yml'
|
- '.github/workflows/natives.yml'
|
||||||
|
- 'gradle/wrapper/gradle-wrapper.properties'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Windows:
|
Natives:
|
||||||
runs-on: windows-latest
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os:
|
||||||
|
- windows
|
||||||
|
- ubuntu
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: gradle/wrapper-validation-action@v1
|
- uses: gradle/wrapper-validation-action@v1
|
||||||
|
|
||||||
- name: Setup Java 1.8
|
- name: Setup Java 11
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
java-version: 1.8
|
java-version: 11
|
||||||
|
distribution: adopt
|
||||||
- name: Cache Gradle wrapper
|
cache: gradle
|
||||||
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
|
- name: Build with Gradle
|
||||||
run: ./gradlew :flatlaf-natives-windows:build
|
# --no-daemon is necessary on Windows otherwise caching Gradle would fail with:
|
||||||
|
# tar.exe: Couldn't open ~/.gradle/caches/modules-2/modules-2.lock: Permission denied
|
||||||
|
run: ./gradlew build-natives --no-daemon
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: FlatLaf-natives-windows-build-artifacts
|
name: FlatLaf-natives-build-artifacts-${{ matrix.os }}
|
||||||
path: |
|
path: |
|
||||||
flatlaf-natives/flatlaf-natives-windows/build
|
flatlaf-core/src/main/resources/com/formdev/flatlaf/natives
|
||||||
|
flatlaf-natives/flatlaf-natives-*/build
|
||||||
|
|||||||
496
CHANGELOG.md
496
CHANGELOG.md
@@ -1,6 +1,500 @@
|
|||||||
FlatLaf Change Log
|
FlatLaf Change Log
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
## 2.5
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Linux: Use X11 window manager events to move window and to show window menu
|
||||||
|
(right-click on window title bar), if custom window decorations are enabled.
|
||||||
|
This gives FlatLaf windows a more "native" feeling. (issue #482)
|
||||||
|
- MenuBar: Support different menu selection style UI defaults for `MenuBar` and
|
||||||
|
`MenuItem`. (issue #587)
|
||||||
|
- MenuBar: Top level menus now use `MenuBar.font` instead of `Menu.font`. (issue
|
||||||
|
#589)
|
||||||
|
- PasswordField: Reveal button is now hidden (and turned off) if password field
|
||||||
|
is disabled. (issue #501)
|
||||||
|
- TabbedPane: New option to disable tab run rotation in wrap layout. Set UI
|
||||||
|
value `TabbedPane.rotateTabRuns` to `false`. (issue #574)
|
||||||
|
- Window decorations:
|
||||||
|
- Added client property to mark components in embedded menu bar as "caption"
|
||||||
|
(allow moving window). (issue #569)
|
||||||
|
- Option to show window icon only in frames, but not in dialogs. Set UI value
|
||||||
|
`TitlePane.showIconInDialogs` to `false`. (issue #589)
|
||||||
|
- Added UI value `TitlePane.font` to customize window title font. (issue #589)
|
||||||
|
- Added system property `flatlaf.updateUIOnSystemFontChange` to allow disabling
|
||||||
|
automatic UI update when system font changes. (issue #580)
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Fixed missing UI value `MenuItem.acceleratorDelimiter` on macOS. (was `null`,
|
||||||
|
is now an empty string)
|
||||||
|
- Fixed possible exception in `FlatUIUtils.resetRenderingHints()`. (issue #575)
|
||||||
|
- Fixed AWT components on macOS, which use Swing components internally. (issue
|
||||||
|
#583)
|
||||||
|
- SwingX: Fixed missing highlighting of "today" in `JXMonthView` and
|
||||||
|
`JXDatePicker`.
|
||||||
|
|
||||||
|
|
||||||
|
## 2.4
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Native window decorations (Windows 10/11 only):
|
||||||
|
- There is now a small area at top of the embedded menu bar to resize the
|
||||||
|
window.
|
||||||
|
- Improved window title bar layout for small window widths:
|
||||||
|
- Width of iconify/maximize/close buttons is reduced (if necessary) to give
|
||||||
|
more space to embedded menu bar and title.
|
||||||
|
- Window title now has a minimum width to always allow moving window
|
||||||
|
(click-and-drag on window title). Instead, embedded menu bar is made
|
||||||
|
smaller.
|
||||||
|
- Option to show window icon beside window title, if menu bar is embedded or
|
||||||
|
title is centered. Set UI value `TitlePane.showIconBesideTitle` to `true`.
|
||||||
|
- No longer reduce height of window title bar if it has an embedded menu bar
|
||||||
|
and is maximized.
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- ComboBox: Fixed vertical alignment of text in popup list with text in combo
|
||||||
|
box in IntelliJ/Darcula themes.
|
||||||
|
- Menus: Fixed application freeze under very special conditions (invoking
|
||||||
|
`FlatLaf.initialize()` twice in NetBeans GUI builder) and using menu that has
|
||||||
|
submenus. See
|
||||||
|
[NetBeans issue #4231](https://github.com/apache/netbeans/issues/4231#issuecomment-1179611682)
|
||||||
|
for details.
|
||||||
|
- MenuItem: Fixed sometimes wrapped HTML text on HiDPI screens on Windows.
|
||||||
|
- TableHeader: Fixed exception when changing table structure (e.g. removing
|
||||||
|
column) from a table header popup menu action. (issue #532)
|
||||||
|
- `HiDPIUtils.paintAtScale1x()` now supports rotated graphics. (issue #557)
|
||||||
|
- Typography: No longer use `Consolas` or `Courier New` as monospaced font on
|
||||||
|
Windows because they have bad vertically placement.
|
||||||
|
- Native window decorations (Windows 10/11 only):
|
||||||
|
- Do not center window title if embedded menu bar is empty or has no menus at
|
||||||
|
left side, but some components at right side. (issue #558)
|
||||||
|
- Do not use window decorations if system property `sun.java2d.opengl` is
|
||||||
|
`true` on Windows 10. (issue #540)
|
||||||
|
- Fixed missing top window border in dark themes if window drop shadows are
|
||||||
|
disabled in system settings. (issue #554; Windows 10 only)
|
||||||
|
- Right-to-left component orientation of title bar was lost when switching
|
||||||
|
theme.
|
||||||
|
|
||||||
|
|
||||||
|
## 2.3
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- FileChooser: Added (optional) shortcuts panel. On Windows it contains "Recent
|
||||||
|
Items", "Desktop", "Documents", "This PC" and "Network". On macOS and Linux it
|
||||||
|
is empty/hidden. (issue #100)
|
||||||
|
- Button and ToggleButton: Added missing foreground colors for hover, pressed,
|
||||||
|
focused and selected states. (issue #535)
|
||||||
|
- Table: Optionally paint alternating rows below table if table is smaller than
|
||||||
|
scroll pane. Set UI value `Table.paintOutsideAlternateRows` to `true`.
|
||||||
|
Requires that `Table.alternateRowColor` is set to a color. (issue #504)
|
||||||
|
- ToggleButton: Made the underline placement of tab-style toggle buttons
|
||||||
|
configurable. (PR #530; issue #529)
|
||||||
|
- Added spanish translation. (PR #525)
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- IntelliJ Themes: Fixed `TitledBorder` text color in "Monokai Pro" theme.
|
||||||
|
(issue #524)
|
||||||
|
|
||||||
|
|
||||||
|
## 2.2
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- SplitPane: Allow limiting one-touch expanding to a single side (set client
|
||||||
|
property `JSplitPane.expandableSide` to `"left"` or `"right"`). (issue #355)
|
||||||
|
- TabbedPane: Selected tab underline color now changes depending on whether the
|
||||||
|
focus is within the tab content. (issue #398)
|
||||||
|
- IntelliJ Themes:
|
||||||
|
- Added "Monokai Pro" and "Xcode-Dark" themes.
|
||||||
|
- TabbedPane now use different background color for selected tabs in all "Arc"
|
||||||
|
themes, in "Hiberbee Dark" and in all "Material UI Lite" themes.
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Native window decorations (Windows 10/11 only): Fixed wrong window title
|
||||||
|
character encoding used in Windows taskbar. (issue #502)
|
||||||
|
- Button: Fixed icon layout and preferred width of default buttons that use bold
|
||||||
|
font. (issue #506)
|
||||||
|
- FileChooser: Enabled full row selection for details view to fix alternate row
|
||||||
|
coloring. (issue #512)
|
||||||
|
- SplitPane: Fixed `StackOverflowError` caused by layout loop that may occur
|
||||||
|
under special circumstances. (issue #513)
|
||||||
|
- Table: Slightly changed grid colors to make grid better recognizable. (issue
|
||||||
|
#514)
|
||||||
|
- ToolBar: Fixed endless loop in focus navigation that may occur under special
|
||||||
|
circumstances. (issue #505)
|
||||||
|
- IntelliJ Themes: `Component.accentColor` UI property now has useful theme
|
||||||
|
specific values. (issue #507)
|
||||||
|
|
||||||
|
|
||||||
|
## 2.1
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Menus: Improved usability of submenus. (PR #490; issue #247)
|
||||||
|
- Menus: Scroll large menus using mouse wheel or up/down arrows. (issue #225)
|
||||||
|
- Linux: Support using custom window decorations. Enable with
|
||||||
|
`JFrame.setDefaultLookAndFeelDecorated(true)` and
|
||||||
|
`JDialog.setDefaultLookAndFeelDecorated(true)` before creating a window.
|
||||||
|
(issue #482)
|
||||||
|
- ScrollBar: Added UI value `ScrollBar.minimumButtonSize` to specify minimum
|
||||||
|
scroll arrow button size (if shown). (issue #493)
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- PasswordField: Fixed reveal button appearance in IntelliJ themes. (issue #494)
|
||||||
|
- ScrollBar: Center and scale arrows in scroll up/down buttons (if shown).
|
||||||
|
(issue #493)
|
||||||
|
- TextArea, TextPane and EditorPane: No longer select all text when component is
|
||||||
|
focused for the first time. (issue #498; regression in FlatLaf 2.0)
|
||||||
|
- TabbedPane: Disable all items in "Show Hidden Tabs" popup menu if tabbed pane
|
||||||
|
is disabled.
|
||||||
|
|
||||||
|
#### Incompatibilities
|
||||||
|
|
||||||
|
- Method `FlatUIUtils.paintArrow()` (and class `FlatArrowButton`) now paints
|
||||||
|
arrows one pixel smaller than before. To fix this, increase parameter
|
||||||
|
`arrowSize` by one.
|
||||||
|
|
||||||
|
|
||||||
|
## 2.0.2
|
||||||
|
|
||||||
|
- Native window decorations (Windows 10/11 only): Fixed rendering artifacts on
|
||||||
|
HiDPI screens when dragging window partly offscreen and back into screen
|
||||||
|
bounds. (issue #477)
|
||||||
|
- Repaint component when setting client property `JComponent.outline` (issue
|
||||||
|
#480).
|
||||||
|
- macOS: Fixed NPE when using some icons in main menu items. (issue #483)
|
||||||
|
|
||||||
|
|
||||||
|
## 2.0.1
|
||||||
|
|
||||||
|
- Fixed memory leak in Panel, Separator and ToolBarSeparator. (issue #471;
|
||||||
|
regression in FlatLaf 2.0)
|
||||||
|
- ToolTip: Fixed wrong tooltip location if component overrides
|
||||||
|
`JComponent.getToolTipLocation()` and wants place tooltip under mouse
|
||||||
|
location. (issue #468)
|
||||||
|
- Extras: Added copy constructor to `FlatSVGIcon`. (issue #465)
|
||||||
|
- Moved `module-info.class` from `META-INF\versions\9\` to root folder of JARs.
|
||||||
|
(issue #466)
|
||||||
|
|
||||||
|
|
||||||
|
## 2.0
|
||||||
|
|
||||||
|
- Added system property `flatlaf.nativeLibraryPath` to load native libraries
|
||||||
|
from a directory. (PR #453)
|
||||||
|
- Fixed "endless recursion in font" exception in
|
||||||
|
`FlatLaf$ActiveFont.createValue()` if `UIManager.getFont()` is invoked from
|
||||||
|
multiple threads. (issue #456)
|
||||||
|
- PasswordField: Preserve reveal button state when switching theme. (PR #442;
|
||||||
|
issue #173)
|
||||||
|
- PasswordField: Reveal button did not show password if
|
||||||
|
`JPasswordField.setEchoChar()` was invoked from application. (PR #442; issue
|
||||||
|
#173)
|
||||||
|
- Slider: Fixed/improved focused indicator color when changing accent color. (PR
|
||||||
|
#375)
|
||||||
|
- TextField:
|
||||||
|
- Improved hover/pressed/selected colors of leading/trailing buttons (e.g.
|
||||||
|
"reveal" button in password field). (issue #452)
|
||||||
|
- Clear button no longer paints over round border. (issue #451)
|
||||||
|
- Extras: Fixed concurrent loading of SVG icons on multiple threads. (issue
|
||||||
|
#459)
|
||||||
|
- Use FlatLaf native window decorations by default when running in
|
||||||
|
[JetBrains Runtime](https://github.com/JetBrains/JetBrainsRuntime/wiki)
|
||||||
|
(instead of using JetBrains custom decorations). System variable
|
||||||
|
`flatlaf.useJetBrainsCustomDecorations` is now `false` by default (was `true`
|
||||||
|
in FlatLaf 1.x). (issue #454)
|
||||||
|
- Native window decorations:
|
||||||
|
- Fixed blurry iconify/maximize/close button hover rectangles at 125%, 150% or
|
||||||
|
175% scaling. (issue #431)
|
||||||
|
- Updated maximize and restore icons for Windows 11 style. (requires Java
|
||||||
|
8u321, 11.0.14, 17.0.2 or 18+)
|
||||||
|
- Updated hover and pressed colors of iconify/maximize/close buttons for
|
||||||
|
Windows 11 style.
|
||||||
|
|
||||||
|
|
||||||
|
## 2.0-rc1
|
||||||
|
|
||||||
|
#### 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 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 a
|
||||||
|
`javax.swing.Icon`). (PR #378; issue #368)
|
||||||
|
- Support leading and trailing components (set client property
|
||||||
|
`JTextField.leadingComponent` or `JTextField.trailingComponent` to a
|
||||||
|
`java.awt.Component`). (PR #386)
|
||||||
|
- Support "clear" (or "cancel") button to empty text field. Only shown if text
|
||||||
|
field is not empty, editable and enabled. (set client property
|
||||||
|
`JTextField.showClearButton` to `true`). (PR #442)
|
||||||
|
- PasswordField: Support reveal (or "eye") button to show password. (see UI
|
||||||
|
value `PasswordField.showRevealButton`) (PR #442; issue #173)
|
||||||
|
- 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)
|
||||||
|
- MenuItem:
|
||||||
|
- Paint the selected icon when the item is selected. (PR #415)
|
||||||
|
- Vertically align text if icons have different widths. (issue #437)
|
||||||
|
- Panel: Support painting background with rounded corners. (issue #367)
|
||||||
|
- 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)
|
||||||
|
- SwingX:
|
||||||
|
- New "column control" icon for `JXTable` that scales and uses antialiasing.
|
||||||
|
(issue #434)
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Native window decorations: Fixed `UnsatisfiedLinkError` on Windows 11 for ARM
|
||||||
|
processors. (issue #443)
|
||||||
|
- MenuBar: Do not fill background if non-opaque and having custom background
|
||||||
|
color. (issue #409)
|
||||||
|
- InternalFrame: Fill background to avoid that parent may shine through internal
|
||||||
|
frame if it contains non-opaque components. (better fix for issue #274)
|
||||||
|
- SwingX: Fixed `NullPointerException` in `FlatCaret` when using
|
||||||
|
`org.jdesktop.swingx.prompt.PromptSupport.setPrompt()` on a text field and
|
||||||
|
then switching theme.
|
||||||
|
|
||||||
|
|
||||||
|
## 1.6.5
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Linux: Fixed font problems when running on Oracle Java (OpenJDK is not
|
||||||
|
affected):
|
||||||
|
- oversized text if system font is "Inter" (issue #427)
|
||||||
|
- missing text if system font is "Cantarell" (on Fedora)
|
||||||
|
- MenuItem: Changed accelerator delimiter from `-` to `+`. (Windows and Linux).
|
||||||
|
- ComboBox: Fixed occasional `StackOverflowError` when modifying combo box not
|
||||||
|
on AWT thread. (issue #432)
|
||||||
|
- macOS: Fixed `NullPointerException` when using AWT component
|
||||||
|
`java.awt.Choice`. (issue #439)
|
||||||
|
- Native window decorations: Do not exit application with `UnsatisfiedLinkError`
|
||||||
|
in case that FlatLaf DLL cannot be executed because of restrictions on
|
||||||
|
temporary directory. Instead, continue with default window decorations. (issue
|
||||||
|
#436)
|
||||||
|
|
||||||
|
|
||||||
|
## 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
|
## 1.3
|
||||||
|
|
||||||
#### New features and improvements
|
#### New features and improvements
|
||||||
@@ -10,7 +504,7 @@ FlatLaf Change Log
|
|||||||
`PasswordField.focusedBackground`, `FormattedTextField.focusedBackground`,
|
`PasswordField.focusedBackground`, `FormattedTextField.focusedBackground`,
|
||||||
`TextArea.focusedBackground`, `TextPane.focusedBackground`,
|
`TextArea.focusedBackground`, `TextPane.focusedBackground`,
|
||||||
`EditorPane.focusedBackground`, `ComboBox.focusedBackground`,
|
`EditorPane.focusedBackground`, `ComboBox.focusedBackground`,
|
||||||
`ComboBox.buttonFocusedBackground`, `ComboBox.popupFocusedBackground` and
|
`ComboBox.buttonFocusedBackground`, `ComboBox.popupBackground` and
|
||||||
`Spinner.focusedBackground`). (issue #335)
|
`Spinner.focusedBackground`). (issue #335)
|
||||||
|
|
||||||
#### Fixed bugs
|
#### Fixed bugs
|
||||||
|
|||||||
45
README.md
45
README.md
@@ -11,9 +11,9 @@ scales on **HiDPI** displays and runs on Java 8 or newer.
|
|||||||
The look is heavily inspired by **Darcula** and **IntelliJ** themes from
|
The look is heavily inspired by **Darcula** and **IntelliJ** themes from
|
||||||
IntelliJ IDEA 2019.2+ and uses almost the same colors and icons.
|
IntelliJ IDEA 2019.2+ and uses almost the same colors and icons.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
IntelliJ Platform Themes
|
IntelliJ Platform Themes
|
||||||
@@ -67,10 +67,13 @@ docs).
|
|||||||
Addons
|
Addons
|
||||||
------
|
------
|
||||||
|
|
||||||
- [IntelliJ Themes Pack](flatlaf-intellij-themes)
|
- [IntelliJ Themes Pack](flatlaf-intellij-themes) - bundles many popular
|
||||||
- [Extras](flatlaf-extras)
|
open-source 3rd party themes
|
||||||
- [SwingX](flatlaf-swingx)
|
- [Extras](flatlaf-extras) - SVG icon, tri-state check box, UI inspectors, and
|
||||||
- [JIDE Common Layer](flatlaf-jide-oss)
|
more
|
||||||
|
- [SwingX](flatlaf-swingx) - support for SwingX components
|
||||||
|
- [JIDE Common Layer](flatlaf-jide-oss) - support for JIDE Common Layer
|
||||||
|
components
|
||||||
|
|
||||||
|
|
||||||
Getting started
|
Getting started
|
||||||
@@ -96,10 +99,22 @@ For more information and documentation visit
|
|||||||
- [Customizing](https://www.formdev.com/flatlaf/customizing/)
|
- [Customizing](https://www.formdev.com/flatlaf/customizing/)
|
||||||
- [How to Customize](https://www.formdev.com/flatlaf/how-to-customize/)
|
- [How to Customize](https://www.formdev.com/flatlaf/how-to-customize/)
|
||||||
- [Properties Files](https://www.formdev.com/flatlaf/properties-files/)
|
- [Properties Files](https://www.formdev.com/flatlaf/properties-files/)
|
||||||
|
- [Components UI Properties](https://www.formdev.com/flatlaf/components/)
|
||||||
|
- [Typography](https://www.formdev.com/flatlaf/typography/)
|
||||||
- [Client Properties](https://www.formdev.com/flatlaf/client-properties/)
|
- [Client Properties](https://www.formdev.com/flatlaf/client-properties/)
|
||||||
- [System Properties](https://www.formdev.com/flatlaf/system-properties/)
|
- [System Properties](https://www.formdev.com/flatlaf/system-properties/)
|
||||||
|
|
||||||
|
|
||||||
|
Theme Editor
|
||||||
|
------------
|
||||||
|
|
||||||
|
The Theme Editor that supports editing FlatLaf theme properties files. See
|
||||||
|
[Theme Editor documentation](https://www.formdev.com/flatlaf/theme-editor/) for
|
||||||
|
details and downloads.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
Buzz
|
Buzz
|
||||||
----
|
----
|
||||||
|
|
||||||
@@ -111,6 +126,13 @@ Buzz
|
|||||||
Applications using FlatLaf
|
Applications using FlatLaf
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
|
-  [Ultorg](https://www.ultorg.com/) (**commercial**) - a
|
||||||
|
visual query system for relational databases
|
||||||
|
-  [MooInfo](https://github.com/rememberber/MooInfo) -
|
||||||
|
visual implementation of OSHI, to view information about the system and
|
||||||
|
hardware
|
||||||
|
-  [Jailer](https://github.com/Wisser/Jailer) 11.2 -
|
||||||
|
database subsetting and relational data browsing tool
|
||||||
- [Apache NetBeans](https://netbeans.apache.org/) 11.3 - IDE for Java, PHP, HTML
|
- [Apache NetBeans](https://netbeans.apache.org/) 11.3 - IDE for Java, PHP, HTML
|
||||||
and much more
|
and much more
|
||||||
- [jclasslib bytecode viewer](https://github.com/ingokegel/jclasslib) 5.5
|
- [jclasslib bytecode viewer](https://github.com/ingokegel/jclasslib) 5.5
|
||||||
@@ -138,13 +160,16 @@ Applications using FlatLaf
|
|||||||
[OpenStreetMap](https://www.openstreetmap.org/) (requires FlatLaf JOSM plugin)
|
[OpenStreetMap](https://www.openstreetmap.org/) (requires FlatLaf JOSM plugin)
|
||||||
- [jAlbum](https://jalbum.net/) 21 (**commercial**) - creates photo album
|
- [jAlbum](https://jalbum.net/) 21 (**commercial**) - creates photo album
|
||||||
websites
|
websites
|
||||||
|
-  [PDF Studio](https://www.qoppa.com/pdfstudio/) 2021
|
||||||
|
(**commercial**) - create, review and edit PDF documents
|
||||||
- [XMLmind XML Editor](https://www.xmlmind.com/xmleditor/) 9.3 (**commercial**)
|
- [XMLmind XML Editor](https://www.xmlmind.com/xmleditor/) 9.3 (**commercial**)
|
||||||
- [Total Validator](https://www.totalvalidator.com/) 15 (**commercial**) -
|
- [Total Validator](https://www.totalvalidator.com/) 15 (**commercial**) -
|
||||||
checks your website
|
checks your website
|
||||||
- [j-lawyer](https://github.com/jlawyerorg/j-lawyer-org) - Kanzleisoftware
|
- [j-lawyer](https://github.com/jlawyerorg/j-lawyer-org) - Kanzleisoftware
|
||||||
- [MegaMek](https://github.com/MegaMek/megamek) v0.47.4 and
|
- [MegaMek](https://github.com/MegaMek/megamek),
|
||||||
[MekHQ](https://github.com/MegaMek/mekhq) v0.47.5 - a turn-based sci-fi board
|
[MegaMekLab](https://github.com/MegaMek/megameklab) and
|
||||||
game
|
[MekHQ](https://github.com/MegaMek/mekhq) v0.47.5+ - a sci-fi tabletop
|
||||||
|
BattleTech simulator suite handling battles, unit building, and campaigns
|
||||||
- [GUIslice Builder](https://github.com/ImpulseAdventure/GUIslice-Builder)
|
- [GUIslice Builder](https://github.com/ImpulseAdventure/GUIslice-Builder)
|
||||||
0.13.b024 - GUI builder for
|
0.13.b024 - GUI builder for
|
||||||
[GUIslice](https://github.com/ImpulseAdventure/GUIslice), a lightweight GUI
|
[GUIslice](https://github.com/ImpulseAdventure/GUIslice), a lightweight GUI
|
||||||
@@ -170,7 +195,7 @@ Applications using FlatLaf
|
|||||||
systems development platform
|
systems development platform
|
||||||
- [JPass](https://github.com/gaborbata/jpass) - password manager with strong
|
- [JPass](https://github.com/gaborbata/jpass) - password manager with strong
|
||||||
encryption
|
encryption
|
||||||
- [Jes - Die Java-EÜR](https://www.jes-eur.de)
|
- [Jes - Die Java-EÜR](https://www.jes-eur.de)
|
||||||
- [Mapton](https://mapton.org/) 2.0
|
- [Mapton](https://mapton.org/) 2.0
|
||||||
([source code](https://github.com/trixon/mapton)) - some kind of map
|
([source code](https://github.com/trixon/mapton)) - some kind of map
|
||||||
application (based on NetBeans platform)
|
application (based on NetBeans platform)
|
||||||
|
|||||||
@@ -14,8 +14,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
val releaseVersion = "1.3"
|
val releaseVersion = "2.5"
|
||||||
val developmentVersion = "1.4-SNAPSHOT"
|
val developmentVersion = "3.0-SNAPSHOT"
|
||||||
|
|
||||||
version = if( java.lang.Boolean.getBoolean( "release" ) ) releaseVersion else developmentVersion
|
version = if( java.lang.Boolean.getBoolean( "release" ) ) releaseVersion else developmentVersion
|
||||||
|
|
||||||
|
|||||||
46
buildSrc/src/main/kotlin/flatlaf-cpp-library.gradle.kts
Normal file
46
buildSrc/src/main/kotlin/flatlaf-cpp-library.gradle.kts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
`cpp-library`
|
||||||
|
}
|
||||||
|
|
||||||
|
library {
|
||||||
|
// disable debuggable for release builds to make shared libraries smaller
|
||||||
|
binaries.configureEach( CppSharedLibrary::class ) {
|
||||||
|
with( compileTask.get() ) {
|
||||||
|
if( name.contains( "Release" ) )
|
||||||
|
isDebuggable = false
|
||||||
|
}
|
||||||
|
with( linkTask.get() ) {
|
||||||
|
if( name.contains( "Release" ) )
|
||||||
|
debuggable.set( false )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
withType<CppCompile>().configureEach {
|
||||||
|
doFirst {
|
||||||
|
println( "Used Tool Chain:" )
|
||||||
|
println( " - ${toolChain.get()}" )
|
||||||
|
println( "Available Tool Chains:" )
|
||||||
|
toolChains.forEach {
|
||||||
|
println( " - $it" )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,7 +28,7 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
add( "java9Compile", sourceSets.main.get().output )
|
add( "java9Implementation", sourceSets.main.get().output )
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
|
|||||||
36
buildSrc/src/main/kotlin/flatlaf-jni-headers.gradle.kts
Normal file
36
buildSrc/src/main/kotlin/flatlaf-jni-headers.gradle.kts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
open class JniHeadersExtension {
|
||||||
|
var headers: List<String> = emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
val extension = project.extensions.create<JniHeadersExtension>( "flatlafJniHeaders" )
|
||||||
|
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
register<Copy>( "jni-headers" ) {
|
||||||
|
// depend on :flatlaf-core:compileJava because it generates the JNI headers
|
||||||
|
dependsOn( ":flatlaf-core:compileJava" )
|
||||||
|
|
||||||
|
from( project( ":flatlaf-core" ).buildDir.resolve( "generated/jni-headers" ) )
|
||||||
|
into( "src/main/headers" )
|
||||||
|
include( extension.headers )
|
||||||
|
filter<org.apache.tools.ant.filters.FixCrLfFilter>(
|
||||||
|
"eol" to org.apache.tools.ant.filters.FixCrLfFilter.CrLf.newInstance( "lf" )
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -61,13 +61,9 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
manifest.attributes( "Multi-Release" to "true" )
|
|
||||||
|
|
||||||
into( "META-INF/versions/9" ) {
|
|
||||||
from( sourceSets["module-info"].output ) {
|
from( sourceSets["module-info"].output ) {
|
||||||
include( "module-info.class" )
|
include( "module-info.class" )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,17 @@ plugins {
|
|||||||
`flatlaf-publish`
|
`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.7" )
|
||||||
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
withSourcesJar()
|
withSourcesJar()
|
||||||
withJavadocJar()
|
withJavadocJar()
|
||||||
@@ -32,11 +43,6 @@ tasks {
|
|||||||
options.headerOutputDirectory.set( buildDir.resolve( "generated/jni-headers" ) )
|
options.headerOutputDirectory.set( buildDir.resolve( "generated/jni-headers" ) )
|
||||||
}
|
}
|
||||||
|
|
||||||
processResources {
|
|
||||||
// build native libraries
|
|
||||||
dependsOn( ":flatlaf-natives-windows:assemble" )
|
|
||||||
}
|
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
archiveBaseName.set( "flatlaf" )
|
archiveBaseName.set( "flatlaf" )
|
||||||
|
|
||||||
@@ -52,6 +58,64 @@ tasks {
|
|||||||
named<Jar>( "javadocJar" ) {
|
named<Jar>( "javadocJar" ) {
|
||||||
archiveBaseName.set( "flatlaf" )
|
archiveBaseName.set( "flatlaf" )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
check {
|
||||||
|
dependsOn( "sigtestCheck" )
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
||||||
|
|
||||||
|
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 )
|
||||||
|
jvmArgs( listOf( "--add-opens", "java.desktop/javax.swing.plaf.basic=ALL-UNNAMED" ) )
|
||||||
|
}
|
||||||
|
|
||||||
|
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" )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
register( "sigtestCheck" ) {
|
||||||
|
group = "verification"
|
||||||
|
dependsOn( "jar" )
|
||||||
|
|
||||||
|
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" )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flatlafPublish {
|
flatlafPublish {
|
||||||
|
|||||||
1130
flatlaf-core/flatlaf-core-sigtest.txt
Normal file
1130
flatlaf-core/flatlaf-core-sigtest.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -126,6 +126,57 @@ public interface FlatClientProperties
|
|||||||
|
|
||||||
//---- JComponent ---------------------------------------------------------
|
//---- 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.
|
* Specifies minimum width of a component.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -202,6 +253,19 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String COMPONENT_FOCUS_OWNER = "JComponent.focusOwner";
|
String COMPONENT_FOCUS_OWNER = "JComponent.focusOwner";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether a component in an embedded menu bar should behave as caption
|
||||||
|
* (left-click allows moving window, right-click shows window system menu).
|
||||||
|
* The component does not receive mouse pressed/released/clicked/dragged events,
|
||||||
|
* but it gets mouse entered/exited/moved events.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
|
*
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
String COMPONENT_TITLE_BAR_CAPTION = "JComponent.titleBarCaption";
|
||||||
|
|
||||||
//---- Popup --------------------------------------------------------------
|
//---- Popup --------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -280,6 +344,24 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded";
|
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).
|
* Background color of window title bar (requires enabled window decorations).
|
||||||
* <p>
|
* <p>
|
||||||
@@ -322,8 +404,71 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String SCROLL_PANE_SMOOTH_SCROLLING = "JScrollPane.smoothScrolling";
|
String SCROLL_PANE_SMOOTH_SCROLLING = "JScrollPane.smoothScrolling";
|
||||||
|
|
||||||
|
//---- JSplitPane ---------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies what side of the spilt pane is allowed to expand
|
||||||
|
* via one-touch expanding arrow buttons.
|
||||||
|
* Requires that one-touch expanding is enabled with
|
||||||
|
* {@link javax.swing.JSplitPane#setOneTouchExpandable(boolean)}.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JSplitPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||||
|
* <strong>Allowed Values</strong>
|
||||||
|
* {@link #SPLIT_PANE_EXPANDABLE_SIDE_LEFT} or
|
||||||
|
* {@link #SPLIT_PANE_EXPANDABLE_SIDE_RIGHT}
|
||||||
|
*
|
||||||
|
* @since 2.2
|
||||||
|
*/
|
||||||
|
String SPLIT_PANE_EXPANDABLE_SIDE = "JSplitPane.expandableSide";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow expanding only left/top side of the split pane.
|
||||||
|
*
|
||||||
|
* @see #SPLIT_PANE_EXPANDABLE_SIDE
|
||||||
|
* @since 2.2
|
||||||
|
*/
|
||||||
|
String SPLIT_PANE_EXPANDABLE_SIDE_LEFT = "left";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow expanding only right/bottom side of the split pane.
|
||||||
|
*
|
||||||
|
* @see #SPLIT_PANE_EXPANDABLE_SIDE
|
||||||
|
* @since 2.2
|
||||||
|
*/
|
||||||
|
String SPLIT_PANE_EXPANDABLE_SIDE_RIGHT = "right";
|
||||||
|
|
||||||
//---- JTabbedPane --------------------------------------------------------
|
//---- 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.
|
* Specifies whether separators are shown between tabs.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -665,9 +810,9 @@ public interface FlatClientProperties
|
|||||||
/**
|
/**
|
||||||
* Specifies a component that will be placed at the leading edge of the tabs area.
|
* Specifies a component that will be placed at the leading edge of the tabs area.
|
||||||
* <p>
|
* <p>
|
||||||
* For top and bottom tab placement, the layed out component size will be
|
* For top and bottom tab placement, the laid out component size will be
|
||||||
* the preferred component width and the tab area height.<br>
|
* the preferred component width and the tab area height.<br>
|
||||||
* For left and right tab placement, the layed out component size will be
|
* For left and right tab placement, the laid out component size will be
|
||||||
* the tab area width and the preferred component height.
|
* the tab area width and the preferred component height.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||||
@@ -678,9 +823,9 @@ public interface FlatClientProperties
|
|||||||
/**
|
/**
|
||||||
* Specifies a component that will be placed at the trailing edge of the tabs area.
|
* Specifies a component that will be placed at the trailing edge of the tabs area.
|
||||||
* <p>
|
* <p>
|
||||||
* For top and bottom tab placement, the layed out component size will be
|
* For top and bottom tab placement, the laid out component size will be
|
||||||
* the available horizontal space (minimum is preferred component width) and the tab area height.<br>
|
* 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
|
* For left and right tab placement, the laid out component size will be
|
||||||
* the tab area width and the available vertical space (minimum is preferred component height).
|
* the tab area width and the available vertical space (minimum is preferred component height).
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||||
@@ -733,10 +878,149 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String PLACEHOLDER_TEXT = "JTextField.placeholderText";
|
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";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies a component that will be placed at the leading edge of the text field.
|
||||||
|
* <p>
|
||||||
|
* The component will be positioned inside and aligned to the visible text field border.
|
||||||
|
* There is no gap between the visible border and the component.
|
||||||
|
* The laid out component size will be the preferred component width
|
||||||
|
* and the inner text field height.
|
||||||
|
* <p>
|
||||||
|
* The component should be not opaque because the text field border is painted
|
||||||
|
* slightly inside the usually visible border in some cases.
|
||||||
|
* E.g. when focused (in some themes) or when an outline color is specified
|
||||||
|
* (see {@link #OUTLINE}).
|
||||||
|
* <p>
|
||||||
|
* The component is prepared in the following way:
|
||||||
|
* <ul>
|
||||||
|
* <li>Component client property {@link #STYLE_CLASS} is set to {@code inTextField}.
|
||||||
|
* <li>If component is a button or toggle button, client property {@link #BUTTON_TYPE}
|
||||||
|
* is set to {@link #BUTTON_TYPE_TOOLBAR_BUTTON}
|
||||||
|
* and button cursor is set to default cursor (if not set).
|
||||||
|
* <li>If component is a toolbar, client property {@link #STYLE_CLASS}
|
||||||
|
* is set to {@code inTextField} on all toolbar children
|
||||||
|
* and toolbar cursor is set to default cursor (if not set).
|
||||||
|
* </ul>
|
||||||
|
* Because text fields use the text cursor by default and the cursor is inherited by child components,
|
||||||
|
* it may be necessary to explicitly set component cursor if you e.g. need the default arrow cursor.
|
||||||
|
* E.g. {@code comp.setCursor( Cursor.getDefaultCursor() )}.
|
||||||
|
* <p>
|
||||||
|
* Styling is used to modify insets/margins and appearance of buttons and toolbars
|
||||||
|
* so that they fit nicely into the text field and do not increase text field height.
|
||||||
|
* See styles {@code [style]Button.inTextField} and {@code [style]ToolBar.inTextField}
|
||||||
|
* in {@code Flat[Light|Dark]Laf.properties}.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
|
||||||
|
* <strong>Value type</strong> {@link javax.swing.JComponent}
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
String TEXT_FIELD_LEADING_COMPONENT = "JTextField.leadingComponent";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies a component that will be placed at the trailing edge of the text field.
|
||||||
|
* <p>
|
||||||
|
* See {@link #TEXT_FIELD_LEADING_COMPONENT} for details.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
|
||||||
|
* <strong>Value type</strong> {@link javax.swing.JComponent}
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
String TEXT_FIELD_TRAILING_COMPONENT = "JTextField.trailingComponent";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether a "clear" (or "cancel") button is shown on the trailing side
|
||||||
|
* if the text field is not empty, editable and enabled. Default is {@code false}.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
String TEXT_FIELD_SHOW_CLEAR_BUTTON = "JTextField.showClearButton";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the callback that is invoked when a "clear" (or "cancel") button is clicked.
|
||||||
|
* If a callback is specified than it is responsible for clearing the text field.
|
||||||
|
* Without callback, the text field clears itself.
|
||||||
|
* <p>
|
||||||
|
* Either use a {@link java.lang.Runnable}:
|
||||||
|
* <pre>{@code
|
||||||
|
* myTextField.putClientProperty( "JTextField.clearCallback",
|
||||||
|
* (Runnable) () -> {
|
||||||
|
* // clear field here or cancel search
|
||||||
|
* } );
|
||||||
|
* }</pre>
|
||||||
|
* Or use a {@link java.util.function.Consumer}<javax.swing.text.JTextComponent>
|
||||||
|
* that receives the text field as parameter:
|
||||||
|
* <pre>{@code
|
||||||
|
* myTextField.putClientProperty( "JTextField.clearCallback",
|
||||||
|
* (Consumer<JTextComponent>) textField -> {
|
||||||
|
* // clear field here or cancel search
|
||||||
|
* } );
|
||||||
|
* }</pre>
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Runnable}
|
||||||
|
* or {@link java.util.function.Consumer}<javax.swing.text.JTextComponent>
|
||||||
|
*
|
||||||
|
* @see #TEXT_FIELD_SHOW_CLEAR_BUTTON
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
String TEXT_FIELD_CLEAR_CALLBACK = "JTextField.clearCallback";
|
||||||
|
|
||||||
//---- JToggleButton ------------------------------------------------------
|
//---- JToggleButton ------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Height of underline if toggle button type is {@link #BUTTON_TYPE_TAB}.
|
* Placement of underline if toggle button type is {@link #BUTTON_TYPE_TAB}
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Integer}<br>
|
||||||
|
* <strong>SupportedValues:</strong>
|
||||||
|
* {@link SwingConstants#BOTTOM} (default)
|
||||||
|
* {@link SwingConstants#TOP},
|
||||||
|
* {@link SwingConstants#LEFT} or
|
||||||
|
* {@link SwingConstants#RIGHT}
|
||||||
|
*
|
||||||
|
* @since 2.3
|
||||||
|
*/
|
||||||
|
String TAB_BUTTON_UNDERLINE_PLACEMENT = "JToggleButton.tab.underlinePlacement";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thickness of underline if toggle button type is {@link #BUTTON_TYPE_TAB}.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
|
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Integer}
|
* <strong>Value type</strong> {@link java.lang.Integer}
|
||||||
@@ -801,8 +1085,7 @@ public interface FlatClientProperties
|
|||||||
* If the client property is not set, or not a {@link Boolean}, defaultValue is returned.
|
* If the client property is not set, or not a {@link Boolean}, defaultValue is returned.
|
||||||
*/
|
*/
|
||||||
static Boolean clientPropertyBooleanStrict( JComponent c, String key, Boolean defaultValue ) {
|
static Boolean clientPropertyBooleanStrict( JComponent c, String key, Boolean defaultValue ) {
|
||||||
Object value = c.getClientProperty( key );
|
return clientProperty( c, key, defaultValue, Boolean.class );
|
||||||
return (value instanceof Boolean) ? (Boolean) value : defaultValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -819,7 +1102,18 @@ public interface FlatClientProperties
|
|||||||
* If the client property is not set, or not a color, defaultValue is returned.
|
* If the client property is not set, or not a color, defaultValue is returned.
|
||||||
*/
|
*/
|
||||||
static Color clientPropertyColor( JComponent c, String key, Color defaultValue ) {
|
static Color clientPropertyColor( JComponent c, String key, Color defaultValue ) {
|
||||||
|
return clientProperty( c, key, defaultValue, Color.class );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 );
|
Object value = c.getClientProperty( key );
|
||||||
return (value instanceof Color) ? (Color) value : defaultValue;
|
return type.isInstance( value ) ? (T) value : defaultValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ public class FlatDarculaLaf
|
|||||||
/**
|
/**
|
||||||
* Sets the application look and feel to this LaF
|
* Sets the application look and feel to this LaF
|
||||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||||
|
*
|
||||||
|
* @since 1.2
|
||||||
*/
|
*/
|
||||||
public static boolean setup() {
|
public static boolean setup() {
|
||||||
return setup( new FlatDarculaLaf() );
|
return setup( new FlatDarculaLaf() );
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ public class FlatDarkLaf
|
|||||||
/**
|
/**
|
||||||
* Sets the application look and feel to this LaF
|
* Sets the application look and feel to this LaF
|
||||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||||
|
*
|
||||||
|
* @since 1.2
|
||||||
*/
|
*/
|
||||||
public static boolean setup() {
|
public static boolean setup() {
|
||||||
return setup( new FlatDarkLaf() );
|
return setup( new FlatDarkLaf() );
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import javax.swing.UIDefaults;
|
|||||||
* Allows loading of additional .properties files from addon JARs.
|
* Allows loading of additional .properties files from addon JARs.
|
||||||
* {@link java.util.ServiceLoader} is used to load extensions of this class from addon JARs.
|
* {@link java.util.ServiceLoader} is used to load extensions of this class from addon JARs.
|
||||||
* <p>
|
* <p>
|
||||||
* If you extend this class in a addon JAR, you also have to add a text file named
|
* If you extend this class in an addon JAR, you also have to add a text file named
|
||||||
* {@code META-INF/services/com.formdev.flatlaf.FlatDefaultsAddon}
|
* {@code META-INF/services/com.formdev.flatlaf.FlatDefaultsAddon}
|
||||||
* to the addon JAR. The file must contain a single line with the class name.
|
* to the addon JAR. The file must contain a single line with the class name.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -61,7 +61,7 @@ public abstract class FlatDefaultsAddon
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the priority used to sort addon loading.
|
* Returns the priority used to sort addon loading.
|
||||||
* The order is only important if you want overwrite UI defaults of other addons.
|
* The order is only important if you want to overwrite UI defaults of other addons.
|
||||||
* Lower numbers mean higher priority.
|
* Lower numbers mean higher priority.
|
||||||
* Returns 10000 by default.
|
* Returns 10000 by default.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package com.formdev.flatlaf;
|
|||||||
/**
|
/**
|
||||||
* Default color palette for action icons and object icons.
|
* Default color palette for action icons and object icons.
|
||||||
* <p>
|
* <p>
|
||||||
* The idea is to use only this well defined set of colors in SVG icons and
|
* The idea is to use only this well-defined set of colors in SVG icons, and
|
||||||
* then they are replaced at runtime to dark variants or to other theme colors.
|
* then they are replaced at runtime to dark variants or to other theme colors.
|
||||||
* Then a single SVG icon (light variant) can be used for dark themes too.
|
* Then a single SVG icon (light variant) can be used for dark themes too.
|
||||||
* IntelliJ Platform uses this mechanism to allow themes to change IntelliJ Platform icons.
|
* IntelliJ Platform uses this mechanism to allow themes to change IntelliJ Platform icons.
|
||||||
@@ -35,7 +35,7 @@ package com.formdev.flatlaf;
|
|||||||
* <p>
|
* <p>
|
||||||
* You may use these colors also in your application (outside of SVG icons), but do
|
* You may use these colors also in your application (outside of SVG icons), but do
|
||||||
* not use the RGB values defined in this enum.<br>
|
* not use the RGB values defined in this enum.<br>
|
||||||
* Instead use {@code UIManager.getColor( FlatIconColors.ACTIONS_GREY.key )}.
|
* Instead, use {@code UIManager.getColor( FlatIconColors.ACTIONS_GREY.key )}.
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -596,7 +596,7 @@ class FlatInputMaps
|
|||||||
//---- class LazyInputMapEx -----------------------------------------------
|
//---- class LazyInputMapEx -----------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lazily creates a input map.
|
* Lazily creates an input map.
|
||||||
* Similar to {@link UIDefaults.LazyInputMap}, but can use multiple bindings arrays.
|
* Similar to {@link UIDefaults.LazyInputMap}, but can use multiple bindings arrays.
|
||||||
*/
|
*/
|
||||||
private static class LazyInputMapEx
|
private static class LazyInputMapEx
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ public class FlatIntelliJLaf
|
|||||||
/**
|
/**
|
||||||
* Sets the application look and feel to this LaF
|
* Sets the application look and feel to this LaF
|
||||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||||
|
*
|
||||||
|
* @since 1.2
|
||||||
*/
|
*/
|
||||||
public static boolean setup() {
|
public static boolean setup() {
|
||||||
return setup( new FlatIntelliJLaf() );
|
return setup( new FlatIntelliJLaf() );
|
||||||
|
|||||||
@@ -30,30 +30,43 @@ import java.awt.image.ImageProducer;
|
|||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Enumeration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.MissingResourceException;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
import java.util.ServiceLoader;
|
import java.util.ServiceLoader;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.function.IntUnaryOperator;
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
import javax.swing.JFrame;
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JMenuBar;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.PopupFactory;
|
import javax.swing.PopupFactory;
|
||||||
import javax.swing.RootPaneContainer;
|
import javax.swing.RootPaneContainer;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIDefaults;
|
import javax.swing.UIDefaults;
|
||||||
import javax.swing.UIDefaults.ActiveValue;
|
import javax.swing.UIDefaults.ActiveValue;
|
||||||
|
import javax.swing.UIDefaults.LazyValue;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.UnsupportedLookAndFeelException;
|
import javax.swing.UnsupportedLookAndFeelException;
|
||||||
import javax.swing.plaf.ColorUIResource;
|
import javax.swing.plaf.ColorUIResource;
|
||||||
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.FontUIResource;
|
import javax.swing.plaf.FontUIResource;
|
||||||
import javax.swing.plaf.IconUIResource;
|
import javax.swing.plaf.IconUIResource;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
@@ -63,9 +76,12 @@ import javax.swing.text.html.HTMLEditorKit;
|
|||||||
import com.formdev.flatlaf.ui.FlatNativeWindowBorder;
|
import com.formdev.flatlaf.ui.FlatNativeWindowBorder;
|
||||||
import com.formdev.flatlaf.ui.FlatPopupFactory;
|
import com.formdev.flatlaf.ui.FlatPopupFactory;
|
||||||
import com.formdev.flatlaf.ui.FlatRootPaneUI;
|
import com.formdev.flatlaf.ui.FlatRootPaneUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.util.GrayFilter;
|
import com.formdev.flatlaf.util.GrayFilter;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
|
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
|
||||||
|
import com.formdev.flatlaf.util.StringUtils;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -80,6 +96,8 @@ public abstract class FlatLaf
|
|||||||
private static final String DESKTOPFONTHINTS = "awt.font.desktophints";
|
private static final String DESKTOPFONTHINTS = "awt.font.desktophints";
|
||||||
|
|
||||||
private static List<Object> customDefaultsSources;
|
private static List<Object> customDefaultsSources;
|
||||||
|
private static Map<String, String> globalExtraDefaults;
|
||||||
|
private Map<String, String> extraDefaults;
|
||||||
|
|
||||||
private String desktopPropertyName;
|
private String desktopPropertyName;
|
||||||
private String desktopPropertyName2;
|
private String desktopPropertyName2;
|
||||||
@@ -90,12 +108,16 @@ public abstract class FlatLaf
|
|||||||
|
|
||||||
private PopupFactory oldPopupFactory;
|
private PopupFactory oldPopupFactory;
|
||||||
private MnemonicHandler mnemonicHandler;
|
private MnemonicHandler mnemonicHandler;
|
||||||
|
private boolean subMenuUsabilityHelperInstalled;
|
||||||
|
|
||||||
private Consumer<UIDefaults> postInitialization;
|
private Consumer<UIDefaults> postInitialization;
|
||||||
|
private List<Function<Object, Object>> uiDefaultsGetters;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the application look and feel to the given LaF
|
* Sets the application look and feel to the given LaF
|
||||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||||
|
*
|
||||||
|
* @since 1.2
|
||||||
*/
|
*/
|
||||||
public static boolean setup( LookAndFeel newLookAndFeel ) {
|
public static boolean setup( LookAndFeel newLookAndFeel ) {
|
||||||
try {
|
try {
|
||||||
@@ -152,18 +174,19 @@ public abstract class FlatLaf
|
|||||||
* Returns whether FlatLaf supports custom window decorations.
|
* Returns whether FlatLaf supports custom window decorations.
|
||||||
* This depends on the operating system and on the used Java runtime.
|
* This depends on the operating system and on the used Java runtime.
|
||||||
* <p>
|
* <p>
|
||||||
* This method returns {@code true} on Windows 10 (see exception below), {@code false} otherwise.
|
* This method returns {@code true} on Windows 10/11 (see exception below)
|
||||||
|
* and on Linux, {@code false} otherwise.
|
||||||
* <p>
|
* <p>
|
||||||
* Returns also {@code false} on Windows 10 if:
|
* Returns also {@code false} on Windows 10/11 if:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>FlatLaf native window border support is available (requires Windows 10)</li>
|
* <li>FlatLaf native window border support is available (requires Windows 10/11)</li>
|
||||||
* <li>running in
|
* <li>running in
|
||||||
* <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime 11 (or later)</a>
|
* <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime 11 (or later)</a>
|
||||||
* (<a href="https://github.com/JetBrains/JetBrainsRuntime">source code on github</a>)
|
* (<a href="https://github.com/JetBrains/JetBrainsRuntime">source code on github</a>)
|
||||||
* and JBR supports custom window decorations
|
* and JBR supports custom window decorations
|
||||||
* </li>
|
* </li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* In this cases, custom decorations are enabled by the root pane.
|
* In these cases, custom decorations are enabled by the root pane.
|
||||||
* Usage of {@link JFrame#setDefaultLookAndFeelDecorated(boolean)} or
|
* Usage of {@link JFrame#setDefaultLookAndFeelDecorated(boolean)} or
|
||||||
* {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} is not necessary.
|
* {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} is not necessary.
|
||||||
*/
|
*/
|
||||||
@@ -176,7 +199,7 @@ public abstract class FlatLaf
|
|||||||
FlatNativeWindowBorder.isSupported() )
|
FlatNativeWindowBorder.isSupported() )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return SystemInfo.isWindows_10_orLater;
|
return SystemInfo.isWindows_10_orLater || SystemInfo.isLinux;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -216,6 +239,15 @@ public abstract class FlatLaf
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
|
// do not initialize if this is not the current look and feel
|
||||||
|
// This is only necessary for special Laf usage. E.g. in GUI builders,
|
||||||
|
// which may use multiple Lafs and may invoke this method directly.
|
||||||
|
// This avoids that listeners and factories are installed multiple times.
|
||||||
|
// In case of the NetBeans GUI builder, which does not invoke uninitialize(),
|
||||||
|
// this also avoids that listeners stay registered in the system.
|
||||||
|
if( UIManager.getLookAndFeel() != this )
|
||||||
|
return;
|
||||||
|
|
||||||
if( SystemInfo.isMacOS )
|
if( SystemInfo.isMacOS )
|
||||||
initializeAqua();
|
initializeAqua();
|
||||||
|
|
||||||
@@ -229,6 +261,9 @@ public abstract class FlatLaf
|
|||||||
mnemonicHandler = new MnemonicHandler();
|
mnemonicHandler = new MnemonicHandler();
|
||||||
mnemonicHandler.install();
|
mnemonicHandler.install();
|
||||||
|
|
||||||
|
// install submenu usability helper
|
||||||
|
subMenuUsabilityHelperInstalled = SubMenuUsabilityHelper.install();
|
||||||
|
|
||||||
// listen to desktop property changes to update UI if system font or scaling changes
|
// listen to desktop property changes to update UI if system font or scaling changes
|
||||||
if( SystemInfo.isWindows ) {
|
if( SystemInfo.isWindows ) {
|
||||||
// Windows 10 allows increasing font size independent of scaling:
|
// Windows 10 allows increasing font size independent of scaling:
|
||||||
@@ -246,6 +281,9 @@ public abstract class FlatLaf
|
|||||||
}
|
}
|
||||||
if( desktopPropertyName != null ) {
|
if( desktopPropertyName != null ) {
|
||||||
desktopPropertyListener = e -> {
|
desktopPropertyListener = e -> {
|
||||||
|
if( !FlatSystemProperties.getBoolean( FlatSystemProperties.UPDATE_UI_ON_SYSTEM_FONT_CHANGE, true ) )
|
||||||
|
return;
|
||||||
|
|
||||||
String propertyName = e.getPropertyName();
|
String propertyName = e.getPropertyName();
|
||||||
if( desktopPropertyName.equals( propertyName ) || propertyName.equals( desktopPropertyName2 ) )
|
if( desktopPropertyName.equals( propertyName ) || propertyName.equals( desktopPropertyName2 ) )
|
||||||
reSetLookAndFeel();
|
reSetLookAndFeel();
|
||||||
@@ -257,6 +295,12 @@ public abstract class FlatLaf
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
||||||
|
|
||||||
|
// make sure that AWT desktop properties are initialized (on Linux)
|
||||||
|
// before invoking toolkit.addPropertyChangeListener()
|
||||||
|
// https://github.com/JFormDesigner/FlatLaf/issues/405#issuecomment-960242342
|
||||||
|
toolkit.getDesktopProperty( "dummy" );
|
||||||
|
|
||||||
toolkit.addPropertyChangeListener( desktopPropertyName, desktopPropertyListener );
|
toolkit.addPropertyChangeListener( desktopPropertyName, desktopPropertyListener );
|
||||||
if( desktopPropertyName2 != null )
|
if( desktopPropertyName2 != null )
|
||||||
toolkit.addPropertyChangeListener( desktopPropertyName2, desktopPropertyListener );
|
toolkit.addPropertyChangeListener( desktopPropertyName2, desktopPropertyListener );
|
||||||
@@ -278,6 +322,10 @@ public abstract class FlatLaf
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void uninitialize() {
|
public void uninitialize() {
|
||||||
|
// do not uninitialize if this is not the current look and feel
|
||||||
|
if( UIManager.getLookAndFeel() != this )
|
||||||
|
return;
|
||||||
|
|
||||||
// remove desktop property listener
|
// remove desktop property listener
|
||||||
if( desktopPropertyListener != null ) {
|
if( desktopPropertyListener != null ) {
|
||||||
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
||||||
@@ -302,6 +350,12 @@ public abstract class FlatLaf
|
|||||||
mnemonicHandler = null;
|
mnemonicHandler = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// uninstall submenu usability helper
|
||||||
|
if( subMenuUsabilityHelperInstalled ) {
|
||||||
|
SubMenuUsabilityHelper.uninstall();
|
||||||
|
subMenuUsabilityHelperInstalled = false;
|
||||||
|
}
|
||||||
|
|
||||||
// restore default link color
|
// restore default link color
|
||||||
new HTMLEditorKit().getStyleSheet().addRule( "a, address { color: blue; }" );
|
new HTMLEditorKit().getStyleSheet().addRule( "a, address { color: blue; }" );
|
||||||
postInitialization = null;
|
postInitialization = null;
|
||||||
@@ -350,14 +404,21 @@ public abstract class FlatLaf
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UIDefaults getDefaults() {
|
public UIDefaults getDefaults() {
|
||||||
UIDefaults defaults = super.getDefaults();
|
// use larger initial capacity to avoid resizing UI defaults hash table
|
||||||
|
// (from 610 to 1221 to 2443 entries) and to save some memory
|
||||||
|
UIDefaults defaults = new FlatUIDefaults( 1500, 0.75f );
|
||||||
|
|
||||||
|
// initialize basic defaults (see super.getDefaults())
|
||||||
|
initClassDefaults( defaults );
|
||||||
|
initSystemColorDefaults( defaults );
|
||||||
|
initComponentDefaults( defaults );
|
||||||
|
|
||||||
// add flag that indicates whether the LaF is light or dark
|
// add flag that indicates whether the LaF is light or dark
|
||||||
// (can be queried without using FlatLaf API)
|
// (can be queried without using FlatLaf API)
|
||||||
defaults.put( "laf.dark", isDark() );
|
defaults.put( "laf.dark", isDark() );
|
||||||
|
|
||||||
// add resource bundle for localized texts
|
// init resource bundle for localized texts
|
||||||
defaults.addResourceBundle( "com.formdev.flatlaf.resources.Bundle" );
|
initResourceBundle( defaults, "com.formdev.flatlaf.resources.Bundle" );
|
||||||
|
|
||||||
// initialize some defaults (for overriding) that are used in UI delegates,
|
// initialize some defaults (for overriding) that are used in UI delegates,
|
||||||
// but are not set in BasicLookAndFeel
|
// but are not set in BasicLookAndFeel
|
||||||
@@ -367,6 +428,7 @@ public abstract class FlatLaf
|
|||||||
"EditorPane.inactiveBackground",
|
"EditorPane.inactiveBackground",
|
||||||
"FormattedTextField.disabledBackground",
|
"FormattedTextField.disabledBackground",
|
||||||
"PasswordField.disabledBackground",
|
"PasswordField.disabledBackground",
|
||||||
|
"RootPane.background",
|
||||||
"Spinner.disabledBackground",
|
"Spinner.disabledBackground",
|
||||||
"TextArea.disabledBackground",
|
"TextArea.disabledBackground",
|
||||||
"TextArea.inactiveBackground",
|
"TextArea.inactiveBackground",
|
||||||
@@ -385,7 +447,8 @@ public abstract class FlatLaf
|
|||||||
"Spinner.disabledForeground",
|
"Spinner.disabledForeground",
|
||||||
"ToggleButton.disabledText" );
|
"ToggleButton.disabledText" );
|
||||||
putDefaults( defaults, defaults.getColor( "textText" ),
|
putDefaults( defaults, defaults.getColor( "textText" ),
|
||||||
"DesktopIcon.foreground" );
|
"DesktopIcon.foreground",
|
||||||
|
"RootPane.foreground" );
|
||||||
|
|
||||||
initFonts( defaults );
|
initFonts( defaults );
|
||||||
initIconColors( defaults, isDark() );
|
initIconColors( defaults, isDark() );
|
||||||
@@ -395,7 +458,7 @@ public abstract class FlatLaf
|
|||||||
// (using defaults.remove() to avoid that lazy value is resolved and icon loaded here)
|
// (using defaults.remove() to avoid that lazy value is resolved and icon loaded here)
|
||||||
Object icon = defaults.remove( "InternalFrame.icon" );
|
Object icon = defaults.remove( "InternalFrame.icon" );
|
||||||
defaults.put( "InternalFrame.icon", icon );
|
defaults.put( "InternalFrame.icon", icon );
|
||||||
defaults.put( "TitlePane.icon", icon );
|
defaults.put( "TitlePane.icon", icon ); // no longer used, but keep for compatibility
|
||||||
|
|
||||||
// get addons and sort them by priority
|
// get addons and sort them by priority
|
||||||
ServiceLoader<FlatDefaultsAddon> addonLoader = ServiceLoader.load( FlatDefaultsAddon.class );
|
ServiceLoader<FlatDefaultsAddon> addonLoader = ServiceLoader.load( FlatDefaultsAddon.class );
|
||||||
@@ -411,6 +474,10 @@ public abstract class FlatLaf
|
|||||||
else
|
else
|
||||||
UIDefaultsLoader.loadDefaultsFromProperties( getClass(), addons, getAdditionalDefaults(), isDark(), defaults );
|
UIDefaultsLoader.loadDefaultsFromProperties( getClass(), addons, getAdditionalDefaults(), isDark(), defaults );
|
||||||
|
|
||||||
|
// setup default font after loading defaults from properties
|
||||||
|
// to allow defining "defaultFont" in properties
|
||||||
|
initDefaultFont( defaults );
|
||||||
|
|
||||||
// use Aqua MenuBarUI if Mac screen menubar is enabled
|
// use Aqua MenuBarUI if Mac screen menubar is enabled
|
||||||
if( SystemInfo.isMacOS && Boolean.getBoolean( "apple.laf.useScreenMenuBar" ) ) {
|
if( SystemInfo.isMacOS && Boolean.getBoolean( "apple.laf.useScreenMenuBar" ) ) {
|
||||||
defaults.put( "MenuBarUI", "com.apple.laf.AquaMenuBarUI" );
|
defaults.put( "MenuBarUI", "com.apple.laf.AquaMenuBarUI" );
|
||||||
@@ -450,12 +517,77 @@ public abstract class FlatLaf
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Properties getAdditionalDefaults() {
|
protected Properties getAdditionalDefaults() {
|
||||||
|
if( globalExtraDefaults == null && extraDefaults == null )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
Properties properties = new Properties();
|
||||||
|
if( globalExtraDefaults != null )
|
||||||
|
properties.putAll( globalExtraDefaults );
|
||||||
|
if( extraDefaults != null )
|
||||||
|
properties.putAll( extraDefaults );
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initResourceBundle( UIDefaults defaults, String bundleName ) {
|
||||||
|
// add resource bundle for localized texts
|
||||||
|
defaults.addResourceBundle( bundleName );
|
||||||
|
|
||||||
|
// Check whether Swing can not load the FlatLaf resource bundle,
|
||||||
|
// which can happen in applications that use some plugin system
|
||||||
|
// and load FlatLaf in a plugin that uses its own classloader.
|
||||||
|
// (e.g. Apache NetBeans)
|
||||||
|
if( defaults.get( "FileChooser.fileNameHeaderText" ) != null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// load FlatLaf resource bundle and add content to defaults
|
||||||
|
try {
|
||||||
|
ResourceBundle bundle = ResourceBundle.getBundle( bundleName, defaults.getDefaultLocale() );
|
||||||
|
|
||||||
|
Enumeration<String> keys = bundle.getKeys();
|
||||||
|
while( keys.hasMoreElements() ) {
|
||||||
|
String key = keys.nextElement();
|
||||||
|
String value = bundle.getString( key );
|
||||||
|
|
||||||
|
String baseKey = StringUtils.removeTrailing( key, ".textAndMnemonic" );
|
||||||
|
if( baseKey != key ) {
|
||||||
|
String text = value.replace( "&", "" );
|
||||||
|
String mnemonic = null;
|
||||||
|
int index = value.indexOf( '&' );
|
||||||
|
if( index >= 0 )
|
||||||
|
mnemonic = Integer.toString( Character.toUpperCase( value.charAt( index + 1 ) ) );
|
||||||
|
|
||||||
|
defaults.put( baseKey + "Text", text );
|
||||||
|
if( mnemonic != null )
|
||||||
|
defaults.put( baseKey + "Mnemonic", mnemonic );
|
||||||
|
} else
|
||||||
|
defaults.put( key, value );
|
||||||
|
}
|
||||||
|
} catch( MissingResourceException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initFonts( UIDefaults defaults ) {
|
private void initFonts( UIDefaults defaults ) {
|
||||||
|
// use active value for all fonts to allow changing fonts in all components with:
|
||||||
|
// UIManager.put( "defaultFont", myFont );
|
||||||
|
// (this is similar as in Nimbus L&F)
|
||||||
|
Object activeFont = new ActiveFont( null, null, -1, 0, 0, 0, 0 );
|
||||||
|
|
||||||
|
// override fonts
|
||||||
|
for( Object key : defaults.keySet() ) {
|
||||||
|
if( key instanceof String && (((String)key).endsWith( ".font" ) || ((String)key).endsWith( "Font" )) )
|
||||||
|
defaults.put( key, activeFont );
|
||||||
|
}
|
||||||
|
|
||||||
|
// add fonts that are not set in BasicLookAndFeel
|
||||||
|
defaults.put( "RootPane.font", activeFont );
|
||||||
|
defaults.put( "TitlePane.font", activeFont );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initDefaultFont( UIDefaults defaults ) {
|
||||||
FontUIResource uiFont = null;
|
FontUIResource uiFont = null;
|
||||||
|
|
||||||
|
// determine UI font based on operating system
|
||||||
if( SystemInfo.isWindows ) {
|
if( SystemInfo.isWindows ) {
|
||||||
Font winFont = (Font) Toolkit.getDefaultToolkit().getDesktopProperty( "win.messagebox.font" );
|
Font winFont = (Font) Toolkit.getDefaultToolkit().getDesktopProperty( "win.messagebox.font" );
|
||||||
if( winFont != null ) {
|
if( winFont != null ) {
|
||||||
@@ -498,22 +630,20 @@ public abstract class FlatLaf
|
|||||||
if( uiFont == null )
|
if( uiFont == null )
|
||||||
uiFont = createCompositeFont( Font.SANS_SERIF, Font.PLAIN, 12 );
|
uiFont = createCompositeFont( Font.SANS_SERIF, Font.PLAIN, 12 );
|
||||||
|
|
||||||
// increase font size if system property "flatlaf.uiScale" is set
|
// get/remove "defaultFont" from defaults if set in properties files
|
||||||
uiFont = UIScale.applyCustomScaleFactor( uiFont );
|
// (use remove() to avoid that ActiveFont.createValue() gets invoked)
|
||||||
|
Object defaultFont = defaults.remove( "defaultFont" );
|
||||||
|
|
||||||
// use active value for all fonts to allow changing fonts in all components
|
// use font from OS as base font and derive the UI font from it
|
||||||
// (similar as in Nimbus L&F) with:
|
if( defaultFont instanceof ActiveFont ) {
|
||||||
// UIManager.put( "defaultFont", myFont );
|
Font baseFont = uiFont;
|
||||||
Object activeFont = new ActiveFont( 1 );
|
uiFont = ((ActiveFont)defaultFont).derive( baseFont, fontSize -> {
|
||||||
|
return Math.round( fontSize * UIScale.computeFontScaleFactor( baseFont ) );
|
||||||
// override fonts
|
} );
|
||||||
for( Object key : defaults.keySet() ) {
|
|
||||||
if( key instanceof String && (((String)key).endsWith( ".font" ) || ((String)key).endsWith( "Font" )) )
|
|
||||||
defaults.put( key, activeFont );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// use smaller font for progress bar
|
// increase font size if system property "flatlaf.uiScale" is set
|
||||||
defaults.put( "ProgressBar.font", new ActiveFont( 0.85f ) );
|
uiFont = UIScale.applyCustomScaleFactor( uiFont );
|
||||||
|
|
||||||
// set default font
|
// set default font
|
||||||
defaults.put( "defaultFont", uiFont );
|
defaults.put( "defaultFont", uiFont );
|
||||||
@@ -527,11 +657,9 @@ public abstract class FlatLaf
|
|||||||
return (font instanceof FontUIResource) ? (FontUIResource) font : new FontUIResource( font );
|
return (font instanceof FontUIResource) ? (FontUIResource) font : new FontUIResource( font );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @since 1.1 */
|
||||||
* @since 1.1
|
|
||||||
*/
|
|
||||||
public static ActiveValue createActiveFontValue( float scaleFactor ) {
|
public static ActiveValue createActiveFontValue( float scaleFactor ) {
|
||||||
return new ActiveFont( scaleFactor );
|
return new ActiveFont( null, null, -1, 0, 0, 0, scaleFactor );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -665,6 +793,9 @@ public abstract class FlatLaf
|
|||||||
* and can therefore override all UI defaults.
|
* and can therefore override all UI defaults.
|
||||||
* <p>
|
* <p>
|
||||||
* Invoke this method before setting the look and feel.
|
* Invoke this method before setting the look and feel.
|
||||||
|
* <p>
|
||||||
|
* If using Java modules, the package must be opened in {@code module-info.java}.
|
||||||
|
* Otherwise, use {@link #registerCustomDefaultsSource(URL)}.
|
||||||
*
|
*
|
||||||
* @param packageName a package name (e.g. "com.myapp.resources")
|
* @param packageName a package name (e.g. "com.myapp.resources")
|
||||||
*/
|
*/
|
||||||
@@ -706,6 +837,32 @@ public abstract class FlatLaf
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a package where FlatLaf searches for properties files with custom UI defaults.
|
||||||
|
* <p>
|
||||||
|
* See {@link #registerCustomDefaultsSource(String)} for details.
|
||||||
|
* <p>
|
||||||
|
* This method is useful if using Java modules and the package containing the properties files
|
||||||
|
* is not opened in {@code module-info.java}.
|
||||||
|
* E.g. {@code FlatLaf.registerCustomDefaultsSource( MyApp.class.getResource( "/com/myapp/themes/" ) )}.
|
||||||
|
*
|
||||||
|
* @param packageUrl a package URL
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
public static void registerCustomDefaultsSource( URL packageUrl ) {
|
||||||
|
if( customDefaultsSources == null )
|
||||||
|
customDefaultsSources = new ArrayList<>();
|
||||||
|
customDefaultsSources.add( packageUrl );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public static void unregisterCustomDefaultsSource( URL packageUrl ) {
|
||||||
|
if( customDefaultsSources == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
customDefaultsSources.remove( packageUrl );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a folder where FlatLaf searches for properties files with custom UI defaults.
|
* Registers a folder where FlatLaf searches for properties files with custom UI defaults.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -726,6 +883,102 @@ public abstract class FlatLaf
|
|||||||
customDefaultsSources.remove( folder );
|
customDefaultsSources.remove( folder );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets global extra UI defaults; or {@code null}.
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
public static Map<String, String> getGlobalExtraDefaults() {
|
||||||
|
return globalExtraDefaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets global extra UI defaults, which are only used when setting up the application look and feel.
|
||||||
|
* E.g. using {@link UIManager#setLookAndFeel(LookAndFeel)} or {@link #setup(LookAndFeel)}.
|
||||||
|
* <p>
|
||||||
|
* The global extra defaults are useful for smaller additional defaults that may change.
|
||||||
|
* E.g. accent color. Otherwise, FlatLaf properties files should be used.
|
||||||
|
* See {@link #registerCustomDefaultsSource(String)}.
|
||||||
|
* <p>
|
||||||
|
* The keys and values are strings in same format as in FlatLaf properties files.
|
||||||
|
* <p>
|
||||||
|
* Sample that setups "FlatLaf Light" theme with red accent color:
|
||||||
|
* <pre>{@code
|
||||||
|
* FlatLaf.setGlobalExtraDefaults( Collections.singletonMap( "@accentColor", "#f00" ) );
|
||||||
|
* FlatLightLaf.setup();
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @see #setExtraDefaults(Map)
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
public static void setGlobalExtraDefaults( Map<String, String> globalExtraDefaults ) {
|
||||||
|
FlatLaf.globalExtraDefaults = globalExtraDefaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets extra UI defaults; or {@code null}.
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
public Map<String, String> getExtraDefaults() {
|
||||||
|
return extraDefaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets extra UI defaults, which are only used when setting up the application look and feel.
|
||||||
|
* E.g. using {@link UIManager#setLookAndFeel(LookAndFeel)} or {@link #setup(LookAndFeel)}.
|
||||||
|
* <p>
|
||||||
|
* The extra defaults are useful for smaller additional defaults that may change.
|
||||||
|
* E.g. accent color. Otherwise, FlatLaf properties files should be used.
|
||||||
|
* See {@link #registerCustomDefaultsSource(String)}.
|
||||||
|
* <p>
|
||||||
|
* The keys and values are strings in same format as in FlatLaf properties files.
|
||||||
|
* <p>
|
||||||
|
* Sample that setups "FlatLaf Light" theme with red accent color:
|
||||||
|
* <pre>{@code
|
||||||
|
* FlatLaf laf = new FlatLightLaf();
|
||||||
|
* laf.setExtraDefaults( Collections.singletonMap( "@accentColor", "#f00" ) );
|
||||||
|
* FlatLaf.setup( laf );
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @see #setGlobalExtraDefaults(Map)
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
public void setExtraDefaults( Map<String, String> extraDefaults ) {
|
||||||
|
this.extraDefaults = extraDefaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a UI defaults value string and converts it into a binary object.
|
||||||
|
* <p>
|
||||||
|
* See: <a href="https://www.formdev.com/flatlaf/properties-files/">https://www.formdev.com/flatlaf/properties-files/</a>
|
||||||
|
*
|
||||||
|
* @param key the key, which is used to determine the value type if parameter {@code valueType} is {@code null}
|
||||||
|
* @param value the value string
|
||||||
|
* @param valueType the expected value type, or {@code null}
|
||||||
|
* @return the binary value
|
||||||
|
* @throws IllegalArgumentException on syntax errors
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
public static Object parseDefaultsValue( String key, String value, Class<?> valueType )
|
||||||
|
throws IllegalArgumentException
|
||||||
|
{
|
||||||
|
// resolve variables
|
||||||
|
value = UIDefaultsLoader.resolveValueFromUIManager( value );
|
||||||
|
|
||||||
|
// parse value
|
||||||
|
Object val = UIDefaultsLoader.parseValue( key, value, valueType, null,
|
||||||
|
v -> UIDefaultsLoader.resolveValueFromUIManager( v ), Collections.emptyList() );
|
||||||
|
|
||||||
|
// create actual value if lazy or active
|
||||||
|
if( val instanceof LazyValue )
|
||||||
|
val = ((LazyValue)val).createValue( null );
|
||||||
|
else if( val instanceof ActiveValue )
|
||||||
|
val = ((ActiveValue)val).createValue( null );
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
private static void reSetLookAndFeel() {
|
private static void reSetLookAndFeel() {
|
||||||
EventQueue.invokeLater( () -> {
|
EventQueue.invokeLater( () -> {
|
||||||
LookAndFeel lookAndFeel = UIManager.getLookAndFeel();
|
LookAndFeel lookAndFeel = UIManager.getLookAndFeel();
|
||||||
@@ -733,7 +986,7 @@ public abstract class FlatLaf
|
|||||||
// re-set current LaF
|
// re-set current LaF
|
||||||
UIManager.setLookAndFeel( lookAndFeel );
|
UIManager.setLookAndFeel( lookAndFeel );
|
||||||
|
|
||||||
// must fire property change events ourself because old and new LaF are the same
|
// must fire property change events ourselves because old and new LaF are the same
|
||||||
PropertyChangeEvent e = new PropertyChangeEvent( UIManager.class, "lookAndFeel", lookAndFeel, lookAndFeel );
|
PropertyChangeEvent e = new PropertyChangeEvent( UIManager.class, "lookAndFeel", lookAndFeel, lookAndFeel );
|
||||||
for( PropertyChangeListener l : UIManager.getPropertyChangeListeners() )
|
for( PropertyChangeListener l : UIManager.getPropertyChangeListeners() )
|
||||||
l.propertyChange( e );
|
l.propertyChange( e );
|
||||||
@@ -777,7 +1030,7 @@ public abstract class FlatLaf
|
|||||||
/**
|
/**
|
||||||
* Returns whether native window decorations are supported on current platform.
|
* Returns whether native window decorations are supported on current platform.
|
||||||
* <p>
|
* <p>
|
||||||
* This requires Windows 10, but may be disabled if running in special environments
|
* This requires Windows 10/11, but may be disabled if running in special environments
|
||||||
* (JetBrains Projector, Webswing or WinPE) or if loading native library fails.
|
* (JetBrains Projector, Webswing or WinPE) or if loading native library fails.
|
||||||
* If system property {@link FlatSystemProperties#USE_WINDOW_DECORATIONS} is set to
|
* If system property {@link FlatSystemProperties#USE_WINDOW_DECORATIONS} is set to
|
||||||
* {@code false}, then this method also returns {@code false}.
|
* {@code false}, then this method also returns {@code false}.
|
||||||
@@ -819,12 +1072,23 @@ public abstract class FlatLaf
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Revalidate and repaint all displayable frames and dialogs.
|
* Revalidate and repaint all displayable frames and dialogs.
|
||||||
|
* <p>
|
||||||
|
* Useful to update UI after changing {@code TitlePane.menuBarEmbedded}.
|
||||||
*
|
*
|
||||||
* @since 1.1.2
|
* @since 1.1.2
|
||||||
*/
|
*/
|
||||||
public static void revalidateAndRepaintAllFramesAndDialogs() {
|
public static void revalidateAndRepaintAllFramesAndDialogs() {
|
||||||
for( Window w : Window.getWindows() ) {
|
for( Window w : Window.getWindows() ) {
|
||||||
if( isDisplayableFrameOrDialog( w ) ) {
|
if( isDisplayableFrameOrDialog( w ) ) {
|
||||||
|
// revalidate menu bar
|
||||||
|
JMenuBar menuBar = (w instanceof JFrame)
|
||||||
|
? ((JFrame)w).getJMenuBar()
|
||||||
|
: (w instanceof JDialog
|
||||||
|
? ((JDialog)w).getJMenuBar()
|
||||||
|
: null);
|
||||||
|
if( menuBar != null )
|
||||||
|
menuBar.revalidate();
|
||||||
|
|
||||||
w.revalidate();
|
w.revalidate();
|
||||||
w.repaint();
|
w.repaint();
|
||||||
}
|
}
|
||||||
@@ -833,6 +1097,9 @@ public abstract class FlatLaf
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Repaint all displayable frames and dialogs.
|
* Repaint all displayable frames and dialogs.
|
||||||
|
* <p>
|
||||||
|
* Useful to update UI after changing {@code TitlePane.unifiedBackground},
|
||||||
|
* {@code MenuItem.selectionType} or {@code Component.hideMnemonics}.
|
||||||
*
|
*
|
||||||
* @since 1.1.2
|
* @since 1.1.2
|
||||||
*/
|
*/
|
||||||
@@ -871,46 +1138,328 @@ public abstract class FlatLaf
|
|||||||
return super.hashCode();
|
return super.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class ActiveFont ---------------------------------------------------
|
/**
|
||||||
|
* Registers a UI defaults getter function that is invoked before the standard getter.
|
||||||
|
* This allows using different UI defaults for special purposes
|
||||||
|
* (e.g. using multiple themes at the same time).
|
||||||
|
* <p>
|
||||||
|
* The key is passed as parameter to the function.
|
||||||
|
* If the function returns {@code null}, then the next registered function is invoked.
|
||||||
|
* If all registered functions return {@code null}, then the current look and feel is asked.
|
||||||
|
* If the function returns {@link #NULL_VALUE}, then the UI value becomes {@code null}.
|
||||||
|
*
|
||||||
|
* @see #unregisterUIDefaultsGetter(Function)
|
||||||
|
* @see #runWithUIDefaultsGetter(Function, Runnable)
|
||||||
|
* @since 1.6
|
||||||
|
*/
|
||||||
|
public void registerUIDefaultsGetter( Function<Object, Object> uiDefaultsGetter ) {
|
||||||
|
if( uiDefaultsGetters == null )
|
||||||
|
uiDefaultsGetters = new ArrayList<>();
|
||||||
|
|
||||||
private static class ActiveFont
|
uiDefaultsGetters.remove( uiDefaultsGetter );
|
||||||
implements ActiveValue
|
uiDefaultsGetters.add( uiDefaultsGetter );
|
||||||
|
|
||||||
|
// disable shared UIs
|
||||||
|
FlatUIUtils.setUseSharedUIs( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters a UI defaults getter function that was invoked before the standard getter.
|
||||||
|
*
|
||||||
|
* @see #registerUIDefaultsGetter(Function)
|
||||||
|
* @see #runWithUIDefaultsGetter(Function, Runnable)
|
||||||
|
* @since 1.6
|
||||||
|
*/
|
||||||
|
public void unregisterUIDefaultsGetter( Function<Object, Object> uiDefaultsGetter ) {
|
||||||
|
if( uiDefaultsGetters == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
uiDefaultsGetters.remove( uiDefaultsGetter );
|
||||||
|
|
||||||
|
// enable shared UIs
|
||||||
|
if( uiDefaultsGetters.isEmpty() )
|
||||||
|
FlatUIUtils.setUseSharedUIs( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a UI defaults getter function that is invoked before the standard getter,
|
||||||
|
* runs the given runnable and unregisters the UI defaults getter function again.
|
||||||
|
* This allows using different UI defaults for special purposes
|
||||||
|
* (e.g. using multiple themes at the same time).
|
||||||
|
* If the current look and feel is not FlatLaf, then the getter is ignored and
|
||||||
|
* the given runnable invoked.
|
||||||
|
* <p>
|
||||||
|
* The key is passed as parameter to the function.
|
||||||
|
* If the function returns {@code null}, then the next registered function is invoked.
|
||||||
|
* If all registered functions return {@code null}, then the current look and feel is asked.
|
||||||
|
* If the function returns {@link #NULL_VALUE}, then the UI value becomes {@code null}.
|
||||||
|
* <p>
|
||||||
|
* Example:
|
||||||
|
* <pre>{@code
|
||||||
|
* // create secondary theme
|
||||||
|
* UIDefaults darkDefaults = new FlatDarkLaf().getDefaults();
|
||||||
|
*
|
||||||
|
* // create panel using secondary theme
|
||||||
|
* FlatLaf.runWithUIDefaultsGetter( key -> {
|
||||||
|
* Object value = darkDefaults.get( key );
|
||||||
|
* return (value != null) ? value : FlatLaf.NULL_VALUE;
|
||||||
|
* }, () -> {
|
||||||
|
* // TODO create components that should use secondary theme here
|
||||||
|
* } );
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @see #registerUIDefaultsGetter(Function)
|
||||||
|
* @see #unregisterUIDefaultsGetter(Function)
|
||||||
|
* @since 1.6
|
||||||
|
*/
|
||||||
|
public static void runWithUIDefaultsGetter( Function<Object, Object> uiDefaultsGetter, Runnable runnable ) {
|
||||||
|
LookAndFeel laf = UIManager.getLookAndFeel();
|
||||||
|
if( laf instanceof FlatLaf ) {
|
||||||
|
((FlatLaf)laf).registerUIDefaultsGetter( uiDefaultsGetter );
|
||||||
|
try {
|
||||||
|
runnable.run();
|
||||||
|
} finally {
|
||||||
|
((FlatLaf)laf).unregisterUIDefaultsGetter( uiDefaultsGetter );
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
runnable.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special value returned by functions used in {@link #runWithUIDefaultsGetter(Function, Runnable)}
|
||||||
|
* or {@link #registerUIDefaultsGetter(Function)} to indicate that the UI value should
|
||||||
|
* become {@code null}.
|
||||||
|
*
|
||||||
|
* @see #runWithUIDefaultsGetter(Function, Runnable)
|
||||||
|
* @see #registerUIDefaultsGetter(Function)
|
||||||
|
* @since 1.6
|
||||||
|
*/
|
||||||
|
public static final Object NULL_VALUE = new Object();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns information about styleable values of a component.
|
||||||
|
* <p>
|
||||||
|
* This is equivalent to: {@code ((StyleableUI)c.getUI()).getStyleableInfos(c)}
|
||||||
|
*
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
public static Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
StyleableUI ui = getStyleableUI( c );
|
||||||
|
return (ui != null) ? ui.getStyleableInfos( c ) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the (styled) value for the given key from the given component.
|
||||||
|
* <p>
|
||||||
|
* This is equivalent to: {@code ((StyleableUI)c.getUI()).getStyleableValue(c, key)}
|
||||||
|
*
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
@SuppressWarnings( "unchecked" )
|
||||||
|
public static <T> T getStyleableValue( JComponent c, String key ) {
|
||||||
|
StyleableUI ui = getStyleableUI( c );
|
||||||
|
return (ui != null) ? (T) ui.getStyleableValue( c, key ) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static StyleableUI getStyleableUI( JComponent c ) {
|
||||||
|
if( !getUIMethodInitialized ) {
|
||||||
|
getUIMethodInitialized = true;
|
||||||
|
|
||||||
|
if( SystemInfo.isJava_9_orLater ) {
|
||||||
|
try {
|
||||||
|
// JComponent.getUI() is available since Java 9
|
||||||
|
getUIMethod = MethodHandles.lookup().findVirtual( JComponent.class, "getUI",
|
||||||
|
MethodType.methodType( ComponentUI.class ) );
|
||||||
|
} catch( Exception ex ) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Object ui;
|
||||||
|
if( getUIMethod != null )
|
||||||
|
ui = getUIMethod.invoke( c );
|
||||||
|
else
|
||||||
|
ui = c.getClass().getMethod( "getUI" ).invoke( c );
|
||||||
|
return (ui instanceof StyleableUI) ? (StyleableUI) ui : null;
|
||||||
|
} catch( Throwable ex ) {
|
||||||
|
// ignore
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean getUIMethodInitialized;
|
||||||
|
private static MethodHandle getUIMethod;
|
||||||
|
|
||||||
|
//---- class FlatUIDefaults -----------------------------------------------
|
||||||
|
|
||||||
|
private class FlatUIDefaults
|
||||||
|
extends UIDefaults
|
||||||
{
|
{
|
||||||
private final float scaleFactor;
|
FlatUIDefaults( int initialCapacity, float loadFactor ) {
|
||||||
|
super( initialCapacity, loadFactor );
|
||||||
// cache (scaled) font
|
|
||||||
private Font font;
|
|
||||||
private Font lastDefaultFont;
|
|
||||||
|
|
||||||
ActiveFont( float scaleFactor ) {
|
|
||||||
this.scaleFactor = scaleFactor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object createValue( UIDefaults table ) {
|
public Object get( Object key ) {
|
||||||
Font defaultFont = UIManager.getFont( "defaultFont" );
|
Object value = getValue( key );
|
||||||
|
return (value != null) ? (value != NULL_VALUE ? value : null) : super.get( key );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object get( Object key, Locale l ) {
|
||||||
|
Object value = getValue( key );
|
||||||
|
return (value != null) ? (value != NULL_VALUE ? value : null) : super.get( key, l );
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getValue( Object key ) {
|
||||||
|
// use local variable for getters to avoid potential multi-threading issues
|
||||||
|
List<Function<Object, Object>> uiDefaultsGetters = FlatLaf.this.uiDefaultsGetters;
|
||||||
|
|
||||||
|
if( uiDefaultsGetters == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
for( int i = uiDefaultsGetters.size() - 1; i >= 0; i-- ) {
|
||||||
|
Object value = uiDefaultsGetters.get( i ).apply( key );
|
||||||
|
if( value != null )
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class ActiveFont ---------------------------------------------------
|
||||||
|
|
||||||
|
static class ActiveFont
|
||||||
|
implements ActiveValue
|
||||||
|
{
|
||||||
|
private final String baseFontKey;
|
||||||
|
private final List<String> families;
|
||||||
|
private final int style;
|
||||||
|
private final int styleChange;
|
||||||
|
private final int absoluteSize;
|
||||||
|
private final int relativeSize;
|
||||||
|
private final float scaleSize;
|
||||||
|
|
||||||
|
// cache (scaled/derived) font
|
||||||
|
private FontUIResource font;
|
||||||
|
private Font lastBaseFont;
|
||||||
|
|
||||||
|
private boolean inCreateValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param families list of font families, or {@code null}
|
||||||
|
* @param style new style of font, or {@code -1}
|
||||||
|
* @param styleChange derive style of base font; or {@code 0}
|
||||||
|
* (the lower 16 bits are added; the upper 16 bits are removed)
|
||||||
|
* @param absoluteSize new size of font, or {@code 0}
|
||||||
|
* @param relativeSize added to size of base font, or {@code 0}
|
||||||
|
* @param scaleSize multiply size of base font, or {@code 0}
|
||||||
|
*/
|
||||||
|
ActiveFont( String baseFontKey, List<String> families, int style, int styleChange,
|
||||||
|
int absoluteSize, int relativeSize, float scaleSize )
|
||||||
|
{
|
||||||
|
this.baseFontKey = baseFontKey;
|
||||||
|
this.families = families;
|
||||||
|
this.style = style;
|
||||||
|
this.styleChange = styleChange;
|
||||||
|
this.absoluteSize = absoluteSize;
|
||||||
|
this.relativeSize = relativeSize;
|
||||||
|
this.scaleSize = scaleSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// using synchronized to avoid exception if invoked at the same time on multiple threads
|
||||||
|
@Override
|
||||||
|
public synchronized Object createValue( UIDefaults table ) {
|
||||||
|
if( inCreateValue )
|
||||||
|
throw new IllegalStateException( "FlatLaf: endless recursion in font" );
|
||||||
|
|
||||||
|
Font baseFont = null;
|
||||||
|
|
||||||
|
inCreateValue = true;
|
||||||
|
try {
|
||||||
|
if( baseFontKey != null )
|
||||||
|
baseFont = (Font) UIDefaultsLoader.lazyUIManagerGet( baseFontKey );
|
||||||
|
|
||||||
|
if( baseFont == null )
|
||||||
|
baseFont = UIManager.getFont( "defaultFont" );
|
||||||
|
|
||||||
// fallback (to avoid NPE in case that this is used in another Laf)
|
// fallback (to avoid NPE in case that this is used in another Laf)
|
||||||
if( defaultFont == null )
|
if( baseFont == null )
|
||||||
defaultFont = UIManager.getFont( "Label.font" );
|
baseFont = UIManager.getFont( "Label.font" );
|
||||||
|
} finally {
|
||||||
if( lastDefaultFont != defaultFont ) {
|
inCreateValue = false;
|
||||||
lastDefaultFont = defaultFont;
|
|
||||||
|
|
||||||
if( scaleFactor != 1 ) {
|
|
||||||
// scale font
|
|
||||||
int newFontSize = Math.round( defaultFont.getSize() * scaleFactor );
|
|
||||||
font = new FontUIResource( defaultFont.deriveFont( (float) newFontSize ) );
|
|
||||||
} else {
|
|
||||||
// make sure that font is a UIResource for LaF switching
|
|
||||||
font = (defaultFont instanceof UIResource)
|
|
||||||
? defaultFont
|
|
||||||
: new FontUIResource( defaultFont );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( lastBaseFont != baseFont ) {
|
||||||
|
lastBaseFont = baseFont;
|
||||||
|
|
||||||
|
font = derive( baseFont, fontSize -> UIScale.scale( fontSize ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
return font;
|
return font;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FontUIResource derive( Font baseFont, IntUnaryOperator scale ) {
|
||||||
|
int baseStyle = baseFont.getStyle();
|
||||||
|
int baseSize = baseFont.getSize();
|
||||||
|
|
||||||
|
// new style
|
||||||
|
int newStyle = (style != -1)
|
||||||
|
? style
|
||||||
|
: (styleChange != 0)
|
||||||
|
? baseStyle & ~((styleChange >> 16) & 0xffff) | (styleChange & 0xffff)
|
||||||
|
: baseStyle;
|
||||||
|
|
||||||
|
// new size
|
||||||
|
int newSize = (absoluteSize > 0)
|
||||||
|
? scale.applyAsInt( absoluteSize )
|
||||||
|
: (relativeSize != 0)
|
||||||
|
? (baseSize + scale.applyAsInt( relativeSize ))
|
||||||
|
: (scaleSize > 0)
|
||||||
|
? Math.round( baseSize * scaleSize )
|
||||||
|
: baseSize;
|
||||||
|
if( newSize <= 0 )
|
||||||
|
newSize = 1;
|
||||||
|
|
||||||
|
// create font for family
|
||||||
|
if( families != null && !families.isEmpty() ) {
|
||||||
|
for( String family : families ) {
|
||||||
|
Font font = createCompositeFont( family, newStyle, newSize );
|
||||||
|
if( !isFallbackFont( font ) || family.equalsIgnoreCase( Font.DIALOG ) )
|
||||||
|
return toUIResource( font );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// derive font
|
||||||
|
if( newStyle != baseStyle || newSize != baseSize ) {
|
||||||
|
// hack for font "Ubuntu Medium" on Linux, which curiously belongs
|
||||||
|
// to family "Ubuntu Light" and using deriveFont() would create a light font
|
||||||
|
if( "Ubuntu Medium".equalsIgnoreCase( baseFont.getName() ) &&
|
||||||
|
"Ubuntu Light".equalsIgnoreCase( baseFont.getFamily() ) )
|
||||||
|
{
|
||||||
|
Font font = createCompositeFont( "Ubuntu Medium", newStyle, newSize );
|
||||||
|
if( !isFallbackFont( font ) )
|
||||||
|
return toUIResource( font );
|
||||||
|
}
|
||||||
|
|
||||||
|
return toUIResource( baseFont.deriveFont( newStyle, newSize ) );
|
||||||
|
} else
|
||||||
|
return toUIResource( baseFont );
|
||||||
|
}
|
||||||
|
|
||||||
|
private FontUIResource toUIResource( Font font ) {
|
||||||
|
// make sure that font is a UIResource for LaF switching
|
||||||
|
return (font instanceof FontUIResource)
|
||||||
|
? (FontUIResource) font
|
||||||
|
: new FontUIResource( font );
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isFallbackFont( Font font ) {
|
||||||
|
return Font.DIALOG.equalsIgnoreCase( font.getFamily() );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class ImageIconUIResource ------------------------------------------
|
//---- class ImageIconUIResource ------------------------------------------
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ public class FlatLightLaf
|
|||||||
/**
|
/**
|
||||||
* Sets the application look and feel to this LaF
|
* Sets the application look and feel to this LaF
|
||||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||||
|
*
|
||||||
|
* @since 1.2
|
||||||
*/
|
*/
|
||||||
public static boolean setup() {
|
public static boolean setup() {
|
||||||
return setup( new FlatLightLaf() );
|
return setup( new FlatLightLaf() );
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf;
|
package com.formdev.flatlaf;
|
||||||
|
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -34,7 +35,7 @@ public interface FlatSystemProperties
|
|||||||
* To replace the Java 9+ system scale factor, use system property "sun.java2d.uiScale",
|
* To replace the Java 9+ system scale factor, use system property "sun.java2d.uiScale",
|
||||||
* which has the same syntax as this one.
|
* which has the same syntax as this one.
|
||||||
* <p>
|
* <p>
|
||||||
* Since FlatLaf 1.1.2: Scale factors less then 100% are allowed.
|
* Since FlatLaf 1.1.2: Scale factors less than 100% are allowed.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Allowed Values</strong> e.g. {@code 1.5}, {@code 1.5x}, {@code 150%} or {@code 144dpi} (96dpi is 100%)<br>
|
* <strong>Allowed Values</strong> e.g. {@code 1.5}, {@code 1.5x}, {@code 150%} or {@code 144dpi} (96dpi is 100%)<br>
|
||||||
*/
|
*/
|
||||||
@@ -81,7 +82,7 @@ public interface FlatSystemProperties
|
|||||||
* {@link FlatClientProperties#USE_WINDOW_DECORATIONS} and
|
* {@link FlatClientProperties#USE_WINDOW_DECORATIONS} and
|
||||||
* UI default {@code TitlePane.useWindowDecorations}.
|
* UI default {@code TitlePane.useWindowDecorations}.
|
||||||
* <p>
|
* <p>
|
||||||
* (requires Window 10)
|
* (requires Window 10/11)
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||||
* <strong>Default</strong> none
|
* <strong>Default</strong> none
|
||||||
@@ -92,16 +93,16 @@ public interface FlatSystemProperties
|
|||||||
* Specifies whether JetBrains Runtime custom window decorations should be used
|
* Specifies whether JetBrains Runtime custom window decorations should be used
|
||||||
* when creating {@code JFrame} or {@code JDialog}.
|
* when creating {@code JFrame} or {@code JDialog}.
|
||||||
* Requires that the application runs in a
|
* Requires that the application runs in a
|
||||||
* <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime</a>
|
* <a href="https://github.com/JetBrains/JetBrainsRuntime/wiki">JetBrains Runtime</a>
|
||||||
* (based on OpenJDK).
|
* (based on OpenJDK).
|
||||||
* <p>
|
* <p>
|
||||||
* Setting this to {@code false} disables using JetBrains Runtime custom window decorations.
|
* Setting this to {@code false} disables using JetBrains Runtime custom window decorations.
|
||||||
* Then FlatLaf native window decorations are used.
|
* Then FlatLaf native window decorations are used.
|
||||||
* <p>
|
* <p>
|
||||||
* (requires Window 10)
|
* (requires Window 10/11)
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||||
* <strong>Default</strong> {@code true}
|
* <strong>Default</strong> {@code false} (since v2; was {@code true} in v1)
|
||||||
*/
|
*/
|
||||||
String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations";
|
String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations";
|
||||||
|
|
||||||
@@ -116,7 +117,7 @@ public interface FlatSystemProperties
|
|||||||
* {@link FlatClientProperties#MENU_BAR_EMBEDDED} and
|
* {@link FlatClientProperties#MENU_BAR_EMBEDDED} and
|
||||||
* UI default {@code TitlePane.menuBarEmbedded}.
|
* UI default {@code TitlePane.menuBarEmbedded}.
|
||||||
* <p>
|
* <p>
|
||||||
* (requires Window 10)
|
* (requires Window 10/11)
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||||
* <strong>Default</strong> none
|
* <strong>Default</strong> none
|
||||||
@@ -139,6 +140,29 @@ public interface FlatSystemProperties
|
|||||||
*/
|
*/
|
||||||
String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection";
|
String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether FlatLaf updates the UI when the system font changes.
|
||||||
|
* If {@code true}, {@link SwingUtilities#updateComponentTreeUI(java.awt.Component)}
|
||||||
|
* gets invoked for all windows if the system font has changed.
|
||||||
|
* This is the similar to when switching to another look and feel (theme).
|
||||||
|
* Applications that do not work correctly when switching look and feel,
|
||||||
|
* should disable this option to avoid corrupted UI.
|
||||||
|
* <p>
|
||||||
|
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||||
|
* <strong>Default</strong> {@code true}
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
String UPDATE_UI_ON_SYSTEM_FONT_CHANGE = "flatlaf.updateUIOnSystemFontChange";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies a directory in which the native FlatLaf library have been extracted.
|
||||||
|
* The path can be absolute or relative to current application working directory.
|
||||||
|
* This can be used to avoid extraction of the native libraries to the temporary directory at runtime.
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
String NATIVE_LIBRARY_PATH = "flatlaf.nativeLibraryPath";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether a system property is set and returns {@code true} if its value
|
* Checks whether a system property is set and returns {@code true} if its value
|
||||||
* is {@code "true"} (case-insensitive), otherwise it returns {@code false}.
|
* is {@code "true"} (case-insensitive), otherwise it returns {@code false}.
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import com.formdev.flatlaf.json.ParseException;
|
|||||||
import com.formdev.flatlaf.util.ColorFunctions;
|
import com.formdev.flatlaf.util.ColorFunctions;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.StringUtils;
|
import com.formdev.flatlaf.util.StringUtils;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class supports loading IntelliJ .theme.json files and using them as a Laf.
|
* This class supports loading IntelliJ .theme.json files and using them as a Laf.
|
||||||
@@ -72,6 +73,8 @@ public class IntelliJTheme
|
|||||||
*
|
*
|
||||||
* The input stream is automatically closed.
|
* The input stream is automatically closed.
|
||||||
* Using a buffered input stream is not necessary.
|
* Using a buffered input stream is not necessary.
|
||||||
|
*
|
||||||
|
* @since 1.2
|
||||||
*/
|
*/
|
||||||
public static boolean setup( InputStream in ) {
|
public static boolean setup( InputStream in ) {
|
||||||
try {
|
try {
|
||||||
@@ -162,8 +165,11 @@ public class IntelliJTheme
|
|||||||
applyCheckBoxColors( defaults );
|
applyCheckBoxColors( defaults );
|
||||||
|
|
||||||
// copy values
|
// copy values
|
||||||
for( Map.Entry<String, String> e : uiKeyCopying.entrySet() )
|
for( Map.Entry<String, String> e : uiKeyCopying.entrySet() ) {
|
||||||
defaults.put( e.getKey(), defaults.get( e.getValue() ) );
|
Object value = defaults.get( e.getValue() );
|
||||||
|
if( value != null )
|
||||||
|
defaults.put( e.getKey(), value );
|
||||||
|
}
|
||||||
|
|
||||||
// IDEA does not paint button background if disabled, but FlatLaf does
|
// IDEA does not paint button background if disabled, but FlatLaf does
|
||||||
Object panelBackground = defaults.get( "Panel.background" );
|
Object panelBackground = defaults.get( "Panel.background" );
|
||||||
@@ -175,7 +181,7 @@ public class IntelliJTheme
|
|||||||
defaults.put( "Button.hoverBorderColor", defaults.get( "Button.focusedBorderColor" ) );
|
defaults.put( "Button.hoverBorderColor", defaults.get( "Button.focusedBorderColor" ) );
|
||||||
defaults.put( "HelpButton.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
|
// IDEA uses an SVG icon for the help button, but paints the background with Button.startBackground and Button.endBackground
|
||||||
Object helpButtonBackground = defaults.get( "Button.startBackground" );
|
Object helpButtonBackground = defaults.get( "Button.startBackground" );
|
||||||
Object helpButtonBorderColor = defaults.get( "Button.startBorderColor" );
|
Object helpButtonBorderColor = defaults.get( "Button.startBorderColor" );
|
||||||
if( helpButtonBackground == null )
|
if( helpButtonBackground == null )
|
||||||
@@ -242,14 +248,26 @@ public class IntelliJTheme
|
|||||||
defaults.put( "Tree.rowHeight", 22 );
|
defaults.put( "Tree.rowHeight", 22 );
|
||||||
|
|
||||||
// apply theme specific UI defaults at the end to allow overwriting
|
// apply theme specific UI defaults at the end to allow overwriting
|
||||||
defaults.putAll( themeSpecificDefaults );
|
for( Map.Entry<Object, Object> e : themeSpecificDefaults.entrySet() ) {
|
||||||
|
Object key = e.getKey();
|
||||||
|
Object value = e.getValue();
|
||||||
|
|
||||||
|
// append styles to existing styles
|
||||||
|
if( key instanceof String && ((String)key).startsWith( "[style]" ) ) {
|
||||||
|
Object oldValue = defaults.get( key );
|
||||||
|
if( oldValue != null )
|
||||||
|
value = oldValue + "; " + value;
|
||||||
|
}
|
||||||
|
|
||||||
|
defaults.put( key, value );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<Object, Object> removeThemeSpecificDefaults( UIDefaults defaults ) {
|
private Map<Object, Object> removeThemeSpecificDefaults( UIDefaults defaults ) {
|
||||||
// search for theme specific UI defaults keys
|
// search for theme specific UI defaults keys
|
||||||
ArrayList<String> themeSpecificKeys = new ArrayList<>();
|
ArrayList<String> themeSpecificKeys = new ArrayList<>();
|
||||||
for( Object key : defaults.keySet() ) {
|
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 );
|
themeSpecificKeys.add( (String) key );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,8 +317,19 @@ public class IntelliJTheme
|
|||||||
@SuppressWarnings( "unchecked" )
|
@SuppressWarnings( "unchecked" )
|
||||||
private void apply( String key, Object value, UIDefaults defaults, ArrayList<Object> defaultsKeysCache, Set<String> uiKeys ) {
|
private void apply( String key, Object value, UIDefaults defaults, ArrayList<Object> defaultsKeysCache, Set<String> uiKeys ) {
|
||||||
if( value instanceof Map ) {
|
if( value instanceof Map ) {
|
||||||
for( Map.Entry<String, Object> e : ((Map<String, Object>)value).entrySet() )
|
Map<String, Object> map = (Map<String, Object>)value;
|
||||||
|
if( map.containsKey( "os.default" ) || map.containsKey( "os.windows" ) || map.containsKey( "os.mac" ) || map.containsKey( "os.linux" ) ) {
|
||||||
|
String osKey = SystemInfo.isWindows ? "os.windows"
|
||||||
|
: SystemInfo.isMacOS ? "os.mac"
|
||||||
|
: SystemInfo.isLinux ? "os.linux" : null;
|
||||||
|
if( osKey != null && map.containsKey( osKey ) )
|
||||||
|
apply( key, map.get( osKey ), defaults, defaultsKeysCache, uiKeys );
|
||||||
|
else if( map.containsKey( "os.default" ) )
|
||||||
|
apply( key, map.get( "os.default" ), defaults, defaultsKeysCache, uiKeys );
|
||||||
|
} else {
|
||||||
|
for( Map.Entry<String, Object> e : map.entrySet() )
|
||||||
apply( key + '.' + e.getKey(), e.getValue(), defaults, defaultsKeysCache, uiKeys );
|
apply( key + '.' + e.getKey(), e.getValue(), defaults, defaultsKeysCache, uiKeys );
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if( "".equals( value ) )
|
if( "".equals( value ) )
|
||||||
return; // ignore empty value
|
return; // ignore empty value
|
||||||
@@ -338,7 +367,7 @@ public class IntelliJTheme
|
|||||||
|
|
||||||
// parse value
|
// parse value
|
||||||
try {
|
try {
|
||||||
uiValue = UIDefaultsLoader.parseValue( key, valueStr );
|
uiValue = UIDefaultsLoader.parseValue( key, valueStr, null );
|
||||||
} catch( RuntimeException ex ) {
|
} catch( RuntimeException ex ) {
|
||||||
UIDefaultsLoader.logParseError( key, valueStr, ex, false );
|
UIDefaultsLoader.logParseError( key, valueStr, ex, false );
|
||||||
return; // ignore invalid value
|
return; // ignore invalid value
|
||||||
@@ -504,7 +533,7 @@ public class IntelliJTheme
|
|||||||
// for filled checkbox/radiobutton used in light themes
|
// for filled checkbox/radiobutton used in light themes
|
||||||
defaults.remove( "CheckBox.icon[filled].focusWidth" );
|
defaults.remove( "CheckBox.icon[filled].focusWidth" );
|
||||||
defaults.put( "CheckBox.icon[filled].hoverBorderColor", defaults.get( "CheckBox.icon[filled].focusedBorderColor" ) );
|
defaults.put( "CheckBox.icon[filled].hoverBorderColor", defaults.get( "CheckBox.icon[filled].focusedBorderColor" ) );
|
||||||
defaults.put( "CheckBox.icon[filled].selectedFocusedBackground", defaults.get( "CheckBox.icon[filled].selectedBackground" ) );
|
defaults.put( "CheckBox.icon[filled].focusedSelectedBackground", defaults.get( "CheckBox.icon[filled].selectedBackground" ) );
|
||||||
|
|
||||||
if( dark ) {
|
if( dark ) {
|
||||||
// IDEA Darcula checkBoxFocused.svg, checkBoxSelectedFocused.svg,
|
// IDEA Darcula checkBoxFocused.svg, checkBoxSelectedFocused.svg,
|
||||||
@@ -513,9 +542,9 @@ public class IntelliJTheme
|
|||||||
// --> add alpha to focused border colors
|
// --> add alpha to focused border colors
|
||||||
String[] focusedBorderColorKeys = new String[] {
|
String[] focusedBorderColorKeys = new String[] {
|
||||||
"CheckBox.icon.focusedBorderColor",
|
"CheckBox.icon.focusedBorderColor",
|
||||||
"CheckBox.icon.selectedFocusedBorderColor",
|
"CheckBox.icon.focusedSelectedBorderColor",
|
||||||
"CheckBox.icon[filled].focusedBorderColor",
|
"CheckBox.icon[filled].focusedBorderColor",
|
||||||
"CheckBox.icon[filled].selectedFocusedBorderColor",
|
"CheckBox.icon[filled].focusedSelectedBorderColor",
|
||||||
};
|
};
|
||||||
for( String key : focusedBorderColorKeys ) {
|
for( String key : focusedBorderColorKeys ) {
|
||||||
Color color = defaults.getColor( key );
|
Color color = defaults.getColor( key );
|
||||||
@@ -534,12 +563,12 @@ public class IntelliJTheme
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Rename UI default keys (key --> value). */
|
/** Rename UI default keys (key --> value). */
|
||||||
private static Map<String, String> uiKeyMapping = new HashMap<>();
|
private static final Map<String, String> uiKeyMapping = new HashMap<>();
|
||||||
/** Copy UI default keys (value --> key). */
|
/** Copy UI default keys (value --> key). */
|
||||||
private static Map<String, String> uiKeyCopying = new HashMap<>();
|
private static final Map<String, String> uiKeyCopying = new HashMap<>();
|
||||||
private static Map<String, String> uiKeyInverseMapping = new HashMap<>();
|
private static final Map<String, String> uiKeyInverseMapping = new HashMap<>();
|
||||||
private static Map<String, String> checkboxKeyMapping = new HashMap<>();
|
private static final Map<String, String> checkboxKeyMapping = new HashMap<>();
|
||||||
private static Map<String, String> checkboxDuplicateColors = new HashMap<>();
|
private static final Map<String, String> checkboxDuplicateColors = new HashMap<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// ComboBox
|
// ComboBox
|
||||||
@@ -549,6 +578,8 @@ public class IntelliJTheme
|
|||||||
uiKeyMapping.put( "ComboBox.ArrowButton.disabledIconColor", "ComboBox.buttonDisabledArrowColor" );
|
uiKeyMapping.put( "ComboBox.ArrowButton.disabledIconColor", "ComboBox.buttonDisabledArrowColor" );
|
||||||
uiKeyMapping.put( "ComboBox.ArrowButton.iconColor", "ComboBox.buttonArrowColor" );
|
uiKeyMapping.put( "ComboBox.ArrowButton.iconColor", "ComboBox.buttonArrowColor" );
|
||||||
uiKeyMapping.put( "ComboBox.ArrowButton.nonEditableBackground", "ComboBox.buttonBackground" );
|
uiKeyMapping.put( "ComboBox.ArrowButton.nonEditableBackground", "ComboBox.buttonBackground" );
|
||||||
|
uiKeyCopying.put( "ComboBox.buttonSeparatorColor", "Component.borderColor" );
|
||||||
|
uiKeyCopying.put( "ComboBox.buttonDisabledSeparatorColor", "Component.disabledBorderColor" );
|
||||||
|
|
||||||
// Component
|
// Component
|
||||||
uiKeyMapping.put( "Component.inactiveErrorFocusColor", "Component.error.borderColor" );
|
uiKeyMapping.put( "Component.inactiveErrorFocusColor", "Component.error.borderColor" );
|
||||||
@@ -594,6 +625,15 @@ public class IntelliJTheme
|
|||||||
uiKeyCopying.put( "Slider.thumbColor", "ProgressBar.foreground" );
|
uiKeyCopying.put( "Slider.thumbColor", "ProgressBar.foreground" );
|
||||||
uiKeyCopying.put( "Slider.trackColor", "ProgressBar.background" );
|
uiKeyCopying.put( "Slider.trackColor", "ProgressBar.background" );
|
||||||
|
|
||||||
|
// Spinner
|
||||||
|
uiKeyCopying.put( "Spinner.buttonSeparatorColor", "Component.borderColor" );
|
||||||
|
uiKeyCopying.put( "Spinner.buttonDisabledSeparatorColor", "Component.disabledBorderColor" );
|
||||||
|
|
||||||
|
// TabbedPane
|
||||||
|
uiKeyCopying.put( "TabbedPane.selectedBackground", "DefaultTabs.underlinedTabBackground" );
|
||||||
|
uiKeyCopying.put( "TabbedPane.selectedForeground", "DefaultTabs.underlinedTabForeground" );
|
||||||
|
uiKeyCopying.put( "TabbedPane.inactiveUnderlineColor", "DefaultTabs.inactiveUnderlineColor" );
|
||||||
|
|
||||||
// TitlePane
|
// TitlePane
|
||||||
uiKeyCopying.put( "TitlePane.inactiveBackground", "TitlePane.background" );
|
uiKeyCopying.put( "TitlePane.inactiveBackground", "TitlePane.background" );
|
||||||
uiKeyMapping.put( "TitlePane.infoForeground", "TitlePane.foreground" );
|
uiKeyMapping.put( "TitlePane.infoForeground", "TitlePane.foreground" );
|
||||||
@@ -618,7 +658,7 @@ public class IntelliJTheme
|
|||||||
checkboxKeyMapping.put( "Checkbox.Background.Selected", "CheckBox.icon.selectedBackground" );
|
checkboxKeyMapping.put( "Checkbox.Background.Selected", "CheckBox.icon.selectedBackground" );
|
||||||
checkboxKeyMapping.put( "Checkbox.Border.Selected", "CheckBox.icon.selectedBorderColor" );
|
checkboxKeyMapping.put( "Checkbox.Border.Selected", "CheckBox.icon.selectedBorderColor" );
|
||||||
checkboxKeyMapping.put( "Checkbox.Foreground.Selected", "CheckBox.icon.checkmarkColor" );
|
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.Background.Default.Dark", "Checkbox.Background.Selected.Dark" );
|
||||||
checkboxDuplicateColors.put( "Checkbox.Border.Default.Dark", "Checkbox.Border.Selected.Dark" );
|
checkboxDuplicateColors.put( "Checkbox.Border.Default.Dark", "Checkbox.Border.Selected.Dark" );
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.formdev.flatlaf;
|
package com.formdev.flatlaf;
|
||||||
|
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
|
import java.awt.FontMetrics;
|
||||||
import java.awt.GraphicsConfiguration;
|
import java.awt.GraphicsConfiguration;
|
||||||
import java.awt.GraphicsEnvironment;
|
import java.awt.GraphicsEnvironment;
|
||||||
import java.awt.Toolkit;
|
import java.awt.Toolkit;
|
||||||
@@ -28,7 +29,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
import javax.swing.text.StyleContext;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.StringUtils;
|
import com.formdev.flatlaf.util.StringUtils;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
@@ -121,14 +122,25 @@ class LinuxFontPolicy
|
|||||||
for(;;) {
|
for(;;) {
|
||||||
Font font = createFont( family, style, size, dsize );
|
Font font = createFont( family, style, size, dsize );
|
||||||
|
|
||||||
// if the font family does not match any font on the system, "Dialog" family is returned
|
if( Font.DIALOG.equals( family ) )
|
||||||
if( !"Dialog".equals( font.getFamily() ) || "Dialog".equals( family ) )
|
|
||||||
return font;
|
return font;
|
||||||
|
|
||||||
|
// if the font family does not match any font on the system, "Dialog" family is returned
|
||||||
|
if( !Font.DIALOG.equals( font.getFamily() ) ) {
|
||||||
|
// check for font problems
|
||||||
|
// - font height much larger than expected (e.g. font Inter; Oracle Java 8)
|
||||||
|
// - character width is zero (e.g. font Cantarell; Fedora; Oracle Java 8)
|
||||||
|
FontMetrics fm = StyleContext.getDefaultStyleContext().getFontMetrics( font );
|
||||||
|
if( fm.getHeight() > size * 2 || fm.stringWidth( "a" ) == 0 )
|
||||||
|
return createFont( Font.DIALOG, style, size, dsize );
|
||||||
|
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
|
||||||
// find last word in family
|
// find last word in family
|
||||||
int index = family.lastIndexOf( ' ' );
|
int index = family.lastIndexOf( ' ' );
|
||||||
if( index < 0 )
|
if( index < 0 )
|
||||||
return createFont( "Dialog", style, size, dsize );
|
return createFont( Font.DIALOG, style, size, dsize );
|
||||||
|
|
||||||
// check whether last work contains some font weight (e.g. Ultra-Bold or Heavy)
|
// check whether last work contains some font weight (e.g. Ultra-Bold or Heavy)
|
||||||
String lastWord = family.substring( index + 1 ).toLowerCase();
|
String lastWord = family.substring( index + 1 ).toLowerCase();
|
||||||
@@ -158,7 +170,7 @@ class LinuxFontPolicy
|
|||||||
|
|
||||||
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "gnome.Xft/DPI" );
|
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "gnome.Xft/DPI" );
|
||||||
if( value instanceof Integer ) {
|
if( value instanceof Integer ) {
|
||||||
int dpi = ((Integer)value).intValue() / 1024;
|
int dpi = (Integer) value / 1024;
|
||||||
if( dpi == -1 )
|
if( dpi == -1 )
|
||||||
dpi = 96;
|
dpi = 96;
|
||||||
if( dpi < 50 )
|
if( dpi < 50 )
|
||||||
@@ -185,7 +197,7 @@ class LinuxFontPolicy
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the default font for KDE for KDE configuration files.
|
* Gets the default font for KDE from KDE configuration files.
|
||||||
*
|
*
|
||||||
* The Swing fonts are not updated when the user changes system font size
|
* The Swing fonts are not updated when the user changes system font size
|
||||||
* (System Settings > Fonts > Force Font DPI). A application restart is necessary.
|
* (System Settings > Fonts > Force Font DPI). A application restart is necessary.
|
||||||
@@ -266,7 +278,7 @@ class LinuxFontPolicy
|
|||||||
// read config file
|
// read config file
|
||||||
ArrayList<String> lines = new ArrayList<>( 200 );
|
ArrayList<String> lines = new ArrayList<>( 200 );
|
||||||
try( BufferedReader reader = new BufferedReader( new FileReader( file ) ) ) {
|
try( BufferedReader reader = new BufferedReader( new FileReader( file ) ) ) {
|
||||||
String line = null;
|
String line;
|
||||||
while( (line = reader.readLine()) != null )
|
while( (line = reader.readLine()) != null )
|
||||||
lines.add( line );
|
lines.add( line );
|
||||||
} catch( IOException ex ) {
|
} catch( IOException ex ) {
|
||||||
@@ -309,6 +321,9 @@ class LinuxFontPolicy
|
|||||||
* - running on JetBrains Runtime 11 or later and scaling is enabled in system Settings
|
* - running on JetBrains Runtime 11 or later and scaling is enabled in system Settings
|
||||||
*/
|
*/
|
||||||
private static boolean isSystemScaling() {
|
private static boolean isSystemScaling() {
|
||||||
|
if( GraphicsEnvironment.isHeadless() )
|
||||||
|
return true;
|
||||||
|
|
||||||
GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment()
|
GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment()
|
||||||
.getDefaultScreenDevice().getDefaultConfiguration();
|
.getDefaultScreenDevice().getDefaultConfiguration();
|
||||||
return UIScale.getSystemScaleFactor( gc ) > 1;
|
return UIScale.getSystemScaleFactor( gc ) > 1;
|
||||||
|
|||||||
@@ -0,0 +1,336 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 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;
|
||||||
|
|
||||||
|
import java.awt.AWTEvent;
|
||||||
|
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.MouseInfo;
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.awt.PointerInfo;
|
||||||
|
import java.awt.Polygon;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.Toolkit;
|
||||||
|
import java.awt.Window;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JLayeredPane;
|
||||||
|
import javax.swing.JMenu;
|
||||||
|
import javax.swing.JPopupMenu;
|
||||||
|
import javax.swing.MenuElement;
|
||||||
|
import javax.swing.MenuSelectionManager;
|
||||||
|
import javax.swing.RootPaneContainer;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.Timer;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Improves usability of submenus by using a
|
||||||
|
* <a href="https://height.app/blog/guide-to-build-context-menus#safe-triangle">safe triangle</a>
|
||||||
|
* to avoid that the submenu closes while the user moves the mouse to it.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
class SubMenuUsabilityHelper
|
||||||
|
implements ChangeListener
|
||||||
|
{
|
||||||
|
private static final String KEY_USE_SAFE_TRIANGLE = "Menu.useSafeTriangle";
|
||||||
|
private static final String KEY_SHOW_SAFE_TRIANGLE = "FlatLaf.debug.menu.showSafeTriangle";
|
||||||
|
|
||||||
|
// Using a static field to ensure that there is only one instance in the system.
|
||||||
|
// Multiple instances would freeze the application.
|
||||||
|
// https://github.com/apache/netbeans/issues/4231#issuecomment-1179616607
|
||||||
|
private static SubMenuUsabilityHelper instance;
|
||||||
|
|
||||||
|
private SubMenuEventQueue subMenuEventQueue;
|
||||||
|
private SafeTrianglePainter safeTrianglePainter;
|
||||||
|
private boolean changePending;
|
||||||
|
|
||||||
|
// mouse location in screen coordinates
|
||||||
|
private int mouseX;
|
||||||
|
private int mouseY;
|
||||||
|
|
||||||
|
// target popup bounds in screen coordinates
|
||||||
|
private int targetX;
|
||||||
|
private int targetTopY;
|
||||||
|
private int targetBottomY;
|
||||||
|
|
||||||
|
private Rectangle invokerBounds;
|
||||||
|
|
||||||
|
static synchronized boolean install() {
|
||||||
|
if( instance != null )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
instance = new SubMenuUsabilityHelper();
|
||||||
|
MenuSelectionManager.defaultManager().addChangeListener( instance );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static synchronized void uninstall() {
|
||||||
|
if( instance == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
MenuSelectionManager.defaultManager().removeChangeListener( instance );
|
||||||
|
instance.uninstallEventQueue();
|
||||||
|
instance = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stateChanged( ChangeEvent e ) {
|
||||||
|
if( !FlatUIUtils.getUIBoolean( KEY_USE_SAFE_TRIANGLE, true ))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// handle menu selection change later, but only once in case of temporary changes
|
||||||
|
// e.g. moving mouse from one menu item to another one, fires two events:
|
||||||
|
// 1. old menu item is removed from menu selection
|
||||||
|
// 2. new menu item is added to menu selection
|
||||||
|
synchronized( this ) {
|
||||||
|
if( changePending )
|
||||||
|
return;
|
||||||
|
changePending = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventQueue.invokeLater( () -> {
|
||||||
|
synchronized( this ) {
|
||||||
|
changePending = false;
|
||||||
|
}
|
||||||
|
menuSelectionChanged();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void menuSelectionChanged() {
|
||||||
|
MenuElement[] path = MenuSelectionManager.defaultManager().getSelectedPath();
|
||||||
|
|
||||||
|
/*debug
|
||||||
|
System.out.println( "--- " + path.length );
|
||||||
|
for( int i = 0; i < path.length; i++ )
|
||||||
|
System.out.println( " " + i + ": " + path[i].getClass().getName() );
|
||||||
|
debug*/
|
||||||
|
|
||||||
|
// find submenu in menu selection
|
||||||
|
int subMenuIndex = findSubMenu( path );
|
||||||
|
|
||||||
|
// uninstall if there is no submenu in selection
|
||||||
|
if( subMenuIndex < 0 || subMenuIndex != path.length - 1 ) {
|
||||||
|
uninstallEventQueue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get current mouse location
|
||||||
|
PointerInfo pointerInfo = MouseInfo.getPointerInfo();
|
||||||
|
Point mouseLocation = (pointerInfo != null) ? pointerInfo.getLocation() : new Point();
|
||||||
|
mouseX = mouseLocation.x;
|
||||||
|
mouseY = mouseLocation.y;
|
||||||
|
|
||||||
|
// check whether popup is showing, which is e.g. not the case if it is empty
|
||||||
|
JPopupMenu popup = (JPopupMenu) path[subMenuIndex];
|
||||||
|
if( !popup.isShowing() ) {
|
||||||
|
uninstallEventQueue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get invoker screen bounds
|
||||||
|
Component invoker = popup.getInvoker();
|
||||||
|
invokerBounds = (invoker != null)
|
||||||
|
? new Rectangle( invoker.getLocationOnScreen(), invoker.getSize() )
|
||||||
|
: null;
|
||||||
|
|
||||||
|
// check whether mouse location is within invoker
|
||||||
|
if( invokerBounds != null && !invokerBounds.contains( mouseX, mouseY ) ) {
|
||||||
|
uninstallEventQueue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute top/bottom target locations
|
||||||
|
Point popupLocation = popup.getLocationOnScreen();
|
||||||
|
Dimension popupSize = popup.getSize();
|
||||||
|
targetX = (mouseX < popupLocation.x + (popupSize.width / 2))
|
||||||
|
? popupLocation.x
|
||||||
|
: popupLocation.x + popupSize.width;
|
||||||
|
targetTopY = popupLocation.y;
|
||||||
|
targetBottomY = popupLocation.y + popupSize.height;
|
||||||
|
|
||||||
|
// install own event queue to supress mouse events when mouse is moved within safe triangle
|
||||||
|
if( subMenuEventQueue == null )
|
||||||
|
subMenuEventQueue = new SubMenuEventQueue();
|
||||||
|
|
||||||
|
// create safe triangle painter
|
||||||
|
if( safeTrianglePainter == null && UIManager.getBoolean( KEY_SHOW_SAFE_TRIANGLE ) )
|
||||||
|
safeTrianglePainter = new SafeTrianglePainter( popup );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void uninstallEventQueue() {
|
||||||
|
if( subMenuEventQueue != null ) {
|
||||||
|
subMenuEventQueue.uninstall();
|
||||||
|
subMenuEventQueue = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( safeTrianglePainter != null ) {
|
||||||
|
safeTrianglePainter.uninstall();
|
||||||
|
safeTrianglePainter = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int findSubMenu( MenuElement[] path ) {
|
||||||
|
for( int i = path.length - 1; i >= 1; i-- ) {
|
||||||
|
if( path[i] instanceof JPopupMenu &&
|
||||||
|
path[i - 1] instanceof JMenu &&
|
||||||
|
!((JMenu)path[i - 1]).isTopLevelMenu() )
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Polygon createSafeTriangle() {
|
||||||
|
return new Polygon(
|
||||||
|
new int[] { mouseX, targetX, targetX },
|
||||||
|
new int[] { mouseY, targetTopY, targetBottomY },
|
||||||
|
3 );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class SubMenuEventQueue --------------------------------------------
|
||||||
|
|
||||||
|
private class SubMenuEventQueue
|
||||||
|
extends EventQueue
|
||||||
|
{
|
||||||
|
private Timer mouseUpdateTimer;
|
||||||
|
private Timer timeoutTimer;
|
||||||
|
|
||||||
|
private int newMouseX;
|
||||||
|
private int newMouseY;
|
||||||
|
private AWTEvent lastMouseEvent;
|
||||||
|
|
||||||
|
SubMenuEventQueue() {
|
||||||
|
// timer used to slightly delay update of mouse location used for safe triangle
|
||||||
|
mouseUpdateTimer = new Timer( 50, e -> {
|
||||||
|
mouseX = newMouseX;
|
||||||
|
mouseY = newMouseY;
|
||||||
|
|
||||||
|
if( safeTrianglePainter != null )
|
||||||
|
safeTrianglePainter.repaint();
|
||||||
|
} );
|
||||||
|
mouseUpdateTimer.setRepeats( false );
|
||||||
|
|
||||||
|
// timer used to timeout safe triangle when mouse stops moving
|
||||||
|
timeoutTimer = new Timer( 200, e -> {
|
||||||
|
if( invokerBounds != null && !invokerBounds.contains( newMouseX, newMouseY ) ) {
|
||||||
|
// post last mouse event, which selects menu item at mouse location
|
||||||
|
if( lastMouseEvent != null ) {
|
||||||
|
postEvent( lastMouseEvent );
|
||||||
|
lastMouseEvent = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstallEventQueue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
timeoutTimer.setRepeats( false );
|
||||||
|
|
||||||
|
Toolkit.getDefaultToolkit().getSystemEventQueue().push( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
void uninstall() {
|
||||||
|
mouseUpdateTimer.stop();
|
||||||
|
mouseUpdateTimer = null;
|
||||||
|
|
||||||
|
timeoutTimer.stop();
|
||||||
|
timeoutTimer = null;
|
||||||
|
|
||||||
|
lastMouseEvent = null;
|
||||||
|
|
||||||
|
super.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void dispatchEvent( AWTEvent e ) {
|
||||||
|
int id = e.getID();
|
||||||
|
|
||||||
|
if( e instanceof MouseEvent &&
|
||||||
|
(id == MouseEvent.MOUSE_MOVED || id == MouseEvent.MOUSE_DRAGGED) )
|
||||||
|
{
|
||||||
|
newMouseX = ((MouseEvent)e).getXOnScreen();
|
||||||
|
newMouseY = ((MouseEvent)e).getYOnScreen();
|
||||||
|
|
||||||
|
if( safeTrianglePainter != null )
|
||||||
|
safeTrianglePainter.repaint();
|
||||||
|
|
||||||
|
mouseUpdateTimer.stop();
|
||||||
|
timeoutTimer.stop();
|
||||||
|
|
||||||
|
// check whether mouse moved within safe triangle
|
||||||
|
if( createSafeTriangle().contains( newMouseX, newMouseY ) ) {
|
||||||
|
// update mouse location delayed (this changes the safe triangle)
|
||||||
|
mouseUpdateTimer.start();
|
||||||
|
|
||||||
|
timeoutTimer.start();
|
||||||
|
|
||||||
|
// remember last mouse event, which will be posted if the mouse stops moving
|
||||||
|
lastMouseEvent = e;
|
||||||
|
|
||||||
|
// ignore mouse event
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update mouse location immediately (this changes the safe triangle)
|
||||||
|
mouseX = newMouseX;
|
||||||
|
mouseY = newMouseY;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.dispatchEvent( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class SafeTrianglePainter ------------------------------------------
|
||||||
|
|
||||||
|
private class SafeTrianglePainter
|
||||||
|
extends JComponent
|
||||||
|
{
|
||||||
|
SafeTrianglePainter( JPopupMenu popup ) {
|
||||||
|
Window window = SwingUtilities.windowForComponent( popup.getInvoker() );
|
||||||
|
if( window instanceof RootPaneContainer ) {
|
||||||
|
JLayeredPane layeredPane = ((RootPaneContainer)window).getLayeredPane();
|
||||||
|
setSize( layeredPane.getSize() );
|
||||||
|
layeredPane.add( this, Integer.valueOf( JLayeredPane.POPUP_LAYER + 1 ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void uninstall() {
|
||||||
|
Container parent = getParent();
|
||||||
|
if( parent != null ) {
|
||||||
|
parent.remove( this );
|
||||||
|
parent.repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintComponent( Graphics g ) {
|
||||||
|
Point locationOnScreen = getLocationOnScreen();
|
||||||
|
g.translate( -locationOnScreen.x, -locationOnScreen.y );
|
||||||
|
|
||||||
|
g.setColor( Color.red );
|
||||||
|
((Graphics2D)g).draw( createSafeTriangle() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,11 +18,16 @@ package com.formdev.flatlaf;
|
|||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Font;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.StreamTokenizer;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -33,8 +38,10 @@ import java.util.Map;
|
|||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import javax.swing.Icon;
|
||||||
import javax.swing.UIDefaults;
|
import javax.swing.UIDefaults;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.border.Border;
|
||||||
import javax.swing.UIDefaults.ActiveValue;
|
import javax.swing.UIDefaults.ActiveValue;
|
||||||
import javax.swing.UIDefaults.LazyValue;
|
import javax.swing.UIDefaults.LazyValue;
|
||||||
import javax.swing.plaf.ColorUIResource;
|
import javax.swing.plaf.ColorUIResource;
|
||||||
@@ -48,6 +55,7 @@ import com.formdev.flatlaf.util.DerivedColor;
|
|||||||
import com.formdev.flatlaf.util.GrayFilter;
|
import com.formdev.flatlaf.util.GrayFilter;
|
||||||
import com.formdev.flatlaf.util.HSLColor;
|
import com.formdev.flatlaf.util.HSLColor;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
import com.formdev.flatlaf.util.SoftCache;
|
||||||
import com.formdev.flatlaf.util.StringUtils;
|
import com.formdev.flatlaf.util.StringUtils;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
@@ -72,8 +80,12 @@ class UIDefaultsLoader
|
|||||||
private static final String OPTIONAL_PREFIX = "?";
|
private static final String OPTIONAL_PREFIX = "?";
|
||||||
private static final String WILDCARD_PREFIX = "*.";
|
private static final String WILDCARD_PREFIX = "*.";
|
||||||
|
|
||||||
|
static final String KEY_VARIABLES = "FlatLaf.internal.variables";
|
||||||
|
|
||||||
private static int parseColorDepth;
|
private static int parseColorDepth;
|
||||||
|
|
||||||
|
private static final SoftCache<String, Object> fontCache = new SoftCache<>();
|
||||||
|
|
||||||
static void loadDefaultsFromProperties( Class<?> lookAndFeelClass, List<FlatDefaultsAddon> addons,
|
static void loadDefaultsFromProperties( Class<?> lookAndFeelClass, List<FlatDefaultsAddon> addons,
|
||||||
Properties additionalDefaults, boolean dark, UIDefaults defaults )
|
Properties additionalDefaults, boolean dark, UIDefaults defaults )
|
||||||
{
|
{
|
||||||
@@ -146,6 +158,18 @@ class UIDefaultsLoader
|
|||||||
properties.load( in );
|
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 ) {
|
} else if( source instanceof File ) {
|
||||||
// load from folder
|
// load from folder
|
||||||
File folder = (File) source;
|
File folder = (File) source;
|
||||||
@@ -234,18 +258,24 @@ class UIDefaultsLoader
|
|||||||
};
|
};
|
||||||
|
|
||||||
// parse and add properties to UI defaults
|
// parse and add properties to UI defaults
|
||||||
|
Map<String, String> variables = new HashMap<>( 50 );
|
||||||
for( Map.Entry<Object, Object> e : properties.entrySet() ) {
|
for( Map.Entry<Object, Object> e : properties.entrySet() ) {
|
||||||
String key = (String) e.getKey();
|
String key = (String) e.getKey();
|
||||||
if( key.startsWith( VARIABLE_PREFIX ) )
|
if( key.startsWith( VARIABLE_PREFIX ) ) {
|
||||||
|
variables.put( key, (String) e.getValue() );
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
String value = resolveValue( (String) e.getValue(), propertiesGetter );
|
String value = resolveValue( (String) e.getValue(), propertiesGetter );
|
||||||
try {
|
try {
|
||||||
defaults.put( key, parseValue( key, value, null, resolver, addonClassLoaders ) );
|
defaults.put( key, parseValue( key, value, null, null, resolver, addonClassLoaders ) );
|
||||||
} catch( RuntimeException ex ) {
|
} catch( RuntimeException ex ) {
|
||||||
logParseError( key, value, ex, true );
|
logParseError( key, value, ex, true );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remember variables in defaults to allow using them in styles
|
||||||
|
defaults.put( KEY_VARIABLES, variables );
|
||||||
} catch( IOException ex ) {
|
} catch( IOException ex ) {
|
||||||
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to load properties files.", ex );
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to load properties files.", ex );
|
||||||
}
|
}
|
||||||
@@ -288,26 +318,118 @@ class UIDefaultsLoader
|
|||||||
return resolveValue( newValue, propertiesGetter );
|
return resolveValue( newValue, propertiesGetter );
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ValueType { UNKNOWN, STRING, BOOLEAN, CHARACTER, INTEGER, FLOAT, BORDER, ICON, INSETS, DIMENSION, COLOR,
|
static String resolveValueFromUIManager( String value ) {
|
||||||
SCALEDINTEGER, SCALEDFLOAT, SCALEDINSETS, SCALEDDIMENSION, INSTANCE, CLASS, GRAYFILTER, NULL, LAZY }
|
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" );
|
||||||
|
|
||||||
private static ValueType[] tempResultValueType = new ValueType[1];
|
return newValue;
|
||||||
|
|
||||||
static Object parseValue( String key, String value ) {
|
|
||||||
return parseValue( key, value, null, v -> v, Collections.emptyList() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Object parseValue( String key, String value, ValueType[] resultValueType,
|
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 final ValueType[] tempResultValueType = new ValueType[1];
|
||||||
|
private static Map<Class<?>, ValueType> javaValueTypes;
|
||||||
|
private static Map<String, ValueType> knownValueTypes;
|
||||||
|
|
||||||
|
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, Class<?> javaValueType, ValueType[] resultValueType,
|
||||||
Function<String, String> resolver, List<ClassLoader> addonClassLoaders )
|
Function<String, String> resolver, List<ClassLoader> addonClassLoaders )
|
||||||
{
|
{
|
||||||
if( resultValueType == null )
|
if( resultValueType == null )
|
||||||
resultValueType = tempResultValueType;
|
resultValueType = tempResultValueType;
|
||||||
|
|
||||||
|
// do not parse styles here
|
||||||
|
if( key.startsWith( "[style]" ) ) {
|
||||||
|
resultValueType[0] = ValueType.STRING;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
value = value.trim();
|
value = value.trim();
|
||||||
|
|
||||||
// null, false, true
|
// null
|
||||||
|
if( value.equals( "null" ) || value.isEmpty() ) {
|
||||||
|
resultValueType[0] = ValueType.NULL;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 ) {
|
switch( value ) {
|
||||||
case "null": resultValueType[0] = ValueType.NULL; return null;
|
|
||||||
case "false": resultValueType[0] = ValueType.BOOLEAN; return false;
|
case "false": resultValueType[0] = ValueType.BOOLEAN; return false;
|
||||||
case "true": resultValueType[0] = ValueType.BOOLEAN; return true;
|
case "true": resultValueType[0] = ValueType.BOOLEAN; return true;
|
||||||
}
|
}
|
||||||
@@ -316,21 +438,16 @@ class UIDefaultsLoader
|
|||||||
// Syntax: lazy(uiKey)
|
// Syntax: lazy(uiKey)
|
||||||
if( value.startsWith( "lazy(" ) && value.endsWith( ")" ) ) {
|
if( value.startsWith( "lazy(" ) && value.endsWith( ")" ) ) {
|
||||||
resultValueType[0] = ValueType.LAZY;
|
resultValueType[0] = ValueType.LAZY;
|
||||||
String uiKey = value.substring( 5, value.length() - 1 ).trim();
|
String uiKey = StringUtils.substringTrimmed( value, 5, value.length() - 1 );
|
||||||
return (LazyValue) t -> {
|
return (LazyValue) t -> {
|
||||||
return lazyUIManagerGet( uiKey );
|
return lazyUIManagerGet( uiKey );
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueType valueType = ValueType.UNKNOWN;
|
|
||||||
|
|
||||||
// check whether value type is specified in the value
|
// check whether value type is specified in the value
|
||||||
if( value.startsWith( "#" ) )
|
if( value.startsWith( "#" ) )
|
||||||
valueType = ValueType.COLOR;
|
valueType = ValueType.COLOR;
|
||||||
else if( value.startsWith( "\"" ) && value.endsWith( "\"" ) ) {
|
else if( value.startsWith( TYPE_PREFIX ) ) {
|
||||||
valueType = ValueType.STRING;
|
|
||||||
value = value.substring( 1, value.length() - 1 );
|
|
||||||
} else if( value.startsWith( TYPE_PREFIX ) ) {
|
|
||||||
int end = value.indexOf( TYPE_PREFIX_END );
|
int end = value.indexOf( TYPE_PREFIX_END );
|
||||||
if( end != -1 ) {
|
if( end != -1 ) {
|
||||||
try {
|
try {
|
||||||
@@ -345,45 +462,78 @@ class UIDefaultsLoader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( valueType == ValueType.UNKNOWN ) {
|
||||||
|
if( knownValueTypes == null ) {
|
||||||
|
// create lazy
|
||||||
|
knownValueTypes = new HashMap<>();
|
||||||
|
// system colors
|
||||||
|
knownValueTypes.put( "activeCaptionBorder", ValueType.COLOR );
|
||||||
|
knownValueTypes.put( "inactiveCaptionBorder", ValueType.COLOR );
|
||||||
|
knownValueTypes.put( "windowBorder", ValueType.COLOR );
|
||||||
|
// 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
|
// determine value type from key
|
||||||
if( valueType == ValueType.UNKNOWN ) {
|
if( valueType == ValueType.UNKNOWN ) {
|
||||||
if( key.endsWith( "UI" ) )
|
if( key.endsWith( "UI" ) )
|
||||||
valueType = ValueType.STRING;
|
valueType = ValueType.STRING;
|
||||||
else if( key.endsWith( "Color" ) ||
|
else if( key.endsWith( "Color" ) ||
|
||||||
(key.endsWith( "ground" ) &&
|
(key.endsWith( "ground" ) &&
|
||||||
(key.endsWith( ".background" ) || key.endsWith( "Background" ) ||
|
(key.endsWith( ".background" ) || key.endsWith( "Background" ) || key.equals( "background" ) ||
|
||||||
key.endsWith( ".foreground" ) || key.endsWith( "Foreground" ))) )
|
key.endsWith( ".foreground" ) || key.endsWith( "Foreground" ) || key.equals( "foreground" ))) )
|
||||||
valueType = ValueType.COLOR;
|
valueType = ValueType.COLOR;
|
||||||
else if( key.endsWith( ".border" ) || key.endsWith( "Border" ) )
|
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;
|
valueType = ValueType.BORDER;
|
||||||
else if( key.endsWith( ".icon" ) || key.endsWith( "Icon" ) )
|
else if( key.endsWith( ".icon" ) || key.endsWith( "Icon" ) || key.equals( "icon" ) )
|
||||||
valueType = ValueType.ICON;
|
valueType = ValueType.ICON;
|
||||||
else if( key.endsWith( ".margin" ) || key.endsWith( ".padding" ) ||
|
else if( key.endsWith( ".margin" ) || key.equals( "margin" ) ||
|
||||||
|
key.endsWith( ".padding" ) || key.equals( "padding" ) ||
|
||||||
key.endsWith( "Margins" ) || key.endsWith( "Insets" ) )
|
key.endsWith( "Margins" ) || key.endsWith( "Insets" ) )
|
||||||
valueType = ValueType.INSETS;
|
valueType = ValueType.INSETS;
|
||||||
else if( key.endsWith( "Size" ) )
|
else if( key.endsWith( "Size" ) )
|
||||||
valueType = ValueType.DIMENSION;
|
valueType = ValueType.DIMENSION;
|
||||||
else if( key.endsWith( "Width" ) || key.endsWith( "Height" ) )
|
else if( key.endsWith( "Width" ) || key.endsWith( "Height" ) )
|
||||||
valueType = ValueType.INTEGER;
|
valueType = ValueType.INTEGERORFLOAT;
|
||||||
else if( key.endsWith( "Char" ) )
|
else if( key.endsWith( "Char" ) )
|
||||||
valueType = ValueType.CHARACTER;
|
valueType = ValueType.CHARACTER;
|
||||||
else if( key.endsWith( "grayFilter" ) )
|
else if( key.endsWith( "grayFilter" ) )
|
||||||
valueType = ValueType.GRAYFILTER;
|
valueType = ValueType.GRAYFILTER;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resultValueType[0] = valueType;
|
resultValueType[0] = valueType;
|
||||||
|
|
||||||
// parse value
|
// parse value
|
||||||
switch( valueType ) {
|
switch( valueType ) {
|
||||||
case STRING: return value;
|
case STRING: return value;
|
||||||
|
case BOOLEAN: return parseBoolean( value );
|
||||||
case CHARACTER: return parseCharacter( value );
|
case CHARACTER: return parseCharacter( value );
|
||||||
case INTEGER: return parseInteger( value, true );
|
case INTEGER: return parseInteger( value, true );
|
||||||
|
case INTEGERORFLOAT:return parseIntegerOrFloat( value, true );
|
||||||
case FLOAT: return parseFloat( value, true );
|
case FLOAT: return parseFloat( value, true );
|
||||||
case BORDER: return parseBorder( value, resolver, addonClassLoaders );
|
case BORDER: return parseBorder( value, resolver, addonClassLoaders );
|
||||||
case ICON: return parseInstance( value, addonClassLoaders );
|
case ICON: return parseInstance( value, addonClassLoaders );
|
||||||
case INSETS: return parseInsets( value );
|
case INSETS: return parseInsets( value );
|
||||||
case DIMENSION: return parseDimension( value );
|
case DIMENSION: return parseDimension( value );
|
||||||
case COLOR: return parseColorOrFunction( value, resolver, true );
|
case COLOR: return parseColorOrFunction( value, resolver, true );
|
||||||
|
case FONT: return parseFont( value );
|
||||||
case SCALEDINTEGER: return parseScaledInteger( value );
|
case SCALEDINTEGER: return parseScaledInteger( value );
|
||||||
case SCALEDFLOAT: return parseScaledFloat( value );
|
case SCALEDFLOAT: return parseScaledFloat( value );
|
||||||
case SCALEDINSETS: return parseScaledInsets( value );
|
case SCALEDINSETS: return parseScaledInsets( value );
|
||||||
@@ -393,6 +543,12 @@ class UIDefaultsLoader
|
|||||||
case GRAYFILTER: return parseGrayFilter( value );
|
case GRAYFILTER: return parseGrayFilter( value );
|
||||||
case UNKNOWN:
|
case UNKNOWN:
|
||||||
default:
|
default:
|
||||||
|
// string
|
||||||
|
if( value.startsWith( "\"" ) && value.endsWith( "\"" ) ) {
|
||||||
|
resultValueType[0] = ValueType.STRING;
|
||||||
|
return value.substring( 1, value.length() - 1 );
|
||||||
|
}
|
||||||
|
|
||||||
// colors
|
// colors
|
||||||
Object color = parseColorOrFunction( value, resolver, false );
|
Object color = parseColorOrFunction( value, resolver, false );
|
||||||
if( color != null ) {
|
if( color != null ) {
|
||||||
@@ -420,19 +576,34 @@ 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 ) {
|
private static Object parseBorder( String value, Function<String, String> resolver, List<ClassLoader> addonClassLoaders ) {
|
||||||
if( value.indexOf( ',' ) >= 0 ) {
|
if( value.indexOf( ',' ) >= 0 ) {
|
||||||
// top,left,bottom,right[,lineColor[,lineThickness]]
|
// top,left,bottom,right[,lineColor[,lineThickness[,arc]]]
|
||||||
List<String> parts = split( value, ',' );
|
List<String> parts = splitFunctionParams( value, ',' );
|
||||||
Insets insets = parseInsets( value );
|
Insets insets = parseInsets( value );
|
||||||
ColorUIResource lineColor = (parts.size() >= 5)
|
ColorUIResource lineColor = (parts.size() >= 5)
|
||||||
? (ColorUIResource) parseColorOrFunction( resolver.apply( parts.get( 4 ) ), resolver, true )
|
? (ColorUIResource) parseColorOrFunction( resolver.apply( parts.get( 4 ) ), resolver, true )
|
||||||
: null;
|
: null;
|
||||||
float lineThickness = (parts.size() >= 6) ? parseFloat( parts.get( 5 ), true ) : 1f;
|
float lineThickness = (parts.size() >= 6 && !parts.get( 5 ).isEmpty()) ? parseFloat( parts.get( 5 ), true ) : 1f;
|
||||||
|
int arc = (parts.size() >= 7) ? parseInteger( parts.get( 6 ), true ) : 0;
|
||||||
|
|
||||||
return (LazyValue) t -> {
|
return (LazyValue) t -> {
|
||||||
return (lineColor != null)
|
return (lineColor != null)
|
||||||
? new FlatLineBorder( insets, lineColor, lineThickness )
|
? new FlatLineBorder( insets, lineColor, lineThickness, arc )
|
||||||
: new FlatEmptyBorder( insets );
|
: new FlatEmptyBorder( insets );
|
||||||
};
|
};
|
||||||
} else
|
} else
|
||||||
@@ -480,7 +651,7 @@ class UIDefaultsLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Insets parseInsets( String value ) {
|
private static Insets parseInsets( String value ) {
|
||||||
List<String> numbers = split( value, ',' );
|
List<String> numbers = StringUtils.split( value, ',', true, false );
|
||||||
try {
|
try {
|
||||||
return new InsetsUIResource(
|
return new InsetsUIResource(
|
||||||
Integer.parseInt( numbers.get( 0 ) ),
|
Integer.parseInt( numbers.get( 0 ) ),
|
||||||
@@ -493,7 +664,7 @@ class UIDefaultsLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Dimension parseDimension( String value ) {
|
private static Dimension parseDimension( String value ) {
|
||||||
List<String> numbers = split( value, ',' );
|
List<String> numbers = StringUtils.split( value, ',', true, false );
|
||||||
try {
|
try {
|
||||||
return new DimensionUIResource(
|
return new DimensionUIResource(
|
||||||
Integer.parseInt( numbers.get( 0 ) ),
|
Integer.parseInt( numbers.get( 0 ) ),
|
||||||
@@ -581,10 +752,10 @@ class UIDefaultsLoader
|
|||||||
return null;
|
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 ), ',' );
|
List<String> params = splitFunctionParams( value.substring( paramsStart + 1, value.length() - 1 ), ',' );
|
||||||
if( params.isEmpty() )
|
if( params.isEmpty() )
|
||||||
throw new IllegalArgumentException( "missing parameters in function '" + value + "'" );
|
throwMissingParametersException( value );
|
||||||
|
|
||||||
if( parseColorDepth > 100 )
|
if( parseColorDepth > 100 )
|
||||||
throw new IllegalArgumentException( "endless recursion in color function '" + value + "'" );
|
throw new IllegalArgumentException( "endless recursion in color function '" + value + "'" );
|
||||||
@@ -592,6 +763,7 @@ class UIDefaultsLoader
|
|||||||
parseColorDepth++;
|
parseColorDepth++;
|
||||||
try {
|
try {
|
||||||
switch( function ) {
|
switch( function ) {
|
||||||
|
case "if": return parseColorIf( value, params, resolver, reportError );
|
||||||
case "rgb": return parseColorRgbOrRgba( false, params, resolver, reportError );
|
case "rgb": return parseColorRgbOrRgba( false, params, resolver, reportError );
|
||||||
case "rgba": return parseColorRgbOrRgba( true, params, resolver, reportError );
|
case "rgba": return parseColorRgbOrRgba( true, params, resolver, reportError );
|
||||||
case "hsl": return parseColorHslOrHsla( false, params );
|
case "hsl": return parseColorHslOrHsla( false, params );
|
||||||
@@ -604,6 +776,15 @@ class UIDefaultsLoader
|
|||||||
case "fadeout": return parseColorHSLIncreaseDecrease( 3, false, params, resolver, reportError );
|
case "fadeout": return parseColorHSLIncreaseDecrease( 3, false, params, resolver, reportError );
|
||||||
case "fade": return parseColorFade( params, resolver, reportError );
|
case "fade": return parseColorFade( params, resolver, reportError );
|
||||||
case "spin": return parseColorSpin( 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 );
|
||||||
|
case "over": return parseColorOver( params, resolver, reportError );
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
parseColorDepth--;
|
parseColorDepth--;
|
||||||
@@ -612,6 +793,21 @@ class UIDefaultsLoader
|
|||||||
throw new IllegalArgumentException( "unknown color function '" + value + "'" );
|
throw new IllegalArgumentException( "unknown color function '" + value + "'" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
* Syntax: rgb(red,green,blue) or rgba(red,green,blue,alpha)
|
||||||
* - red: an integer 0-255 or a percentage 0-100%
|
* - red: an integer 0-255 or a percentage 0-100%
|
||||||
@@ -656,7 +852,7 @@ class UIDefaultsLoader
|
|||||||
int lightness = parsePercentage( params.get( 2 ) );
|
int lightness = parsePercentage( params.get( 2 ) );
|
||||||
int alpha = hasAlpha ? parsePercentage( params.get( 3 ) ) : 100;
|
int alpha = hasAlpha ? parsePercentage( params.get( 3 ) ) : 100;
|
||||||
|
|
||||||
float[] hsl = new float[] { hue, saturation, lightness };
|
float[] hsl = { hue, saturation, lightness };
|
||||||
return new ColorUIResource( HSLColor.toRGB( hsl, alpha / 100f ) );
|
return new ColorUIResource( HSLColor.toRGB( hsl, alpha / 100f ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -711,21 +907,32 @@ class UIDefaultsLoader
|
|||||||
* Syntax: fade(color,amount[,options])
|
* Syntax: fade(color,amount[,options])
|
||||||
* - color: a color (e.g. #f00) or a color function
|
* - color: a color (e.g. #f00) or a color function
|
||||||
* - amount: percentage 0-100%
|
* - amount: percentage 0-100%
|
||||||
* - options: [derived]
|
* - options: [derived] [lazy]
|
||||||
*/
|
*/
|
||||||
private static Object parseColorFade( List<String> params, Function<String, String> resolver, boolean reportError ) {
|
private static Object parseColorFade( List<String> params, Function<String, String> resolver, boolean reportError ) {
|
||||||
String colorStr = params.get( 0 );
|
String colorStr = params.get( 0 );
|
||||||
int amount = parsePercentage( params.get( 1 ) );
|
int amount = parsePercentage( params.get( 1 ) );
|
||||||
boolean derived = false;
|
boolean derived = false;
|
||||||
|
boolean lazy = false;
|
||||||
|
|
||||||
if( params.size() > 2 ) {
|
if( params.size() > 2 ) {
|
||||||
String options = params.get( 2 );
|
String options = params.get( 2 );
|
||||||
derived = options.contains( "derived" );
|
derived = options.contains( "derived" );
|
||||||
|
lazy = options.contains( "lazy" );
|
||||||
}
|
}
|
||||||
|
|
||||||
// create function
|
// create function
|
||||||
ColorFunction function = new ColorFunctions.Fade( amount );
|
ColorFunction function = new ColorFunctions.Fade( amount );
|
||||||
|
|
||||||
|
if( lazy ) {
|
||||||
|
return (LazyValue) t -> {
|
||||||
|
Object color = lazyUIManagerGet( colorStr );
|
||||||
|
return (color instanceof Color)
|
||||||
|
? new ColorUIResource( ColorFunctions.applyFunctions( (Color) color, function ) )
|
||||||
|
: null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// parse base color, apply function and create derived color
|
// parse base color, apply function and create derived color
|
||||||
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
|
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
|
||||||
}
|
}
|
||||||
@@ -753,6 +960,120 @@ class UIDefaultsLoader
|
|||||||
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Syntax: over(foreground,background)
|
||||||
|
* - foreground: a foreground color (e.g. #f00) or a color function;
|
||||||
|
* the alpha of this color is used as weight to mix the two colors
|
||||||
|
* - background: a background color (e.g. #f00) or a color function
|
||||||
|
*/
|
||||||
|
private static ColorUIResource parseColorOver( List<String> params, Function<String, String> resolver, boolean reportError ) {
|
||||||
|
String foregroundStr = params.get( 0 );
|
||||||
|
String backgroundStr = params.get( 1 );
|
||||||
|
|
||||||
|
// parse foreground color
|
||||||
|
ColorUIResource foreground = (ColorUIResource) parseColorOrFunction( resolver.apply( foregroundStr ), resolver, reportError );
|
||||||
|
if( foreground == null || foreground.getAlpha() == 255 )
|
||||||
|
return foreground;
|
||||||
|
|
||||||
|
// foreground color without alpha
|
||||||
|
ColorUIResource foreground2 = new ColorUIResource( foreground.getRGB() );
|
||||||
|
|
||||||
|
// parse background color
|
||||||
|
ColorUIResource background = (ColorUIResource) parseColorOrFunction( resolver.apply( backgroundStr ), resolver, reportError );
|
||||||
|
if( background == null )
|
||||||
|
return foreground2;
|
||||||
|
|
||||||
|
// create new color
|
||||||
|
float weight = foreground.getAlpha() / 255f;
|
||||||
|
return new ColorUIResource( ColorFunctions.mix( foreground2, background, weight ) );
|
||||||
|
}
|
||||||
|
|
||||||
private static Object parseFunctionBaseColor( String colorStr, ColorFunction function,
|
private static Object parseFunctionBaseColor( String colorStr, ColorFunction function,
|
||||||
boolean derived, Function<String, String> resolver, boolean reportError )
|
boolean derived, Function<String, String> resolver, boolean reportError )
|
||||||
{
|
{
|
||||||
@@ -783,6 +1104,107 @@ class UIDefaultsLoader
|
|||||||
return new ColorUIResource( newColor );
|
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 ) {
|
private static int parsePercentage( String value ) {
|
||||||
if( !value.endsWith( "%" ) )
|
if( !value.endsWith( "%" ) )
|
||||||
throw new NumberFormatException( "invalid percentage '" + value + "'" );
|
throw new NumberFormatException( "invalid percentage '" + value + "'" );
|
||||||
@@ -799,6 +1221,14 @@ class UIDefaultsLoader
|
|||||||
return val;
|
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 ) {
|
private static Character parseCharacter( String value ) {
|
||||||
if( value.length() != 1 )
|
if( value.length() != 1 )
|
||||||
throw new IllegalArgumentException( "invalid character '" + value + "'" );
|
throw new IllegalArgumentException( "invalid character '" + value + "'" );
|
||||||
@@ -812,7 +1242,7 @@ class UIDefaultsLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
Integer integer = parseInteger( value, true );
|
Integer integer = parseInteger( value, true );
|
||||||
if( integer.intValue() < min || integer.intValue() > max )
|
if( integer < min || integer > max )
|
||||||
throw new NumberFormatException( "integer '" + value + "' out of range (" + min + '-' + max + ')' );
|
throw new NumberFormatException( "integer '" + value + "' out of range (" + min + '-' + max + ')' );
|
||||||
return integer;
|
return integer;
|
||||||
}
|
}
|
||||||
@@ -827,6 +1257,20 @@ class UIDefaultsLoader
|
|||||||
return null;
|
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 ) {
|
private static Float parseFloat( String value, boolean reportError ) {
|
||||||
try {
|
try {
|
||||||
return Float.parseFloat( value );
|
return Float.parseFloat( value );
|
||||||
@@ -839,34 +1283,34 @@ class UIDefaultsLoader
|
|||||||
|
|
||||||
private static ActiveValue parseScaledInteger( String value ) {
|
private static ActiveValue parseScaledInteger( String value ) {
|
||||||
int val = parseInteger( value, true );
|
int val = parseInteger( value, true );
|
||||||
return (ActiveValue) t -> {
|
return t -> {
|
||||||
return UIScale.scale( val );
|
return UIScale.scale( val );
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ActiveValue parseScaledFloat( String value ) {
|
private static ActiveValue parseScaledFloat( String value ) {
|
||||||
float val = parseFloat( value, true );
|
float val = parseFloat( value, true );
|
||||||
return (ActiveValue) t -> {
|
return t -> {
|
||||||
return UIScale.scale( val );
|
return UIScale.scale( val );
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ActiveValue parseScaledInsets( String value ) {
|
private static ActiveValue parseScaledInsets( String value ) {
|
||||||
Insets insets = parseInsets( value );
|
Insets insets = parseInsets( value );
|
||||||
return (ActiveValue) t -> {
|
return t -> {
|
||||||
return UIScale.scale( insets );
|
return UIScale.scale( insets );
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ActiveValue parseScaledDimension( String value ) {
|
private static ActiveValue parseScaledDimension( String value ) {
|
||||||
Dimension dimension = parseDimension( value );
|
Dimension dimension = parseDimension( value );
|
||||||
return (ActiveValue) t -> {
|
return t -> {
|
||||||
return UIScale.scale( dimension );
|
return UIScale.scale( dimension );
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Object parseGrayFilter( String value ) {
|
private static Object parseGrayFilter( String value ) {
|
||||||
List<String> numbers = split( value, ',' );
|
List<String> numbers = StringUtils.split( value, ',', true, false );
|
||||||
try {
|
try {
|
||||||
int brightness = Integer.parseInt( numbers.get( 0 ) );
|
int brightness = Integer.parseInt( numbers.get( 0 ) );
|
||||||
int contrast = Integer.parseInt( numbers.get( 1 ) );
|
int contrast = Integer.parseInt( numbers.get( 1 ) );
|
||||||
@@ -880,20 +1324,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.
|
* Splits function parameters and allows using functions as parameters.
|
||||||
* In other words: Delimiters surrounded by '(' and ')' are ignored.
|
* In other words: Delimiters surrounded by '(' and ')' are ignored.
|
||||||
@@ -910,11 +1340,11 @@ class UIDefaultsLoader
|
|||||||
else if( ch == ')' )
|
else if( ch == ')' )
|
||||||
nestLevel--;
|
nestLevel--;
|
||||||
else if( nestLevel == 0 && ch == delim ) {
|
else if( nestLevel == 0 && ch == delim ) {
|
||||||
strs.add( str.substring( start, i ).trim() );
|
strs.add( StringUtils.substringTrimmed( str, start, i ) );
|
||||||
start = i + 1;
|
start = i + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
strs.add( str.substring( start ).trim() );
|
strs.add( StringUtils.substringTrimmed( str, start ) );
|
||||||
|
|
||||||
return strs;
|
return strs;
|
||||||
}
|
}
|
||||||
@@ -923,7 +1353,7 @@ class UIDefaultsLoader
|
|||||||
* For use in LazyValue to get value for given key from UIManager and report error
|
* 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.
|
* 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;
|
boolean optional = false;
|
||||||
if( uiKey.startsWith( OPTIONAL_PREFIX ) ) {
|
if( uiKey.startsWith( OPTIONAL_PREFIX ) ) {
|
||||||
uiKey = uiKey.substring( OPTIONAL_PREFIX.length() );
|
uiKey = uiKey.substring( OPTIONAL_PREFIX.length() );
|
||||||
@@ -935,4 +1365,8 @@ class UIDefaultsLoader
|
|||||||
LoggingFacade.INSTANCE.logSevere( "FlatLaf: '" + uiKey + "' not found in UI defaults.", null );
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: '" + uiKey + "' not found in UI defaults.", null );
|
||||||
return value;
|
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 width;
|
||||||
protected final int height;
|
protected final int height;
|
||||||
protected final Color color;
|
protected Color color;
|
||||||
|
|
||||||
public FlatAbstractIcon( int width, int height, Color color ) {
|
public FlatAbstractIcon( int width, int height, Color color ) {
|
||||||
this.width = width;
|
this.width = width;
|
||||||
|
|||||||
@@ -23,13 +23,13 @@ import java.awt.Graphics2D;
|
|||||||
import com.formdev.flatlaf.util.AnimatedIcon;
|
import com.formdev.flatlaf.util.AnimatedIcon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for animated icons that scales width and height, creates and initializes
|
* Base class for animated icons that scale width and height, creates and initializes
|
||||||
* a scaled graphics context for icon painting.
|
* a scaled graphics context for icon painting.
|
||||||
* <p>
|
* <p>
|
||||||
* Subclasses do not need to scale icon painting.
|
* Subclasses do not need to scale icon painting.
|
||||||
* <p>
|
* <p>
|
||||||
* This class does not store any state information (needed for animation) in its instance.
|
* This class does not store any state information (needed for animation) in its instance.
|
||||||
* Instead a client property is set on the painted component.
|
* Instead, a client property is set on the painted component.
|
||||||
* This makes it possible to use a share icon instance for multiple components.
|
* This makes it possible to use a share icon instance for multiple components.
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
|
|||||||
@@ -21,7 +21,11 @@ import java.awt.Color;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
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;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,8 +39,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
public class FlatAscendingSortIcon
|
public class FlatAscendingSortIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
{
|
{
|
||||||
protected final boolean chevron = FlatUIUtils.isChevron( UIManager.getString( "Component.arrowType" ) );
|
protected boolean chevron = FlatUIUtils.isChevron( UIManager.getString( "Component.arrowType" ) );
|
||||||
protected final Color sortIconColor = UIManager.getColor( "Table.sortIconColor" );
|
protected Color sortIconColor = UIManager.getColor( "Table.sortIconColor" );
|
||||||
|
|
||||||
public FlatAscendingSortIcon() {
|
public FlatAscendingSortIcon() {
|
||||||
super( 10, 5, null );
|
super( 10, 5, null );
|
||||||
@@ -44,7 +48,28 @@ public class FlatAscendingSortIcon
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
|
boolean chevron = this.chevron;
|
||||||
|
Color sortIconColor = this.sortIconColor;
|
||||||
|
|
||||||
|
// Because this icon is 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 );
|
g.setColor( sortIconColor );
|
||||||
|
paintArrow( c, g, chevron );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void paintArrow( Component c, Graphics2D g, boolean chevron ) {
|
||||||
if( chevron ) {
|
if( chevron ) {
|
||||||
// chevron arrow
|
// chevron arrow
|
||||||
Path2D path = FlatUIUtils.createPath( false, 1,4, 5,0, 9,4 );
|
Path2D path = FlatUIUtils.createPath( false, 1,4, 5,0, 9,4 );
|
||||||
|
|||||||
@@ -16,12 +16,14 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.icons;
|
package com.formdev.flatlaf.icons;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.awt.geom.RoundRectangle2D;
|
import java.awt.geom.RoundRectangle2D;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,13 +40,30 @@ public class FlatCapsLockIcon
|
|||||||
super( 16, 16, UIManager.getColor( "PasswordField.capsLockIconColor" ) );
|
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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
switch( key ) {
|
||||||
|
case "capsLockIconColor": return color;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
/*
|
/*
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
<g fill="none" fill-rule="evenodd">
|
<g fill="none" fill-rule="evenodd">
|
||||||
<rect width="16" height="16" fill="#6E6E6E" rx="3"/>
|
<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"/>
|
<path fill="#FFF" d="M2,8 L8,2 L14,8 L11,8 L11,10 L5,10 L5,8 L2,8 Z"/>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -52,7 +71,7 @@ public class FlatCapsLockIcon
|
|||||||
|
|
||||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||||
path.append( new RoundRectangle2D.Float( 0, 0, 16, 16, 6, 6 ), false );
|
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 );
|
path.append( FlatUIUtils.createPath( 2,8, 8,2, 14,8, 11,8, 11,10, 5,10, 5,8 ), false );
|
||||||
g.fill( path );
|
g.fill( path );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,83 +23,115 @@ import java.awt.Component;
|
|||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
import java.awt.geom.RoundRectangle2D;
|
import java.awt.geom.RoundRectangle2D;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import com.formdev.flatlaf.ui.FlatButtonUI;
|
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;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Icon for {@link javax.swing.JCheckBox}.
|
* Icon for {@link javax.swing.JCheckBox}.
|
||||||
*
|
* <p>
|
||||||
* Note: If Component.focusWidth is greater than zero, then the outline focus border
|
* <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
|
* is painted outside of the icon bounds. Make sure that the checkbox
|
||||||
* has margins, which are equal or greater than focusWidth.
|
* 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.focusWidth int
|
||||||
|
* @uiDefault Component.borderWidth int
|
||||||
* @uiDefault Component.focusColor Color
|
* @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.focusColor Color optional; defaults to Component.focusColor
|
||||||
* @uiDefault CheckBox.icon.borderColor Color
|
* @uiDefault CheckBox.icon.borderColor Color
|
||||||
* @uiDefault CheckBox.icon.background Color
|
* @uiDefault CheckBox.icon.background Color
|
||||||
* @uiDefault CheckBox.icon.selectedBorderColor Color
|
* @uiDefault CheckBox.icon.selectedBorderColor Color
|
||||||
* @uiDefault CheckBox.icon.selectedBackground Color
|
* @uiDefault CheckBox.icon.selectedBackground Color
|
||||||
* @uiDefault CheckBox.icon.checkmarkColor Color
|
* @uiDefault CheckBox.icon.checkmarkColor Color
|
||||||
|
*
|
||||||
* @uiDefault CheckBox.icon.disabledBorderColor Color
|
* @uiDefault CheckBox.icon.disabledBorderColor Color
|
||||||
* @uiDefault CheckBox.icon.disabledBackground 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.disabledCheckmarkColor Color
|
||||||
|
*
|
||||||
* @uiDefault CheckBox.icon.focusedBorderColor Color optional
|
* @uiDefault CheckBox.icon.focusedBorderColor Color optional
|
||||||
* @uiDefault CheckBox.icon.focusedBackground Color optional
|
* @uiDefault CheckBox.icon.focusedBackground Color optional
|
||||||
* @uiDefault CheckBox.icon.selectedFocusedBorderColor Color optional; CheckBox.icon.focusedBorderColor is used if not specified
|
* @uiDefault CheckBox.icon.focusedSelectedBorderColor Color optional; CheckBox.icon.focusedBorderColor is used if not specified
|
||||||
* @uiDefault CheckBox.icon.selectedFocusedBackground Color optional; CheckBox.icon.focusedBackground is used if not specified
|
* @uiDefault CheckBox.icon.focusedSelectedBackground Color optional; CheckBox.icon.focusedBackground is used if not specified
|
||||||
* @uiDefault CheckBox.icon.selectedFocusedCheckmarkColor Color optional; CheckBox.icon.checkmarkColor 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.hoverBorderColor Color optional
|
||||||
* @uiDefault CheckBox.icon.hoverBackground Color optional
|
* @uiDefault CheckBox.icon.hoverBackground Color optional
|
||||||
* @uiDefault CheckBox.icon.selectedHoverBackground Color optional; CheckBox.icon.hoverBackground is used if not specified
|
* @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.pressedBackground Color optional
|
||||||
* @uiDefault CheckBox.icon.selectedPressedBackground Color optional; CheckBox.icon.pressedBackground is used if not specified
|
* @uiDefault CheckBox.icon.pressedSelectedBorderColor Color optional; CheckBox.icon.pressedBorderColor is used if not specified
|
||||||
* @uiDefault CheckBox.arc int
|
* @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
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatCheckBoxIcon
|
public class FlatCheckBoxIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
{
|
{
|
||||||
protected final String style = UIManager.getString( "CheckBox.icon.style" );
|
protected final String style = UIManager.getString( getPropertyPrefix() + "icon.style" );
|
||||||
public final int focusWidth = getUIInt( "CheckBox.icon.focusWidth",
|
@Styleable protected float focusWidth = getUIFloat( "CheckBox.icon.focusWidth", UIManager.getInt( "Component.focusWidth" ), style );
|
||||||
UIManager.getInt( "Component.focusWidth" ), style );
|
@Styleable protected Color focusColor = FlatUIUtils.getUIColor( "CheckBox.icon.focusColor", UIManager.getColor( "Component.focusColor" ) );
|
||||||
protected final Color focusColor = FlatUIUtils.getUIColor( "CheckBox.icon.focusColor",
|
/** @since 2 */ @Styleable protected float borderWidth = getUIFloat( "CheckBox.icon.borderWidth", FlatUIUtils.getUIFloat( "Component.borderWidth", 1 ), style );
|
||||||
UIManager.getColor( "Component.focusColor" ) );
|
/** @since 2 */ @Styleable protected float selectedBorderWidth = getUIFloat( "CheckBox.icon.selectedBorderWidth", Float.MIN_VALUE, style );
|
||||||
protected final int arc = FlatUIUtils.getUIInt( "CheckBox.arc", 2 );
|
/** @since 2 */ @Styleable protected float disabledSelectedBorderWidth = getUIFloat( "CheckBox.icon.disabledSelectedBorderWidth", Float.MIN_VALUE, style );
|
||||||
|
@Styleable protected int arc = FlatUIUtils.getUIInt( "CheckBox.arc", 2 );
|
||||||
|
|
||||||
// enabled
|
// enabled
|
||||||
protected final Color borderColor = getUIColor( "CheckBox.icon.borderColor", style );
|
@Styleable protected Color borderColor = getUIColor( "CheckBox.icon.borderColor", style );
|
||||||
protected final Color background = getUIColor( "CheckBox.icon.background", style );
|
@Styleable protected Color background = getUIColor( "CheckBox.icon.background", style );
|
||||||
protected final Color selectedBorderColor = getUIColor( "CheckBox.icon.selectedBorderColor", style );
|
@Styleable protected Color selectedBorderColor = getUIColor( "CheckBox.icon.selectedBorderColor", style );
|
||||||
protected final Color selectedBackground = getUIColor( "CheckBox.icon.selectedBackground", style );
|
@Styleable protected Color selectedBackground = getUIColor( "CheckBox.icon.selectedBackground", style );
|
||||||
protected final Color checkmarkColor = getUIColor( "CheckBox.icon.checkmarkColor", style );
|
@Styleable protected Color checkmarkColor = getUIColor( "CheckBox.icon.checkmarkColor", style );
|
||||||
|
|
||||||
// disabled
|
// disabled
|
||||||
protected final Color disabledBorderColor = getUIColor( "CheckBox.icon.disabledBorderColor", style );
|
@Styleable protected Color disabledBorderColor = getUIColor( "CheckBox.icon.disabledBorderColor", style );
|
||||||
protected final Color disabledBackground = getUIColor( "CheckBox.icon.disabledBackground", style );
|
@Styleable protected Color disabledBackground = getUIColor( "CheckBox.icon.disabledBackground", style );
|
||||||
protected final Color disabledCheckmarkColor = getUIColor( "CheckBox.icon.disabledCheckmarkColor", 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
|
// focused
|
||||||
protected final Color focusedBorderColor = getUIColor( "CheckBox.icon.focusedBorderColor", style );
|
@Styleable protected Color focusedBorderColor = getUIColor( "CheckBox.icon.focusedBorderColor", style );
|
||||||
protected final Color focusedBackground = getUIColor( "CheckBox.icon.focusedBackground", style );
|
@Styleable protected Color focusedBackground = getUIColor( "CheckBox.icon.focusedBackground", style );
|
||||||
protected final Color selectedFocusedBorderColor = getUIColor( "CheckBox.icon.selectedFocusedBorderColor", style );
|
/** @since 2 */ @Styleable protected Color focusedSelectedBorderColor = getUIColor( "CheckBox.icon.focusedSelectedBorderColor", style );
|
||||||
protected final Color selectedFocusedBackground = getUIColor( "CheckBox.icon.selectedFocusedBackground", style );
|
/** @since 2 */ @Styleable protected Color focusedSelectedBackground = getUIColor( "CheckBox.icon.focusedSelectedBackground", style );
|
||||||
protected final Color selectedFocusedCheckmarkColor = getUIColor( "CheckBox.icon.selectedFocusedCheckmarkColor", style );
|
/** @since 2 */ @Styleable protected Color focusedCheckmarkColor = getUIColor( "CheckBox.icon.focusedCheckmarkColor", style );
|
||||||
|
|
||||||
// hover
|
// hover
|
||||||
protected final Color hoverBorderColor = getUIColor( "CheckBox.icon.hoverBorderColor", style );
|
@Styleable protected Color hoverBorderColor = getUIColor( "CheckBox.icon.hoverBorderColor", style );
|
||||||
protected final Color hoverBackground = getUIColor( "CheckBox.icon.hoverBackground", style );
|
@Styleable protected Color hoverBackground = getUIColor( "CheckBox.icon.hoverBackground", style );
|
||||||
protected final Color selectedHoverBackground = getUIColor( "CheckBox.icon.selectedHoverBackground", 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
|
// pressed
|
||||||
protected final Color pressedBackground = getUIColor( "CheckBox.icon.pressedBackground", style );
|
/** @since 2 */ @Styleable protected Color pressedBorderColor = getUIColor( "CheckBox.icon.pressedBorderColor", style );
|
||||||
protected final Color selectedPressedBackground = getUIColor( "CheckBox.icon.selectedPressedBackground", 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 ) {
|
protected static Color getUIColor( String key, String style ) {
|
||||||
if( style != null ) {
|
if( style != null ) {
|
||||||
@@ -110,13 +142,14 @@ public class FlatCheckBoxIcon
|
|||||||
return UIManager.getColor( key );
|
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 ) {
|
if( style != null ) {
|
||||||
Object value = UIManager.get( styleKey( key, style ) );
|
float value = FlatUIUtils.getUIFloat( styleKey( key, style ), Float.MIN_VALUE );
|
||||||
if( value instanceof Integer )
|
if( value != Float.MIN_VALUE )
|
||||||
return (Integer) value;
|
return value;
|
||||||
}
|
}
|
||||||
return FlatUIUtils.getUIInt( key, defaultValue );
|
return FlatUIUtils.getUIFloat( key, defaultValue );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String styleKey( String key, String style ) {
|
private static String styleKey( String key, String style ) {
|
||||||
@@ -129,11 +162,31 @@ public class FlatCheckBoxIcon
|
|||||||
super( ICON_SIZE, ICON_SIZE, null );
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
boolean indeterminate = isIndeterminate( c );
|
boolean indeterminate = isIndeterminate( c );
|
||||||
boolean selected = indeterminate || isSelected( c );
|
boolean selected = indeterminate || isSelected( c );
|
||||||
boolean isFocused = FlatUIUtils.isPermanentFocusOwner( 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
|
// paint focused border
|
||||||
if( isFocused && focusWidth > 0 && FlatButtonUI.isFocusPainted( c ) ) {
|
if( isFocused && focusWidth > 0 && FlatButtonUI.isFocusPainted( c ) ) {
|
||||||
@@ -143,7 +196,7 @@ public class FlatCheckBoxIcon
|
|||||||
|
|
||||||
// paint border
|
// paint border
|
||||||
g.setColor( getBorderColor( c, selected ) );
|
g.setColor( getBorderColor( c, selected ) );
|
||||||
paintBorder( c, g );
|
paintBorder( c, g, bw );
|
||||||
|
|
||||||
// paint background
|
// paint background
|
||||||
Color bg = FlatUIUtils.deriveColor( getBackground( c, selected ),
|
Color bg = FlatUIUtils.deriveColor( getBackground( c, selected ),
|
||||||
@@ -151,14 +204,14 @@ public class FlatCheckBoxIcon
|
|||||||
if( bg.getAlpha() < 255 ) {
|
if( bg.getAlpha() < 255 ) {
|
||||||
// fill background with default color before filling with non-opaque background
|
// fill background with default color before filling with non-opaque background
|
||||||
g.setColor( selected ? selectedBackground : background );
|
g.setColor( selected ? selectedBackground : background );
|
||||||
paintBackground( c, g );
|
paintBackground( c, g, bw );
|
||||||
}
|
}
|
||||||
g.setColor( bg );
|
g.setColor( bg );
|
||||||
paintBackground( c, g );
|
paintBackground( c, g, bw );
|
||||||
|
|
||||||
// paint checkmark
|
// paint checkmark
|
||||||
if( selected || indeterminate ) {
|
if( selected ) {
|
||||||
g.setColor( getCheckmarkColor( c, selected, isFocused ) );
|
g.setColor( getCheckmarkColor( c ) );
|
||||||
if( indeterminate )
|
if( indeterminate )
|
||||||
paintIndeterminate( c, g );
|
paintIndeterminate( c, g );
|
||||||
else
|
else
|
||||||
@@ -167,20 +220,25 @@ public class FlatCheckBoxIcon
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void paintFocusBorder( Component c, Graphics2D g ) {
|
protected void paintFocusBorder( Component c, Graphics2D g ) {
|
||||||
// the outline focus border is painted outside of the icon
|
// the outer focus border is painted outside of the icon
|
||||||
int wh = ICON_SIZE - 1 + (focusWidth * 2);
|
float wh = ICON_SIZE - 1 + (focusWidth * 2);
|
||||||
int arcwh = arc + (focusWidth * 2);
|
float arcwh = arc + (focusWidth * 2);
|
||||||
g.fillRoundRect( -focusWidth + 1, -focusWidth, wh, wh, arcwh, arcwh );
|
g.fill( new RoundRectangle2D.Float( -focusWidth + 1, -focusWidth, wh, wh, arcwh, arcwh ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void paintBorder( Component c, Graphics2D g ) {
|
protected void paintBorder( Component c, Graphics2D g, float borderWidth ) {
|
||||||
|
if( borderWidth == 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
int arcwh = arc;
|
int arcwh = arc;
|
||||||
g.fillRoundRect( 1, 0, 14, 14, arcwh, arcwh );
|
g.fillRoundRect( 1, 0, 14, 14, arcwh, arcwh );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void paintBackground( Component c, Graphics2D g ) {
|
protected void paintBackground( Component c, Graphics2D g, float borderWidth ) {
|
||||||
int arcwh = arc - 1;
|
float xy = borderWidth;
|
||||||
g.fillRoundRect( 2, 1, 12, 12, arcwh, arcwh );
|
float wh = 14 - (borderWidth * 2);
|
||||||
|
float arcwh = arc - borderWidth;
|
||||||
|
g.fill( new RoundRectangle2D.Float( 1 + xy, xy, wh, wh, arcwh, arcwh ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void paintCheckmark( Component c, Graphics2D g ) {
|
protected void paintCheckmark( Component c, Graphics2D g ) {
|
||||||
@@ -205,6 +263,11 @@ public class FlatCheckBoxIcon
|
|||||||
return c instanceof AbstractButton && ((AbstractButton)c).isSelected();
|
return c instanceof AbstractButton && ((AbstractButton)c).isSelected();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public float getFocusWidth() {
|
||||||
|
return focusWidth;
|
||||||
|
}
|
||||||
|
|
||||||
protected Color getFocusColor( Component c ) {
|
protected Color getFocusColor( Component c ) {
|
||||||
return focusColor;
|
return focusColor;
|
||||||
}
|
}
|
||||||
@@ -212,26 +275,27 @@ public class FlatCheckBoxIcon
|
|||||||
protected Color getBorderColor( Component c, boolean selected ) {
|
protected Color getBorderColor( Component c, boolean selected ) {
|
||||||
return FlatButtonUI.buttonStateColor( c,
|
return FlatButtonUI.buttonStateColor( c,
|
||||||
selected ? selectedBorderColor : borderColor,
|
selected ? selectedBorderColor : borderColor,
|
||||||
disabledBorderColor,
|
(selected && disabledSelectedBorderColor != null) ? disabledSelectedBorderColor : disabledBorderColor,
|
||||||
selected && selectedFocusedBorderColor != null ? selectedFocusedBorderColor : focusedBorderColor,
|
(selected && focusedSelectedBorderColor != null) ? focusedSelectedBorderColor : focusedBorderColor,
|
||||||
hoverBorderColor,
|
(selected && hoverSelectedBorderColor != null) ? hoverSelectedBorderColor : hoverBorderColor,
|
||||||
null );
|
(selected && pressedSelectedBorderColor != null) ? pressedSelectedBorderColor : pressedBorderColor );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Color getBackground( Component c, boolean selected ) {
|
protected Color getBackground( Component c, boolean selected ) {
|
||||||
return FlatButtonUI.buttonStateColor( c,
|
return FlatButtonUI.buttonStateColor( c,
|
||||||
selected ? selectedBackground : background,
|
selected ? selectedBackground : background,
|
||||||
disabledBackground,
|
(selected && disabledSelectedBackground != null) ? disabledSelectedBackground : disabledBackground,
|
||||||
(selected && selectedFocusedBackground != null) ? selectedFocusedBackground : focusedBackground,
|
(selected && focusedSelectedBackground != null) ? focusedSelectedBackground : focusedBackground,
|
||||||
(selected && selectedHoverBackground != null) ? selectedHoverBackground : hoverBackground,
|
(selected && hoverSelectedBackground != null) ? hoverSelectedBackground : hoverBackground,
|
||||||
(selected && selectedPressedBackground != null) ? selectedPressedBackground : pressedBackground );
|
(selected && pressedSelectedBackground != null) ? pressedSelectedBackground : pressedBackground );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Color getCheckmarkColor( Component c, boolean selected, boolean isFocused ) {
|
protected Color getCheckmarkColor( Component c ) {
|
||||||
return c.isEnabled()
|
return FlatButtonUI.buttonStateColor( c,
|
||||||
? ((selected && isFocused && selectedFocusedCheckmarkColor != null)
|
checkmarkColor,
|
||||||
? selectedFocusedCheckmarkColor
|
disabledCheckmarkColor,
|
||||||
: checkmarkColor)
|
focusedCheckmarkColor,
|
||||||
: disabledCheckmarkColor;
|
hoverCheckmarkColor,
|
||||||
|
pressedCheckmarkColor );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,15 +21,18 @@ import java.awt.Color;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
import javax.swing.JMenuItem;
|
import javax.swing.JMenuItem;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Icon for {@link javax.swing.JCheckBoxMenuItem}.
|
* Icon for {@link javax.swing.JCheckBoxMenuItem}.
|
||||||
*
|
*
|
||||||
* @uiDefault MenuItemCheckBox.icon.checkmarkColor Color
|
* @uiDefault CheckBoxMenuItem.icon.checkmarkColor Color
|
||||||
* @uiDefault MenuItemCheckBox.icon.disabledCheckmarkColor Color
|
* @uiDefault CheckBoxMenuItem.icon.disabledCheckmarkColor Color
|
||||||
* @uiDefault MenuItem.selectionForeground Color
|
* @uiDefault MenuItem.selectionForeground Color
|
||||||
* @uiDefault MenuItem.selectionType String
|
* @uiDefault MenuItem.selectionType String
|
||||||
*
|
*
|
||||||
@@ -38,14 +41,29 @@ import javax.swing.UIManager;
|
|||||||
public class FlatCheckBoxMenuItemIcon
|
public class FlatCheckBoxMenuItemIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
{
|
{
|
||||||
protected final Color checkmarkColor = UIManager.getColor( "MenuItemCheckBox.icon.checkmarkColor" );
|
@Styleable protected Color checkmarkColor = UIManager.getColor( "CheckBoxMenuItem.icon.checkmarkColor" );
|
||||||
protected final Color disabledCheckmarkColor = UIManager.getColor( "MenuItemCheckBox.icon.disabledCheckmarkColor" );
|
@Styleable protected Color disabledCheckmarkColor = UIManager.getColor( "CheckBoxMenuItem.icon.disabledCheckmarkColor" );
|
||||||
protected final Color selectionForeground = UIManager.getColor( "MenuItem.selectionForeground" );
|
@Styleable protected Color selectionForeground = UIManager.getColor( "MenuItem.selectionForeground" );
|
||||||
|
|
||||||
public FlatCheckBoxMenuItemIcon() {
|
public FlatCheckBoxMenuItemIcon() {
|
||||||
super( 15, 15, null );
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g2 ) {
|
protected void paintIcon( Component c, Graphics2D g2 ) {
|
||||||
boolean selected = (c instanceof AbstractButton) && ((AbstractButton)c).isSelected();
|
boolean selected = (c instanceof AbstractButton) && ((AbstractButton)c).isSelected();
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* 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" );
|
||||||
|
|
||||||
|
private final boolean ignoreButtonState;
|
||||||
|
|
||||||
|
public FlatClearIcon() {
|
||||||
|
this( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public FlatClearIcon( boolean ignoreButtonState ) {
|
||||||
|
super( 16, 16, null );
|
||||||
|
this.ignoreButtonState = ignoreButtonState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
|
if( !ignoreButtonState && 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,11 +17,9 @@
|
|||||||
package com.formdev.flatlaf.icons;
|
package com.formdev.flatlaf.icons;
|
||||||
|
|
||||||
import java.awt.BasicStroke;
|
import java.awt.BasicStroke;
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
import javax.swing.UIManager;
|
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,18 +31,14 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatDescendingSortIcon
|
public class FlatDescendingSortIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAscendingSortIcon
|
||||||
{
|
{
|
||||||
protected final boolean chevron = FlatUIUtils.isChevron( UIManager.getString( "Component.arrowType" ) );
|
|
||||||
protected final Color sortIconColor = UIManager.getColor( "Table.sortIconColor" );
|
|
||||||
|
|
||||||
public FlatDescendingSortIcon() {
|
public FlatDescendingSortIcon() {
|
||||||
super( 10, 5, null );
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintArrow( Component c, Graphics2D g, boolean chevron ) {
|
||||||
g.setColor( sortIconColor );
|
|
||||||
if( chevron ) {
|
if( chevron ) {
|
||||||
// chevron arrow
|
// chevron arrow
|
||||||
Path2D path = FlatUIUtils.createPath( false, 1,0, 5,4, 9,0 );
|
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.Graphics2D;
|
||||||
import java.awt.geom.Ellipse2D;
|
import java.awt.geom.Ellipse2D;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import com.formdev.flatlaf.ui.FlatButtonUI;
|
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;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,29 +53,42 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
public class FlatHelpButtonIcon
|
public class FlatHelpButtonIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
{
|
{
|
||||||
protected final int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
@Styleable protected int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
||||||
protected final Color focusColor = UIManager.getColor( "Component.focusColor" );
|
@Styleable protected Color focusColor = UIManager.getColor( "Component.focusColor" );
|
||||||
protected final float innerFocusWidth = FlatUIUtils.getUIFloat( "HelpButton.innerFocusWidth", FlatUIUtils.getUIFloat( "Component.innerFocusWidth", 0 ) );
|
@Styleable protected float innerFocusWidth = FlatUIUtils.getUIFloat( "HelpButton.innerFocusWidth", FlatUIUtils.getUIFloat( "Component.innerFocusWidth", 0 ) );
|
||||||
protected final int borderWidth = FlatUIUtils.getUIInt( "HelpButton.borderWidth", 1 );
|
@Styleable protected int borderWidth = FlatUIUtils.getUIInt( "HelpButton.borderWidth", 1 );
|
||||||
|
|
||||||
protected final Color borderColor = UIManager.getColor( "HelpButton.borderColor" );
|
@Styleable protected Color borderColor = UIManager.getColor( "HelpButton.borderColor" );
|
||||||
protected final Color disabledBorderColor = UIManager.getColor( "HelpButton.disabledBorderColor" );
|
@Styleable protected Color disabledBorderColor = UIManager.getColor( "HelpButton.disabledBorderColor" );
|
||||||
protected final Color focusedBorderColor = UIManager.getColor( "HelpButton.focusedBorderColor" );
|
@Styleable protected Color focusedBorderColor = UIManager.getColor( "HelpButton.focusedBorderColor" );
|
||||||
protected final Color hoverBorderColor = UIManager.getColor( "HelpButton.hoverBorderColor" );
|
@Styleable protected Color hoverBorderColor = UIManager.getColor( "HelpButton.hoverBorderColor" );
|
||||||
protected final Color background = UIManager.getColor( "HelpButton.background" );
|
@Styleable protected Color background = UIManager.getColor( "HelpButton.background" );
|
||||||
protected final Color disabledBackground = UIManager.getColor( "HelpButton.disabledBackground" );
|
@Styleable protected Color disabledBackground = UIManager.getColor( "HelpButton.disabledBackground" );
|
||||||
protected final Color focusedBackground = UIManager.getColor( "HelpButton.focusedBackground" );
|
@Styleable protected Color focusedBackground = UIManager.getColor( "HelpButton.focusedBackground" );
|
||||||
protected final Color hoverBackground = UIManager.getColor( "HelpButton.hoverBackground" );
|
@Styleable protected Color hoverBackground = UIManager.getColor( "HelpButton.hoverBackground" );
|
||||||
protected final Color pressedBackground = UIManager.getColor( "HelpButton.pressedBackground" );
|
@Styleable protected Color pressedBackground = UIManager.getColor( "HelpButton.pressedBackground" );
|
||||||
protected final Color questionMarkColor = UIManager.getColor( "HelpButton.questionMarkColor" );
|
@Styleable protected Color questionMarkColor = UIManager.getColor( "HelpButton.questionMarkColor" );
|
||||||
protected final Color disabledQuestionMarkColor = UIManager.getColor( "HelpButton.disabledQuestionMarkColor" );
|
@Styleable protected Color disabledQuestionMarkColor = UIManager.getColor( "HelpButton.disabledQuestionMarkColor" );
|
||||||
|
|
||||||
protected final int iconSize = 22 + (focusWidth * 2);
|
|
||||||
|
|
||||||
public FlatHelpButtonIcon() {
|
public FlatHelpButtonIcon() {
|
||||||
super( 0, 0, null );
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g2 ) {
|
protected void paintIcon( Component c, Graphics2D g2 ) {
|
||||||
/*
|
/*
|
||||||
@@ -85,11 +101,11 @@ public class FlatHelpButtonIcon
|
|||||||
</svg>
|
</svg>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
boolean enabled = c.isEnabled();
|
boolean enabled = c == null || c.isEnabled();
|
||||||
boolean focused = FlatUIUtils.isPermanentFocusOwner( c );
|
boolean focused = c != null && FlatUIUtils.isPermanentFocusOwner( c );
|
||||||
|
|
||||||
float xy = 0.5f;
|
float xy = 0.5f;
|
||||||
float wh = iconSize - 1;
|
float wh = iconSize() - 1;
|
||||||
|
|
||||||
// paint outer focus border
|
// paint outer focus border
|
||||||
if( focused && FlatButtonUI.isFocusPainted( c ) ) {
|
if( focused && FlatButtonUI.isFocusPainted( c ) ) {
|
||||||
@@ -151,11 +167,15 @@ public class FlatHelpButtonIcon
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getIconWidth() {
|
public int getIconWidth() {
|
||||||
return scale( iconSize );
|
return scale( iconSize() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getIconHeight() {
|
public int getIconHeight() {
|
||||||
return scale( iconSize );
|
return scale( iconSize() );
|
||||||
|
}
|
||||||
|
|
||||||
|
private int iconSize() {
|
||||||
|
return 22 + (focusWidth * 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,12 @@ import java.awt.Color;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.JMenu;
|
import javax.swing.JMenu;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "arrow" icon for {@link javax.swing.JMenu}.
|
* "arrow" icon for {@link javax.swing.JMenu}.
|
||||||
@@ -39,22 +42,37 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
public class FlatMenuArrowIcon
|
public class FlatMenuArrowIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
{
|
{
|
||||||
protected final boolean chevron = FlatUIUtils.isChevron( UIManager.getString( "Component.arrowType" ) );
|
@Styleable protected String arrowType = UIManager.getString( "Component.arrowType" );
|
||||||
protected final Color arrowColor = UIManager.getColor( "Menu.icon.arrowColor" );
|
@Styleable protected Color arrowColor = UIManager.getColor( "Menu.icon.arrowColor" );
|
||||||
protected final Color disabledArrowColor = UIManager.getColor( "Menu.icon.disabledArrowColor" );
|
@Styleable protected Color disabledArrowColor = UIManager.getColor( "Menu.icon.disabledArrowColor" );
|
||||||
protected final Color selectionForeground = UIManager.getColor( "Menu.selectionForeground" );
|
@Styleable protected Color selectionForeground = UIManager.getColor( "Menu.selectionForeground" );
|
||||||
|
|
||||||
public FlatMenuArrowIcon() {
|
public FlatMenuArrowIcon() {
|
||||||
super( 6, 10, null );
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
if( !c.getComponentOrientation().isLeftToRight() )
|
if( c != null && !c.getComponentOrientation().isLeftToRight() )
|
||||||
g.rotate( Math.toRadians( 180 ), width / 2., height / 2. );
|
g.rotate( Math.toRadians( 180 ), width / 2., height / 2. );
|
||||||
|
|
||||||
g.setColor( getArrowColor( c ) );
|
g.setColor( getArrowColor( c ) );
|
||||||
if( chevron ) {
|
if( FlatUIUtils.isChevron( arrowType ) ) {
|
||||||
// chevron arrow
|
// chevron arrow
|
||||||
Path2D path = FlatUIUtils.createPath( false, 1,1, 5,5, 1,9 );
|
Path2D path = FlatUIUtils.createPath( false, 1,1, 5,5, 1,9 );
|
||||||
g.setStroke( new BasicStroke( 1f ) );
|
g.setStroke( new BasicStroke( 1f ) );
|
||||||
@@ -69,7 +87,7 @@ public class FlatMenuArrowIcon
|
|||||||
if( c instanceof JMenu && ((JMenu)c).isSelected() && !isUnderlineSelection() )
|
if( c instanceof JMenu && ((JMenu)c).isSelected() && !isUnderlineSelection() )
|
||||||
return selectionForeground;
|
return selectionForeground;
|
||||||
|
|
||||||
return c.isEnabled() ? arrowColor : disabledArrowColor;
|
return c == null || c.isEnabled() ? arrowColor : disabledArrowColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isUnderlineSelection() {
|
protected boolean isUnderlineSelection() {
|
||||||
|
|||||||
@@ -21,14 +21,16 @@ import java.awt.Graphics;
|
|||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "arrow" icon for {@link javax.swing.JMenuItem}.
|
* "arrow" icon for {@link javax.swing.JMenuItem}, {@link javax.swing.JCheckBoxMenuItem}
|
||||||
|
* and {@link javax.swing.JRadioButtonMenuItem}.
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatMenuItemArrowIcon
|
public class FlatMenuItemArrowIcon
|
||||||
extends FlatMenuArrowIcon
|
extends FlatAbstractIcon
|
||||||
{
|
{
|
||||||
public FlatMenuItemArrowIcon() {
|
public FlatMenuItemArrowIcon() {
|
||||||
|
super( 6, 10, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -19,38 +19,51 @@ package com.formdev.flatlaf.icons;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Ellipse2D;
|
import java.awt.geom.Ellipse2D;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Icon for {@link javax.swing.JRadioButton}.
|
* Icon for {@link javax.swing.JRadioButton}.
|
||||||
*
|
* <p>
|
||||||
* Note: If Component.focusWidth is greater than zero, then the outline focus border
|
* <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
|
* is painted outside of the icon bounds. Make sure that the radiobutton
|
||||||
* has margins, which are equal or greater than focusWidth.
|
* 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
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatRadioButtonIcon
|
public class FlatRadioButtonIcon
|
||||||
extends FlatCheckBoxIcon
|
extends FlatCheckBoxIcon
|
||||||
{
|
{
|
||||||
protected final int centerDiameter = getUIInt( "RadioButton.icon.centerDiameter", 8, style );
|
@Styleable protected float centerDiameter = getUIFloat( "RadioButton.icon.centerDiameter", 8, style );
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintFocusBorder( Component c, Graphics2D g ) {
|
protected String getPropertyPrefix() {
|
||||||
// the outline focus border is painted outside of the icon
|
return "RadioButton.";
|
||||||
int wh = ICON_SIZE + (focusWidth * 2);
|
|
||||||
g.fillOval( -focusWidth, -focusWidth, wh, wh );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintBorder( Component c, Graphics2D g ) {
|
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 paintBorder( Component c, Graphics2D g, float borderWidth ) {
|
||||||
|
if( borderWidth == 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
g.fillOval( 0, 0, 15, 15 );
|
g.fillOval( 0, 0, 15, 15 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintBackground( Component c, Graphics2D g ) {
|
protected void paintBackground( Component c, Graphics2D g, float borderWidth ) {
|
||||||
g.fillOval( 1, 1, 13, 13 );
|
float xy = borderWidth;
|
||||||
|
float wh = 15 - (borderWidth * 2);
|
||||||
|
g.fill( new Ellipse2D.Float( xy, xy, wh, wh ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* 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 java.awt.geom.Area;
|
||||||
|
import java.awt.geom.Ellipse2D;
|
||||||
|
import java.awt.geom.Path2D;
|
||||||
|
import java.awt.geom.Rectangle2D;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "eye" icon for {@link javax.swing.JPasswordField}.
|
||||||
|
*
|
||||||
|
* @uiDefault PasswordField.revealIconColor Color
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
public class FlatRevealIcon
|
||||||
|
extends FlatAbstractIcon
|
||||||
|
{
|
||||||
|
public FlatRevealIcon() {
|
||||||
|
super( 16, 16, UIManager.getColor( "PasswordField.revealIconColor" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
|
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||||
|
path.append( new Ellipse2D.Float( 5.15f, 6.15f, 5.7f, 5.7f ), false );
|
||||||
|
path.append( new Ellipse2D.Float( 6, 7, 4, 4 ), false );
|
||||||
|
g.fill( path );
|
||||||
|
|
||||||
|
Path2D path2 = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||||
|
path2.append( new Ellipse2D.Float( 2.15f, 4.15f, 11.7f, 11.7f ), false );
|
||||||
|
path2.append( new Ellipse2D.Float( 3, 5, 10, 10 ), false );
|
||||||
|
Area area = new Area( path2 );
|
||||||
|
area.subtract( new Area( new Rectangle2D.Float( 0, 9.5f, 16, 16 ) ) );
|
||||||
|
g.fill( area );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* 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" );
|
||||||
|
|
||||||
|
private final boolean ignoreButtonState;
|
||||||
|
|
||||||
|
public FlatSearchIcon() {
|
||||||
|
this( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public FlatSearchIcon( boolean ignoreButtonState ) {
|
||||||
|
super( 16, 16, null );
|
||||||
|
this.ignoreButtonState = ignoreButtonState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, 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-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( ignoreButtonState
|
||||||
|
? searchIconColor
|
||||||
|
: 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,61 @@
|
|||||||
|
/*
|
||||||
|
* 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() {
|
||||||
|
this( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public FlatSearchWithHistoryIcon( boolean ignoreButtonState ) {
|
||||||
|
super( ignoreButtonState );
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,8 +23,11 @@ import java.awt.Dimension;
|
|||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Line2D;
|
import java.awt.geom.Line2D;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import com.formdev.flatlaf.ui.FlatButtonUI;
|
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;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,39 +50,54 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
public class FlatTabbedPaneCloseIcon
|
public class FlatTabbedPaneCloseIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
{
|
{
|
||||||
protected final Dimension size = UIManager.getDimension( "TabbedPane.closeSize" );
|
@Styleable protected Dimension closeSize = UIManager.getDimension( "TabbedPane.closeSize" );
|
||||||
protected final int arc = UIManager.getInt( "TabbedPane.closeArc" );
|
@Styleable protected int closeArc = UIManager.getInt( "TabbedPane.closeArc" );
|
||||||
protected final float crossPlainSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossPlainSize", 7.5f );
|
@Styleable protected float closeCrossPlainSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossPlainSize", 7.5f );
|
||||||
protected final float crossFilledSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossFilledSize", crossPlainSize );
|
@Styleable protected float closeCrossFilledSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossFilledSize", closeCrossPlainSize );
|
||||||
protected final float closeCrossLineWidth = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossLineWidth", 1f );
|
@Styleable protected float closeCrossLineWidth = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossLineWidth", 1f );
|
||||||
protected final Color background = UIManager.getColor( "TabbedPane.closeBackground" );
|
@Styleable protected Color closeBackground = UIManager.getColor( "TabbedPane.closeBackground" );
|
||||||
protected final Color foreground = UIManager.getColor( "TabbedPane.closeForeground" );
|
@Styleable protected Color closeForeground = UIManager.getColor( "TabbedPane.closeForeground" );
|
||||||
protected final Color hoverBackground = UIManager.getColor( "TabbedPane.closeHoverBackground" );
|
@Styleable protected Color closeHoverBackground = UIManager.getColor( "TabbedPane.closeHoverBackground" );
|
||||||
protected final Color hoverForeground = UIManager.getColor( "TabbedPane.closeHoverForeground" );
|
@Styleable protected Color closeHoverForeground = UIManager.getColor( "TabbedPane.closeHoverForeground" );
|
||||||
protected final Color pressedBackground = UIManager.getColor( "TabbedPane.closePressedBackground" );
|
@Styleable protected Color closePressedBackground = UIManager.getColor( "TabbedPane.closePressedBackground" );
|
||||||
protected final Color pressedForeground = UIManager.getColor( "TabbedPane.closePressedForeground" );
|
@Styleable protected Color closePressedForeground = UIManager.getColor( "TabbedPane.closePressedForeground" );
|
||||||
|
|
||||||
public FlatTabbedPaneCloseIcon() {
|
public FlatTabbedPaneCloseIcon() {
|
||||||
super( 16, 16, null );
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
// paint background
|
// paint background
|
||||||
Color bg = FlatButtonUI.buttonStateColor( c, background, null, null, hoverBackground, pressedBackground );
|
Color bg = FlatButtonUI.buttonStateColor( c, closeBackground, null, null, closeHoverBackground, closePressedBackground );
|
||||||
if( bg != null ) {
|
if( bg != null ) {
|
||||||
g.setColor( FlatUIUtils.deriveColor( bg, c.getBackground() ) );
|
g.setColor( FlatUIUtils.deriveColor( bg, c.getBackground() ) );
|
||||||
g.fillRoundRect( (width - size.width) / 2, (height - size.height) / 2,
|
g.fillRoundRect( (width - closeSize.width) / 2, (height - closeSize.height) / 2,
|
||||||
size.width, size.height, arc, arc );
|
closeSize.width, closeSize.height, closeArc, closeArc );
|
||||||
}
|
}
|
||||||
|
|
||||||
// set cross color
|
// set cross color
|
||||||
Color fg = FlatButtonUI.buttonStateColor( c, foreground, null, null, hoverForeground, pressedForeground );
|
Color fg = FlatButtonUI.buttonStateColor( c, closeForeground, null, null, closeHoverForeground, closePressedForeground );
|
||||||
g.setColor( FlatUIUtils.deriveColor( fg, c.getForeground() ) );
|
g.setColor( FlatUIUtils.deriveColor( fg, c.getForeground() ) );
|
||||||
|
|
||||||
float mx = width / 2;
|
float mx = width / 2;
|
||||||
float my = height / 2;
|
float my = height / 2;
|
||||||
float r = ((bg != null) ? crossFilledSize : crossPlainSize) / 2;
|
float r = ((bg != null) ? closeCrossFilledSize : closeCrossPlainSize) / 2;
|
||||||
|
|
||||||
// paint cross
|
// paint cross
|
||||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ public class FlatTreeClosedIcon
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
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">
|
<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"/>
|
<polygon fill="#6E6E6E" fill-rule="evenodd" points="1 2 6 2 8 4 15 4 15 13 1 13"/>
|
||||||
|
|||||||
@@ -19,7 +19,12 @@ package com.formdev.flatlaf.icons;
|
|||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import javax.swing.JTree;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.plaf.TreeUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatTreeUI;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,8 +51,12 @@ public class FlatTreeCollapsedIcon
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
|
setStyleColorFromTreeUI( c, g );
|
||||||
rotate( c, g );
|
rotate( c, g );
|
||||||
|
|
||||||
|
String arrowType = getStyleFromTreeUI( c, ui -> ui.iconArrowType );
|
||||||
|
boolean chevron = (arrowType != null) ? FlatUIUtils.isChevron( arrowType ) : this.chevron;
|
||||||
|
|
||||||
if( chevron ) {
|
if( chevron ) {
|
||||||
// chevron arrow
|
// 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 ) );
|
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 ) {
|
void rotate( Component c, Graphics2D g ) {
|
||||||
if( !c.getComponentOrientation().isLeftToRight() )
|
if( !c.getComponentOrientation().isLeftToRight() )
|
||||||
g.rotate( Math.toRadians( 180 ), width / 2., height / 2. );
|
g.rotate( Math.toRadians( 180 ), width / 2., height / 2. );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Because this icon is 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" ) );
|
super( UIManager.getColor( "Tree.icon.expandedColor" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void setStyleColorFromTreeUI( Component c, Graphics2D g ) {
|
||||||
|
setStyleColorFromTreeUI( c, g, ui -> ui.iconExpandedColor );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void rotate( Component c, Graphics2D g ) {
|
void rotate( Component c, Graphics2D g ) {
|
||||||
g.rotate( Math.toRadians( 90 ), width / 2., height / 2. );
|
g.rotate( Math.toRadians( 90 ), width / 2., height / 2. );
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ public class FlatTreeLeafIcon
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
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">
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
<g fill="none" fill-rule="evenodd">
|
<g fill="none" fill-rule="evenodd">
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ public class FlatTreeOpenIcon
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
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">
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
<g fill="none" fill-rule="evenodd">
|
<g fill="none" fill-rule="evenodd">
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import java.awt.Color;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.RenderingHints;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import com.formdev.flatlaf.ui.FlatButtonUI;
|
import com.formdev.flatlaf.ui.FlatButtonUI;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
@@ -65,8 +66,14 @@ public abstract class FlatWindowAbstractIcon
|
|||||||
protected void paintBackground( Component c, Graphics2D g ) {
|
protected void paintBackground( Component c, Graphics2D g ) {
|
||||||
Color background = FlatButtonUI.buttonStateColor( c, null, null, null, hoverBackground, pressedBackground );
|
Color background = FlatButtonUI.buttonStateColor( c, null, null, null, hoverBackground, pressedBackground );
|
||||||
if( background != null ) {
|
if( background != null ) {
|
||||||
|
// disable antialiasing for background rectangle painting to avoid blury edges when scaled (e.g. at 125% or 175%)
|
||||||
|
Object oldHint = g.getRenderingHint( RenderingHints.KEY_ANTIALIASING );
|
||||||
|
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
|
||||||
|
|
||||||
g.setColor( FlatUIUtils.deriveColor( background, c.getBackground() ) );
|
g.setColor( FlatUIUtils.deriveColor( background, c.getBackground() ) );
|
||||||
g.fillRect( 0, 0, width, height );
|
g.fillRect( 0, 0, width, height );
|
||||||
|
|
||||||
|
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, oldHint );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import java.awt.geom.Line2D;
|
|||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import com.formdev.flatlaf.ui.FlatButtonUI;
|
import com.formdev.flatlaf.ui.FlatButtonUI;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "close" icon for windows (frames and dialogs).
|
* "close" icon for windows (frames and dialogs).
|
||||||
@@ -54,7 +55,7 @@ public class FlatWindowCloseIcon
|
|||||||
int iy = y + ((height - iwh) / 2);
|
int iy = y + ((height - iwh) / 2);
|
||||||
int ix2 = ix + iwh - 1;
|
int ix2 = ix + iwh - 1;
|
||||||
int iy2 = iy + iwh - 1;
|
int iy2 = iy + iwh - 1;
|
||||||
int thickness = (int) scaleFactor;
|
float thickness = SystemInfo.isWindows_11_orLater ? (float) scaleFactor : (int) scaleFactor;
|
||||||
|
|
||||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||||
path.append( new Line2D.Float( ix, iy, ix2, iy2 ), false );
|
path.append( new Line2D.Float( ix, iy, ix2, iy2 ), false );
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.formdev.flatlaf.icons;
|
|||||||
|
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "maximize" icon for windows (frames and dialogs).
|
* "maximize" icon for windows (frames and dialogs).
|
||||||
@@ -35,8 +36,11 @@ public class FlatWindowMaximizeIcon
|
|||||||
int iwh = (int) (10 * scaleFactor);
|
int iwh = (int) (10 * scaleFactor);
|
||||||
int ix = x + ((width - iwh) / 2);
|
int ix = x + ((width - iwh) / 2);
|
||||||
int iy = y + ((height - iwh) / 2);
|
int iy = y + ((height - iwh) / 2);
|
||||||
int thickness = (int) scaleFactor;
|
float thickness = SystemInfo.isWindows_11_orLater ? (float) scaleFactor : (int) scaleFactor;
|
||||||
|
int arc = Math.max( (int) (1.5 * scaleFactor), 2 );
|
||||||
|
|
||||||
g.fill( FlatUIUtils.createRectangle( ix, iy, iwh, iwh, thickness ) );
|
g.fill( SystemInfo.isWindows_11_orLater
|
||||||
|
? FlatUIUtils.createRoundRectangle( ix, iy, iwh, iwh, thickness, arc, arc, arc, arc )
|
||||||
|
: FlatUIUtils.createRectangle( ix, iy, iwh, iwh, thickness ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import java.awt.geom.Area;
|
|||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "restore" icon for windows (frames and dialogs).
|
* "restore" icon for windows (frames and dialogs).
|
||||||
@@ -38,18 +39,33 @@ public class FlatWindowRestoreIcon
|
|||||||
int iwh = (int) (10 * scaleFactor);
|
int iwh = (int) (10 * scaleFactor);
|
||||||
int ix = x + ((width - iwh) / 2);
|
int ix = x + ((width - iwh) / 2);
|
||||||
int iy = y + ((height - iwh) / 2);
|
int iy = y + ((height - iwh) / 2);
|
||||||
int thickness = (int) scaleFactor;
|
float thickness = SystemInfo.isWindows_11_orLater ? (float) scaleFactor : (int) scaleFactor;
|
||||||
|
int arc = Math.max( (int) (1.5 * scaleFactor), 2 );
|
||||||
|
int arcOuter = (int) (arc + (1.5 * scaleFactor));
|
||||||
|
|
||||||
int rwh = (int) (8 * scaleFactor);
|
int rwh = (int) (8 * scaleFactor);
|
||||||
int ro2 = iwh - rwh;
|
int ro2 = iwh - rwh;
|
||||||
|
|
||||||
Path2D r1 = FlatUIUtils.createRectangle( ix + ro2, iy, rwh, rwh, thickness );
|
// upper-right rectangle
|
||||||
Path2D r2 = FlatUIUtils.createRectangle( ix, iy + ro2, rwh, rwh, thickness );
|
Path2D r1 = SystemInfo.isWindows_11_orLater
|
||||||
|
? FlatUIUtils.createRoundRectangle( ix + ro2, iy, rwh, rwh, thickness, arc, arcOuter, arc, arc )
|
||||||
|
: FlatUIUtils.createRectangle( ix + ro2, iy, rwh, rwh, thickness );
|
||||||
|
|
||||||
|
// lower-left rectangle
|
||||||
|
Path2D r2 = SystemInfo.isWindows_11_orLater
|
||||||
|
? FlatUIUtils.createRoundRectangle( ix, iy + ro2, rwh, rwh, thickness, arc, arc, arc, arc )
|
||||||
|
: FlatUIUtils.createRectangle( ix, iy + ro2, rwh, rwh, thickness );
|
||||||
|
|
||||||
|
// paint upper-right rectangle
|
||||||
Area area = new Area( r1 );
|
Area area = new Area( r1 );
|
||||||
|
if( SystemInfo.isWindows_11_orLater ) {
|
||||||
|
area.subtract( new Area( new Rectangle2D.Float( ix, (float) (iy + scaleFactor), rwh, rwh ) ) );
|
||||||
|
area.subtract( new Area( new Rectangle2D.Float( (float) (ix + scaleFactor), iy + ro2, rwh, rwh ) ) );
|
||||||
|
} else
|
||||||
area.subtract( new Area( new Rectangle2D.Float( ix, iy + ro2, rwh, rwh ) ) );
|
area.subtract( new Area( new Rectangle2D.Float( ix, iy + ro2, rwh, rwh ) ) );
|
||||||
g.fill( area );
|
g.fill( area );
|
||||||
|
|
||||||
|
// paint lower-left rectangle
|
||||||
g.fill( r2 );
|
g.fill( r2 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ 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.
|
* 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.
|
* Otherwise, the compiler outputs a warning because this package is opened in module-info.java.
|
||||||
* Also when using --patch-module (e.g. from an IDE), an error would occur for empty packages.
|
* Also, when using --patch-module (e.g. from an IDE), an error would occur for empty packages.
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -37,15 +37,15 @@ public class FlatArrowButton
|
|||||||
extends BasicArrowButton
|
extends BasicArrowButton
|
||||||
implements UIResource
|
implements UIResource
|
||||||
{
|
{
|
||||||
public static final int DEFAULT_ARROW_WIDTH = 8;
|
public static final int DEFAULT_ARROW_WIDTH = 9;
|
||||||
|
|
||||||
protected final boolean chevron;
|
protected boolean chevron;
|
||||||
protected final Color foreground;
|
protected Color foreground;
|
||||||
protected final Color disabledForeground;
|
protected Color disabledForeground;
|
||||||
protected final Color hoverForeground;
|
protected Color hoverForeground;
|
||||||
protected final Color hoverBackground;
|
protected Color hoverBackground;
|
||||||
protected final Color pressedForeground;
|
protected Color pressedForeground;
|
||||||
protected final Color pressedBackground;
|
protected Color pressedBackground;
|
||||||
|
|
||||||
private int arrowWidth = DEFAULT_ARROW_WIDTH;
|
private int arrowWidth = DEFAULT_ARROW_WIDTH;
|
||||||
private float xOffset = 0;
|
private float xOffset = 0;
|
||||||
@@ -58,14 +58,8 @@ public class FlatArrowButton
|
|||||||
Color hoverForeground, Color hoverBackground, Color pressedForeground, Color pressedBackground )
|
Color hoverForeground, Color hoverBackground, Color pressedForeground, Color pressedBackground )
|
||||||
{
|
{
|
||||||
super( direction, Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE );
|
super( direction, Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE );
|
||||||
|
updateStyle( type, foreground, disabledForeground, hoverForeground, hoverBackground,
|
||||||
this.chevron = FlatUIUtils.isChevron( type );
|
pressedForeground, pressedBackground );
|
||||||
this.foreground = foreground;
|
|
||||||
this.disabledForeground = disabledForeground;
|
|
||||||
this.hoverForeground = hoverForeground;
|
|
||||||
this.hoverBackground = hoverBackground;
|
|
||||||
this.pressedForeground = pressedForeground;
|
|
||||||
this.pressedBackground = pressedBackground;
|
|
||||||
|
|
||||||
setOpaque( false );
|
setOpaque( false );
|
||||||
setBorder( null );
|
setBorder( null );
|
||||||
@@ -101,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() {
|
public int getArrowWidth() {
|
||||||
return arrowWidth;
|
return arrowWidth;
|
||||||
}
|
}
|
||||||
@@ -204,6 +211,6 @@ public class FlatArrowButton
|
|||||||
if( vert && parent instanceof JComponent && FlatUIUtils.hasRoundBorder( (JComponent) parent ) )
|
if( vert && parent instanceof JComponent && FlatUIUtils.hasRoundBorder( (JComponent) parent ) )
|
||||||
x -= scale( parent.getComponentOrientation().isLeftToRight() ? 1 : -1 );
|
x -= scale( parent.getComponentOrientation().isLeftToRight() ? 1 : -1 );
|
||||||
|
|
||||||
FlatUIUtils.paintArrow( g, x, 0, getWidth(), getHeight(), getDirection(), chevron, arrowWidth, xOffset, yOffset );
|
FlatUIUtils.paintArrow( g, x, 0, getWidth(), getHeight(), getDirection(), chevron, getArrowWidth(), getXOffset(), getYOffset() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import java.awt.Graphics;
|
|||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.Paint;
|
import java.awt.Paint;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.JComboBox;
|
import javax.swing.JComboBox;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
@@ -31,20 +32,25 @@ import javax.swing.JViewport;
|
|||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.basic.BasicBorders;
|
import javax.swing.plaf.basic.BasicBorders;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
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;
|
import com.formdev.flatlaf.util.DerivedColor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Border for various components (e.g. {@link javax.swing.JTextField}).
|
* 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,
|
* There is empty space around the component border, if Component.focusWidth is greater than zero,
|
||||||
* which is used to paint outer focus border.
|
* which is used to paint outer focus border.
|
||||||
*
|
* <p>
|
||||||
* Because there is empty space (if outer focus border is not painted),
|
* Because there is empty space (if outer focus border is not painted),
|
||||||
* UI delegates that use this border (or subclasses) must invoke
|
* 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.focusWidth int
|
||||||
* @uiDefault Component.innerFocusWidth int or float
|
* @uiDefault Component.innerFocusWidth int or float
|
||||||
|
* @uiDefault Component.innerOutlineWidth int or float
|
||||||
|
* @uiDefault Component.borderWidth int or float
|
||||||
|
*
|
||||||
* @uiDefault Component.focusColor Color
|
* @uiDefault Component.focusColor Color
|
||||||
* @uiDefault Component.borderColor Color
|
* @uiDefault Component.borderColor Color
|
||||||
* @uiDefault Component.disabledBorderColor Color
|
* @uiDefault Component.disabledBorderColor Color
|
||||||
@@ -60,20 +66,46 @@ import com.formdev.flatlaf.util.DerivedColor;
|
|||||||
*/
|
*/
|
||||||
public class FlatBorder
|
public class FlatBorder
|
||||||
extends BasicBorders.MarginBorder
|
extends BasicBorders.MarginBorder
|
||||||
|
implements StyleableBorder
|
||||||
{
|
{
|
||||||
protected final int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
@Styleable protected int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
||||||
protected final float innerFocusWidth = FlatUIUtils.getUIFloat( "Component.innerFocusWidth", 0 );
|
@Styleable protected float innerFocusWidth = FlatUIUtils.getUIFloat( "Component.innerFocusWidth", 0 );
|
||||||
protected final float innerOutlineWidth = FlatUIUtils.getUIFloat( "Component.innerOutlineWidth", 0 );
|
@Styleable protected float innerOutlineWidth = FlatUIUtils.getUIFloat( "Component.innerOutlineWidth", 0 );
|
||||||
protected final Color focusColor = UIManager.getColor( "Component.focusColor" );
|
/** @since 2 */ @Styleable protected float borderWidth = FlatUIUtils.getUIFloat( "Component.borderWidth", 1 );
|
||||||
protected final Color borderColor = UIManager.getColor( "Component.borderColor" );
|
|
||||||
protected final Color disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
|
|
||||||
protected final Color focusedBorderColor = UIManager.getColor( "Component.focusedBorderColor" );
|
|
||||||
|
|
||||||
protected final Color errorBorderColor = UIManager.getColor( "Component.error.borderColor" );
|
@Styleable protected Color focusColor = UIManager.getColor( "Component.focusColor" );
|
||||||
protected final Color errorFocusedBorderColor = UIManager.getColor( "Component.error.focusedBorderColor" );
|
@Styleable protected Color borderColor = UIManager.getColor( "Component.borderColor" );
|
||||||
protected final Color warningBorderColor = UIManager.getColor( "Component.warning.borderColor" );
|
@Styleable protected Color disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
|
||||||
protected final Color warningFocusedBorderColor = UIManager.getColor( "Component.warning.focusedBorderColor" );
|
@Styleable protected Color focusedBorderColor = UIManager.getColor( "Component.focusedBorderColor" );
|
||||||
protected final Color customBorderColor = UIManager.getColor( "Component.custom.borderColor" );
|
|
||||||
|
@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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
@@ -82,9 +114,11 @@ public class FlatBorder
|
|||||||
FlatUIUtils.setRenderingHints( g2 );
|
FlatUIUtils.setRenderingHints( g2 );
|
||||||
|
|
||||||
float focusWidth = scale( (float) getFocusWidth( c ) );
|
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 ) );
|
float arc = scale( (float) getArc( c ) );
|
||||||
Color outlineColor = getOutlineColor( c );
|
Color outlineColor = getOutlineColor( c );
|
||||||
|
Color focusColor = null;
|
||||||
|
|
||||||
// paint outer border
|
// paint outer border
|
||||||
if( outlineColor != null || isFocused( c ) ) {
|
if( outlineColor != null || isFocused( c ) ) {
|
||||||
@@ -93,15 +127,16 @@ public class FlatBorder
|
|||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
if( focusWidth > 0 || innerWidth > 0 ) {
|
if( focusWidth > 0 || innerWidth > 0 ) {
|
||||||
g2.setColor( (outlineColor != null) ? outlineColor : getFocusColor( c ) );
|
focusColor = (outlineColor != null) ? outlineColor : getFocusColor( c );
|
||||||
FlatUIUtils.paintComponentOuterBorder( g2, x, y, width, height,
|
focusInnerWidth = borderWidth + scale( innerWidth );
|
||||||
focusWidth, borderWidth + scale( innerWidth ), arc );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// paint border
|
// paint border
|
||||||
g2.setPaint( (outlineColor != null) ? outlineColor : getBorderColor( c ) );
|
Paint borderColor = (outlineColor != null) ? outlineColor : getBorderColor( c );
|
||||||
FlatUIUtils.paintComponentBorder( g2, x, y, width, height, focusWidth, borderWidth, arc );
|
FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height,
|
||||||
|
focusWidth, 1, focusInnerWidth, borderWidth, arc,
|
||||||
|
focusColor, borderColor, null );
|
||||||
} finally {
|
} finally {
|
||||||
g2.dispose();
|
g2.dispose();
|
||||||
}
|
}
|
||||||
@@ -116,6 +151,17 @@ public class FlatBorder
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
Object outline = ((JComponent)c).getClientProperty( FlatClientProperties.OUTLINE );
|
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 ) {
|
if( outline instanceof String ) {
|
||||||
switch( (String) outline ) {
|
switch( (String) outline ) {
|
||||||
case FlatClientProperties.OUTLINE_ERROR:
|
case FlatClientProperties.OUTLINE_ERROR:
|
||||||
@@ -176,13 +222,14 @@ public class FlatBorder
|
|||||||
@Override
|
@Override
|
||||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
float focusWidth = scale( (float) getFocusWidth( c ) );
|
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 = super.getBorderInsets( c, insets );
|
||||||
insets.top = Math.round( scale( (float) insets.top ) + ow );
|
|
||||||
insets.left = Math.round( scale( (float) insets.left ) + ow );
|
insets.top = scale( insets.top ) + ow;
|
||||||
insets.bottom = Math.round( scale( (float) insets.bottom ) + ow );
|
insets.left = scale( insets.left ) + ow;
|
||||||
insets.right = Math.round( scale( (float) insets.right ) + ow );
|
insets.bottom = scale( insets.bottom ) + ow;
|
||||||
|
insets.right = scale( insets.right ) + ow;
|
||||||
|
|
||||||
if( isCellEditor( c ) ) {
|
if( isCellEditor( c ) ) {
|
||||||
// remove top and bottom insets if used as cell editor
|
// remove top and bottom insets if used as cell editor
|
||||||
@@ -227,8 +274,8 @@ public class FlatBorder
|
|||||||
* Returns the (unscaled) line thickness used to paint the border.
|
* Returns the (unscaled) line thickness used to paint the border.
|
||||||
* This may be different to {@link #getLineWidth}.
|
* This may be different to {@link #getLineWidth}.
|
||||||
*/
|
*/
|
||||||
protected int getBorderWidth( Component c ) {
|
protected float getBorderWidth( Component c ) {
|
||||||
return getLineWidth( c );
|
return borderWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -20,56 +20,72 @@ import java.awt.Color;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.GradientPaint;
|
import java.awt.GradientPaint;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.Paint;
|
import java.awt.Paint;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Border for {@link javax.swing.JButton}.
|
* 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.borderColor Color
|
||||||
* @uiDefault Button.startBorderColor Color optional; if set, a gradient paint is used and Button.borderColor is ignored
|
* @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.endBorderColor Color optional; if set, a gradient paint is used
|
||||||
* @uiDefault Button.disabledBorderColor Color
|
* @uiDefault Button.disabledBorderColor Color
|
||||||
* @uiDefault Button.focusedBorderColor Color
|
* @uiDefault Button.focusedBorderColor Color
|
||||||
* @uiDefault Button.hoverBorderColor Color optional
|
* @uiDefault Button.hoverBorderColor Color optional
|
||||||
|
*
|
||||||
|
* @uiDefault Button.default.borderWidth int or float
|
||||||
* @uiDefault Button.default.borderColor Color
|
* @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.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.endBorderColor Color optional; if set, a gradient paint is used
|
||||||
* @uiDefault Button.default.hoverBorderColor Color optional
|
|
||||||
* @uiDefault Button.default.focusedBorderColor Color
|
* @uiDefault Button.default.focusedBorderColor Color
|
||||||
* @uiDefault Button.default.focusColor Color
|
* @uiDefault Button.default.focusColor Color
|
||||||
* @uiDefault Button.borderWidth int
|
* @uiDefault Button.default.hoverBorderColor Color optional
|
||||||
* @uiDefault Button.default.borderWidth int
|
*
|
||||||
* @uiDefault Button.innerFocusWidth int or float optional; defaults to Component.innerFocusWidth
|
* @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.margin Insets
|
||||||
* @uiDefault Button.toolbar.spacingInsets Insets
|
* @uiDefault Button.toolbar.spacingInsets Insets
|
||||||
* @uiDefault Button.arc int
|
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatButtonBorder
|
public class FlatButtonBorder
|
||||||
extends FlatBorder
|
extends FlatBorder
|
||||||
{
|
{
|
||||||
protected final Color borderColor = FlatUIUtils.getUIColor( "Button.startBorderColor", "Button.borderColor" );
|
@Styleable protected int arc = UIManager.getInt( "Button.arc" );
|
||||||
protected final Color endBorderColor = UIManager.getColor( "Button.endBorderColor" );
|
|
||||||
protected final Color disabledBorderColor = UIManager.getColor( "Button.disabledBorderColor" );
|
protected Color endBorderColor = UIManager.getColor( "Button.endBorderColor" );
|
||||||
protected final Color focusedBorderColor = UIManager.getColor( "Button.focusedBorderColor" );
|
@Styleable protected Color hoverBorderColor = UIManager.getColor( "Button.hoverBorderColor" );
|
||||||
protected final Color hoverBorderColor = UIManager.getColor( "Button.hoverBorderColor" );
|
|
||||||
protected final Color defaultBorderColor = FlatUIUtils.getUIColor( "Button.default.startBorderColor", "Button.default.borderColor" );
|
@Styleable(dot=true) protected float defaultBorderWidth = FlatUIUtils.getUIFloat( "Button.default.borderWidth", 1 );
|
||||||
protected final Color defaultEndBorderColor = UIManager.getColor( "Button.default.endBorderColor" );
|
@Styleable(dot=true) protected Color defaultBorderColor = FlatUIUtils.getUIColor( "Button.default.startBorderColor", "Button.default.borderColor" );
|
||||||
protected final Color defaultHoverBorderColor = UIManager.getColor( "Button.default.hoverBorderColor" );
|
protected Color defaultEndBorderColor = UIManager.getColor( "Button.default.endBorderColor" );
|
||||||
protected final Color defaultFocusedBorderColor = UIManager.getColor( "Button.default.focusedBorderColor" );
|
@Styleable(dot=true) protected Color defaultFocusedBorderColor = UIManager.getColor( "Button.default.focusedBorderColor" );
|
||||||
protected final Color defaultFocusColor = UIManager.getColor( "Button.default.focusColor" );
|
@Styleable(dot=true) protected Color defaultFocusColor = UIManager.getColor( "Button.default.focusColor" );
|
||||||
protected final int borderWidth = UIManager.getInt( "Button.borderWidth" );
|
@Styleable(dot=true) protected Color defaultHoverBorderColor = UIManager.getColor( "Button.default.hoverBorderColor" );
|
||||||
protected final int defaultBorderWidth = UIManager.getInt( "Button.default.borderWidth" );
|
|
||||||
protected final float buttonInnerFocusWidth = FlatUIUtils.getUIFloat( "Button.innerFocusWidth", innerFocusWidth );
|
/** @since 1.4 */ @Styleable(dot=true) protected float toolbarFocusWidth = FlatUIUtils.getUIFloat( "Button.toolbar.focusWidth", 1.5f );
|
||||||
protected final Insets toolbarMargin = UIManager.getInsets( "Button.toolbar.margin" );
|
/** @since 1.4 */ @Styleable(dot=true) protected Color toolbarFocusColor = UIManager.getColor( "Button.toolbar.focusColor" );
|
||||||
protected final Insets toolbarSpacingInsets = UIManager.getInsets( "Button.toolbar.spacingInsets" );
|
@Styleable(dot=true) protected Insets toolbarMargin = UIManager.getInsets( "Button.toolbar.margin" );
|
||||||
protected final int arc = UIManager.getInt( "Button.arc" );
|
@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
|
@Override
|
||||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
@@ -79,11 +95,40 @@ public class FlatButtonBorder
|
|||||||
!FlatButtonUI.isHelpButton( c ) &&
|
!FlatButtonUI.isHelpButton( c ) &&
|
||||||
!FlatToggleButtonUI.isTabButton( c ) )
|
!FlatToggleButtonUI.isTabButton( c ) )
|
||||||
super.paintBorder( c, g, x, y, width, height );
|
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
|
@Override
|
||||||
protected Color getFocusColor( Component c ) {
|
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
|
@Override
|
||||||
@@ -114,7 +159,7 @@ public class FlatButtonBorder
|
|||||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
if( FlatButtonUI.isToolBarButton( c ) ) {
|
if( FlatButtonUI.isToolBarButton( c ) ) {
|
||||||
// In toolbars, use button margin only if explicitly set.
|
// In toolbars, use button margin only if explicitly set.
|
||||||
// Otherwise use toolbar margin specified in UI defaults.
|
// Otherwise, use toolbar margin specified in UI defaults.
|
||||||
Insets margin = (c instanceof AbstractButton)
|
Insets margin = (c instanceof AbstractButton)
|
||||||
? ((AbstractButton)c).getMargin()
|
? ((AbstractButton)c).getMargin()
|
||||||
: null;
|
: null;
|
||||||
@@ -138,12 +183,7 @@ public class FlatButtonBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected float getInnerFocusWidth( Component c ) {
|
protected float getBorderWidth( Component c ) {
|
||||||
return buttonInnerFocusWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int getBorderWidth( Component c ) {
|
|
||||||
return FlatButtonUI.isDefaultButton( c ) ? defaultBorderWidth : borderWidth;
|
return FlatButtonUI.isDefaultButton( c ) ? defaultBorderWidth : borderWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,20 +30,33 @@ import java.awt.Insets;
|
|||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.geom.RoundRectangle2D;
|
import java.awt.geom.RoundRectangle2D;
|
||||||
import java.beans.PropertyChangeEvent;
|
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.AbstractButton;
|
||||||
import javax.swing.ButtonModel;
|
import javax.swing.ButtonModel;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JTextField;
|
||||||
import javax.swing.JToggleButton;
|
import javax.swing.JToggleButton;
|
||||||
import javax.swing.JToolBar;
|
import javax.swing.JToolBar;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.plaf.ButtonUI;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicButtonListener;
|
import javax.swing.plaf.basic.BasicButtonListener;
|
||||||
import javax.swing.plaf.basic.BasicButtonUI;
|
import javax.swing.plaf.basic.BasicButtonUI;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
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;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,20 +78,27 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault Button.startBackground Color optional; if set, a gradient paint is used and Button.background is ignored
|
* @uiDefault Button.startBackground Color optional; if set, a gradient paint is used and Button.background is ignored
|
||||||
* @uiDefault Button.endBackground Color optional; if set, a gradient paint is used
|
* @uiDefault Button.endBackground Color optional; if set, a gradient paint is used
|
||||||
* @uiDefault Button.focusedBackground Color optional
|
* @uiDefault Button.focusedBackground Color optional
|
||||||
|
* @uiDefault Button.focusedForeground Color optional
|
||||||
* @uiDefault Button.hoverBackground Color optional
|
* @uiDefault Button.hoverBackground Color optional
|
||||||
|
* @uiDefault Button.hoverForeground Color optional
|
||||||
* @uiDefault Button.pressedBackground Color optional
|
* @uiDefault Button.pressedBackground Color optional
|
||||||
|
* @uiDefault Button.pressedForeground Color optional
|
||||||
* @uiDefault Button.selectedBackground Color
|
* @uiDefault Button.selectedBackground Color
|
||||||
* @uiDefault Button.selectedForeground Color
|
* @uiDefault Button.selectedForeground Color
|
||||||
* @uiDefault Button.disabledBackground Color optional
|
* @uiDefault Button.disabledBackground Color optional
|
||||||
* @uiDefault Button.disabledText Color
|
* @uiDefault Button.disabledText Color
|
||||||
* @uiDefault Button.disabledSelectedBackground Color
|
* @uiDefault Button.disabledSelectedBackground Color
|
||||||
|
* @uiDefault Button.disabledSelectedForeground Color optional
|
||||||
* @uiDefault Button.default.background Color
|
* @uiDefault Button.default.background Color
|
||||||
* @uiDefault Button.default.startBackground Color optional; if set, a gradient paint is used and Button.default.background is ignored
|
* @uiDefault Button.default.startBackground Color optional; if set, a gradient paint is used and Button.default.background is ignored
|
||||||
* @uiDefault Button.default.endBackground Color optional; if set, a gradient paint is used
|
* @uiDefault Button.default.endBackground Color optional; if set, a gradient paint is used
|
||||||
* @uiDefault Button.default.foreground Color
|
* @uiDefault Button.default.foreground Color
|
||||||
* @uiDefault Button.default.focusedBackground Color optional
|
* @uiDefault Button.default.focusedBackground Color optional
|
||||||
|
* @uiDefault Button.default.focusedForeground Color optional
|
||||||
* @uiDefault Button.default.hoverBackground Color optional
|
* @uiDefault Button.default.hoverBackground Color optional
|
||||||
|
* @uiDefault Button.default.hoverForeground Color optional
|
||||||
* @uiDefault Button.default.pressedBackground Color optional
|
* @uiDefault Button.default.pressedBackground Color optional
|
||||||
|
* @uiDefault Button.default.pressedForeground Color optional
|
||||||
* @uiDefault Button.default.boldText boolean
|
* @uiDefault Button.default.boldText boolean
|
||||||
* @uiDefault Button.paintShadow boolean default is false
|
* @uiDefault Button.paintShadow boolean default is false
|
||||||
* @uiDefault Button.shadowWidth int default is 2
|
* @uiDefault Button.shadowWidth int default is 2
|
||||||
@@ -86,15 +106,21 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault Button.default.shadowColor Color optional
|
* @uiDefault Button.default.shadowColor Color optional
|
||||||
* @uiDefault Button.toolbar.spacingInsets Insets
|
* @uiDefault Button.toolbar.spacingInsets Insets
|
||||||
* @uiDefault Button.toolbar.hoverBackground Color
|
* @uiDefault Button.toolbar.hoverBackground Color
|
||||||
|
* @uiDefault Button.toolbar.hoverForeground Color optional
|
||||||
* @uiDefault Button.toolbar.pressedBackground Color
|
* @uiDefault Button.toolbar.pressedBackground Color
|
||||||
|
* @uiDefault Button.toolbar.pressedForeground Color optional
|
||||||
* @uiDefault Button.toolbar.selectedBackground Color
|
* @uiDefault Button.toolbar.selectedBackground Color
|
||||||
|
* @uiDefault Button.toolbar.selectedForeground Color optional
|
||||||
|
* @uiDefault Button.toolbar.disabledSelectedBackground Color optional
|
||||||
|
* @uiDefault Button.toolbar.disabledSelectedForeground Color optional
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatButtonUI
|
public class FlatButtonUI
|
||||||
extends BasicButtonUI
|
extends BasicButtonUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
protected int minimumWidth;
|
@Styleable protected int minimumWidth;
|
||||||
protected int iconTextGap;
|
protected int iconTextGap;
|
||||||
|
|
||||||
protected Color background;
|
protected Color background;
|
||||||
@@ -102,38 +128,81 @@ public class FlatButtonUI
|
|||||||
|
|
||||||
protected Color startBackground;
|
protected Color startBackground;
|
||||||
protected Color endBackground;
|
protected Color endBackground;
|
||||||
protected Color focusedBackground;
|
@Styleable protected Color focusedBackground;
|
||||||
protected Color hoverBackground;
|
/** @since 2.3 */ @Styleable protected Color focusedForeground;
|
||||||
protected Color pressedBackground;
|
@Styleable protected Color hoverBackground;
|
||||||
protected Color selectedBackground;
|
/** @since 2.3 */ @Styleable protected Color hoverForeground;
|
||||||
protected Color selectedForeground;
|
@Styleable protected Color pressedBackground;
|
||||||
protected Color disabledBackground;
|
/** @since 2.3 */ @Styleable protected Color pressedForeground;
|
||||||
protected Color disabledText;
|
@Styleable protected Color selectedBackground;
|
||||||
protected Color disabledSelectedBackground;
|
@Styleable protected Color selectedForeground;
|
||||||
|
@Styleable protected Color disabledBackground;
|
||||||
|
@Styleable protected Color disabledText;
|
||||||
|
@Styleable protected Color disabledSelectedBackground;
|
||||||
|
/** @since 2.3 */ @Styleable protected Color disabledSelectedForeground;
|
||||||
|
|
||||||
protected Color defaultBackground;
|
@Styleable(dot=true) protected Color defaultBackground;
|
||||||
protected Color defaultEndBackground;
|
protected Color defaultEndBackground;
|
||||||
protected Color defaultForeground;
|
@Styleable(dot=true) protected Color defaultForeground;
|
||||||
protected Color defaultFocusedBackground;
|
@Styleable(dot=true) protected Color defaultFocusedBackground;
|
||||||
protected Color defaultHoverBackground;
|
/** @since 2.3 */ @Styleable(dot=true) protected Color defaultFocusedForeground;
|
||||||
protected Color defaultPressedBackground;
|
@Styleable(dot=true) protected Color defaultHoverBackground;
|
||||||
protected boolean defaultBoldText;
|
/** @since 2.3 */ @Styleable(dot=true) protected Color defaultHoverForeground;
|
||||||
|
@Styleable(dot=true) protected Color defaultPressedBackground;
|
||||||
|
/** @since 2.3 */ @Styleable(dot=true) protected Color defaultPressedForeground;
|
||||||
|
@Styleable(dot=true) protected boolean defaultBoldText;
|
||||||
|
|
||||||
protected int shadowWidth;
|
@Styleable protected boolean paintShadow;
|
||||||
protected Color shadowColor;
|
@Styleable protected int shadowWidth;
|
||||||
protected Color defaultShadowColor;
|
@Styleable protected Color shadowColor;
|
||||||
|
@Styleable(dot=true) protected Color defaultShadowColor;
|
||||||
|
|
||||||
protected Insets toolbarSpacingInsets;
|
@Styleable(dot=true) protected Color toolbarHoverBackground;
|
||||||
protected Color toolbarHoverBackground;
|
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarHoverForeground;
|
||||||
protected Color toolbarPressedBackground;
|
@Styleable(dot=true) protected Color toolbarPressedBackground;
|
||||||
protected Color toolbarSelectedBackground;
|
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarPressedForeground;
|
||||||
|
@Styleable(dot=true) protected Color toolbarSelectedBackground;
|
||||||
|
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarSelectedForeground;
|
||||||
|
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarDisabledSelectedBackground;
|
||||||
|
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarDisabledSelectedForeground;
|
||||||
|
|
||||||
|
// 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 Icon helpButtonIcon;
|
||||||
|
private Insets defaultMargin;
|
||||||
|
|
||||||
|
private final boolean shared;
|
||||||
|
private boolean helpButtonIconShared = true;
|
||||||
private boolean defaults_initialized = false;
|
private boolean defaults_initialized = false;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
private AtomicBoolean borderShared;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return FlatUIUtils.createSharedUI( FlatButtonUI.class, FlatButtonUI::new );
|
return FlatUIUtils.canUseSharedUI( c ) && !FlatUIUtils.needsLightAWTPeer( 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 ) {
|
||||||
|
if( FlatUIUtils.needsLightAWTPeer( c ) )
|
||||||
|
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
|
||||||
|
else
|
||||||
|
installUIImpl( c );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installUIImpl( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle( (AbstractButton) c );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -152,39 +221,47 @@ public class FlatButtonUI
|
|||||||
startBackground = UIManager.getColor( prefix + "startBackground" );
|
startBackground = UIManager.getColor( prefix + "startBackground" );
|
||||||
endBackground = UIManager.getColor( prefix + "endBackground" );
|
endBackground = UIManager.getColor( prefix + "endBackground" );
|
||||||
focusedBackground = UIManager.getColor( prefix + "focusedBackground" );
|
focusedBackground = UIManager.getColor( prefix + "focusedBackground" );
|
||||||
|
focusedForeground = UIManager.getColor( prefix + "focusedForeground" );
|
||||||
hoverBackground = UIManager.getColor( prefix + "hoverBackground" );
|
hoverBackground = UIManager.getColor( prefix + "hoverBackground" );
|
||||||
|
hoverForeground = UIManager.getColor( prefix + "hoverForeground" );
|
||||||
pressedBackground = UIManager.getColor( prefix + "pressedBackground" );
|
pressedBackground = UIManager.getColor( prefix + "pressedBackground" );
|
||||||
|
pressedForeground = UIManager.getColor( prefix + "pressedForeground" );
|
||||||
selectedBackground = UIManager.getColor( prefix + "selectedBackground" );
|
selectedBackground = UIManager.getColor( prefix + "selectedBackground" );
|
||||||
selectedForeground = UIManager.getColor( prefix + "selectedForeground" );
|
selectedForeground = UIManager.getColor( prefix + "selectedForeground" );
|
||||||
disabledBackground = UIManager.getColor( prefix + "disabledBackground" );
|
disabledBackground = UIManager.getColor( prefix + "disabledBackground" );
|
||||||
disabledText = UIManager.getColor( prefix + "disabledText" );
|
disabledText = UIManager.getColor( prefix + "disabledText" );
|
||||||
disabledSelectedBackground = UIManager.getColor( prefix + "disabledSelectedBackground" );
|
disabledSelectedBackground = UIManager.getColor( prefix + "disabledSelectedBackground" );
|
||||||
|
disabledSelectedForeground = UIManager.getColor( prefix + "disabledSelectedForeground" );
|
||||||
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" );
|
defaultBackground = FlatUIUtils.getUIColor( "Button.default.startBackground", "Button.default.background" );
|
||||||
defaultEndBackground = UIManager.getColor( "Button.default.endBackground" );
|
defaultEndBackground = UIManager.getColor( "Button.default.endBackground" );
|
||||||
defaultForeground = UIManager.getColor( "Button.default.foreground" );
|
defaultForeground = UIManager.getColor( "Button.default.foreground" );
|
||||||
defaultFocusedBackground = UIManager.getColor( "Button.default.focusedBackground" );
|
defaultFocusedBackground = UIManager.getColor( "Button.default.focusedBackground" );
|
||||||
|
defaultFocusedForeground = UIManager.getColor( "Button.default.focusedForeground" );
|
||||||
defaultHoverBackground = UIManager.getColor( "Button.default.hoverBackground" );
|
defaultHoverBackground = UIManager.getColor( "Button.default.hoverBackground" );
|
||||||
|
defaultHoverForeground = UIManager.getColor( "Button.default.hoverForeground" );
|
||||||
defaultPressedBackground = UIManager.getColor( "Button.default.pressedBackground" );
|
defaultPressedBackground = UIManager.getColor( "Button.default.pressedBackground" );
|
||||||
|
defaultPressedForeground = UIManager.getColor( "Button.default.pressedForeground" );
|
||||||
defaultBoldText = UIManager.getBoolean( "Button.default.boldText" );
|
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" );
|
toolbarHoverBackground = UIManager.getColor( prefix + "toolbar.hoverBackground" );
|
||||||
|
toolbarHoverForeground = UIManager.getColor( prefix + "toolbar.hoverForeground" );
|
||||||
toolbarPressedBackground = UIManager.getColor( prefix + "toolbar.pressedBackground" );
|
toolbarPressedBackground = UIManager.getColor( prefix + "toolbar.pressedBackground" );
|
||||||
|
toolbarPressedForeground = UIManager.getColor( prefix + "toolbar.pressedForeground" );
|
||||||
toolbarSelectedBackground = UIManager.getColor( prefix + "toolbar.selectedBackground" );
|
toolbarSelectedBackground = UIManager.getColor( prefix + "toolbar.selectedBackground" );
|
||||||
|
toolbarSelectedForeground = UIManager.getColor( prefix + "toolbar.selectedForeground" );
|
||||||
|
toolbarDisabledSelectedBackground = UIManager.getColor( prefix + "toolbar.disabledSelectedBackground" );
|
||||||
|
toolbarDisabledSelectedForeground = UIManager.getColor( prefix + "toolbar.disabledSelectedForeground" );
|
||||||
|
|
||||||
helpButtonIcon = UIManager.getIcon( "HelpButton.icon" );
|
helpButtonIcon = UIManager.getIcon( "HelpButton.icon" );
|
||||||
|
defaultMargin = UIManager.getInsets( prefix + "margin" );
|
||||||
|
|
||||||
|
helpButtonIconShared = true;
|
||||||
defaults_initialized = true;
|
defaults_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,6 +281,9 @@ public class FlatButtonUI
|
|||||||
protected void uninstallDefaults( AbstractButton b ) {
|
protected void uninstallDefaults( AbstractButton b ) {
|
||||||
super.uninstallDefaults( b );
|
super.uninstallDefaults( b );
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
borderShared = null;
|
||||||
|
|
||||||
MigLayoutVisualPadding.uninstall( b );
|
MigLayoutVisualPadding.uninstall( b );
|
||||||
defaults_initialized = false;
|
defaults_initialized = false;
|
||||||
}
|
}
|
||||||
@@ -225,9 +305,86 @@ public class FlatButtonUI
|
|||||||
b.revalidate();
|
b.revalidate();
|
||||||
b.repaint();
|
b.repaint();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OUTLINE:
|
||||||
|
b.repaint();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STYLE:
|
||||||
|
case STYLE_CLASS:
|
||||||
|
if( shared && FlatStylingSupport.hasStyleProperty( b ) ) {
|
||||||
|
// unshare component UI if necessary
|
||||||
|
// updateUI() invokes installStyle() 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
if( key.startsWith( "help." ) ) {
|
||||||
|
return (helpButtonIcon instanceof FlatHelpButtonIcon)
|
||||||
|
? ((FlatHelpButtonIcon)helpButtonIcon).getStyleableValue( key.substring( "help.".length() ) )
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, c.getBorder(), key );
|
||||||
|
}
|
||||||
|
|
||||||
static boolean isContentAreaFilled( Component c ) {
|
static boolean isContentAreaFilled( Component c ) {
|
||||||
return !(c instanceof AbstractButton) || ((AbstractButton)c).isContentAreaFilled();
|
return !(c instanceof AbstractButton) || ((AbstractButton)c).isContentAreaFilled();
|
||||||
}
|
}
|
||||||
@@ -242,7 +399,7 @@ public class FlatButtonUI
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the button has an icon but no text,
|
* Returns true if the button has an icon but no text,
|
||||||
* or it it does not have an icon and the text is either "..." or one character.
|
* or it does not have an icon and the text is either "..." or one character.
|
||||||
*/
|
*/
|
||||||
static boolean isIconOnlyOrSingleCharacterButton( Component c ) {
|
static boolean isIconOnlyOrSingleCharacterButton( Component c ) {
|
||||||
if( !(c instanceof JButton) && !(c instanceof JToggleButton) )
|
if( !(c instanceof JButton) && !(c instanceof JToggleButton) )
|
||||||
@@ -265,11 +422,11 @@ public class FlatButtonUI
|
|||||||
if( !(c instanceof AbstractButton) )
|
if( !(c instanceof AbstractButton) )
|
||||||
return TYPE_OTHER;
|
return TYPE_OTHER;
|
||||||
|
|
||||||
Object value = ((AbstractButton)c).getClientProperty( BUTTON_TYPE );
|
String value = getButtonTypeStr( (AbstractButton) c );
|
||||||
if( !(value instanceof String) )
|
if( value == null )
|
||||||
return TYPE_OTHER;
|
return TYPE_OTHER;
|
||||||
|
|
||||||
switch( (String) value ) {
|
switch( value ) {
|
||||||
case BUTTON_TYPE_SQUARE: return TYPE_SQUARE;
|
case BUTTON_TYPE_SQUARE: return TYPE_SQUARE;
|
||||||
case BUTTON_TYPE_ROUND_RECT: return TYPE_ROUND_RECT;
|
case BUTTON_TYPE_ROUND_RECT: return TYPE_ROUND_RECT;
|
||||||
default: return TYPE_OTHER;
|
default: return TYPE_OTHER;
|
||||||
@@ -277,16 +434,27 @@ public class FlatButtonUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
static boolean isHelpButton( Component c ) {
|
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 ) {
|
static boolean isToolBarButton( Component c ) {
|
||||||
return c.getParent() instanceof JToolBar ||
|
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 ) {
|
static boolean isBorderlessButton( Component c ) {
|
||||||
return c instanceof AbstractButton && clientPropertyEquals( (AbstractButton) c, BUTTON_TYPE, BUTTON_TYPE_BORDERLESS );
|
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
|
@Override
|
||||||
@@ -315,28 +483,46 @@ public class FlatButtonUI
|
|||||||
try {
|
try {
|
||||||
FlatUIUtils.setRenderingHints( g2 );
|
FlatUIUtils.setRenderingHints( g2 );
|
||||||
|
|
||||||
|
boolean def = isDefaultButton( c );
|
||||||
boolean isToolBarButton = isToolBarButton( c );
|
boolean isToolBarButton = isToolBarButton( c );
|
||||||
float focusWidth = isToolBarButton ? 0 : FlatUIUtils.getBorderFocusWidth( c );
|
float focusWidth = isToolBarButton ? 0 : FlatUIUtils.getBorderFocusWidth( c );
|
||||||
float arc = FlatUIUtils.getBorderArc( c );
|
float arc = FlatUIUtils.getBorderArc( c );
|
||||||
|
float textFieldArc = 0;
|
||||||
|
|
||||||
boolean def = isDefaultButton( c );
|
// if toolbar button is in leading/trailing component of a text field,
|
||||||
|
// increase toolbar button arc to match text field arc (if necessary)
|
||||||
|
if( isToolBarButton &&
|
||||||
|
FlatClientProperties.clientProperty( c, STYLE_CLASS, "", String.class ).contains( "inTextField" ) )
|
||||||
|
{
|
||||||
|
JTextField textField = (JTextField) SwingUtilities.getAncestorOfClass( JTextField.class, c );
|
||||||
|
if( textField != null )
|
||||||
|
textFieldArc = FlatUIUtils.getBorderArc( textField );
|
||||||
|
}
|
||||||
|
|
||||||
int x = 0;
|
int x = 0;
|
||||||
int y = 0;
|
int y = 0;
|
||||||
int width = c.getWidth();
|
int width = c.getWidth();
|
||||||
int height = c.getHeight();
|
int height = c.getHeight();
|
||||||
|
|
||||||
if( isToolBarButton ) {
|
if( isToolBarButton && c.getBorder() instanceof FlatButtonBorder ) {
|
||||||
Insets spacing = UIScale.scale( toolbarSpacingInsets );
|
Insets spacing = UIScale.scale( ((FlatButtonBorder)c.getBorder()).toolbarSpacingInsets );
|
||||||
x += spacing.left;
|
x += spacing.left;
|
||||||
y += spacing.top;
|
y += spacing.top;
|
||||||
width -= spacing.left + spacing.right;
|
width -= spacing.left + spacing.right;
|
||||||
height -= spacing.top + spacing.bottom;
|
height -= spacing.top + spacing.bottom;
|
||||||
|
|
||||||
|
// reduce text field arc
|
||||||
|
textFieldArc -= spacing.top + spacing.bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// increase toolbar button arc to match text field arc (if necessary)
|
||||||
|
if( arc < textFieldArc )
|
||||||
|
arc = textFieldArc;
|
||||||
|
|
||||||
// paint shadow
|
// paint shadow
|
||||||
Color shadowColor = def ? defaultShadowColor : this.shadowColor;
|
Color shadowColor = def ? defaultShadowColor : this.shadowColor;
|
||||||
if( shadowColor != null && shadowWidth > 0 && focusWidth > 0 && c.isEnabled() &&
|
if( paintShadow &&
|
||||||
|
shadowColor != null && shadowWidth > 0 && focusWidth > 0 && c.isEnabled() &&
|
||||||
!isToolBarButton && !isBorderlessButton( c ) &&
|
!isToolBarButton && !isBorderlessButton( c ) &&
|
||||||
!(isFocusPainted( c ) && FlatUIUtils.isPermanentFocusOwner( c )) )
|
!(isFocusPainted( c ) && FlatUIUtils.isPermanentFocusOwner( c )) )
|
||||||
{
|
{
|
||||||
@@ -364,6 +550,23 @@ public class FlatButtonUI
|
|||||||
super.paint( FlatLabelUI.createGraphicsHTMLTextYCorrection( g, c ), c );
|
super.paint( FlatLabelUI.createGraphicsHTMLTextYCorrection( g, c ), c );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintIcon( Graphics g, JComponent c, Rectangle iconRect ) {
|
||||||
|
// correct icon location when using bold font for default button
|
||||||
|
int xOffset = defaultBoldPlainWidthDiff( c ) / 2;
|
||||||
|
if( xOffset > 0 ) {
|
||||||
|
boolean ltr = c.getComponentOrientation().isLeftToRight();
|
||||||
|
switch( ((AbstractButton)c).getHorizontalTextPosition() ) {
|
||||||
|
case SwingConstants.RIGHT: iconRect.x -= xOffset; break;
|
||||||
|
case SwingConstants.LEFT: iconRect.x += xOffset; break;
|
||||||
|
case SwingConstants.TRAILING: iconRect.x -= ltr ? xOffset : -xOffset; break;
|
||||||
|
case SwingConstants.LEADING: iconRect.x += ltr ? xOffset : -xOffset; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.paintIcon( g, c, iconRect );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text ) {
|
protected void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text ) {
|
||||||
if( isHelpButton( b ) )
|
if( isHelpButton( b ) )
|
||||||
@@ -384,6 +587,8 @@ public class FlatButtonUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text, Color foreground ) {
|
public static void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text, Color foreground ) {
|
||||||
|
if(foreground == null)
|
||||||
|
foreground=Color.red;
|
||||||
FontMetrics fm = b.getFontMetrics( b.getFont() );
|
FontMetrics fm = b.getFontMetrics( b.getFont() );
|
||||||
int mnemonicIndex = FlatLaf.isShowMnemonics() ? b.getDisplayedMnemonicIndex() : -1;
|
int mnemonicIndex = FlatLaf.isShowMnemonics() ? b.getDisplayedMnemonicIndex() : -1;
|
||||||
|
|
||||||
@@ -397,11 +602,14 @@ public class FlatButtonUI
|
|||||||
|
|
||||||
// selected state
|
// selected state
|
||||||
if( ((AbstractButton)c).isSelected() ) {
|
if( ((AbstractButton)c).isSelected() ) {
|
||||||
// in toolbar use same background colors for disabled and enabled because
|
// in toolbar, if toolbarDisabledSelectedBackground is null,
|
||||||
|
// use same background colors for disabled and enabled because
|
||||||
// we assume that toolbar icon is shown disabled
|
// we assume that toolbar icon is shown disabled
|
||||||
return buttonStateColor( c,
|
return buttonStateColor( c,
|
||||||
toolBarButton ? toolbarSelectedBackground : selectedBackground,
|
toolBarButton ? toolbarSelectedBackground : selectedBackground,
|
||||||
toolBarButton ? toolbarSelectedBackground : disabledSelectedBackground,
|
toolBarButton
|
||||||
|
? (toolbarDisabledSelectedBackground != null ? toolbarDisabledSelectedBackground : toolbarSelectedBackground)
|
||||||
|
: disabledSelectedBackground,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
toolBarButton ? toolbarPressedBackground : pressedBackground );
|
toolBarButton ? toolbarPressedBackground : pressedBackground );
|
||||||
@@ -428,6 +636,9 @@ public class FlatButtonUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Color getBackgroundBase( JComponent c, boolean def ) {
|
protected Color getBackgroundBase( JComponent c, boolean def ) {
|
||||||
|
if( FlatUIUtils.isAWTPeer( c ) )
|
||||||
|
return background;
|
||||||
|
|
||||||
// use component background if explicitly set
|
// use component background if explicitly set
|
||||||
Color bg = c.getBackground();
|
Color bg = c.getBackground();
|
||||||
if( isCustomBackground( bg ) )
|
if( isCustomBackground( bg ) )
|
||||||
@@ -443,6 +654,9 @@ public class FlatButtonUI
|
|||||||
public static Color buttonStateColor( Component c, Color enabledColor, Color disabledColor,
|
public static Color buttonStateColor( Component c, Color enabledColor, Color disabledColor,
|
||||||
Color focusedColor, Color hoverColor, Color pressedColor )
|
Color focusedColor, Color hoverColor, Color pressedColor )
|
||||||
{
|
{
|
||||||
|
if( c == null )
|
||||||
|
return enabledColor;
|
||||||
|
|
||||||
if( !c.isEnabled() )
|
if( !c.isEnabled() )
|
||||||
return disabledColor;
|
return disabledColor;
|
||||||
|
|
||||||
@@ -463,18 +677,48 @@ public class FlatButtonUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Color getForeground( JComponent c ) {
|
protected Color getForeground( JComponent c ) {
|
||||||
if( !c.isEnabled() )
|
boolean toolBarButton = isToolBarButton( c ) || isBorderlessButton( c );
|
||||||
return disabledText;
|
|
||||||
|
|
||||||
if( ((AbstractButton)c).isSelected() && !(isToolBarButton( c ) || isBorderlessButton( c )) )
|
// selected state
|
||||||
return selectedForeground;
|
if( ((AbstractButton)c).isSelected() ) {
|
||||||
|
return buttonStateColor( c,
|
||||||
|
toolBarButton
|
||||||
|
? (toolbarSelectedForeground != null ? toolbarSelectedForeground : c.getForeground())
|
||||||
|
: selectedForeground,
|
||||||
|
toolBarButton
|
||||||
|
? (toolbarDisabledSelectedForeground != null ? toolbarDisabledSelectedForeground : disabledText)
|
||||||
|
: (disabledSelectedForeground != null ? disabledSelectedForeground : disabledText),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
toolBarButton ? toolbarPressedForeground : pressedForeground );
|
||||||
|
}
|
||||||
|
|
||||||
|
// toolbar button
|
||||||
|
if( toolBarButton ) {
|
||||||
|
return buttonStateColor( c,
|
||||||
|
c.getForeground(),
|
||||||
|
disabledText,
|
||||||
|
null,
|
||||||
|
toolbarHoverForeground,
|
||||||
|
toolbarPressedForeground );
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean def = isDefaultButton( c );
|
||||||
|
return buttonStateColor( c,
|
||||||
|
getForegroundBase( c, def ),
|
||||||
|
disabledText,
|
||||||
|
isCustomForeground( c.getForeground() ) ? null : (def ? defaultFocusedForeground : focusedForeground),
|
||||||
|
def ? defaultHoverForeground : hoverForeground,
|
||||||
|
def ? defaultPressedForeground : pressedForeground );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.3 */
|
||||||
|
protected Color getForegroundBase( JComponent c, boolean def ) {
|
||||||
// use component foreground if explicitly set
|
// use component foreground if explicitly set
|
||||||
Color fg = c.getForeground();
|
Color fg = c.getForeground();
|
||||||
if( isCustomForeground( fg ) )
|
if( isCustomForeground( fg ) )
|
||||||
return fg;
|
return fg;
|
||||||
|
|
||||||
boolean def = isDefaultButton( c );
|
|
||||||
return def ? defaultForeground : fg;
|
return def ? defaultForeground : fg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,24 +735,51 @@ public class FlatButtonUI
|
|||||||
if( prefSize == null )
|
if( prefSize == null )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
// increase width when using bold font for default button
|
||||||
|
prefSize.width += defaultBoldPlainWidthDiff( c );
|
||||||
|
|
||||||
// make square or apply minimum width/height
|
// make square or apply minimum width/height
|
||||||
boolean isIconOnlyOrSingleCharacter = isIconOnlyOrSingleCharacterButton( c );
|
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)
|
// make button square (increase width or height so that they are equal)
|
||||||
prefSize.width = prefSize.height = Math.max( prefSize.width, prefSize.height );
|
prefSize.width = prefSize.height = Math.max( prefSize.width, prefSize.height );
|
||||||
} else if( isIconOnlyOrSingleCharacter && ((AbstractButton)c).getIcon() == null ) {
|
} else if( isIconOnlyOrSingleCharacter && ((AbstractButton)c).getIcon() == null ) {
|
||||||
// make single-character-no-icon button square (increase width)
|
// make single-character-no-icon button square (increase width)
|
||||||
prefSize.width = Math.max( prefSize.width, prefSize.height );
|
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
|
// apply minimum width/height
|
||||||
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
|
int fw = Math.round( FlatUIUtils.getBorderFocusWidth( c ) * 2 );
|
||||||
prefSize.width = Math.max( prefSize.width, scale( FlatUIUtils.minimumWidth( c, minimumWidth ) ) + Math.round( focusWidth * 2 ) );
|
prefSize.width = Math.max( prefSize.width, scale( FlatUIUtils.minimumWidth( c, minimumWidth ) ) + fw );
|
||||||
prefSize.height = Math.max( prefSize.height, scale( FlatUIUtils.minimumHeight( c, 0 ) ) + Math.round( focusWidth * 2 ) );
|
prefSize.height = Math.max( prefSize.height, scale( FlatUIUtils.minimumHeight( c, minimumHeight ) ) + fw );
|
||||||
}
|
}
|
||||||
|
|
||||||
return prefSize;
|
return prefSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int defaultBoldPlainWidthDiff( JComponent c ) {
|
||||||
|
if( defaultBoldText && isDefaultButton( c ) && c.getFont() instanceof UIResource ) {
|
||||||
|
String text = ((AbstractButton)c).getText();
|
||||||
|
if( text == null || text.isEmpty() )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Font font = c.getFont();
|
||||||
|
Font boldFont = font.deriveFont( Font.BOLD );
|
||||||
|
int boldWidth = c.getFontMetrics( boldFont ).stringWidth( text );
|
||||||
|
int plainWidth = c.getFontMetrics( font ).stringWidth( text );
|
||||||
|
if( boldWidth > plainWidth )
|
||||||
|
return boldWidth - plainWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasDefaultMargins( JComponent c ) {
|
||||||
|
Insets margin = ((AbstractButton)c).getMargin();
|
||||||
|
return margin instanceof UIResource && Objects.equals( margin, defaultMargin );
|
||||||
|
}
|
||||||
|
|
||||||
//---- class FlatButtonListener -------------------------------------------
|
//---- class FlatButtonListener -------------------------------------------
|
||||||
|
|
||||||
protected class FlatButtonListener
|
protected class FlatButtonListener
|
||||||
|
|||||||
@@ -18,16 +18,27 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import static com.formdev.flatlaf.FlatClientProperties.*;
|
import static com.formdev.flatlaf.FlatClientProperties.*;
|
||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.FocusEvent;
|
import java.awt.event.FocusEvent;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
|
import javax.swing.Action;
|
||||||
|
import javax.swing.ActionMap;
|
||||||
import javax.swing.JFormattedTextField;
|
import javax.swing.JFormattedTextField;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
|
import javax.swing.text.BadLocationException;
|
||||||
import javax.swing.text.DefaultCaret;
|
import javax.swing.text.DefaultCaret;
|
||||||
|
import javax.swing.text.DefaultEditorKit;
|
||||||
import javax.swing.text.Document;
|
import javax.swing.text.Document;
|
||||||
import javax.swing.text.JTextComponent;
|
import javax.swing.text.JTextComponent;
|
||||||
|
import javax.swing.text.Utilities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Caret that can select all text on focus gained.
|
* 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
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
@@ -35,12 +46,19 @@ public class FlatCaret
|
|||||||
extends DefaultCaret
|
extends DefaultCaret
|
||||||
implements UIResource
|
implements UIResource
|
||||||
{
|
{
|
||||||
|
private static final String KEY_CARET_INFO = "FlatLaf.internal.caretInfo";
|
||||||
|
|
||||||
private final String selectAllOnFocusPolicy;
|
private final String selectAllOnFocusPolicy;
|
||||||
private final boolean selectAllOnMouseClick;
|
private final boolean selectAllOnMouseClick;
|
||||||
|
|
||||||
|
private boolean inInstall;
|
||||||
private boolean wasFocused;
|
private boolean wasFocused;
|
||||||
private boolean wasTemporaryLost;
|
private boolean wasTemporaryLost;
|
||||||
private boolean isMousePressed;
|
private boolean isMousePressed;
|
||||||
|
private boolean isWordSelection;
|
||||||
|
private boolean isLineSelection;
|
||||||
|
private int dragSelectionStart;
|
||||||
|
private int dragSelectionEnd;
|
||||||
|
|
||||||
public FlatCaret( String selectAllOnFocusPolicy, boolean selectAllOnMouseClick ) {
|
public FlatCaret( String selectAllOnFocusPolicy, boolean selectAllOnMouseClick ) {
|
||||||
this.selectAllOnFocusPolicy = selectAllOnFocusPolicy;
|
this.selectAllOnFocusPolicy = selectAllOnFocusPolicy;
|
||||||
@@ -49,21 +67,82 @@ public class FlatCaret
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void install( JTextComponent c ) {
|
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
|
// if caret info is too old assume that switched from FlatLaf
|
||||||
// --> move dot to end of text so that all text may be selected when it gains focus
|
// to another Laf and back to FlatLaf
|
||||||
Document doc = c.getDocument();
|
if( System.currentTimeMillis() - 500 > ci[3] )
|
||||||
if( doc != null && getDot() == 0 && getMark() == 0 ) {
|
ci = null;
|
||||||
int length = doc.getLength();
|
}
|
||||||
if( length > 0 )
|
if( ci != null ) {
|
||||||
setDot( length );
|
// 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
|
||||||
|
// a 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( getComponent() == null )
|
||||||
|
return; // was deinstalled
|
||||||
|
|
||||||
|
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
|
@Override
|
||||||
public void focusGained( FocusEvent e ) {
|
public void focusGained( FocusEvent e ) {
|
||||||
if( !wasTemporaryLost && (!isMousePressed || selectAllOnMouseClick) )
|
if( !inInstall && !wasTemporaryLost && (!isMousePressed || selectAllOnMouseClick) )
|
||||||
selectAllOnFocusGained();
|
selectAllOnFocusGained();
|
||||||
wasTemporaryLost = false;
|
wasTemporaryLost = false;
|
||||||
wasFocused = true;
|
wasFocused = true;
|
||||||
@@ -81,25 +160,83 @@ public class FlatCaret
|
|||||||
public void mousePressed( MouseEvent e ) {
|
public void mousePressed( MouseEvent e ) {
|
||||||
isMousePressed = true;
|
isMousePressed = true;
|
||||||
super.mousePressed( e );
|
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
|
@Override
|
||||||
public void mouseReleased( MouseEvent e ) {
|
public void mouseReleased( MouseEvent e ) {
|
||||||
isMousePressed = false;
|
isMousePressed = false;
|
||||||
|
isWordSelection = false;
|
||||||
|
isLineSelection = false;
|
||||||
super.mouseReleased( e );
|
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() {
|
protected void selectAllOnFocusGained() {
|
||||||
JTextComponent c = getComponent();
|
JTextComponent c = getComponent();
|
||||||
Document doc = c.getDocument();
|
Document doc = c.getDocument();
|
||||||
if( doc == null || !c.isEnabled() || !c.isEditable() )
|
if( doc == null || !c.isEnabled() || !c.isEditable() || FlatUIUtils.isCellEditor( c ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Object selectAllOnFocusPolicy = c.getClientProperty( SELECT_ALL_ON_FOCUS_POLICY );
|
Object selectAllOnFocusPolicy = c.getClientProperty( SELECT_ALL_ON_FOCUS_POLICY );
|
||||||
if( selectAllOnFocusPolicy == null )
|
if( selectAllOnFocusPolicy == null )
|
||||||
selectAllOnFocusPolicy = this.selectAllOnFocusPolicy;
|
selectAllOnFocusPolicy = this.selectAllOnFocusPolicy;
|
||||||
|
|
||||||
if( SELECT_ALL_ON_FOCUS_POLICY_NEVER.equals( selectAllOnFocusPolicy ) )
|
if( selectAllOnFocusPolicy == null || SELECT_ALL_ON_FOCUS_POLICY_NEVER.equals( selectAllOnFocusPolicy ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if( !SELECT_ALL_ON_FOCUS_POLICY_ALWAYS.equals( selectAllOnFocusPolicy ) ) {
|
if( !SELECT_ALL_ON_FOCUS_POLICY_ALWAYS.equals( selectAllOnFocusPolicy ) ) {
|
||||||
@@ -119,12 +256,37 @@ public class FlatCaret
|
|||||||
// select all
|
// select all
|
||||||
if( c instanceof JFormattedTextField ) {
|
if( c instanceof JFormattedTextField ) {
|
||||||
EventQueue.invokeLater( () -> {
|
EventQueue.invokeLater( () -> {
|
||||||
setDot( 0 );
|
if( getComponent() == null )
|
||||||
moveDot( doc.getLength() );
|
return; // was deinstalled
|
||||||
|
|
||||||
|
select( 0, doc.getLength() );
|
||||||
} );
|
} );
|
||||||
} else {
|
} else {
|
||||||
setDot( 0 );
|
select( 0, doc.getLength() );
|
||||||
moveDot( 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,11 +18,19 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
|
import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
|
||||||
|
import javax.swing.plaf.basic.BasicMenuItemUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JCheckBoxMenuItem}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JCheckBoxMenuItem}.
|
||||||
@@ -52,15 +60,30 @@ import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
|
|||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="selectionBackground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="selectionForeground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="disabledForeground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorForeground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorSelectionForeground" )
|
||||||
|
|
||||||
public class FlatCheckBoxMenuItemUI
|
public class FlatCheckBoxMenuItemUI
|
||||||
extends BasicCheckBoxMenuItemUI
|
extends BasicCheckBoxMenuItemUI
|
||||||
|
implements StyleableUI, StyleableLookupProvider
|
||||||
{
|
{
|
||||||
private FlatMenuItemRenderer renderer;
|
private FlatMenuItemRenderer renderer;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatCheckBoxMenuItemUI();
|
return new FlatCheckBoxMenuItemUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
@@ -74,13 +97,59 @@ public class FlatCheckBoxMenuItemUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
FlatMenuItemRenderer.clearClientProperties( menuItem.getParent() );
|
||||||
renderer = null;
|
renderer = null;
|
||||||
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected FlatMenuItemRenderer createRenderer() {
|
protected FlatMenuItemRenderer createRenderer() {
|
||||||
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
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 ) {
|
||||||
|
return FlatMenuItemUI.applyStyleProperty( menuItem, this, renderer, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return FlatMenuItemUI.getStyleableInfos( this, renderer );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatMenuItemUI.getStyleableValue( this, renderer, key );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public MethodHandles.Lookup getLookupForStyling() {
|
||||||
|
// MethodHandles.lookup() is caller sensitive and must be invoked in this class,
|
||||||
|
// otherwise it is not possible to access protected fields in JRE superclass
|
||||||
|
return MethodHandles.lookup();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
|
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
|
||||||
return renderer.getPreferredMenuItemSize();
|
return renderer.getPreferredMenuItemSize();
|
||||||
|
|||||||
@@ -43,11 +43,24 @@ public class FlatCheckBoxUI
|
|||||||
extends FlatRadioButtonUI
|
extends FlatRadioButtonUI
|
||||||
{
|
{
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return FlatUIUtils.createSharedUI( FlatCheckBoxUI.class, FlatCheckBoxUI::new );
|
return FlatUIUtils.canUseSharedUI( c ) && !FlatUIUtils.needsLightAWTPeer( c )
|
||||||
|
? FlatUIUtils.createSharedUI( FlatCheckBoxUI.class, () -> new FlatCheckBoxUI( true ) )
|
||||||
|
: new FlatCheckBoxUI( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected FlatCheckBoxUI( boolean shared ) {
|
||||||
|
super( shared );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPropertyPrefix() {
|
public String getPropertyPrefix() {
|
||||||
return "CheckBox.";
|
return "CheckBox.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
String getStyleType() {
|
||||||
|
return "CheckBox";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,12 +16,15 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
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.scale;
|
||||||
|
import static com.formdev.flatlaf.util.UIScale.unscale;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.ComponentOrientation;
|
import java.awt.ComponentOrientation;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
|
import java.awt.FontMetrics;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.GraphicsConfiguration;
|
import java.awt.GraphicsConfiguration;
|
||||||
@@ -39,11 +42,12 @@ import java.awt.event.MouseEvent;
|
|||||||
import java.awt.event.MouseListener;
|
import java.awt.event.MouseListener;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
import javax.swing.CellRendererPane;
|
import javax.swing.CellRendererPane;
|
||||||
import javax.swing.ComboBoxEditor;
|
|
||||||
import javax.swing.DefaultListCellRenderer;
|
import javax.swing.DefaultListCellRenderer;
|
||||||
import javax.swing.InputMap;
|
import javax.swing.InputMap;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
@@ -66,9 +70,12 @@ import javax.swing.plaf.basic.BasicComboBoxUI;
|
|||||||
import javax.swing.plaf.basic.BasicComboPopup;
|
import javax.swing.plaf.basic.BasicComboPopup;
|
||||||
import javax.swing.plaf.basic.ComboPopup;
|
import javax.swing.plaf.basic.ComboPopup;
|
||||||
import javax.swing.text.JTextComponent;
|
import javax.swing.text.JTextComponent;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JComboBox}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JComboBox}.
|
||||||
@@ -82,6 +89,11 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault ComboBox.padding Insets
|
* @uiDefault ComboBox.padding Insets
|
||||||
* @uiDefault ComboBox.squareButton boolean default is true
|
* @uiDefault ComboBox.squareButton boolean default is true
|
||||||
*
|
*
|
||||||
|
* <!-- BasicComboPopup -->
|
||||||
|
*
|
||||||
|
* @uiDefault ComboBox.selectionBackground Color
|
||||||
|
* @uiDefault ComboBox.selectionForeground Color
|
||||||
|
*
|
||||||
* <!-- FlatComboBoxUI -->
|
* <!-- FlatComboBoxUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault ComboBox.minimumWidth int
|
* @uiDefault ComboBox.minimumWidth int
|
||||||
@@ -90,15 +102,16 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault ComboBox.buttonStyle String auto (default), button or none
|
* @uiDefault ComboBox.buttonStyle String auto (default), button or none
|
||||||
* @uiDefault Component.arrowType String chevron (default) or triangle
|
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
* @uiDefault Component.isIntelliJTheme boolean
|
||||||
* @uiDefault Component.borderColor Color
|
|
||||||
* @uiDefault Component.disabledBorderColor Color
|
|
||||||
* @uiDefault ComboBox.editableBackground Color optional; defaults to ComboBox.background
|
* @uiDefault ComboBox.editableBackground Color optional; defaults to ComboBox.background
|
||||||
* @uiDefault ComboBox.focusedBackground Color optional
|
* @uiDefault ComboBox.focusedBackground Color optional
|
||||||
* @uiDefault ComboBox.disabledBackground Color
|
* @uiDefault ComboBox.disabledBackground Color
|
||||||
* @uiDefault ComboBox.disabledForeground Color
|
* @uiDefault ComboBox.disabledForeground Color
|
||||||
* @uiDefault ComboBox.buttonBackground Color
|
* @uiDefault ComboBox.buttonBackground Color optional
|
||||||
* @uiDefault ComboBox.buttonEditableBackground Color
|
* @uiDefault ComboBox.buttonEditableBackground Color optional
|
||||||
* @uiDefault ComboBox.buttonFocusedBackground Color optional; defaults to ComboBox.focusedBackground
|
* @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.buttonArrowColor Color
|
||||||
* @uiDefault ComboBox.buttonDisabledArrowColor Color
|
* @uiDefault ComboBox.buttonDisabledArrowColor Color
|
||||||
* @uiDefault ComboBox.buttonHoverArrowColor Color
|
* @uiDefault ComboBox.buttonHoverArrowColor Color
|
||||||
@@ -107,42 +120,64 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
@StyleableField( cls=BasicComboBoxUI.class, key="padding" )
|
||||||
|
|
||||||
public class FlatComboBoxUI
|
public class FlatComboBoxUI
|
||||||
extends BasicComboBoxUI
|
extends BasicComboBoxUI
|
||||||
|
implements StyleableUI, StyleableLookupProvider
|
||||||
{
|
{
|
||||||
protected int minimumWidth;
|
@Styleable protected int minimumWidth;
|
||||||
protected int editorColumns;
|
@Styleable protected int editorColumns;
|
||||||
protected String buttonStyle;
|
@Styleable protected String buttonStyle;
|
||||||
protected String arrowType;
|
@Styleable protected String arrowType;
|
||||||
protected boolean isIntelliJTheme;
|
protected boolean isIntelliJTheme;
|
||||||
protected Color borderColor;
|
|
||||||
protected Color disabledBorderColor;
|
|
||||||
|
|
||||||
protected Color editableBackground;
|
private Color background;
|
||||||
protected Color focusedBackground;
|
@Styleable protected Color editableBackground;
|
||||||
protected Color disabledBackground;
|
@Styleable protected Color focusedBackground;
|
||||||
protected Color disabledForeground;
|
@Styleable protected Color disabledBackground;
|
||||||
|
@Styleable protected Color disabledForeground;
|
||||||
|
|
||||||
protected Color buttonBackground;
|
@Styleable protected Color buttonBackground;
|
||||||
protected Color buttonEditableBackground;
|
@Styleable protected Color buttonEditableBackground;
|
||||||
protected Color buttonFocusedBackground;
|
@Styleable protected Color buttonFocusedBackground;
|
||||||
protected Color buttonArrowColor;
|
/** @since 2 */ @Styleable protected float buttonSeparatorWidth;
|
||||||
protected Color buttonDisabledArrowColor;
|
/** @since 2 */ @Styleable protected Color buttonSeparatorColor;
|
||||||
protected Color buttonHoverArrowColor;
|
/** @since 2 */ @Styleable protected Color buttonDisabledSeparatorColor;
|
||||||
protected Color buttonPressedArrowColor;
|
@Styleable protected Color buttonArrowColor;
|
||||||
|
@Styleable protected Color buttonDisabledArrowColor;
|
||||||
|
@Styleable protected Color buttonHoverArrowColor;
|
||||||
|
@Styleable protected Color buttonPressedArrowColor;
|
||||||
|
|
||||||
protected Color popupBackground;
|
@Styleable protected Color popupBackground;
|
||||||
|
|
||||||
private MouseListener hoverListener;
|
private MouseListener hoverListener;
|
||||||
protected boolean hover;
|
protected boolean hover;
|
||||||
protected boolean pressed;
|
protected boolean pressed;
|
||||||
|
|
||||||
private WeakReference<Component> lastRendererComponent;
|
private CellPaddingBorder paddingBorder;
|
||||||
|
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
private AtomicBoolean borderShared;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatComboBoxUI();
|
return new FlatComboBoxUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
if( FlatUIUtils.needsLightAWTPeer( c ) )
|
||||||
|
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
|
||||||
|
else
|
||||||
|
installUIImpl( c );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installUIImpl( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installListeners() {
|
protected void installListeners() {
|
||||||
super.installListeners();
|
super.installListeners();
|
||||||
@@ -199,9 +234,8 @@ public class FlatComboBoxUI
|
|||||||
buttonStyle = UIManager.getString( "ComboBox.buttonStyle" );
|
buttonStyle = UIManager.getString( "ComboBox.buttonStyle" );
|
||||||
arrowType = UIManager.getString( "Component.arrowType" );
|
arrowType = UIManager.getString( "Component.arrowType" );
|
||||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
||||||
borderColor = UIManager.getColor( "Component.borderColor" );
|
|
||||||
disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
|
|
||||||
|
|
||||||
|
background = UIManager.getColor( "ComboBox.background" );
|
||||||
editableBackground = UIManager.getColor( "ComboBox.editableBackground" );
|
editableBackground = UIManager.getColor( "ComboBox.editableBackground" );
|
||||||
focusedBackground = UIManager.getColor( "ComboBox.focusedBackground" );
|
focusedBackground = UIManager.getColor( "ComboBox.focusedBackground" );
|
||||||
disabledBackground = UIManager.getColor( "ComboBox.disabledBackground" );
|
disabledBackground = UIManager.getColor( "ComboBox.disabledBackground" );
|
||||||
@@ -210,6 +244,9 @@ public class FlatComboBoxUI
|
|||||||
buttonBackground = UIManager.getColor( "ComboBox.buttonBackground" );
|
buttonBackground = UIManager.getColor( "ComboBox.buttonBackground" );
|
||||||
buttonFocusedBackground = UIManager.getColor( "ComboBox.buttonFocusedBackground" );
|
buttonFocusedBackground = UIManager.getColor( "ComboBox.buttonFocusedBackground" );
|
||||||
buttonEditableBackground = UIManager.getColor( "ComboBox.buttonEditableBackground" );
|
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" );
|
buttonArrowColor = UIManager.getColor( "ComboBox.buttonArrowColor" );
|
||||||
buttonDisabledArrowColor = UIManager.getColor( "ComboBox.buttonDisabledArrowColor" );
|
buttonDisabledArrowColor = UIManager.getColor( "ComboBox.buttonDisabledArrowColor" );
|
||||||
buttonHoverArrowColor = UIManager.getColor( "ComboBox.buttonHoverArrowColor" );
|
buttonHoverArrowColor = UIManager.getColor( "ComboBox.buttonHoverArrowColor" );
|
||||||
@@ -222,8 +259,7 @@ public class FlatComboBoxUI
|
|||||||
if( maximumRowCount > 0 && maximumRowCount != 8 && comboBox.getMaximumRowCount() == 8 )
|
if( maximumRowCount > 0 && maximumRowCount != 8 && comboBox.getMaximumRowCount() == 8 )
|
||||||
comboBox.setMaximumRowCount( maximumRowCount );
|
comboBox.setMaximumRowCount( maximumRowCount );
|
||||||
|
|
||||||
// scale
|
paddingBorder = new CellPaddingBorder( padding );
|
||||||
padding = UIScale.scale( padding );
|
|
||||||
|
|
||||||
MigLayoutVisualPadding.install( comboBox );
|
MigLayoutVisualPadding.install( comboBox );
|
||||||
}
|
}
|
||||||
@@ -232,9 +268,7 @@ public class FlatComboBoxUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
borderColor = null;
|
background = null;
|
||||||
disabledBorderColor = null;
|
|
||||||
|
|
||||||
editableBackground = null;
|
editableBackground = null;
|
||||||
focusedBackground = null;
|
focusedBackground = null;
|
||||||
disabledBackground = null;
|
disabledBackground = null;
|
||||||
@@ -243,6 +277,8 @@ public class FlatComboBoxUI
|
|||||||
buttonBackground = null;
|
buttonBackground = null;
|
||||||
buttonEditableBackground = null;
|
buttonEditableBackground = null;
|
||||||
buttonFocusedBackground = null;
|
buttonFocusedBackground = null;
|
||||||
|
buttonSeparatorColor = null;
|
||||||
|
buttonDisabledSeparatorColor = null;
|
||||||
buttonArrowColor = null;
|
buttonArrowColor = null;
|
||||||
buttonDisabledArrowColor = null;
|
buttonDisabledArrowColor = null;
|
||||||
buttonHoverArrowColor = null;
|
buttonHoverArrowColor = null;
|
||||||
@@ -250,6 +286,11 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
popupBackground = null;
|
popupBackground = null;
|
||||||
|
|
||||||
|
paddingBorder.uninstall();
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
borderShared = null;
|
||||||
|
|
||||||
MigLayoutVisualPadding.uninstall( comboBox );
|
MigLayoutVisualPadding.uninstall( comboBox );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,9 +301,16 @@ public class FlatComboBoxUI
|
|||||||
public void layoutContainer( Container parent ) {
|
public void layoutContainer( Container parent ) {
|
||||||
super.layoutContainer( parent );
|
super.layoutContainer( parent );
|
||||||
|
|
||||||
if( arrowButton != null ) {
|
// on macOS, a Swing combo box is used for AWT component java.awt.Choice
|
||||||
|
// and the font may be (temporary) null
|
||||||
|
|
||||||
|
if( arrowButton != null && comboBox.getFont() != 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();
|
Insets insets = getInsets();
|
||||||
int buttonWidth = parent.getPreferredSize().height - insets.top - insets.bottom;
|
int buttonWidth = Math.min( parent.getPreferredSize().height - insets.top - insets.bottom, maxButtonWidth );
|
||||||
if( buttonWidth != arrowButton.getWidth() ) {
|
if( buttonWidth != arrowButton.getWidth() ) {
|
||||||
// set width of arrow button to preferred height of combobox
|
// set width of arrow button to preferred height of combobox
|
||||||
int xOffset = comboBox.getComponentOrientation().isLeftToRight()
|
int xOffset = comboBox.getComponentOrientation().isLeftToRight()
|
||||||
@@ -276,11 +324,6 @@ public class FlatComboBoxUI
|
|||||||
editor.setBounds( rectangleForCurrentValue() );
|
editor.setBounds( rectangleForCurrentValue() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( editor != null && padding != null ) {
|
|
||||||
// fix editor bounds by subtracting padding
|
|
||||||
editor.setBounds( FlatUIUtils.subtractInsets( editor.getBounds(), padding ) );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -323,12 +366,30 @@ public class FlatComboBoxUI
|
|||||||
} else if( editor != null && source == comboBox && propertyName == "componentOrientation" ) {
|
} else if( editor != null && source == comboBox && propertyName == "componentOrientation" ) {
|
||||||
ComponentOrientation o = (ComponentOrientation) e.getNewValue();
|
ComponentOrientation o = (ComponentOrientation) e.getNewValue();
|
||||||
editor.applyComponentOrientation( o );
|
editor.applyComponentOrientation( o );
|
||||||
} else if( editor != null && FlatClientProperties.PLACEHOLDER_TEXT.equals( propertyName ) )
|
} else {
|
||||||
|
switch( propertyName ) {
|
||||||
|
case PLACEHOLDER_TEXT:
|
||||||
|
if( editor != null )
|
||||||
editor.repaint();
|
editor.repaint();
|
||||||
else if( FlatClientProperties.COMPONENT_ROUND_RECT.equals( propertyName ) )
|
break;
|
||||||
|
|
||||||
|
case COMPONENT_ROUND_RECT:
|
||||||
|
case OUTLINE:
|
||||||
comboBox.repaint();
|
comboBox.repaint();
|
||||||
else if( FlatClientProperties.MINIMUM_WIDTH.equals( propertyName ) )
|
break;
|
||||||
|
|
||||||
|
case MINIMUM_WIDTH:
|
||||||
comboBox.revalidate();
|
comboBox.revalidate();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STYLE:
|
||||||
|
case STYLE_CLASS:
|
||||||
|
installStyle();
|
||||||
|
comboBox.revalidate();
|
||||||
|
comboBox.repaint();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,14 +399,16 @@ public class FlatComboBoxUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ComboBoxEditor createEditor() {
|
protected void configureEditor() {
|
||||||
ComboBoxEditor comboBoxEditor = super.createEditor();
|
super.configureEditor();
|
||||||
|
|
||||||
Component editor = comboBoxEditor.getEditorComponent();
|
|
||||||
if( editor instanceof JTextField ) {
|
if( editor instanceof JTextField ) {
|
||||||
JTextField textField = (JTextField) editor;
|
JTextField textField = (JTextField) editor;
|
||||||
textField.setColumns( editorColumns );
|
textField.setColumns( editorColumns );
|
||||||
|
|
||||||
|
// 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,
|
// 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
|
// otherwise it is replaced with default text field border when switching LaF
|
||||||
// because javax.swing.plaf.basic.BasicComboBoxEditor.BorderlessTextField.setBorder()
|
// because javax.swing.plaf.basic.BasicComboBoxEditor.BorderlessTextField.setBorder()
|
||||||
@@ -353,24 +416,15 @@ public class FlatComboBoxUI
|
|||||||
// instead of "border instanceof javax.swing.plaf.UIResource"
|
// instead of "border instanceof javax.swing.plaf.UIResource"
|
||||||
textField.setBorder( BorderFactory.createEmptyBorder() );
|
textField.setBorder( BorderFactory.createEmptyBorder() );
|
||||||
}
|
}
|
||||||
|
|
||||||
return comboBoxEditor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void configureEditor() {
|
|
||||||
super.configureEditor();
|
|
||||||
|
|
||||||
// remove default text field border from editor
|
|
||||||
if( editor instanceof JTextField && ((JTextField)editor).getBorder() instanceof FlatTextBorder )
|
|
||||||
((JTextField)editor).setBorder( BorderFactory.createEmptyBorder() );
|
|
||||||
|
|
||||||
// explicitly make non-opaque
|
// explicitly make non-opaque
|
||||||
if( editor instanceof JComponent )
|
if( editor instanceof JComponent )
|
||||||
((JComponent)editor).setOpaque( false );
|
((JComponent)editor).setOpaque( false );
|
||||||
|
|
||||||
editor.applyComponentOrientation( comboBox.getComponentOrientation() );
|
editor.applyComponentOrientation( comboBox.getComponentOrientation() );
|
||||||
|
|
||||||
|
updateEditorPadding();
|
||||||
updateEditorColors();
|
updateEditorColors();
|
||||||
|
|
||||||
// macOS
|
// macOS
|
||||||
@@ -387,6 +441,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() {
|
private void updateEditorColors() {
|
||||||
// use non-UIResource colors because when SwingUtilities.updateComponentTreeUI()
|
// use non-UIResource colors because when SwingUtilities.updateComponentTreeUI()
|
||||||
// is used, then the editor is updated after the combobox and the
|
// is used, then the editor is updated after the combobox and the
|
||||||
@@ -403,6 +476,61 @@ public class FlatComboBoxUI
|
|||||||
return new FlatComboBoxButton();
|
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 ) {
|
||||||
|
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 ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this, comboBox.getBorder() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, comboBox.getBorder(), key );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public MethodHandles.Lookup getLookupForStyling() {
|
||||||
|
// MethodHandles.lookup() is caller sensitive and must be invoked in this class,
|
||||||
|
// otherwise it is not possible to access protected fields in JRE superclass
|
||||||
|
return MethodHandles.lookup();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update( Graphics g, JComponent c ) {
|
public void update( Graphics g, JComponent c ) {
|
||||||
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
|
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
|
||||||
@@ -439,11 +567,13 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
// paint arrow button background
|
// paint arrow button background
|
||||||
if( enabled && !isCellRenderer ) {
|
if( enabled && !isCellRenderer ) {
|
||||||
g2.setColor( paintButton
|
Color buttonColor = paintButton
|
||||||
? buttonEditableBackground
|
? buttonEditableBackground
|
||||||
: (buttonFocusedBackground != null || focusedBackground != null) && isPermanentFocusOwner( comboBox )
|
: (buttonFocusedBackground != null || focusedBackground != null) && isPermanentFocusOwner( comboBox )
|
||||||
? (buttonFocusedBackground != null ? buttonFocusedBackground : focusedBackground)
|
? (buttonFocusedBackground != null ? buttonFocusedBackground : focusedBackground)
|
||||||
: buttonBackground );
|
: buttonBackground;
|
||||||
|
if( buttonColor != null ) {
|
||||||
|
g2.setColor( buttonColor );
|
||||||
Shape oldClip = g2.getClip();
|
Shape oldClip = g2.getClip();
|
||||||
if( isLeftToRight )
|
if( isLeftToRight )
|
||||||
g2.clipRect( arrowX, 0, width - arrowX, height );
|
g2.clipRect( arrowX, 0, width - arrowX, height );
|
||||||
@@ -452,15 +582,19 @@ public class FlatComboBoxUI
|
|||||||
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
|
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
|
||||||
g2.setClip( oldClip );
|
g2.setClip( oldClip );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// paint vertical line between value and arrow button
|
// paint vertical line between value and arrow button
|
||||||
if( paintButton ) {
|
if( paintButton ) {
|
||||||
g2.setColor( enabled ? borderColor : disabledBorderColor );
|
Color separatorColor = enabled ? buttonSeparatorColor : buttonDisabledSeparatorColor;
|
||||||
float lw = scale( 1f );
|
if( separatorColor != null && buttonSeparatorWidth > 0 ) {
|
||||||
|
g2.setColor( separatorColor );
|
||||||
|
float lw = scale( buttonSeparatorWidth );
|
||||||
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
|
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
|
||||||
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2)) );
|
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2)) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// avoid that the "current value" renderer is invoked with enabled antialiasing
|
// avoid that the "current value" renderer is invoked with enabled antialiasing
|
||||||
FlatUIUtils.resetRenderingHints( g2, oldRenderingHints );
|
FlatUIUtils.resetRenderingHints( g2, oldRenderingHints );
|
||||||
@@ -471,30 +605,33 @@ public class FlatComboBoxUI
|
|||||||
@Override
|
@Override
|
||||||
@SuppressWarnings( "unchecked" )
|
@SuppressWarnings( "unchecked" )
|
||||||
public void paintCurrentValue( Graphics g, Rectangle bounds, boolean hasFocus ) {
|
public void paintCurrentValue( Graphics g, Rectangle bounds, boolean hasFocus ) {
|
||||||
|
paddingBorder.uninstall();
|
||||||
|
|
||||||
ListCellRenderer<Object> renderer = comboBox.getRenderer();
|
ListCellRenderer<Object> renderer = comboBox.getRenderer();
|
||||||
uninstallCellPaddingBorder( renderer );
|
|
||||||
if( renderer == null )
|
if( renderer == null )
|
||||||
renderer = new DefaultListCellRenderer();
|
renderer = new DefaultListCellRenderer();
|
||||||
Component c = renderer.getListCellRendererComponent( listBox, comboBox.getSelectedItem(), -1, false, false );
|
Component c = renderer.getListCellRendererComponent( listBox, comboBox.getSelectedItem(), -1, false, false );
|
||||||
c.setFont( comboBox.getFont() );
|
c.setFont( comboBox.getFont() );
|
||||||
c.applyComponentOrientation( comboBox.getComponentOrientation() );
|
c.applyComponentOrientation( comboBox.getComponentOrientation() );
|
||||||
uninstallCellPaddingBorder( c );
|
|
||||||
|
|
||||||
boolean enabled = comboBox.isEnabled();
|
boolean enabled = comboBox.isEnabled();
|
||||||
c.setBackground( getBackground( enabled ) );
|
c.setBackground( getBackground( enabled ) );
|
||||||
c.setForeground( getForeground( 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);
|
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, 0 );
|
||||||
currentValuePane.paintComponent( g, c, comboBox, bounds.x, bounds.y, bounds.width, bounds.height, shouldValidate );
|
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
|
@Override
|
||||||
@@ -504,6 +641,9 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
protected Color getBackground( boolean enabled ) {
|
protected Color getBackground( boolean enabled ) {
|
||||||
if( enabled ) {
|
if( enabled ) {
|
||||||
|
if( FlatUIUtils.isAWTPeer( comboBox ) )
|
||||||
|
return background;
|
||||||
|
|
||||||
Color background = comboBox.getBackground();
|
Color background = comboBox.getBackground();
|
||||||
|
|
||||||
// always use explicitly set color
|
// always use explicitly set color
|
||||||
@@ -526,77 +666,49 @@ public class FlatComboBoxUI
|
|||||||
@Override
|
@Override
|
||||||
public Dimension getMinimumSize( JComponent c ) {
|
public Dimension getMinimumSize( JComponent c ) {
|
||||||
Dimension minimumSize = super.getMinimumSize( 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;
|
return minimumSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Dimension getDefaultSize() {
|
protected Dimension getDefaultSize() {
|
||||||
@SuppressWarnings( "unchecked" )
|
paddingBorder.uninstall();
|
||||||
ListCellRenderer<Object> renderer = comboBox.getRenderer();
|
|
||||||
uninstallCellPaddingBorder( renderer );
|
|
||||||
|
|
||||||
Dimension size = super.getDefaultSize();
|
Dimension size = super.getDefaultSize();
|
||||||
|
paddingBorder.uninstall();
|
||||||
uninstallCellPaddingBorder( renderer );
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Dimension getDisplaySize() {
|
protected Dimension getDisplaySize() {
|
||||||
@SuppressWarnings( "unchecked" )
|
paddingBorder.uninstall();
|
||||||
ListCellRenderer<Object> renderer = comboBox.getRenderer();
|
|
||||||
uninstallCellPaddingBorder( renderer );
|
|
||||||
|
|
||||||
Dimension displaySize = super.getDisplaySize();
|
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
|
// recalculate width without hardcoded 100 under special conditions
|
||||||
if( displaySize.width == 100 + padding.left + padding.right &&
|
if( displayWidth == 100 &&
|
||||||
comboBox.isEditable() &&
|
comboBox.isEditable() &&
|
||||||
comboBox.getItemCount() == 0 &&
|
comboBox.getItemCount() == 0 &&
|
||||||
comboBox.getPrototypeDisplayValue() == null )
|
comboBox.getPrototypeDisplayValue() == null )
|
||||||
{
|
{
|
||||||
int width = getDefaultSize().width;
|
displayWidth = Math.max( getDefaultSize().width, editor.getPreferredSize().width );
|
||||||
width = Math.max( width, editor.getPreferredSize().width );
|
|
||||||
width += padding.left + padding.right;
|
|
||||||
displaySize = new Dimension( width, displaySize.height );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uninstallCellPaddingBorder( renderer );
|
return new Dimension( displayWidth, displayHeight );
|
||||||
return displaySize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Dimension getSizeForComponent( Component comp ) {
|
protected Dimension getSizeForComponent( Component comp ) {
|
||||||
|
paddingBorder.install( comp, 0 );
|
||||||
Dimension size = super.getSizeForComponent( comp );
|
Dimension size = super.getSizeForComponent( comp );
|
||||||
|
paddingBorder.uninstall();
|
||||||
// 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 );
|
|
||||||
|
|
||||||
return size;
|
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 void uninstallCellPaddingBorder( Object o ) {
|
|
||||||
CellPaddingBorder.uninstall( o );
|
|
||||||
if( lastRendererComponent != null ) {
|
|
||||||
CellPaddingBorder.uninstall( lastRendererComponent );
|
|
||||||
lastRendererComponent = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isCellRenderer() {
|
private boolean isCellRenderer() {
|
||||||
return comboBox.getParent() instanceof CellRendererPane;
|
return comboBox.getParent() instanceof CellRendererPane;
|
||||||
}
|
}
|
||||||
@@ -607,13 +719,14 @@ public class FlatComboBoxUI
|
|||||||
return parentParent != null && !comboBox.getBackground().equals( parentParent.getBackground() );
|
return parentParent != null && !comboBox.getBackground().equals( parentParent.getBackground() );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @since 1.3 */
|
||||||
* @since 1.3
|
|
||||||
*/
|
|
||||||
public static boolean isPermanentFocusOwner( JComboBox<?> comboBox ) {
|
public static boolean isPermanentFocusOwner( JComboBox<?> comboBox ) {
|
||||||
if( comboBox.isEditable() ) {
|
if( comboBox.isEditable() ) {
|
||||||
|
if( FlatUIUtils.isPermanentFocusOwner( comboBox ) )
|
||||||
|
return true;
|
||||||
|
|
||||||
Component editorComponent = comboBox.getEditor().getEditorComponent();
|
Component editorComponent = comboBox.getEditor().getEditorComponent();
|
||||||
return (editorComponent != null) ? FlatUIUtils.isPermanentFocusOwner( editorComponent ) : false;
|
return editorComponent != null && FlatUIUtils.isPermanentFocusOwner( editorComponent );
|
||||||
} else
|
} else
|
||||||
return FlatUIUtils.isPermanentFocusOwner( comboBox );
|
return FlatUIUtils.isPermanentFocusOwner( comboBox );
|
||||||
}
|
}
|
||||||
@@ -635,6 +748,11 @@ public class FlatComboBoxUI
|
|||||||
hoverForeground, hoverBackground, pressedForeground, pressedBackground );
|
hoverForeground, hoverBackground, pressedForeground, pressedBackground );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void updateStyle() {
|
||||||
|
updateStyle( arrowType, buttonArrowColor, buttonDisabledArrowColor,
|
||||||
|
buttonHoverArrowColor, null, buttonPressedArrowColor, null );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isHover() {
|
protected boolean isHover() {
|
||||||
return super.isHover() || (!comboBox.isEditable() ? hover : false);
|
return super.isHover() || (!comboBox.isEditable() ? hover : false);
|
||||||
@@ -660,13 +778,11 @@ public class FlatComboBoxUI
|
|||||||
protected class FlatComboPopup
|
protected class FlatComboPopup
|
||||||
extends BasicComboPopup
|
extends BasicComboPopup
|
||||||
{
|
{
|
||||||
private CellPaddingBorder paddingBorder;
|
|
||||||
|
|
||||||
protected FlatComboPopup( JComboBox combo ) {
|
protected FlatComboPopup( JComboBox combo ) {
|
||||||
super( combo );
|
super( combo );
|
||||||
|
|
||||||
// BasicComboPopup listens to JComboBox.componentOrientation and updates
|
// 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
|
// switching the LaF and a new combo popup is created, the component
|
||||||
// orientation is not applied.
|
// orientation is not applied.
|
||||||
ComponentOrientation o = comboBox.getComponentOrientation();
|
ComponentOrientation o = comboBox.getComponentOrientation();
|
||||||
@@ -720,9 +836,15 @@ public class FlatComboBoxUI
|
|||||||
protected void configurePopup() {
|
protected void configurePopup() {
|
||||||
super.configurePopup();
|
super.configurePopup();
|
||||||
|
|
||||||
|
// make opaque to avoid that background shines thru border (e.g. at 150% scaling)
|
||||||
|
setOpaque( true );
|
||||||
|
|
||||||
|
// set popup border
|
||||||
|
// use non-UIResource to avoid that it is overwritten when making
|
||||||
|
// popup visible (see JPopupMenu.setInvoker()) in theme editor preview
|
||||||
Border border = UIManager.getBorder( "PopupMenu.border" );
|
Border border = UIManager.getBorder( "PopupMenu.border" );
|
||||||
if( border != null )
|
if( border != null )
|
||||||
setBorder( border );
|
setBorder( FlatUIUtils.nonUIResource( border ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -730,8 +852,17 @@ public class FlatComboBoxUI
|
|||||||
super.configureList();
|
super.configureList();
|
||||||
|
|
||||||
list.setCellRenderer( new PopupListCellRenderer() );
|
list.setCellRenderer( new PopupListCellRenderer() );
|
||||||
|
updateStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateStyle() {
|
||||||
if( popupBackground != null )
|
if( popupBackground != null )
|
||||||
list.setBackground( popupBackground );
|
list.setBackground( popupBackground );
|
||||||
|
|
||||||
|
// set popup background because it may shine thru when scaled (e.g. at 150%)
|
||||||
|
// use non-UIResource to avoid that it is overwritten when making
|
||||||
|
// popup visible (see JPopupMenu.setInvoker()) in theme editor preview
|
||||||
|
setBackground( FlatUIUtils.nonUIResource( list.getBackground() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -745,6 +876,34 @@ public class FlatComboBoxUI
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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 -----
|
//---- class PopupListCellRenderer -----
|
||||||
|
|
||||||
private class PopupListCellRenderer
|
private class PopupListCellRenderer
|
||||||
@@ -754,22 +913,15 @@ public class FlatComboBoxUI
|
|||||||
public Component getListCellRendererComponent( JList list, Object value,
|
public Component getListCellRendererComponent( JList list, Object value,
|
||||||
int index, boolean isSelected, boolean cellHasFocus )
|
int index, boolean isSelected, boolean cellHasFocus )
|
||||||
{
|
{
|
||||||
ListCellRenderer renderer = comboBox.getRenderer();
|
paddingBorder.uninstall();
|
||||||
CellPaddingBorder.uninstall( renderer );
|
|
||||||
CellPaddingBorder.uninstall( lastRendererComponent );
|
|
||||||
|
|
||||||
|
ListCellRenderer renderer = comboBox.getRenderer();
|
||||||
if( renderer == null )
|
if( renderer == null )
|
||||||
renderer = new DefaultListCellRenderer();
|
renderer = new DefaultListCellRenderer();
|
||||||
Component c = renderer.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
|
Component c = renderer.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
|
||||||
c.applyComponentOrientation( comboBox.getComponentOrientation() );
|
c.applyComponentOrientation( comboBox.getComponentOrientation() );
|
||||||
|
|
||||||
if( c instanceof JComponent ) {
|
paddingBorder.install( c, Math.round( FlatUIUtils.getBorderFocusWidth( comboBox ) ) );
|
||||||
if( paddingBorder == null )
|
|
||||||
paddingBorder = new CellPaddingBorder( padding );
|
|
||||||
paddingBorder.install( (JComponent) c );
|
|
||||||
}
|
|
||||||
|
|
||||||
lastRendererComponent = (c != renderer) ? new WeakReference<>( c ) : null;
|
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
@@ -779,50 +931,75 @@ public class FlatComboBoxUI
|
|||||||
//---- class CellPaddingBorder --------------------------------------------
|
//---- 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,
|
* 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.
|
* which vertically aligns text in popup list with text in combobox.
|
||||||
*
|
* <p>
|
||||||
* The renderer border is painted on the outside of this border.
|
* The renderer border is painted on the outer side of this border.
|
||||||
*/
|
*/
|
||||||
private static class CellPaddingBorder
|
private static class CellPaddingBorder
|
||||||
extends AbstractBorder
|
extends AbstractBorder
|
||||||
{
|
{
|
||||||
private final Insets padding;
|
private Insets padding;
|
||||||
|
private JComponent rendererComponent;
|
||||||
private Border rendererBorder;
|
private Border rendererBorder;
|
||||||
|
private int focusWidth;
|
||||||
|
|
||||||
CellPaddingBorder( Insets padding ) {
|
CellPaddingBorder( Insets padding ) {
|
||||||
this.padding = padding;
|
this.padding = padding;
|
||||||
}
|
}
|
||||||
|
|
||||||
void install( JComponent rendererComponent ) {
|
// using synchronized to avoid problems with code that modifies combo box
|
||||||
Border oldBorder = rendererComponent.getBorder();
|
// (model, selection, etc) not on AWT thread (which should be not done)
|
||||||
if( !(oldBorder instanceof CellPaddingBorder) ) {
|
synchronized void install( Component c, int focusWidth ) {
|
||||||
rendererBorder = oldBorder;
|
if( !(c instanceof JComponent) )
|
||||||
rendererComponent.setBorder( this );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void uninstall( Object o ) {
|
|
||||||
if( o instanceof WeakReference )
|
|
||||||
o = ((WeakReference<?>)o).get();
|
|
||||||
|
|
||||||
if( !(o instanceof JComponent) )
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
JComponent rendererComponent = (JComponent) o;
|
this.focusWidth = focusWidth;
|
||||||
Border border = rendererComponent.getBorder();
|
|
||||||
if( border instanceof CellPaddingBorder ) {
|
JComponent jc = (JComponent) c;
|
||||||
CellPaddingBorder paddingBorder = (CellPaddingBorder) border;
|
Border oldBorder = jc.getBorder();
|
||||||
rendererComponent.setBorder( paddingBorder.rendererBorder );
|
if( oldBorder == this )
|
||||||
paddingBorder.rendererBorder = null;
|
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.
|
||||||
|
*/
|
||||||
|
synchronized void uninstall() {
|
||||||
|
if( rendererComponent == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( rendererComponent.getBorder() == this )
|
||||||
|
rendererComponent.setBorder( rendererBorder );
|
||||||
|
rendererComponent = null;
|
||||||
|
rendererBorder = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
synchronized public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
if( rendererBorder != null ) {
|
Insets padding = scale( this.padding );
|
||||||
|
if( rendererBorder != null && !(rendererBorder instanceof CellPaddingBorder) ) {
|
||||||
Insets insideInsets = rendererBorder.getBorderInsets( c );
|
Insets insideInsets = rendererBorder.getBorderInsets( c );
|
||||||
insets.top = Math.max( padding.top, insideInsets.top );
|
insets.top = Math.max( padding.top, insideInsets.top );
|
||||||
insets.left = Math.max( padding.left, insideInsets.left );
|
insets.left = Math.max( padding.left, insideInsets.left );
|
||||||
@@ -834,6 +1011,12 @@ public class FlatComboBoxUI
|
|||||||
insets.bottom = padding.bottom;
|
insets.bottom = padding.bottom;
|
||||||
insets.right = padding.right;
|
insets.right = padding.right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if used in popup list, add focus width for exact vertical alignment
|
||||||
|
// of text in popup list with text in combobox
|
||||||
|
insets.left += focusWidth;
|
||||||
|
insets.right += focusWidth;
|
||||||
|
|
||||||
return insets;
|
return insets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ import java.awt.Image;
|
|||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.RadialGradientPaint;
|
import java.awt.RadialGradientPaint;
|
||||||
import java.awt.image.BufferedImage;
|
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.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -40,14 +43,17 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*/
|
*/
|
||||||
public class FlatDropShadowBorder
|
public class FlatDropShadowBorder
|
||||||
extends FlatEmptyBorder
|
extends FlatEmptyBorder
|
||||||
|
implements StyleableBorder
|
||||||
{
|
{
|
||||||
private final Color shadowColor;
|
@Styleable protected Color shadowColor;
|
||||||
private final Insets shadowInsets;
|
@Styleable protected Insets shadowInsets;
|
||||||
private final float shadowOpacity;
|
@Styleable protected float shadowOpacity;
|
||||||
|
|
||||||
private final int shadowSize;
|
private int shadowSize;
|
||||||
private Image shadowImage;
|
private Image shadowImage;
|
||||||
private Color lastShadowColor;
|
private Color lastShadowColor;
|
||||||
|
private float lastShadowOpacity;
|
||||||
|
private int lastShadowSize;
|
||||||
private double lastSystemScaleFactor;
|
private double lastSystemScaleFactor;
|
||||||
private float lastUserScaleFactor;
|
private float lastUserScaleFactor;
|
||||||
|
|
||||||
@@ -64,17 +70,49 @@ public class FlatDropShadowBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
public FlatDropShadowBorder( Color shadowColor, Insets shadowInsets, float shadowOpacity ) {
|
public FlatDropShadowBorder( Color shadowColor, Insets shadowInsets, float shadowOpacity ) {
|
||||||
super( Math.max( shadowInsets.top, 0 ), Math.max( shadowInsets.left, 0 ),
|
super( nonNegativeInsets( shadowInsets ) );
|
||||||
Math.max( shadowInsets.bottom, 0 ), Math.max( shadowInsets.right, 0 ) );
|
|
||||||
this.shadowColor = shadowColor;
|
this.shadowColor = shadowColor;
|
||||||
this.shadowInsets = shadowInsets;
|
this.shadowInsets = shadowInsets;
|
||||||
this.shadowOpacity = shadowOpacity;
|
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.left, shadowInsets.right ),
|
||||||
Math.max( shadowInsets.top, shadowInsets.bottom ) );
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
if( shadowSize <= 0 )
|
if( shadowSize <= 0 )
|
||||||
@@ -91,12 +129,16 @@ public class FlatDropShadowBorder
|
|||||||
float userScaleFactor = UIScale.getUserScaleFactor();
|
float userScaleFactor = UIScale.getUserScaleFactor();
|
||||||
if( shadowImage == null ||
|
if( shadowImage == null ||
|
||||||
!shadowColor.equals( lastShadowColor ) ||
|
!shadowColor.equals( lastShadowColor ) ||
|
||||||
|
lastShadowOpacity != shadowOpacity ||
|
||||||
|
lastShadowSize != shadowSize ||
|
||||||
lastSystemScaleFactor != scaleFactor ||
|
lastSystemScaleFactor != scaleFactor ||
|
||||||
lastUserScaleFactor != userScaleFactor )
|
lastUserScaleFactor != userScaleFactor )
|
||||||
{
|
{
|
||||||
shadowImage = createShadowImage( shadowColor, shadowSize, shadowOpacity,
|
shadowImage = createShadowImage( shadowColor, shadowSize, shadowOpacity,
|
||||||
(float) (scaleFactor * userScaleFactor) );
|
(float) (scaleFactor * userScaleFactor) );
|
||||||
lastShadowColor = shadowColor;
|
lastShadowColor = shadowColor;
|
||||||
|
lastShadowOpacity = shadowOpacity;
|
||||||
|
lastShadowSize = shadowSize;
|
||||||
lastSystemScaleFactor = scaleFactor;
|
lastSystemScaleFactor = scaleFactor;
|
||||||
lastUserScaleFactor = userScaleFactor;
|
lastUserScaleFactor = userScaleFactor;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,16 +21,22 @@ import java.awt.Color;
|
|||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Insets;
|
||||||
import java.awt.event.FocusListener;
|
import java.awt.event.FocusListener;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JEditorPane;
|
import javax.swing.JEditorPane;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicEditorPaneUI;
|
import javax.swing.plaf.basic.BasicEditorPaneUI;
|
||||||
|
import javax.swing.text.Caret;
|
||||||
import javax.swing.text.JTextComponent;
|
import javax.swing.text.JTextComponent;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
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.HiDPIUtils;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JEditorPane}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JEditorPane}.
|
||||||
@@ -38,8 +44,8 @@ import com.formdev.flatlaf.util.HiDPIUtils;
|
|||||||
* <!-- BasicEditorPaneUI -->
|
* <!-- BasicEditorPaneUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault EditorPane.font Font
|
* @uiDefault EditorPane.font Font
|
||||||
* @uiDefault EditorPane.background Color also used if not editable
|
* @uiDefault EditorPane.background Color
|
||||||
* @uiDefault EditorPane.foreground Color
|
* @uiDefault EditorPane.foreground Color also used if not editable
|
||||||
* @uiDefault EditorPane.caretForeground Color
|
* @uiDefault EditorPane.caretForeground Color
|
||||||
* @uiDefault EditorPane.selectionBackground Color
|
* @uiDefault EditorPane.selectionBackground Color
|
||||||
* @uiDefault EditorPane.selectionForeground Color
|
* @uiDefault EditorPane.selectionForeground Color
|
||||||
@@ -60,18 +66,35 @@ import com.formdev.flatlaf.util.HiDPIUtils;
|
|||||||
*/
|
*/
|
||||||
public class FlatEditorPaneUI
|
public class FlatEditorPaneUI
|
||||||
extends BasicEditorPaneUI
|
extends BasicEditorPaneUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
protected int minimumWidth;
|
@Styleable protected int minimumWidth;
|
||||||
protected boolean isIntelliJTheme;
|
protected boolean isIntelliJTheme;
|
||||||
protected Color focusedBackground;
|
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 Object oldHonorDisplayProperties;
|
||||||
private FocusListener focusListener;
|
private FocusListener focusListener;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatEditorPaneUI();
|
return new FlatEditorPaneUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
@@ -79,8 +102,13 @@ public class FlatEditorPaneUI
|
|||||||
String prefix = getPropertyPrefix();
|
String prefix = getPropertyPrefix();
|
||||||
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
||||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
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" );
|
focusedBackground = UIManager.getColor( prefix + ".focusedBackground" );
|
||||||
|
|
||||||
|
defaultMargin = UIManager.getInsets( prefix + ".margin" );
|
||||||
|
|
||||||
// use component font and foreground for HTML text
|
// use component font and foreground for HTML text
|
||||||
oldHonorDisplayProperties = getComponent().getClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES );
|
oldHonorDisplayProperties = getComponent().getClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES );
|
||||||
getComponent().putClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES, true );
|
getComponent().putClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES, true );
|
||||||
@@ -90,8 +118,16 @@ public class FlatEditorPaneUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
background = null;
|
||||||
|
disabledBackground = null;
|
||||||
|
inactiveBackground = null;
|
||||||
focusedBackground = null;
|
focusedBackground = null;
|
||||||
|
|
||||||
|
oldDisabledBackground = null;
|
||||||
|
oldInactiveBackground = null;
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
|
||||||
getComponent().putClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES, oldHonorDisplayProperties );
|
getComponent().putClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES, oldHonorDisplayProperties );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,30 +149,93 @@ public class FlatEditorPaneUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void propertyChange( PropertyChangeEvent e ) {
|
protected Caret createCaret() {
|
||||||
super.propertyChange( e );
|
return new FlatCaret( null, false );
|
||||||
propertyChange( getComponent(), e );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void propertyChange( JTextComponent c, PropertyChangeEvent e ) {
|
@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() ) {
|
switch( e.getPropertyName() ) {
|
||||||
case FlatClientProperties.MINIMUM_WIDTH:
|
case FlatClientProperties.MINIMUM_WIDTH:
|
||||||
c.revalidate();
|
c.revalidate();
|
||||||
break;
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateBackground() {
|
||||||
|
FlatTextFieldUI.updateBackground( getComponent(), background,
|
||||||
|
disabledBackground, inactiveBackground,
|
||||||
|
oldDisabledBackground, oldInactiveBackground );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
return applyMinimumWidth( c, super.getPreferredSize( c ), minimumWidth );
|
return applyMinimumWidth( c, super.getPreferredSize( c ), minimumWidth, defaultMargin );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getMinimumSize( JComponent c ) {
|
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)
|
// Assume that text area is in a scroll pane (that displays the border)
|
||||||
// and subtract 1px border line width.
|
// and subtract 1px border line width.
|
||||||
// Using "(scale( 1 ) * 2)" instead of "scale( 2 )" to deal with rounding
|
// Using "(scale( 1 ) * 2)" instead of "scale( 2 )" to deal with rounding
|
||||||
|
|||||||
@@ -50,7 +50,13 @@ public class FlatEmptyBorder
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
boolean leftToRight = left == right || c.getComponentOrientation().isLeftToRight();
|
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 == null || c.getComponentOrientation().isLeftToRight();
|
||||||
insets.left = scale( leftToRight ? left : right );
|
insets.left = scale( leftToRight ? left : right );
|
||||||
insets.top = scale( top );
|
insets.top = scale( top );
|
||||||
insets.right = scale( leftToRight ? right : left );
|
insets.right = scale( leftToRight ? right : left );
|
||||||
@@ -61,4 +67,18 @@ public class FlatEmptyBorder
|
|||||||
public Insets getUnscaledBorderInsets() {
|
public Insets getUnscaledBorderInsets() {
|
||||||
return super.getBorderInsets();
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
public Insets getStyleableValue() {
|
||||||
|
return new Insets( top, left, bottom, right );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,11 +19,21 @@ package com.formdev.flatlaf.ui;
|
|||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Image;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
|
import java.awt.LayoutManager;
|
||||||
|
import java.awt.RenderingHints;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.function.Function;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
|
import javax.swing.ButtonGroup;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
@@ -34,12 +44,16 @@ import javax.swing.JPanel;
|
|||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
import javax.swing.JToggleButton;
|
import javax.swing.JToggleButton;
|
||||||
|
import javax.swing.JToolBar;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.filechooser.FileSystemView;
|
||||||
import javax.swing.filechooser.FileView;
|
import javax.swing.filechooser.FileView;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.metal.MetalFileChooserUI;
|
import javax.swing.plaf.metal.MetalFileChooserUI;
|
||||||
import javax.swing.table.TableCellRenderer;
|
import javax.swing.table.TableCellRenderer;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.ScaledImageIcon;
|
import com.formdev.flatlaf.util.ScaledImageIcon;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
@@ -133,12 +147,21 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault FileChooser.listViewActionLabelText String
|
* @uiDefault FileChooser.listViewActionLabelText String
|
||||||
* @uiDefault FileChooser.detailsViewActionLabelText String
|
* @uiDefault FileChooser.detailsViewActionLabelText String
|
||||||
*
|
*
|
||||||
|
* <!-- FlatFileChooserUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault FileChooser.shortcuts.buttonSize Dimension optional; default is 84,64
|
||||||
|
* @uiDefault FileChooser.shortcuts.iconSize Dimension optional; default is 32,32
|
||||||
|
* @uiDefault FileChooser.shortcuts.filesFunction Function<File[], File[]>
|
||||||
|
* @uiDefault FileChooser.shortcuts.displayNameFunction Function<File, String>
|
||||||
|
* @uiDefault FileChooser.shortcuts.iconFunction Function<File, Icon>
|
||||||
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatFileChooserUI
|
public class FlatFileChooserUI
|
||||||
extends MetalFileChooserUI
|
extends MetalFileChooserUI
|
||||||
{
|
{
|
||||||
private final FlatFileView fileView = new FlatFileView();
|
private final FlatFileView fileView = new FlatFileView();
|
||||||
|
private FlatShortcutsPanel shortcutsPanel;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatFileChooserUI( (JFileChooser) c );
|
return new FlatFileChooserUI( (JFileChooser) c );
|
||||||
@@ -153,6 +176,25 @@ public class FlatFileChooserUI
|
|||||||
super.installComponents( fc );
|
super.installComponents( fc );
|
||||||
|
|
||||||
patchUI( fc );
|
patchUI( fc );
|
||||||
|
|
||||||
|
if( !UIManager.getBoolean( "FileChooser.noPlacesBar" ) ) { // same as in Windows L&F
|
||||||
|
FlatShortcutsPanel panel = createShortcutsPanel( fc );
|
||||||
|
if( panel.getComponentCount() > 0 ) {
|
||||||
|
shortcutsPanel = panel;
|
||||||
|
fc.add( shortcutsPanel, BorderLayout.LINE_START );
|
||||||
|
fc.addPropertyChangeListener( shortcutsPanel );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uninstallComponents( JFileChooser fc ) {
|
||||||
|
super.uninstallComponents( fc );
|
||||||
|
|
||||||
|
if( shortcutsPanel != null ) {
|
||||||
|
fc.removePropertyChangeListener( shortcutsPanel );
|
||||||
|
shortcutsPanel = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void patchUI( JFileChooser fc ) {
|
private void patchUI( JFileChooser fc ) {
|
||||||
@@ -192,6 +234,25 @@ public class FlatFileChooserUI
|
|||||||
} catch( ArrayIndexOutOfBoundsException ex ) {
|
} catch( ArrayIndexOutOfBoundsException ex ) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// put north, center and south components into a new panel so that
|
||||||
|
// the shortcuts panel (at west) gets full height
|
||||||
|
LayoutManager layout = fc.getLayout();
|
||||||
|
if( layout instanceof BorderLayout ) {
|
||||||
|
BorderLayout borderLayout = (BorderLayout) layout;
|
||||||
|
borderLayout.setHgap( 8 );
|
||||||
|
|
||||||
|
Component north = borderLayout.getLayoutComponent( BorderLayout.NORTH );
|
||||||
|
Component center = borderLayout.getLayoutComponent( BorderLayout.CENTER );
|
||||||
|
Component south = borderLayout.getLayoutComponent( BorderLayout.SOUTH );
|
||||||
|
if( north != null && center != null && south != null ) {
|
||||||
|
JPanel p = new JPanel( new BorderLayout( 0, 11 ) );
|
||||||
|
p.add( north, BorderLayout.NORTH );
|
||||||
|
p.add( center, BorderLayout.CENTER );
|
||||||
|
p.add( south, BorderLayout.SOUTH );
|
||||||
|
fc.add( p, BorderLayout.CENTER );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -250,9 +311,19 @@ public class FlatFileChooserUI
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.3 */
|
||||||
|
protected FlatShortcutsPanel createShortcutsPanel( JFileChooser fc ) {
|
||||||
|
return new FlatShortcutsPanel( fc );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
return UIScale.scale( super.getPreferredSize( c ) );
|
Dimension prefSize = super.getPreferredSize( c );
|
||||||
|
Dimension minSize = getMinimumSize( c );
|
||||||
|
int shortcutsPanelWidth = (shortcutsPanel != null) ? shortcutsPanel.getPreferredSize().width : 0;
|
||||||
|
return new Dimension(
|
||||||
|
Math.max( prefSize.width, minSize.width + shortcutsPanelWidth ),
|
||||||
|
Math.max( prefSize.height, minSize.height ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -262,14 +333,25 @@ public class FlatFileChooserUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileView getFileView( JFileChooser fc ) {
|
public FileView getFileView( JFileChooser fc ) {
|
||||||
return fileView;
|
return doNotUseSystemIcons() ? super.getFileView( fc ) : fileView;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clearIconCache() {
|
public void clearIconCache() {
|
||||||
|
if( doNotUseSystemIcons() )
|
||||||
|
super.clearIconCache();
|
||||||
|
else
|
||||||
fileView.clearIconCache();
|
fileView.clearIconCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean doNotUseSystemIcons() {
|
||||||
|
// Java 17 32bit craches on Windows when using system icons
|
||||||
|
// fixed in Java 18+ (see https://bugs.openjdk.java.net/browse/JDK-8277299)
|
||||||
|
return SystemInfo.isWindows &&
|
||||||
|
SystemInfo.isX86 &&
|
||||||
|
(SystemInfo.isJava_17_orLater && !SystemInfo.isJava_18_orLater);
|
||||||
|
}
|
||||||
|
|
||||||
//---- class FlatFileView -------------------------------------------------
|
//---- class FlatFileView -------------------------------------------------
|
||||||
|
|
||||||
private class FlatFileView
|
private class FlatFileView
|
||||||
@@ -305,4 +387,234 @@ public class FlatFileChooserUI
|
|||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- class FlatShortcutsPanel -------------------------------------------
|
||||||
|
|
||||||
|
/** @since 2.3 */
|
||||||
|
public static class FlatShortcutsPanel
|
||||||
|
extends JToolBar
|
||||||
|
implements PropertyChangeListener
|
||||||
|
{
|
||||||
|
private final JFileChooser fc;
|
||||||
|
|
||||||
|
private final Dimension buttonSize;
|
||||||
|
private final Dimension iconSize;
|
||||||
|
private final Function<File[], File[]> filesFunction;
|
||||||
|
private final Function<File, String> displayNameFunction;
|
||||||
|
private final Function<File, Icon> iconFunction;
|
||||||
|
|
||||||
|
protected final File[] files;
|
||||||
|
protected final JToggleButton[] buttons;
|
||||||
|
protected final ButtonGroup buttonGroup;
|
||||||
|
|
||||||
|
@SuppressWarnings( "unchecked" )
|
||||||
|
public FlatShortcutsPanel( JFileChooser fc ) {
|
||||||
|
super( JToolBar.VERTICAL );
|
||||||
|
this.fc = fc;
|
||||||
|
setFloatable( false );
|
||||||
|
|
||||||
|
buttonSize = UIScale.scale( getUIDimension( "FileChooser.shortcuts.buttonSize", 84, 64 ) );
|
||||||
|
iconSize = getUIDimension( "FileChooser.shortcuts.iconSize", 32, 32 );
|
||||||
|
|
||||||
|
filesFunction = (Function<File[], File[]>) UIManager.get( "FileChooser.shortcuts.filesFunction" );
|
||||||
|
displayNameFunction = (Function<File, String>) UIManager.get( "FileChooser.shortcuts.displayNameFunction" );
|
||||||
|
iconFunction = (Function<File, Icon>) UIManager.get( "FileChooser.shortcuts.iconFunction" );
|
||||||
|
|
||||||
|
FileSystemView fsv = fc.getFileSystemView();
|
||||||
|
File[] files = getChooserShortcutPanelFiles( fsv );
|
||||||
|
if( filesFunction != null )
|
||||||
|
files = filesFunction.apply( files );
|
||||||
|
this.files = files;
|
||||||
|
|
||||||
|
// create toolbar buttons
|
||||||
|
buttons = new JToggleButton[files.length];
|
||||||
|
buttonGroup = new ButtonGroup();
|
||||||
|
for( int i = 0; i < files.length; i++ ) {
|
||||||
|
// wrap drive path
|
||||||
|
if( fsv.isFileSystemRoot( files[i] ) )
|
||||||
|
files[i] = fsv.createFileObject( files[i].getAbsolutePath() );
|
||||||
|
|
||||||
|
File file = files[i];
|
||||||
|
String name = getDisplayName( fsv, file );
|
||||||
|
Icon icon = getIcon( fsv, file );
|
||||||
|
|
||||||
|
// remove path from name
|
||||||
|
int lastSepIndex = name.lastIndexOf( File.separatorChar );
|
||||||
|
if( lastSepIndex >= 0 && lastSepIndex < name.length() - 1 )
|
||||||
|
name = name.substring( lastSepIndex + 1 );
|
||||||
|
|
||||||
|
// scale icon (if necessary)
|
||||||
|
if( icon instanceof ImageIcon )
|
||||||
|
icon = new ScaledImageIcon( (ImageIcon) icon, iconSize.width, iconSize.height );
|
||||||
|
else if( icon != null )
|
||||||
|
icon = new ShortcutIcon( icon, iconSize.width, iconSize.height );
|
||||||
|
|
||||||
|
// create button
|
||||||
|
JToggleButton button = createButton( name, icon );
|
||||||
|
button.addActionListener( e -> {
|
||||||
|
fc.setCurrentDirectory( file );
|
||||||
|
} );
|
||||||
|
|
||||||
|
add( button );
|
||||||
|
buttonGroup.add( button );
|
||||||
|
buttons[i] = button;
|
||||||
|
}
|
||||||
|
|
||||||
|
directoryChanged( fc.getCurrentDirectory() );
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dimension getUIDimension( String key, int defaultWidth, int defaultHeight ) {
|
||||||
|
Dimension size = UIManager.getDimension( key );
|
||||||
|
if( size == null )
|
||||||
|
size = new Dimension( defaultWidth, defaultHeight );
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected JToggleButton createButton( String name, Icon icon ) {
|
||||||
|
JToggleButton button = new JToggleButton( name, icon );
|
||||||
|
button.setVerticalTextPosition( SwingConstants.BOTTOM );
|
||||||
|
button.setHorizontalTextPosition( SwingConstants.CENTER );
|
||||||
|
button.setAlignmentX( Component.CENTER_ALIGNMENT );
|
||||||
|
button.setIconTextGap( 0 );
|
||||||
|
button.setPreferredSize( buttonSize );
|
||||||
|
button.setMaximumSize( buttonSize );
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected File[] getChooserShortcutPanelFiles( FileSystemView fsv ) {
|
||||||
|
try {
|
||||||
|
if( SystemInfo.isJava_12_orLater ) {
|
||||||
|
Method m = fsv.getClass().getMethod( "getChooserShortcutPanelFiles" );
|
||||||
|
File[] files = (File[]) m.invoke( fsv );
|
||||||
|
|
||||||
|
// on macOS and Linux, files consists only of the user home directory
|
||||||
|
if( files.length == 1 && files[0].equals( new File( System.getProperty( "user.home" ) ) ) )
|
||||||
|
files = new File[0];
|
||||||
|
|
||||||
|
return files;
|
||||||
|
} else if( SystemInfo.isWindows ) {
|
||||||
|
Class<?> cls = Class.forName( "sun.awt.shell.ShellFolder" );
|
||||||
|
Method m = cls.getMethod( "get", String.class );
|
||||||
|
return (File[]) m.invoke( null, "fileChooserShortcutPanelFolders" );
|
||||||
|
}
|
||||||
|
} catch( IllegalAccessException ex ) {
|
||||||
|
// do not log because access may be denied via VM option '--illegal-access=deny'
|
||||||
|
} catch( Exception ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback
|
||||||
|
return new File[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getDisplayName( FileSystemView fsv, File file ) {
|
||||||
|
if( displayNameFunction != null ) {
|
||||||
|
String name = displayNameFunction.apply( file );
|
||||||
|
if( name != null )
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fsv.getSystemDisplayName( file );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Icon getIcon( FileSystemView fsv, File file ) {
|
||||||
|
if( iconFunction != null ) {
|
||||||
|
Icon icon = iconFunction.apply( file );
|
||||||
|
if( icon != null )
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Java 17+ supports getting larger system icons
|
||||||
|
try {
|
||||||
|
if( SystemInfo.isJava_17_orLater ) {
|
||||||
|
Method m = fsv.getClass().getMethod( "getSystemIcon", File.class, int.class, int.class );
|
||||||
|
return (Icon) m.invoke( fsv, file, iconSize.width, iconSize.height );
|
||||||
|
} else if( iconSize.width > 16 || iconSize.height > 16 ) {
|
||||||
|
Class<?> cls = Class.forName( "sun.awt.shell.ShellFolder" );
|
||||||
|
if( cls.isInstance( file ) ) {
|
||||||
|
Method m = file.getClass().getMethod( "getIcon", boolean.class );
|
||||||
|
m.setAccessible( true );
|
||||||
|
Image image = (Image) m.invoke( file, true );
|
||||||
|
if( image != null )
|
||||||
|
return new ImageIcon( image );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch( IllegalAccessException ex ) {
|
||||||
|
// do not log because access may be denied via VM option '--illegal-access=deny'
|
||||||
|
} catch( Exception ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
|
||||||
|
// get system icon in default size 16x16
|
||||||
|
return fsv.getSystemIcon( file );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void directoryChanged( File file ) {
|
||||||
|
if( file != null ) {
|
||||||
|
String absolutePath = file.getAbsolutePath();
|
||||||
|
for( int i = 0; i < files.length; i++ ) {
|
||||||
|
// also compare path because otherwise selecting "Documents"
|
||||||
|
// in "Look in" combobox would not select "Documents" shortcut item
|
||||||
|
if( files[i].equals( file ) || files[i].getAbsolutePath().equals( absolutePath ) ) {
|
||||||
|
buttons[i].setSelected( true );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buttonGroup.clearSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
switch( e.getPropertyName() ) {
|
||||||
|
case JFileChooser.DIRECTORY_CHANGED_PROPERTY:
|
||||||
|
directoryChanged( fc.getCurrentDirectory() );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class ShortcutIcon -------------------------------------------------
|
||||||
|
|
||||||
|
private static class ShortcutIcon
|
||||||
|
implements Icon
|
||||||
|
{
|
||||||
|
private final Icon icon;
|
||||||
|
private final int iconWidth;
|
||||||
|
private final int iconHeight;
|
||||||
|
|
||||||
|
ShortcutIcon( Icon icon, int iconWidth, int iconHeight ) {
|
||||||
|
this.icon = icon;
|
||||||
|
this.iconWidth = iconWidth;
|
||||||
|
this.iconHeight = iconHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paintIcon( Component c, Graphics g, int x, int y ) {
|
||||||
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
|
try {
|
||||||
|
// set rendering hint for the case that the icon is a bitmap (not used for vector icons)
|
||||||
|
g2.setRenderingHint( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC );
|
||||||
|
|
||||||
|
double scale = (double) getIconWidth() / (double) icon.getIconWidth();
|
||||||
|
g2.translate( x, y );
|
||||||
|
g2.scale( scale, scale );
|
||||||
|
|
||||||
|
icon.paintIcon( c, g2, 0, 0 );
|
||||||
|
} finally {
|
||||||
|
g2.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getIconWidth() {
|
||||||
|
return UIScale.scale( iconWidth );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getIconHeight() {
|
||||||
|
return UIScale.scale( iconHeight );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,12 +39,11 @@ import javax.swing.plaf.ComponentUI;
|
|||||||
*
|
*
|
||||||
* <!-- FlatTextFieldUI -->
|
* <!-- FlatTextFieldUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault TextComponent.arc int
|
|
||||||
* @uiDefault Component.focusWidth int
|
|
||||||
* @uiDefault Component.minimumWidth int
|
* @uiDefault Component.minimumWidth int
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
* @uiDefault Component.isIntelliJTheme boolean
|
||||||
* @uiDefault FormattedTextField.placeholderForeground Color
|
* @uiDefault FormattedTextField.placeholderForeground Color
|
||||||
* @uiDefault FormattedTextField.focusedBackground Color optional
|
* @uiDefault FormattedTextField.focusedBackground Color optional
|
||||||
|
* @uiDefault FormattedTextField.iconTextGap int optional, default is 4
|
||||||
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
|
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
|
||||||
* @uiDefault TextComponent.selectAllOnMouseClick boolean
|
* @uiDefault TextComponent.selectAllOnMouseClick boolean
|
||||||
*
|
*
|
||||||
@@ -61,4 +60,10 @@ public class FlatFormattedTextFieldUI
|
|||||||
protected String getPropertyPrefix() {
|
protected String getPropertyPrefix() {
|
||||||
return "FormattedTextField";
|
return "FormattedTextField";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
String getStyleType() {
|
||||||
|
return "FormattedTextField";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import java.awt.Color;
|
|||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.LayoutManager;
|
import java.awt.LayoutManager;
|
||||||
|
import java.awt.Rectangle;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
@@ -146,6 +147,19 @@ public class FlatInternalFrameTitlePane
|
|||||||
closeButton.setVisible( frame.isClosable() );
|
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.
|
* Does nothing because FlatLaf internal frames do not have system menus.
|
||||||
*/
|
*/
|
||||||
@@ -199,6 +213,13 @@ public class FlatInternalFrameTitlePane
|
|||||||
case "componentOrientation":
|
case "componentOrientation":
|
||||||
applyComponentOrientation( frame.getComponentOrientation() );
|
applyComponentOrientation( frame.getComponentOrientation() );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "opaque":
|
||||||
|
// Do not invoke super.propertyChange() here because it always
|
||||||
|
// invokes repaint(), which would cause endless repainting.
|
||||||
|
// The opaque flag is temporary changed in FlatUIUtils.hasOpaqueBeenExplicitlySet(),
|
||||||
|
// invoked from FlatInternalFrameUI.update().
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
super.propertyChange( e );
|
super.propertyChange( e );
|
||||||
|
|||||||
@@ -22,12 +22,22 @@ import java.awt.Component;
|
|||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Insets;
|
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.JComponent;
|
||||||
import javax.swing.JInternalFrame;
|
import javax.swing.JInternalFrame;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.event.MouseInputAdapter;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicInternalFrameUI;
|
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}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JInternalFrame}.
|
||||||
@@ -83,9 +93,13 @@ import javax.swing.plaf.basic.BasicInternalFrameUI;
|
|||||||
*/
|
*/
|
||||||
public class FlatInternalFrameUI
|
public class FlatInternalFrameUI
|
||||||
extends BasicInternalFrameUI
|
extends BasicInternalFrameUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
protected FlatWindowResizer windowResizer;
|
protected FlatWindowResizer windowResizer;
|
||||||
|
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
private AtomicBoolean borderShared;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatInternalFrameUI( (JInternalFrame) c );
|
return new FlatInternalFrameUI( (JInternalFrame) c );
|
||||||
}
|
}
|
||||||
@@ -101,6 +115,8 @@ public class FlatInternalFrameUI
|
|||||||
LookAndFeel.installProperty( frame, "opaque", false );
|
LookAndFeel.installProperty( frame, "opaque", false );
|
||||||
|
|
||||||
windowResizer = createWindowResizer();
|
windowResizer = createWindowResizer();
|
||||||
|
|
||||||
|
installStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -111,6 +127,9 @@ public class FlatInternalFrameUI
|
|||||||
windowResizer.uninstall();
|
windowResizer.uninstall();
|
||||||
windowResizer = null;
|
windowResizer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
borderShared = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -122,15 +141,80 @@ public class FlatInternalFrameUI
|
|||||||
return new FlatWindowResizer.InternalFrameResizer( frame, this::getDesktopManager );
|
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() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, frame.getBorder(), key );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update( Graphics g, JComponent c ) {
|
||||||
|
// The internal frame actually should be opaque and fill its background,
|
||||||
|
// but it must be non-opaque to allow translucent resize handles (outside of visual bounds).
|
||||||
|
// To avoid that parent may shine through internal frame (e.g. if menu bar is non-opaque),
|
||||||
|
// fill background excluding insets (translucent resize handles),
|
||||||
|
// but only if opaque was not set explicitly by application to false.
|
||||||
|
// If applications has set internal frame opacity to false, do not fill background (for compatibility).
|
||||||
|
if( !c.isOpaque() && !FlatUIUtils.hasOpaqueBeenExplicitlySet( c ) ) {
|
||||||
|
Insets insets = c.getInsets();
|
||||||
|
|
||||||
|
g.setColor( c.getBackground() );
|
||||||
|
g.fillRect( insets.left, insets.top,
|
||||||
|
c.getWidth() - insets.left - insets.right,
|
||||||
|
c.getHeight() - insets.top - insets.bottom );
|
||||||
|
}
|
||||||
|
|
||||||
|
super.update( g, c );
|
||||||
|
}
|
||||||
|
|
||||||
//---- class FlatInternalFrameBorder --------------------------------------
|
//---- class FlatInternalFrameBorder --------------------------------------
|
||||||
|
|
||||||
public static class FlatInternalFrameBorder
|
public static class FlatInternalFrameBorder
|
||||||
extends FlatEmptyBorder
|
extends FlatEmptyBorder
|
||||||
|
implements StyleableBorder
|
||||||
{
|
{
|
||||||
private final Color activeBorderColor = UIManager.getColor( "InternalFrame.activeBorderColor" );
|
@Styleable protected Color activeBorderColor = UIManager.getColor( "InternalFrame.activeBorderColor" );
|
||||||
private final Color inactiveBorderColor = UIManager.getColor( "InternalFrame.inactiveBorderColor" );
|
@Styleable protected Color inactiveBorderColor = UIManager.getColor( "InternalFrame.inactiveBorderColor" );
|
||||||
private final int borderLineWidth = FlatUIUtils.getUIInt( "InternalFrame.borderLineWidth", 1 );
|
@Styleable protected int borderLineWidth = FlatUIUtils.getUIInt( "InternalFrame.borderLineWidth", 1 );
|
||||||
private final boolean dropShadowPainted = UIManager.getBoolean( "InternalFrame.dropShadowPainted" );
|
@Styleable protected boolean dropShadowPainted = UIManager.getBoolean( "InternalFrame.dropShadowPainted" );
|
||||||
|
|
||||||
private final FlatDropShadowBorder activeDropShadowBorder = new FlatDropShadowBorder(
|
private final FlatDropShadowBorder activeDropShadowBorder = new FlatDropShadowBorder(
|
||||||
UIManager.getColor( "InternalFrame.activeDropShadowColor" ),
|
UIManager.getColor( "InternalFrame.activeDropShadowColor" ),
|
||||||
@@ -145,6 +229,53 @@ public class FlatInternalFrameUI
|
|||||||
super( UIManager.getInsets( "InternalFrame.borderMargins" ) );
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
switch( key ) {
|
||||||
|
case "borderMargins": return getStyleableValue();
|
||||||
|
|
||||||
|
case "activeDropShadowColor": return activeDropShadowBorder.getStyleableValue( "shadowColor" );
|
||||||
|
case "activeDropShadowInsets": return activeDropShadowBorder.getStyleableValue( "shadowInsets" );
|
||||||
|
case "activeDropShadowOpacity": return activeDropShadowBorder.getStyleableValue( "shadowOpacity" );
|
||||||
|
case "inactiveDropShadowColor": return inactiveDropShadowBorder.getStyleableValue( "shadowColor" );
|
||||||
|
case "inactiveDropShadowInsets": return inactiveDropShadowBorder.getStyleableValue( "shadowInsets" );
|
||||||
|
case "inactiveDropShadowOpacity": return inactiveDropShadowBorder.getStyleableValue( "shadowOpacity" );
|
||||||
|
}
|
||||||
|
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
if( c instanceof JInternalFrame && ((JInternalFrame)c).isMaximum() ) {
|
if( c instanceof JInternalFrame && ((JInternalFrame)c).isMaximum() ) {
|
||||||
@@ -195,4 +326,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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import java.awt.Rectangle;
|
|||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
@@ -33,8 +34,12 @@ import javax.swing.UIManager;
|
|||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicHTML;
|
import javax.swing.plaf.basic.BasicHTML;
|
||||||
import javax.swing.plaf.basic.BasicLabelUI;
|
import javax.swing.plaf.basic.BasicLabelUI;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
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.HiDPIUtils;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,13 +59,30 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*/
|
*/
|
||||||
public class FlatLabelUI
|
public class FlatLabelUI
|
||||||
extends BasicLabelUI
|
extends BasicLabelUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
private Color disabledForeground;
|
@Styleable protected Color disabledForeground;
|
||||||
|
|
||||||
|
private final boolean shared;
|
||||||
private boolean defaults_initialized = false;
|
private boolean defaults_initialized = false;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return FlatUIUtils.createSharedUI( FlatLabelUI.class, FlatLabelUI::new );
|
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
|
@Override
|
||||||
@@ -77,7 +99,9 @@ public class FlatLabelUI
|
|||||||
@Override
|
@Override
|
||||||
protected void uninstallDefaults( JLabel c ) {
|
protected void uninstallDefaults( JLabel c ) {
|
||||||
super.uninstallDefaults( c );
|
super.uninstallDefaults( c );
|
||||||
|
|
||||||
defaults_initialized = false;
|
defaults_initialized = false;
|
||||||
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -94,10 +118,52 @@ public class FlatLabelUI
|
|||||||
if( name == "text" || name == "font" || name == "foreground" ) {
|
if( name == "text" || name == "font" || name == "foreground" ) {
|
||||||
JLabel label = (JLabel) e.getSource();
|
JLabel label = (JLabel) e.getSource();
|
||||||
updateHTMLRenderer( label, label.getText(), true );
|
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 installStyle() from installUI()
|
||||||
|
label.updateUI();
|
||||||
|
} else
|
||||||
|
installStyle( label );
|
||||||
|
label.revalidate();
|
||||||
|
label.repaint();
|
||||||
} else
|
} else
|
||||||
super.propertyChange( e );
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether text contains HTML tags that use "absolute-size" keywords
|
* Checks whether text contains HTML tags that use "absolute-size" keywords
|
||||||
* (e.g. "x-large") for font-size in default style sheet
|
* (e.g. "x-large") for font-size in default style sheet
|
||||||
|
|||||||
@@ -37,15 +37,18 @@ public class FlatLineBorder
|
|||||||
{
|
{
|
||||||
private final Color lineColor;
|
private final Color lineColor;
|
||||||
private final float lineThickness;
|
private final float lineThickness;
|
||||||
|
/** @since 2 */ private final int arc;
|
||||||
|
|
||||||
public FlatLineBorder( Insets insets, Color lineColor ) {
|
public FlatLineBorder( Insets insets, Color lineColor ) {
|
||||||
this( insets, lineColor, 1f );
|
this( insets, lineColor, 1f, 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
public FlatLineBorder( Insets insets, Color lineColor, float lineThickness ) {
|
/** @since 2 */
|
||||||
|
public FlatLineBorder( Insets insets, Color lineColor, float lineThickness, int arc ) {
|
||||||
super( insets );
|
super( insets );
|
||||||
this.lineColor = lineColor;
|
this.lineColor = lineColor;
|
||||||
this.lineThickness = lineThickness;
|
this.lineThickness = lineThickness;
|
||||||
|
this.arc = arc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Color getLineColor() {
|
public Color getLineColor() {
|
||||||
@@ -56,13 +59,18 @@ public class FlatLineBorder
|
|||||||
return lineThickness;
|
return lineThickness;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public int getArc() {
|
||||||
|
return arc;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
Graphics2D g2 = (Graphics2D) g.create();
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
try {
|
try {
|
||||||
FlatUIUtils.setRenderingHints( g2 );
|
FlatUIUtils.setRenderingHints( g2 );
|
||||||
g2.setColor( lineColor );
|
FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height,
|
||||||
FlatUIUtils.paintComponentBorder( g2, x, y, width, height, 0f, scale( lineThickness ), 0f );
|
0, 0, 0, scale( getLineThickness() ), scale( getArc() ), null, getLineColor(), null );
|
||||||
} finally {
|
} finally {
|
||||||
g2.dispose();
|
g2.dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,11 +16,15 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Insets;
|
||||||
|
import java.util.function.Function;
|
||||||
import javax.swing.JList;
|
import javax.swing.JList;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.plaf.ListUI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cell border for {@link javax.swing.DefaultListCellRenderer}
|
* Cell border for {@link javax.swing.DefaultListCellRenderer}
|
||||||
@@ -33,12 +37,54 @@ import javax.swing.UIManager;
|
|||||||
public class FlatListCellBorder
|
public class FlatListCellBorder
|
||||||
extends FlatLineBorder
|
extends FlatLineBorder
|
||||||
{
|
{
|
||||||
final boolean showCellFocusIndicator = UIManager.getBoolean( "List.showCellFocusIndicator" );
|
/** @since 2 */ protected boolean showCellFocusIndicator = UIManager.getBoolean( "List.showCellFocusIndicator" );
|
||||||
|
|
||||||
|
private Component c;
|
||||||
|
|
||||||
protected FlatListCellBorder() {
|
protected FlatListCellBorder() {
|
||||||
super( UIManager.getInsets( "List.cellMargins" ), UIManager.getColor( "List.cellFocusColor" ) );
|
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 border is 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 ------------------------------------------------------
|
//---- class Default ------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,17 +113,19 @@ public class FlatListCellBorder
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Border for selected cell that uses margins and paints focus indicator border
|
* Border for selected cell that uses margins and paints focus indicator border
|
||||||
* if enabled (List.showCellFocusIndicator=true) and exactly one item is selected.
|
* if enabled (List.showCellFocusIndicator=true) and multiple items are selected.
|
||||||
*/
|
*/
|
||||||
public static class Selected
|
public static class Selected
|
||||||
extends FlatListCellBorder
|
extends FlatListCellBorder
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
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 )
|
if( !showCellFocusIndicator )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// paint focus indicator border only if exactly one item is selected
|
// paint focus indicator border only if multiple items are selected
|
||||||
JList<?> list = (JList<?>) SwingUtilities.getAncestorOfClass( JList.class, c );
|
JList<?> list = (JList<?>) SwingUtilities.getAncestorOfClass( JList.class, c );
|
||||||
if( list != null && list.getMinSelectionIndex() == list.getMaxSelectionIndex() )
|
if( list != null && list.getMinSelectionIndex() == list.getMaxSelectionIndex() )
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -18,12 +18,19 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
|
import java.awt.Insets;
|
||||||
import java.awt.event.FocusEvent;
|
import java.awt.event.FocusEvent;
|
||||||
import java.awt.event.FocusListener;
|
import java.awt.event.FocusListener;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicListUI;
|
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}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JList}.
|
||||||
@@ -63,16 +70,38 @@ import javax.swing.plaf.basic.BasicListUI;
|
|||||||
*/
|
*/
|
||||||
public class FlatListUI
|
public class FlatListUI
|
||||||
extends BasicListUI
|
extends BasicListUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
protected Color selectionBackground;
|
@Styleable protected Color selectionBackground;
|
||||||
protected Color selectionForeground;
|
@Styleable protected Color selectionForeground;
|
||||||
protected Color selectionInactiveBackground;
|
@Styleable protected Color selectionInactiveBackground;
|
||||||
protected Color selectionInactiveForeground;
|
@Styleable protected Color selectionInactiveForeground;
|
||||||
|
|
||||||
|
// for FlatListCellBorder
|
||||||
|
/** @since 2 */ @Styleable protected Insets cellMargins;
|
||||||
|
/** @since 2 */ @Styleable protected Color cellFocusColor;
|
||||||
|
/** @since 2 */ @Styleable protected Boolean showCellFocusIndicator;
|
||||||
|
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatListUI();
|
return new FlatListUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
if( FlatUIUtils.needsLightAWTPeer( c ) )
|
||||||
|
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
|
||||||
|
else
|
||||||
|
installUIImpl( c );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installUIImpl( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
@@ -93,6 +122,8 @@ public class FlatListUI
|
|||||||
selectionForeground = null;
|
selectionForeground = null;
|
||||||
selectionInactiveBackground = null;
|
selectionInactiveBackground = null;
|
||||||
selectionInactiveForeground = null;
|
selectionInactiveForeground = null;
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -116,10 +147,85 @@ 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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle selection colors from focused to inactive and vice versa.
|
* Toggle selection colors from focused to inactive and vice versa.
|
||||||
*
|
*
|
||||||
* This is not a optimal solution but much easier than rewriting the whole paint methods.
|
* This is not an optimal solution but much easier than rewriting the whole paint methods.
|
||||||
*
|
*
|
||||||
* Using a LaF specific renderer was avoided because often a custom renderer is
|
* Using a LaF specific renderer was avoided because often a custom renderer is
|
||||||
* already used in applications. Then either the inactive colors are not used,
|
* already used in applications. Then either the inactive colors are not used,
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import javax.swing.plaf.basic.BasicBorders;
|
|||||||
public class FlatMarginBorder
|
public class FlatMarginBorder
|
||||||
extends BasicBorders.MarginBorder
|
extends BasicBorders.MarginBorder
|
||||||
{
|
{
|
||||||
private final int left, right, top, bottom;
|
protected int left, right, top, bottom;
|
||||||
|
|
||||||
public FlatMarginBorder() {
|
public FlatMarginBorder() {
|
||||||
left = right = top = bottom = 0;
|
left = right = top = bottom = 0;
|
||||||
|
|||||||
@@ -21,8 +21,11 @@ import java.awt.Color;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.JMenuBar;
|
import javax.swing.JMenuBar;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableBorder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Border for {@link javax.swing.JMenuBar}.
|
* Border for {@link javax.swing.JMenuBar}.
|
||||||
@@ -33,11 +36,32 @@ import javax.swing.UIManager;
|
|||||||
*/
|
*/
|
||||||
public class FlatMenuBarBorder
|
public class FlatMenuBarBorder
|
||||||
extends FlatMarginBorder
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
|
if( !showBottomSeparator( c ) )
|
||||||
|
return;
|
||||||
|
|
||||||
float lineHeight = scale( (float) 1 );
|
float lineHeight = scale( (float) 1 );
|
||||||
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight );
|
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight );
|
||||||
}
|
}
|
||||||
@@ -53,4 +77,9 @@ public class FlatMenuBarBorder
|
|||||||
insets.right = scale( margin.right );
|
insets.right = scale( margin.right );
|
||||||
return insets;
|
return insets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected boolean showBottomSeparator( Component c ) {
|
||||||
|
return !FlatMenuBarUI.useUnifiedBackground( c );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,11 +17,19 @@
|
|||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Container;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Insets;
|
||||||
|
import java.awt.LayoutManager;
|
||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
import java.awt.event.ActionEvent;
|
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.AbstractAction;
|
||||||
import javax.swing.ActionMap;
|
import javax.swing.ActionMap;
|
||||||
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JMenu;
|
import javax.swing.JMenu;
|
||||||
import javax.swing.JMenuBar;
|
import javax.swing.JMenuBar;
|
||||||
@@ -35,8 +43,13 @@ import javax.swing.plaf.ActionMapUIResource;
|
|||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicMenuBarUI;
|
import javax.swing.plaf.basic.BasicMenuBarUI;
|
||||||
|
import javax.swing.plaf.basic.DefaultMenuLayout;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
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;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenuBar}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenuBar}.
|
||||||
@@ -47,13 +60,32 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
* @uiDefault MenuBar.background Color
|
* @uiDefault MenuBar.background Color
|
||||||
* @uiDefault MenuBar.foreground Color
|
* @uiDefault MenuBar.foreground Color
|
||||||
* @uiDefault MenuBar.border Border
|
* @uiDefault MenuBar.border Border
|
||||||
|
*
|
||||||
|
* <!-- FlatMenuBarUI -->
|
||||||
|
*
|
||||||
* @uiDefault TitlePane.unifiedBackground boolean
|
* @uiDefault TitlePane.unifiedBackground boolean
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatMenuBarUI
|
public class FlatMenuBarUI
|
||||||
extends BasicMenuBarUI
|
extends BasicMenuBarUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
|
// used in FlatMenuItemBorder
|
||||||
|
/** @since 2 */ @Styleable protected Insets itemMargins;
|
||||||
|
|
||||||
|
// used in FlatMenuUI
|
||||||
|
/** @since 2 */ @Styleable protected Color hoverBackground;
|
||||||
|
/** @since 2.5 */ @Styleable protected Color selectionBackground;
|
||||||
|
/** @since 2.5 */ @Styleable protected Color selectionForeground;
|
||||||
|
/** @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 ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatMenuBarUI();
|
return new FlatMenuBarUI();
|
||||||
}
|
}
|
||||||
@@ -63,11 +95,46 @@ public class FlatMenuBarUI
|
|||||||
* Do not add any functionality here.
|
* Do not add any functionality here.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
LookAndFeel.installProperty( menuBar, "opaque", false );
|
LookAndFeel.installProperty( menuBar, "opaque", false );
|
||||||
|
|
||||||
|
LayoutManager layout = menuBar.getLayout();
|
||||||
|
if( layout == null || layout instanceof UIResource )
|
||||||
|
menuBar.setLayout( new FlatMenuBarLayout( menuBar ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
@Override
|
||||||
@@ -82,6 +149,39 @@ public class FlatMenuBarUI
|
|||||||
map.put( "takeFocus", new TakeFocus() );
|
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() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, menuBar.getBorder(), key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update( Graphics g, JComponent c ) {
|
public void update( Graphics g, JComponent c ) {
|
||||||
// paint background
|
// paint background
|
||||||
@@ -97,19 +197,21 @@ public class FlatMenuBarUI
|
|||||||
protected Color getBackground( JComponent c ) {
|
protected Color getBackground( JComponent c ) {
|
||||||
Color background = c.getBackground();
|
Color background = c.getBackground();
|
||||||
|
|
||||||
// paint background if opaque or if having custom background color
|
// paint background if opaque
|
||||||
if( c.isOpaque() || !(background instanceof UIResource) )
|
if( c.isOpaque() )
|
||||||
return background;
|
return background;
|
||||||
|
|
||||||
// paint background if menu bar is not the "main" menu bar
|
// do not paint background if non-opaque and having custom background color
|
||||||
|
if( !(background instanceof UIResource) )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// paint background if menu bar is not the "main" menu bar (e.g. in internal frame)
|
||||||
JRootPane rootPane = SwingUtilities.getRootPane( c );
|
JRootPane rootPane = SwingUtilities.getRootPane( c );
|
||||||
if( rootPane == null || !(rootPane.getParent() instanceof Window) || rootPane.getJMenuBar() != c )
|
if( rootPane == null || !(rootPane.getParent() instanceof Window) || rootPane.getJMenuBar() != c )
|
||||||
return background;
|
return background;
|
||||||
|
|
||||||
// use parent background for unified title pane
|
// use parent background for unified title pane
|
||||||
// (not storing value of "TitlePane.unifiedBackground" in class to allow changing at runtime)
|
if( useUnifiedBackground( c ) )
|
||||||
if( UIManager.getBoolean( "TitlePane.unifiedBackground" ) &&
|
|
||||||
FlatNativeWindowBorder.hasCustomDecoration( (Window) rootPane.getParent() ) )
|
|
||||||
background = FlatUIUtils.getParentBackground( c );
|
background = FlatUIUtils.getParentBackground( c );
|
||||||
|
|
||||||
// paint background in full screen mode
|
// paint background in full screen mode
|
||||||
@@ -120,6 +222,146 @@ public class FlatMenuBarUI
|
|||||||
return FlatRootPaneUI.isMenuBarEmbedded( rootPane ) ? null : background;
|
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 root pane has custom decoration style
|
||||||
|
|
||||||
|
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 &&
|
||||||
|
rootPane.getWindowDecorationStyle() != JRootPane.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class FlatMenuBarLayout --------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2.4
|
||||||
|
*/
|
||||||
|
protected static class FlatMenuBarLayout
|
||||||
|
extends DefaultMenuLayout
|
||||||
|
{
|
||||||
|
public FlatMenuBarLayout( Container target ) {
|
||||||
|
super( target, BoxLayout.LINE_AXIS );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void layoutContainer( Container target ) {
|
||||||
|
super.layoutContainer( target );
|
||||||
|
|
||||||
|
|
||||||
|
// The only purpose of the code below is to make sure that a horizontal glue,
|
||||||
|
// which can be used to move window and displays the window title in embedded menu bar,
|
||||||
|
// is always visible within the menu bar bounds and has a minimum width.
|
||||||
|
// If this is not the case, the horizontal glue is made larger and
|
||||||
|
// components that are on the left side of the glue are made smaller.
|
||||||
|
|
||||||
|
|
||||||
|
// get root pane and check whether this menu bar is the root pane menu bar
|
||||||
|
JRootPane rootPane = SwingUtilities.getRootPane( target );
|
||||||
|
if( rootPane == null || rootPane.getJMenuBar() != target )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// get title pane and check whether menu bar is embedded
|
||||||
|
FlatTitlePane titlePane = FlatRootPaneUI.getTitlePane( rootPane );
|
||||||
|
if( titlePane == null || !titlePane.isMenuBarEmbedded() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// check whether there is a horizontal glue (used for window title in embedded menu bar)
|
||||||
|
// and check minimum width of horizontal glue
|
||||||
|
Component horizontalGlue = titlePane.findHorizontalGlue( (JMenuBar) target );
|
||||||
|
int minTitleWidth = UIScale.scale( titlePane.titleMinimumWidth );
|
||||||
|
if( horizontalGlue != null && horizontalGlue.getWidth() < minTitleWidth ) {
|
||||||
|
// get index of glue component
|
||||||
|
int glueIndex = -1;
|
||||||
|
Component[] components = target.getComponents();
|
||||||
|
for( int i = components.length - 1; i >= 0; i-- ) {
|
||||||
|
if( components[i] == horizontalGlue ) {
|
||||||
|
glueIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( glueIndex < 0 )
|
||||||
|
return; // should never happen
|
||||||
|
|
||||||
|
if( target.getComponentOrientation().isLeftToRight() ) {
|
||||||
|
// left-to-right
|
||||||
|
|
||||||
|
// make horizontal glue wider (minimum title width)
|
||||||
|
int offset = minTitleWidth - horizontalGlue.getWidth();
|
||||||
|
horizontalGlue.setSize( minTitleWidth, horizontalGlue.getHeight() );
|
||||||
|
|
||||||
|
// check whether glue is fully visible
|
||||||
|
int minGlueX = target.getWidth() - target.getInsets().right - minTitleWidth;
|
||||||
|
if( minGlueX < horizontalGlue.getX() ) {
|
||||||
|
// move glue to the left to make it fully visible
|
||||||
|
offset -= (horizontalGlue.getX() - minGlueX);
|
||||||
|
horizontalGlue.setLocation( minGlueX, horizontalGlue.getY() );
|
||||||
|
|
||||||
|
// shrink and move components that are on the left side of the glue
|
||||||
|
for( int i = glueIndex - 1; i >= 0; i-- ) {
|
||||||
|
Component c = components[i];
|
||||||
|
if( c.getX() > minGlueX ) {
|
||||||
|
// move component and set width to zero
|
||||||
|
c.setBounds( minGlueX, c.getY(), 0, c.getHeight() );
|
||||||
|
} else {
|
||||||
|
// reduce size of component
|
||||||
|
c.setSize( minGlueX - c.getX(), c.getHeight() );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// move components that are on the right side of the glue
|
||||||
|
for( int i = glueIndex + 1; i < components.length; i++ ) {
|
||||||
|
Component c = components[i];
|
||||||
|
c.setLocation( c.getX() + offset, c.getY() );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// right-to-left
|
||||||
|
|
||||||
|
// make horizontal glue wider (minimum title width)
|
||||||
|
int offset = minTitleWidth - horizontalGlue.getWidth();
|
||||||
|
horizontalGlue.setBounds( horizontalGlue.getX() - offset, horizontalGlue.getY(),
|
||||||
|
minTitleWidth, horizontalGlue.getHeight() );
|
||||||
|
|
||||||
|
// check whether glue is fully visible
|
||||||
|
int minGlueX = target.getInsets().left;
|
||||||
|
if( minGlueX > horizontalGlue.getX() ) {
|
||||||
|
// move glue to the right to make it fully visible
|
||||||
|
offset -= (horizontalGlue.getX() - minGlueX);
|
||||||
|
horizontalGlue.setLocation( minGlueX, horizontalGlue.getY() );
|
||||||
|
|
||||||
|
// shrink and move components that are on the right side of the glue
|
||||||
|
int x = horizontalGlue.getX() + horizontalGlue.getWidth();
|
||||||
|
for( int i = glueIndex - 1; i >= 0; i-- ) {
|
||||||
|
Component c = components[i];
|
||||||
|
if( c.getX() + c.getWidth() < x ) {
|
||||||
|
// move component and set width to zero
|
||||||
|
c.setBounds( x, c.getY(), 0, c.getHeight() );
|
||||||
|
} else {
|
||||||
|
// move component and reduce size
|
||||||
|
c.setBounds( x, c.getY(), c.getWidth() - (x - c.getX()), c.getHeight() );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// move components that are on the left side of the glue
|
||||||
|
for( int i = glueIndex + 1; i < components.length; i++ ) {
|
||||||
|
Component c = components[i];
|
||||||
|
c.setLocation( c.getX() - offset, c.getY() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//---- class TakeFocus ----------------------------------------------------
|
//---- class TakeFocus ----------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -18,9 +18,11 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
import static com.formdev.flatlaf.util.UIScale.scale;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
|
import java.awt.Container;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import javax.swing.JMenuBar;
|
import javax.swing.JMenuBar;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.plaf.MenuBarUI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Border for {@link javax.swing.JMenu}, {@link javax.swing.JMenuItem},
|
* Border for {@link javax.swing.JMenu}, {@link javax.swing.JMenuItem},
|
||||||
@@ -33,15 +35,22 @@ import javax.swing.UIManager;
|
|||||||
public class FlatMenuItemBorder
|
public class FlatMenuItemBorder
|
||||||
extends FlatMarginBorder
|
extends FlatMarginBorder
|
||||||
{
|
{
|
||||||
|
// only used if parent menubar is not an instance of FlatMenuBarUI
|
||||||
private final Insets menuBarItemMargins = UIManager.getInsets( "MenuBar.itemMargins" );
|
private final Insets menuBarItemMargins = UIManager.getInsets( "MenuBar.itemMargins" );
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
if( c.getParent() instanceof JMenuBar ) {
|
Container parent = c.getParent();
|
||||||
insets.top = scale( menuBarItemMargins.top );
|
if( parent instanceof JMenuBar ) {
|
||||||
insets.left = scale( menuBarItemMargins.left );
|
// get margins from FlatMenuBarUI to allow styling
|
||||||
insets.bottom = scale( menuBarItemMargins.bottom );
|
MenuBarUI ui = ((JMenuBar)parent).getUI();
|
||||||
insets.right = scale( menuBarItemMargins.right );
|
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;
|
return insets;
|
||||||
} else
|
} else
|
||||||
return super.getBorderInsets( c, insets );
|
return super.getBorderInsets( c, insets );
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package com.formdev.flatlaf.ui;
|
|||||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
import static com.formdev.flatlaf.util.UIScale.scale;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
|
import java.awt.Container;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
import java.awt.FontMetrics;
|
import java.awt.FontMetrics;
|
||||||
@@ -30,7 +31,9 @@ import java.awt.Rectangle;
|
|||||||
import java.awt.event.InputEvent;
|
import java.awt.event.InputEvent;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
import java.text.AttributedCharacterIterator;
|
import java.text.AttributedCharacterIterator;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JMenu;
|
import javax.swing.JMenu;
|
||||||
import javax.swing.JMenuItem;
|
import javax.swing.JMenuItem;
|
||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
@@ -38,7 +41,12 @@ import javax.swing.SwingUtilities;
|
|||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.basic.BasicHTML;
|
import javax.swing.plaf.basic.BasicHTML;
|
||||||
import javax.swing.text.View;
|
import javax.swing.text.View;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
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.DerivedColor;
|
||||||
import com.formdev.flatlaf.util.Graphics2DProxy;
|
import com.formdev.flatlaf.util.Graphics2DProxy;
|
||||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
@@ -47,43 +55,49 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
/**
|
/**
|
||||||
* Renderer for menu items.
|
* Renderer for menu items.
|
||||||
*
|
*
|
||||||
|
* @uiDefault MenuItem.verticallyAlignText boolean
|
||||||
* @uiDefault MenuItem.minimumWidth int
|
* @uiDefault MenuItem.minimumWidth int
|
||||||
* @uiDefault MenuItem.minimumIconSize Dimension
|
* @uiDefault MenuItem.minimumIconSize Dimension
|
||||||
* @uiDefault MenuItem.textAcceleratorGap int
|
* @uiDefault MenuItem.textAcceleratorGap int
|
||||||
* @uiDefault MenuItem.textNoAcceleratorGap int
|
* @uiDefault MenuItem.textNoAcceleratorGap int
|
||||||
* @uiDefault MenuItem.acceleratorArrowGap int
|
* @uiDefault MenuItem.acceleratorArrowGap int
|
||||||
* @uiDefault MenuItem.checkBackground Color
|
* @uiDefault MenuItem.checkBackground Color
|
||||||
|
* @uiDefault MenuItem.checkMargins Insets
|
||||||
|
* @uiDefault MenuItem.selectionType String null (default) or underline
|
||||||
* @uiDefault MenuItem.underlineSelectionBackground Color
|
* @uiDefault MenuItem.underlineSelectionBackground Color
|
||||||
* @uiDefault MenuItem.underlineSelectionCheckBackground Color
|
* @uiDefault MenuItem.underlineSelectionCheckBackground Color
|
||||||
* @uiDefault MenuItem.underlineSelectionColor Color
|
* @uiDefault MenuItem.underlineSelectionColor Color
|
||||||
* @uiDefault MenuItem.underlineSelectionHeight int
|
* @uiDefault MenuItem.underlineSelectionHeight int
|
||||||
* @uiDefault MenuItem.selectionBackground Color
|
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatMenuItemRenderer
|
public class FlatMenuItemRenderer
|
||||||
{
|
{
|
||||||
|
private static final String KEY_MAX_ICONS_WIDTH = "FlatLaf.internal.FlatMenuItemRenderer.maxIconWidth";
|
||||||
|
|
||||||
protected final JMenuItem menuItem;
|
protected final JMenuItem menuItem;
|
||||||
protected final Icon checkIcon;
|
protected Icon checkIcon;
|
||||||
protected final Icon arrowIcon;
|
protected Icon arrowIcon;
|
||||||
protected final Font acceleratorFont;
|
@Styleable protected Font acceleratorFont;
|
||||||
protected final String acceleratorDelimiter;
|
protected final String acceleratorDelimiter;
|
||||||
|
|
||||||
protected final int minimumWidth = UIManager.getInt( "MenuItem.minimumWidth" );
|
/** @since 2 */ @Styleable protected boolean verticallyAlignText = FlatUIUtils.getUIBoolean( "MenuItem.verticallyAlignText", true );
|
||||||
protected final Dimension minimumIconSize;
|
@Styleable protected int minimumWidth = UIManager.getInt( "MenuItem.minimumWidth" );
|
||||||
protected final int textAcceleratorGap = FlatUIUtils.getUIInt( "MenuItem.textAcceleratorGap", 28 );
|
@Styleable protected Dimension minimumIconSize;
|
||||||
protected final int textNoAcceleratorGap = FlatUIUtils.getUIInt( "MenuItem.textNoAcceleratorGap", 6 );
|
@Styleable protected int textAcceleratorGap = FlatUIUtils.getUIInt( "MenuItem.textAcceleratorGap", 28 );
|
||||||
protected final int acceleratorArrowGap = FlatUIUtils.getUIInt( "MenuItem.acceleratorArrowGap", 2 );
|
@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" );
|
@Styleable protected Color checkBackground = UIManager.getColor( "MenuItem.checkBackground" );
|
||||||
protected final Insets checkMargins = UIManager.getInsets( "MenuItem.checkMargins" );
|
@Styleable protected Insets checkMargins = UIManager.getInsets( "MenuItem.checkMargins" );
|
||||||
|
|
||||||
protected final Color underlineSelectionBackground = UIManager.getColor( "MenuItem.underlineSelectionBackground" );
|
@Styleable protected Color underlineSelectionBackground = UIManager.getColor( "MenuItem.underlineSelectionBackground" );
|
||||||
protected final Color underlineSelectionCheckBackground = UIManager.getColor( "MenuItem.underlineSelectionCheckBackground" );
|
@Styleable protected Color underlineSelectionCheckBackground = UIManager.getColor( "MenuItem.underlineSelectionCheckBackground" );
|
||||||
protected final Color underlineSelectionColor = UIManager.getColor( "MenuItem.underlineSelectionColor" );
|
@Styleable protected Color underlineSelectionColor = UIManager.getColor( "MenuItem.underlineSelectionColor" );
|
||||||
protected final int underlineSelectionHeight = UIManager.getInt( "MenuItem.underlineSelectionHeight" );
|
@Styleable protected int underlineSelectionHeight = UIManager.getInt( "MenuItem.underlineSelectionHeight" );
|
||||||
|
|
||||||
protected final Color selectionBackground = UIManager.getColor( "MenuItem.selectionBackground" );
|
private boolean iconsShared = true;
|
||||||
|
private final Font menuFont = UIManager.getFont( "Menu.font" );
|
||||||
|
|
||||||
protected FlatMenuItemRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
|
protected FlatMenuItemRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
|
||||||
Font acceleratorFont, String acceleratorDelimiter )
|
Font acceleratorFont, String acceleratorDelimiter )
|
||||||
@@ -98,6 +112,77 @@ public class FlatMenuItemRenderer
|
|||||||
this.minimumIconSize = (minimumIconSize != null) ? minimumIconSize : new Dimension( 16, 16 );
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
if( key.startsWith( "icon." ) ) {
|
||||||
|
String key2 = key.substring( "icon.".length() );
|
||||||
|
if( checkIcon instanceof FlatCheckBoxMenuItemIcon )
|
||||||
|
return ((FlatCheckBoxMenuItemIcon)checkIcon).getStyleableValue( key2 );
|
||||||
|
if( arrowIcon instanceof FlatMenuArrowIcon )
|
||||||
|
return ((FlatMenuArrowIcon)arrowIcon).getStyleableValue( key2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
protected Dimension getPreferredMenuItemSize() {
|
protected Dimension getPreferredMenuItemSize() {
|
||||||
int width = 0;
|
int width = 0;
|
||||||
int height = 0;
|
int height = 0;
|
||||||
@@ -109,7 +194,8 @@ public class FlatMenuItemRenderer
|
|||||||
|
|
||||||
// layout icon and text
|
// layout icon and text
|
||||||
SwingUtilities.layoutCompoundLabel( menuItem,
|
SwingUtilities.layoutCompoundLabel( menuItem,
|
||||||
menuItem.getFontMetrics( menuItem.getFont() ), menuItem.getText(), getIconForLayout(),
|
menuItem.getFontMetrics( isTopLevelMenu ? getTopLevelFont() : menuItem.getFont() ),
|
||||||
|
menuItem.getText(), getIconForLayout(),
|
||||||
menuItem.getVerticalAlignment(), menuItem.getHorizontalAlignment(),
|
menuItem.getVerticalAlignment(), menuItem.getHorizontalAlignment(),
|
||||||
menuItem.getVerticalTextPosition(), menuItem.getHorizontalTextPosition(),
|
menuItem.getVerticalTextPosition(), menuItem.getHorizontalTextPosition(),
|
||||||
viewRect, iconRect, textRect, scale( menuItem.getIconTextGap() ) );
|
viewRect, iconRect, textRect, scale( menuItem.getIconTextGap() ) );
|
||||||
@@ -211,7 +297,8 @@ public class FlatMenuItemRenderer
|
|||||||
|
|
||||||
// layout icon and text
|
// layout icon and text
|
||||||
SwingUtilities.layoutCompoundLabel( menuItem,
|
SwingUtilities.layoutCompoundLabel( menuItem,
|
||||||
menuItem.getFontMetrics( menuItem.getFont() ), menuItem.getText(), getIconForLayout(),
|
menuItem.getFontMetrics( isTopLevelMenu ? getTopLevelFont() : menuItem.getFont() ),
|
||||||
|
menuItem.getText(), getIconForLayout(),
|
||||||
menuItem.getVerticalAlignment(), menuItem.getHorizontalAlignment(),
|
menuItem.getVerticalAlignment(), menuItem.getHorizontalAlignment(),
|
||||||
menuItem.getVerticalTextPosition(), menuItem.getHorizontalTextPosition(),
|
menuItem.getVerticalTextPosition(), menuItem.getHorizontalTextPosition(),
|
||||||
labelRect, iconRect, textRect, scale( menuItem.getIconTextGap() ) );
|
labelRect, iconRect, textRect, scale( menuItem.getIconTextGap() ) );
|
||||||
@@ -254,7 +341,7 @@ debug*/
|
|||||||
paintBackground( g, underlineSelection ? underlineSelectionBackground : selectionBackground );
|
paintBackground( g, underlineSelection ? underlineSelectionBackground : selectionBackground );
|
||||||
if( underlineSelection && isArmedOrSelected( menuItem ) )
|
if( underlineSelection && isArmedOrSelected( menuItem ) )
|
||||||
paintUnderlineSelection( g, underlineSelectionColor, underlineSelectionHeight );
|
paintUnderlineSelection( g, underlineSelectionColor, underlineSelectionHeight );
|
||||||
paintIcon( g, iconRect, getIconForPainting(), underlineSelection ? underlineSelectionCheckBackground : checkBackground );
|
paintIcon( g, iconRect, getIconForPainting(), underlineSelection ? underlineSelectionCheckBackground : checkBackground, selectionBackground );
|
||||||
paintText( g, textRect, menuItem.getText(), selectionForeground, disabledForeground );
|
paintText( g, textRect, menuItem.getText(), selectionForeground, disabledForeground );
|
||||||
paintAccelerator( g, accelRect, getAcceleratorText(), acceleratorForeground, acceleratorSelectionForeground, disabledForeground );
|
paintAccelerator( g, accelRect, getAcceleratorText(), acceleratorForeground, acceleratorSelectionForeground, disabledForeground );
|
||||||
if( !isTopLevelMenu( menuItem ) )
|
if( !isTopLevelMenu( menuItem ) )
|
||||||
@@ -301,7 +388,7 @@ debug*/
|
|||||||
return FlatUIUtils.deriveColor( background, baseColor );
|
return FlatUIUtils.deriveColor( background, baseColor );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void paintIcon( Graphics g, Rectangle iconRect, Icon icon, Color checkBackground ) {
|
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,
|
// 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)
|
// then use filled icon background to indicate selection (instead of using checkIcon)
|
||||||
if( menuItem.isSelected() && checkIcon != null && icon != checkIcon ) {
|
if( menuItem.isSelected() && checkIcon != null && icon != checkIcon ) {
|
||||||
@@ -321,9 +408,10 @@ debug*/
|
|||||||
}
|
}
|
||||||
|
|
||||||
int mnemonicIndex = FlatLaf.isShowMnemonics() ? menuItem.getDisplayedMnemonicIndex() : -1;
|
int mnemonicIndex = FlatLaf.isShowMnemonics() ? menuItem.getDisplayedMnemonicIndex() : -1;
|
||||||
Color foreground = (isTopLevelMenu( menuItem ) ? menuItem.getParent() : menuItem).getForeground();
|
boolean isTopLevelMenu = isTopLevelMenu( menuItem );
|
||||||
|
Color foreground = (isTopLevelMenu ? menuItem.getParent() : menuItem).getForeground();
|
||||||
|
|
||||||
paintText( g, menuItem, textRect, text, mnemonicIndex, menuItem.getFont(),
|
paintText( g, menuItem, textRect, text, mnemonicIndex, isTopLevelMenu ? getTopLevelFont() : menuItem.getFont(),
|
||||||
foreground, isUnderlineSelection() ? foreground : selectionForeground, disabledForeground );
|
foreground, isUnderlineSelection() ? foreground : selectionForeground, disabledForeground );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,11 +431,10 @@ debug*/
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// center because the real icon may be smaller than dimension in iconRect
|
// center because the real icon may be smaller than dimension in iconRect
|
||||||
int x = iconRect.x + centerOffset( iconRect.width, icon.getIconWidth() );
|
|
||||||
int y = iconRect.y + centerOffset( iconRect.height, icon.getIconHeight() );
|
int y = iconRect.y + centerOffset( iconRect.height, icon.getIconHeight() );
|
||||||
|
|
||||||
// paint
|
// paint
|
||||||
icon.paintIcon( menuItem, g, x, y );
|
icon.paintIcon( menuItem, g, iconRect.x, y );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void paintText( Graphics g, JMenuItem menuItem,
|
protected static void paintText( Graphics g, JMenuItem menuItem,
|
||||||
@@ -376,12 +463,29 @@ debug*/
|
|||||||
protected static void paintHTMLText( Graphics g, JMenuItem menuItem,
|
protected static void paintHTMLText( Graphics g, JMenuItem menuItem,
|
||||||
Rectangle textRect, View htmlView, Color selectionForeground )
|
Rectangle textRect, View htmlView, Color selectionForeground )
|
||||||
{
|
{
|
||||||
|
// On Windows, using Segoe UI font, Java 15 or older and scaling greater than 1,
|
||||||
|
// the width of the HTML view may be initially too small (because component
|
||||||
|
// is not connected to a GraphicsConfiguration when getPreferredSize() is invoked).
|
||||||
|
// Since Java 16, this seems to be fixed somehow by doing popup menu layout twice.
|
||||||
|
//
|
||||||
|
// If using a too small width for htmlView.paint(), the view would rearrange
|
||||||
|
// its children and wrap them to two lines. To avoid this, use view preferred X span
|
||||||
|
// for painting. Core Lafs actually do the same in class MenuItemLayoutHelper.
|
||||||
|
//
|
||||||
|
// Example HTML text that causes the problem: "<html>some <b>HTML</b> <i>text</i></html>"
|
||||||
|
textRect = new Rectangle( textRect );
|
||||||
|
textRect.width = (int) htmlView.getPreferredSpan( View.X_AXIS );
|
||||||
|
|
||||||
if( isArmedOrSelected( menuItem ) && selectionForeground != null )
|
if( isArmedOrSelected( menuItem ) && selectionForeground != null )
|
||||||
g = new GraphicsProxyWithTextColor( (Graphics2D) g, selectionForeground );
|
g = new GraphicsProxyWithTextColor( (Graphics2D) g, selectionForeground );
|
||||||
|
|
||||||
htmlView.paint( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ), textRect );
|
htmlView.paint( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ), textRect );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if either the menu item is armed (mouse over item)
|
||||||
|
* or it is a {@code JMenu} and selected (shows submenu).
|
||||||
|
*/
|
||||||
protected static boolean isArmedOrSelected( JMenuItem menuItem ) {
|
protected static boolean isArmedOrSelected( JMenuItem menuItem ) {
|
||||||
return menuItem.isArmed() || (menuItem instanceof JMenu && menuItem.isSelected());
|
return menuItem.isArmed() || (menuItem instanceof JMenu && menuItem.isSelected());
|
||||||
}
|
}
|
||||||
@@ -394,6 +498,11 @@ debug*/
|
|||||||
return "underline".equals( UIManager.getString( "MenuItem.selectionType" ) );
|
return "underline".equals( UIManager.getString( "MenuItem.selectionType" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Font getTopLevelFont() {
|
||||||
|
Font font = menuItem.getFont();
|
||||||
|
return (font != menuFont) ? font : menuItem.getParent().getFont();
|
||||||
|
}
|
||||||
|
|
||||||
private Icon getIconForPainting() {
|
private Icon getIconForPainting() {
|
||||||
Icon icon = menuItem.getIcon();
|
Icon icon = menuItem.getIcon();
|
||||||
|
|
||||||
@@ -412,6 +521,12 @@ debug*/
|
|||||||
return pressedIcon;
|
return pressedIcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( isArmedOrSelected( menuItem ) ) {
|
||||||
|
Icon selectedIcon = menuItem.getSelectedIcon();
|
||||||
|
if( selectedIcon != null )
|
||||||
|
return selectedIcon;
|
||||||
|
}
|
||||||
|
|
||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -498,6 +613,44 @@ debug*/
|
|||||||
shiftGlyph = 0x21E7,
|
shiftGlyph = 0x21E7,
|
||||||
commandGlyph = 0x2318;
|
commandGlyph = 0x2318;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the maximum width of all menu item icons in the popup.
|
||||||
|
*/
|
||||||
|
private int getMaxIconsWidth() {
|
||||||
|
if( !verticallyAlignText )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Container parent = menuItem.getParent();
|
||||||
|
if( !(parent instanceof JComponent) )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int maxWidth = FlatClientProperties.clientPropertyInt( (JComponent) parent, KEY_MAX_ICONS_WIDTH, -1 );
|
||||||
|
if( maxWidth >= 0 )
|
||||||
|
return maxWidth;
|
||||||
|
|
||||||
|
maxWidth = 0;
|
||||||
|
|
||||||
|
for( Component c : parent.getComponents() ) {
|
||||||
|
if( !(c instanceof JMenuItem) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Icon icon = ((JMenuItem)c).getIcon();
|
||||||
|
if( icon != null )
|
||||||
|
maxWidth = Math.max( maxWidth, icon.getIconWidth() );
|
||||||
|
}
|
||||||
|
|
||||||
|
((JComponent)parent).putClientProperty( KEY_MAX_ICONS_WIDTH, maxWidth );
|
||||||
|
return maxWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clearClientProperties( Component c ) {
|
||||||
|
if( !(c instanceof JComponent) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
JComponent jc = (JComponent) c;
|
||||||
|
jc.putClientProperty( FlatMenuItemRenderer.KEY_MAX_ICONS_WIDTH, null );
|
||||||
|
}
|
||||||
|
|
||||||
//---- class MinSizeIcon --------------------------------------------------
|
//---- class MinSizeIcon --------------------------------------------------
|
||||||
|
|
||||||
private class MinSizeIcon
|
private class MinSizeIcon
|
||||||
@@ -512,6 +665,7 @@ debug*/
|
|||||||
@Override
|
@Override
|
||||||
public int getIconWidth() {
|
public int getIconWidth() {
|
||||||
int iconWidth = (delegate != null) ? delegate.getIconWidth() : 0;
|
int iconWidth = (delegate != null) ? delegate.getIconWidth() : 0;
|
||||||
|
iconWidth = Math.max( iconWidth, getMaxIconsWidth() );
|
||||||
return Math.max( iconWidth, scale( minimumIconSize.width ) );
|
return Math.max( iconWidth, scale( minimumIconSize.width ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,11 +18,20 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicMenuItemUI;
|
import javax.swing.plaf.basic.BasicMenuItemUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
||||||
|
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}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenuItem}.
|
||||||
@@ -52,15 +61,30 @@ import javax.swing.plaf.basic.BasicMenuItemUI;
|
|||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="selectionBackground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="selectionForeground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="disabledForeground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorForeground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorSelectionForeground" )
|
||||||
|
|
||||||
public class FlatMenuItemUI
|
public class FlatMenuItemUI
|
||||||
extends BasicMenuItemUI
|
extends BasicMenuItemUI
|
||||||
|
implements StyleableUI, StyleableLookupProvider
|
||||||
{
|
{
|
||||||
private FlatMenuItemRenderer renderer;
|
private FlatMenuItemRenderer renderer;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatMenuItemUI();
|
return new FlatMenuItemUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
@@ -74,13 +98,84 @@ public class FlatMenuItemUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
FlatMenuItemRenderer.clearClientProperties( menuItem.getParent() );
|
||||||
renderer = null;
|
renderer = null;
|
||||||
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected FlatMenuItemRenderer createRenderer() {
|
protected FlatMenuItemRenderer createRenderer() {
|
||||||
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
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 ) {
|
||||||
|
return applyStyleProperty( menuItem, this, renderer, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object applyStyleProperty( JMenuItem menuItem, BasicMenuItemUI ui,
|
||||||
|
FlatMenuItemRenderer renderer, String key, Object value )
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return renderer.applyStyleProperty( key, value );
|
||||||
|
} catch ( UnknownStyleException ex ) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( ui, menuItem, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return getStyleableInfos( this, renderer );
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, Class<?>> getStyleableInfos( BasicMenuItemUI ui, FlatMenuItemRenderer renderer ) {
|
||||||
|
Map<String, Class<?>> infos = FlatStylingSupport.getAnnotatedStyleableInfos( ui );
|
||||||
|
infos.putAll( renderer.getStyleableInfos() );
|
||||||
|
return infos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return getStyleableValue( this, renderer, key );
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object getStyleableValue( BasicMenuItemUI ui, FlatMenuItemRenderer renderer, String key ) {
|
||||||
|
Object value = renderer.getStyleableValue( key );
|
||||||
|
if( value == null )
|
||||||
|
value = FlatStylingSupport.getAnnotatedStyleableValue( ui, key );
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public MethodHandles.Lookup getLookupForStyling() {
|
||||||
|
// MethodHandles.lookup() is caller sensitive and must be invoked in this class,
|
||||||
|
// otherwise it is not possible to access protected fields in JRE superclass
|
||||||
|
return MethodHandles.lookup();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
|
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
|
||||||
return renderer.getPreferredMenuItemSize();
|
return renderer.getPreferredMenuItemSize();
|
||||||
|
|||||||
@@ -20,17 +20,29 @@ import java.awt.Color;
|
|||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Rectangle;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
import javax.swing.ButtonModel;
|
import javax.swing.ButtonModel;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JMenu;
|
import javax.swing.JMenu;
|
||||||
|
import javax.swing.JMenuBar;
|
||||||
import javax.swing.JMenuItem;
|
import javax.swing.JMenuItem;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.event.MouseInputListener;
|
import javax.swing.event.MouseInputListener;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import javax.swing.plaf.MenuBarUI;
|
||||||
|
import javax.swing.plaf.basic.BasicMenuItemUI;
|
||||||
import javax.swing.plaf.basic.BasicMenuUI;
|
import javax.swing.plaf.basic.BasicMenuUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenu}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenu}.
|
||||||
@@ -60,26 +72,42 @@ import javax.swing.plaf.basic.BasicMenuUI;
|
|||||||
* <!-- FlatMenuUI -->
|
* <!-- FlatMenuUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault MenuItem.iconTextGap int
|
* @uiDefault MenuItem.iconTextGap int
|
||||||
* @uiDefault MenuBar.hoverBackground Color
|
|
||||||
*
|
*
|
||||||
* <!-- FlatMenuRenderer -->
|
* <!-- FlatMenuRenderer -->
|
||||||
*
|
*
|
||||||
|
* @uiDefault MenuBar.hoverBackground Color
|
||||||
|
* @uiDefault MenuBar.selectionBackground Color
|
||||||
|
* @uiDefault MenuBar.selectionForeground Color
|
||||||
* @uiDefault MenuBar.underlineSelectionBackground Color
|
* @uiDefault MenuBar.underlineSelectionBackground Color
|
||||||
* @uiDefault MenuBar.underlineSelectionColor Color
|
* @uiDefault MenuBar.underlineSelectionColor Color
|
||||||
* @uiDefault MenuBar.underlineSelectionHeight int
|
* @uiDefault MenuBar.underlineSelectionHeight int
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="selectionBackground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="selectionForeground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="disabledForeground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorForeground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorSelectionForeground" )
|
||||||
|
|
||||||
public class FlatMenuUI
|
public class FlatMenuUI
|
||||||
extends BasicMenuUI
|
extends BasicMenuUI
|
||||||
|
implements StyleableUI, StyleableLookupProvider
|
||||||
{
|
{
|
||||||
private Color hoverBackground;
|
|
||||||
private FlatMenuItemRenderer renderer;
|
private FlatMenuItemRenderer renderer;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatMenuUI();
|
return new FlatMenuUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
@@ -88,7 +116,6 @@ public class FlatMenuUI
|
|||||||
|
|
||||||
menuItem.setRolloverEnabled( true );
|
menuItem.setRolloverEnabled( true );
|
||||||
|
|
||||||
hoverBackground = UIManager.getColor( "MenuBar.hoverBackground" );
|
|
||||||
renderer = createRenderer();
|
renderer = createRenderer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,8 +123,9 @@ public class FlatMenuUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
hoverBackground = null;
|
FlatMenuItemRenderer.clearClientProperties( menuItem.getParent() );
|
||||||
renderer = null;
|
renderer = null;
|
||||||
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected FlatMenuItemRenderer createRenderer() {
|
protected FlatMenuItemRenderer createRenderer() {
|
||||||
@@ -129,6 +157,50 @@ 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 ) {
|
||||||
|
return FlatMenuItemUI.applyStyleProperty( menuItem, this, renderer, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return FlatMenuItemUI.getStyleableInfos( this, renderer );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatMenuItemUI.getStyleableValue( this, renderer, key );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public MethodHandles.Lookup getLookupForStyling() {
|
||||||
|
// MethodHandles.lookup() is caller sensitive and must be invoked in this class,
|
||||||
|
// otherwise it is not possible to access protected fields in JRE superclass
|
||||||
|
return MethodHandles.lookup();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getMinimumSize( JComponent c ) {
|
public Dimension getMinimumSize( JComponent c ) {
|
||||||
// avoid that top-level menus (in menu bar) are made smaller if horizontal space is rare
|
// avoid that top-level menus (in menu bar) are made smaller if horizontal space is rare
|
||||||
@@ -153,9 +225,12 @@ public class FlatMenuUI
|
|||||||
protected class FlatMenuRenderer
|
protected class FlatMenuRenderer
|
||||||
extends FlatMenuItemRenderer
|
extends FlatMenuItemRenderer
|
||||||
{
|
{
|
||||||
protected final Color menuBarUnderlineSelectionBackground = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionBackground", underlineSelectionBackground );
|
protected Color hoverBackground = UIManager.getColor( "MenuBar.hoverBackground" );
|
||||||
protected final Color menuBarUnderlineSelectionColor = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionColor", underlineSelectionColor );
|
protected Color menuBarSelectionBackground = UIManager.getColor( "MenuBar.selectionBackground" );
|
||||||
protected final int menuBarUnderlineSelectionHeight = FlatUIUtils.getUIInt( "MenuBar.underlineSelectionHeight", underlineSelectionHeight );
|
protected Color menuBarSelectionForeground = UIManager.getColor( "MenuBar.selectionForeground" );
|
||||||
|
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,
|
protected FlatMenuRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
|
||||||
Font acceleratorFont, String acceleratorDelimiter )
|
Font acceleratorFont, String acceleratorDelimiter )
|
||||||
@@ -165,27 +240,53 @@ public class FlatMenuUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintBackground( Graphics g, Color selectionBackground ) {
|
protected void paintBackground( Graphics g, Color selectionBackground ) {
|
||||||
if( isUnderlineSelection() && ((JMenu)menuItem).isTopLevelMenu() )
|
if( ((JMenu)menuItem).isTopLevelMenu() ) {
|
||||||
selectionBackground = menuBarUnderlineSelectionBackground;
|
if( isUnderlineSelection() )
|
||||||
|
selectionBackground = getStyleFromMenuBarUI( ui -> ui.underlineSelectionBackground, menuBarUnderlineSelectionBackground );
|
||||||
|
else {
|
||||||
|
selectionBackground = getStyleFromMenuBarUI( ui -> ui.selectionBackground,
|
||||||
|
menuBarSelectionBackground != null ? menuBarSelectionBackground : selectionBackground );
|
||||||
|
}
|
||||||
|
|
||||||
ButtonModel model = menuItem.getModel();
|
ButtonModel model = menuItem.getModel();
|
||||||
if( model.isRollover() && !model.isArmed() && !model.isSelected() &&
|
if( model.isRollover() && !model.isArmed() && !model.isSelected() && model.isEnabled() ) {
|
||||||
model.isEnabled() && ((JMenu)menuItem).isTopLevelMenu() )
|
g.setColor( deriveBackground( getStyleFromMenuBarUI( ui -> ui.hoverBackground, hoverBackground ) ) );
|
||||||
{
|
|
||||||
g.setColor( deriveBackground( hoverBackground ) );
|
|
||||||
g.fillRect( 0, 0, menuItem.getWidth(), menuItem.getHeight() );
|
g.fillRect( 0, 0, menuItem.getWidth(), menuItem.getHeight() );
|
||||||
} else
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
super.paintBackground( g, selectionBackground );
|
super.paintBackground( g, selectionBackground );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintText( Graphics g, Rectangle textRect, String text, Color selectionForeground, Color disabledForeground ) {
|
||||||
|
if( ((JMenu)menuItem).isTopLevelMenu() && !isUnderlineSelection() ) {
|
||||||
|
selectionForeground = getStyleFromMenuBarUI( ui -> ui.selectionForeground,
|
||||||
|
menuBarSelectionForeground != null ? menuBarSelectionForeground : selectionForeground );
|
||||||
|
}
|
||||||
|
|
||||||
|
super.paintText( g, textRect, text, selectionForeground, disabledForeground );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintUnderlineSelection( Graphics g, Color underlineSelectionColor, int underlineSelectionHeight ) {
|
protected void paintUnderlineSelection( Graphics g, Color underlineSelectionColor, int underlineSelectionHeight ) {
|
||||||
if( ((JMenu)menuItem).isTopLevelMenu() ) {
|
if( ((JMenu)menuItem).isTopLevelMenu() ) {
|
||||||
underlineSelectionColor = menuBarUnderlineSelectionColor;
|
underlineSelectionColor = getStyleFromMenuBarUI( ui -> ui.underlineSelectionColor, menuBarUnderlineSelectionColor );
|
||||||
underlineSelectionHeight = menuBarUnderlineSelectionHeight;
|
underlineSelectionHeight = getStyleFromMenuBarUI( ui -> (ui.underlineSelectionHeight != -1)
|
||||||
|
? ui.underlineSelectionHeight : null, menuBarUnderlineSelectionHeight );
|
||||||
}
|
}
|
||||||
|
|
||||||
super.paintUnderlineSelection( g, underlineSelectionColor, underlineSelectionHeight );
|
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,109 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 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.io.File;
|
||||||
|
import com.formdev.flatlaf.FlatSystemProperties;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
import com.formdev.flatlaf.util.NativeLibrary;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to load FlatLaf native library (.dll, .so or .dylib),
|
||||||
|
* if available for current operating system and CPU architecture.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
* @since 2.3
|
||||||
|
*/
|
||||||
|
class FlatNativeLibrary
|
||||||
|
{
|
||||||
|
private static NativeLibrary nativeLibrary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads native library (if available) and returns whether loaded successfully.
|
||||||
|
* Returns {@code false} if no native library is available.
|
||||||
|
*/
|
||||||
|
static synchronized boolean isLoaded() {
|
||||||
|
initialize();
|
||||||
|
return (nativeLibrary != null) ? nativeLibrary.isLoaded() : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void initialize() {
|
||||||
|
if( nativeLibrary != null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
String libraryName;
|
||||||
|
if( SystemInfo.isWindows_10_orLater && (SystemInfo.isX86 || SystemInfo.isX86_64) ) {
|
||||||
|
// Windows: requires Windows 10 (x86 or x86_64)
|
||||||
|
|
||||||
|
libraryName = "flatlaf-windows-x86";
|
||||||
|
if( SystemInfo.isX86_64 )
|
||||||
|
libraryName += "_64";
|
||||||
|
|
||||||
|
// load jawt native library
|
||||||
|
if( !SystemInfo.isJava_9_orLater ) {
|
||||||
|
// In Java 8, load jawt.dll (part of JRE) explicitly because it
|
||||||
|
// is not found when running application with <jdk>/bin/java.exe.
|
||||||
|
// When using <jdk>/jre/bin/java.exe, it is found.
|
||||||
|
// jawt.dll is located in <jdk>/jre/bin/.
|
||||||
|
// Java 9 and later do not have this problem.
|
||||||
|
loadJAWT();
|
||||||
|
}
|
||||||
|
} else if( SystemInfo.isLinux && SystemInfo.isX86_64 ) {
|
||||||
|
// Linux: requires x86_64
|
||||||
|
|
||||||
|
libraryName = "flatlaf-linux-x86_64";
|
||||||
|
|
||||||
|
// Load jawt.so (part of JRE) explicitly because it is not found
|
||||||
|
// in all Java versions/distributions.
|
||||||
|
// E.g. not found in Java 13 and later from openjdk.java.net.
|
||||||
|
// There seems to be also differences between distributions.
|
||||||
|
// E.g. Adoptium Java 17 does not need this, but Java 17 from openjdk.java.net does.
|
||||||
|
loadJAWT();
|
||||||
|
} else
|
||||||
|
return; // no native library available for current OS or CPU architecture
|
||||||
|
|
||||||
|
// load native library
|
||||||
|
nativeLibrary = createNativeLibrary( libraryName );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NativeLibrary createNativeLibrary( String libraryName ) {
|
||||||
|
String libraryPath = System.getProperty( FlatSystemProperties.NATIVE_LIBRARY_PATH );
|
||||||
|
if( libraryPath != null ) {
|
||||||
|
File libraryFile = new File( libraryPath, System.mapLibraryName( libraryName ) );
|
||||||
|
if( libraryFile.exists() )
|
||||||
|
return new NativeLibrary( libraryFile, true );
|
||||||
|
else
|
||||||
|
LoggingFacade.INSTANCE.logSevere( "Did not find external library " + libraryFile + ", using extracted library instead", null );
|
||||||
|
}
|
||||||
|
|
||||||
|
return new NativeLibrary( "com/formdev/flatlaf/natives/" + libraryName, null, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loadJAWT() {
|
||||||
|
try {
|
||||||
|
System.loadLibrary( "jawt" );
|
||||||
|
} catch( UnsatisfiedLinkError ex ) {
|
||||||
|
// log error only if native library jawt.dll not already loaded
|
||||||
|
String message = ex.getMessage();
|
||||||
|
if( message == null || !message.contains( "already loaded in another classloader" ) )
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
} catch( Exception ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 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.Point;
|
||||||
|
import java.awt.Toolkit;
|
||||||
|
import java.awt.Window;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.geom.AffineTransform;
|
||||||
|
import javax.swing.JDialog;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Native methods for Linux.
|
||||||
|
* <p>
|
||||||
|
* <b>Note</b>: This is private API. Do not use!
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
class FlatNativeLinuxLibrary
|
||||||
|
{
|
||||||
|
static boolean isLoaded() {
|
||||||
|
return FlatNativeLibrary.isLoaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
// direction for _NET_WM_MOVERESIZE message
|
||||||
|
// see https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html
|
||||||
|
static final int MOVE = 8;
|
||||||
|
|
||||||
|
private static Boolean isXWindowSystem;
|
||||||
|
|
||||||
|
private static boolean isXWindowSystem() {
|
||||||
|
if( isXWindowSystem == null )
|
||||||
|
isXWindowSystem = Toolkit.getDefaultToolkit().getClass().getName().endsWith( ".XToolkit" );
|
||||||
|
return isXWindowSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isWMUtilsSupported( Window window ) {
|
||||||
|
return hasCustomDecoration( window ) && isXWindowSystem() && isLoaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean moveOrResizeWindow( Window window, MouseEvent e, int direction ) {
|
||||||
|
Point pt = scale( window, e.getLocationOnScreen() );
|
||||||
|
return xMoveOrResizeWindow( window, pt.x, pt.y, direction );
|
||||||
|
|
||||||
|
/*
|
||||||
|
try {
|
||||||
|
Class<?> cls = Class.forName( "com.formdev.flatlaf.natives.jna.linux.X11WmUtils" );
|
||||||
|
java.lang.reflect.Method m = cls.getMethod( "xMoveOrResizeWindow", Window.class, int.class, int.class, int.class );
|
||||||
|
return (Boolean) m.invoke( null, window, pt.x, pt.y, direction );
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean showWindowMenu( Window window, MouseEvent e ) {
|
||||||
|
Point pt = scale( window, e.getLocationOnScreen() );
|
||||||
|
return xShowWindowMenu( window, pt.x, pt.y );
|
||||||
|
|
||||||
|
/*
|
||||||
|
try {
|
||||||
|
Class<?> cls = Class.forName( "com.formdev.flatlaf.natives.jna.linux.X11WmUtils" );
|
||||||
|
java.lang.reflect.Method m = cls.getMethod( "xShowWindowMenu", Window.class, int.class, int.class );
|
||||||
|
return (Boolean) m.invoke( null, window, pt.x, pt.y );
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Point scale( Window window, Point pt ) {
|
||||||
|
AffineTransform transform = window.getGraphicsConfiguration().getDefaultTransform();
|
||||||
|
int x = (int) Math.round( pt.x * transform.getScaleX() );
|
||||||
|
int y = (int) Math.round( pt.y * transform.getScaleY() );
|
||||||
|
return new Point( x, y );
|
||||||
|
}
|
||||||
|
|
||||||
|
// X Window System
|
||||||
|
private static native boolean xMoveOrResizeWindow( Window window, int x, int y, int direction );
|
||||||
|
private static native boolean xShowWindowMenu( Window window, int x, int y );
|
||||||
|
|
||||||
|
private static boolean hasCustomDecoration( Window window ) {
|
||||||
|
return (window instanceof JFrame && JFrame.isDefaultLookAndFeelDecorated() && ((JFrame)window).isUndecorated()) ||
|
||||||
|
(window instanceof JDialog && JDialog.isDefaultLookAndFeelDecorated() && ((JDialog)window).isUndecorated());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.Container;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
@@ -24,7 +25,6 @@ import java.util.List;
|
|||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
import javax.swing.JFrame;
|
import javax.swing.JFrame;
|
||||||
import javax.swing.JRootPane;
|
import javax.swing.JRootPane;
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.event.ChangeListener;
|
import javax.swing.event.ChangeListener;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
@@ -42,11 +42,13 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
public class FlatNativeWindowBorder
|
public class FlatNativeWindowBorder
|
||||||
{
|
{
|
||||||
// can use window decorations if:
|
// can use window decorations if:
|
||||||
// - on Windows 10
|
// - on Windows 10 or later
|
||||||
|
// - not if system property "sun.java2d.opengl" is true on Windows 10
|
||||||
// - not when running in JetBrains Projector, Webswing or WinPE
|
// - not when running in JetBrains Projector, Webswing or WinPE
|
||||||
// - not disabled via system property
|
// - not disabled via system property
|
||||||
private static final boolean canUseWindowDecorations =
|
private static final boolean canUseWindowDecorations =
|
||||||
SystemInfo.isWindows_10_orLater &&
|
SystemInfo.isWindows_10_orLater &&
|
||||||
|
(SystemInfo.isWindows_11_orLater || !FlatSystemProperties.getBoolean( "sun.java2d.opengl", false )) &&
|
||||||
!SystemInfo.isProjector &&
|
!SystemInfo.isProjector &&
|
||||||
!SystemInfo.isWebswing &&
|
!SystemInfo.isWebswing &&
|
||||||
!SystemInfo.isWinPE &&
|
!SystemInfo.isWinPE &&
|
||||||
@@ -56,7 +58,7 @@ public class FlatNativeWindowBorder
|
|||||||
private static final boolean canUseJBRCustomDecorations =
|
private static final boolean canUseJBRCustomDecorations =
|
||||||
canUseWindowDecorations &&
|
canUseWindowDecorations &&
|
||||||
SystemInfo.isJetBrainsJVM_11_orLater &&
|
SystemInfo.isJetBrainsJVM_11_orLater &&
|
||||||
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, true );
|
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, false );
|
||||||
|
|
||||||
private static Boolean supported;
|
private static Boolean supported;
|
||||||
private static Provider nativeProvider;
|
private static Provider nativeProvider;
|
||||||
@@ -76,6 +78,11 @@ public class FlatNativeWindowBorder
|
|||||||
if( !isSupported() )
|
if( !isSupported() )
|
||||||
return null;
|
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
|
// Check whether root pane already has a window, which is the case when
|
||||||
// switching from another LaF to FlatLaf.
|
// switching from another LaF to FlatLaf.
|
||||||
// Also check whether the window is displayable, which is required to install
|
// Also check whether the window is displayable, which is required to install
|
||||||
@@ -83,9 +90,8 @@ public class FlatNativeWindowBorder
|
|||||||
// If the window is not displayable, then it was probably closed/disposed but not yet removed
|
// 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().
|
// 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.
|
// It could be also be a window that is currently hidden, but may be shown later.
|
||||||
Window window = SwingUtilities.windowForComponent( rootPane );
|
if( parent instanceof Window && parent.isDisplayable() )
|
||||||
if( window != null && window.isDisplayable() )
|
install( (Window) parent );
|
||||||
install( window );
|
|
||||||
|
|
||||||
// Install FlatLaf native window border, which must be done late,
|
// Install FlatLaf native window border, which must be done late,
|
||||||
// when the native window is already created, because it needs access to the window.
|
// when the native window is already created, because it needs access to the window.
|
||||||
@@ -174,9 +180,9 @@ public class FlatNativeWindowBorder
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// uninstall native window border
|
// uninstall native window border
|
||||||
Window window = SwingUtilities.windowForComponent( rootPane );
|
Container parent = rootPane.getParent();
|
||||||
if( window != null )
|
if( parent instanceof Window )
|
||||||
uninstall( window );
|
uninstall( (Window) parent );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void uninstall( Window window ) {
|
private static void uninstall( Window window ) {
|
||||||
@@ -231,7 +237,8 @@ public class FlatNativeWindowBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight,
|
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight,
|
||||||
List<Rectangle> hitTestSpots, Rectangle appIconBounds )
|
List<Rectangle> hitTestSpots, Rectangle appIconBounds, Rectangle minimizeButtonBounds,
|
||||||
|
Rectangle maximizeButtonBounds, Rectangle closeButtonBounds )
|
||||||
{
|
{
|
||||||
if( canUseJBRCustomDecorations ) {
|
if( canUseJBRCustomDecorations ) {
|
||||||
JBRCustomDecorations.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots );
|
JBRCustomDecorations.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots );
|
||||||
@@ -241,9 +248,8 @@ public class FlatNativeWindowBorder
|
|||||||
if( !isSupported() )
|
if( !isSupported() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
nativeProvider.setTitleBarHeight( window, titleBarHeight );
|
nativeProvider.updateTitleBarInfo( window, titleBarHeight, hitTestSpots,
|
||||||
nativeProvider.setTitleBarHitTestSpots( window, hitTestSpots );
|
appIconBounds, minimizeButtonBounds, maximizeButtonBounds, closeButtonBounds );
|
||||||
nativeProvider.setTitleBarAppIconBounds( window, appIconBounds );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean showWindow( Window window, int cmd ) {
|
static boolean showWindow( Window window, int cmd ) {
|
||||||
@@ -264,7 +270,7 @@ public class FlatNativeWindowBorder
|
|||||||
try {
|
try {
|
||||||
/*
|
/*
|
||||||
Class<?> cls = Class.forName( "com.formdev.flatlaf.natives.jna.windows.FlatWindowsNativeWindowBorder" );
|
Class<?> cls = Class.forName( "com.formdev.flatlaf.natives.jna.windows.FlatWindowsNativeWindowBorder" );
|
||||||
Method m = cls.getMethod( "getInstance" );
|
java.lang.reflect.Method m = cls.getMethod( "getInstance" );
|
||||||
setNativeProvider( (Provider) m.invoke( null ) );
|
setNativeProvider( (Provider) m.invoke( null ) );
|
||||||
*/
|
*/
|
||||||
setNativeProvider( FlatWindowsNativeWindowBorder.getInstance() );
|
setNativeProvider( FlatWindowsNativeWindowBorder.getInstance() );
|
||||||
@@ -273,9 +279,7 @@ public class FlatNativeWindowBorder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @since 1.1.1 */
|
||||||
* @since 1.1.1
|
|
||||||
*/
|
|
||||||
public static void setNativeProvider( Provider provider ) {
|
public static void setNativeProvider( Provider provider ) {
|
||||||
if( nativeProvider != null )
|
if( nativeProvider != null )
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
@@ -290,9 +294,9 @@ public class FlatNativeWindowBorder
|
|||||||
{
|
{
|
||||||
boolean hasCustomDecoration( Window window );
|
boolean hasCustomDecoration( Window window );
|
||||||
void setHasCustomDecoration( Window window, boolean hasCustomDecoration );
|
void setHasCustomDecoration( Window window, boolean hasCustomDecoration );
|
||||||
void setTitleBarHeight( Window window, int titleBarHeight );
|
void updateTitleBarInfo( Window window, int titleBarHeight, List<Rectangle> hitTestSpots,
|
||||||
void setTitleBarHitTestSpots( Window window, List<Rectangle> hitTestSpots );
|
Rectangle appIconBounds, Rectangle minimizeButtonBounds, Rectangle maximizeButtonBounds,
|
||||||
void setTitleBarAppIconBounds( Window window, Rectangle appIconBounds );
|
Rectangle closeButtonBounds );
|
||||||
|
|
||||||
// commands for showWindow(); values must match Win32 API
|
// commands for showWindow(); values must match Win32 API
|
||||||
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
|
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
|
||||||
@@ -311,6 +315,10 @@ public class FlatNativeWindowBorder
|
|||||||
|
|
||||||
//---- class WindowTopBorder -------------------------------------------
|
//---- class WindowTopBorder -------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Window top border used on Windows 10.
|
||||||
|
* No longer needed since Windows 11.
|
||||||
|
*/
|
||||||
static class WindowTopBorder
|
static class WindowTopBorder
|
||||||
extends JBRCustomDecorations.JBRWindowTopBorder
|
extends JBRCustomDecorations.JBRWindowTopBorder
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -19,17 +19,24 @@ package com.formdev.flatlaf.ui;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
|
||||||
import java.awt.GridBagConstraints;
|
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.JComponent;
|
||||||
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JRootPane;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicHTML;
|
import javax.swing.plaf.basic.BasicHTML;
|
||||||
import javax.swing.plaf.basic.BasicOptionPaneUI;
|
import javax.swing.plaf.basic.BasicOptionPaneUI;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.util.SwingUtils;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -75,6 +82,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*
|
*
|
||||||
* <!-- FlatOptionPaneUI -->
|
* <!-- FlatOptionPaneUI -->
|
||||||
*
|
*
|
||||||
|
* @uiDefault OptionPane.showIcon boolean
|
||||||
* @uiDefault OptionPane.iconMessageGap int
|
* @uiDefault OptionPane.iconMessageGap int
|
||||||
* @uiDefault OptionPane.messagePadding int
|
* @uiDefault OptionPane.messagePadding int
|
||||||
* @uiDefault OptionPane.maxCharactersPerLine int
|
* @uiDefault OptionPane.maxCharactersPerLine int
|
||||||
@@ -84,10 +92,12 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
public class FlatOptionPaneUI
|
public class FlatOptionPaneUI
|
||||||
extends BasicOptionPaneUI
|
extends BasicOptionPaneUI
|
||||||
{
|
{
|
||||||
|
/** @since 2 */ protected boolean showIcon;
|
||||||
protected int iconMessageGap;
|
protected int iconMessageGap;
|
||||||
protected int messagePadding;
|
protected int messagePadding;
|
||||||
protected int maxCharactersPerLine;
|
protected int maxCharactersPerLine;
|
||||||
private int focusWidth;
|
private int focusWidth;
|
||||||
|
private boolean sameSizeButtons;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatOptionPaneUI();
|
return new FlatOptionPaneUI();
|
||||||
@@ -97,10 +107,12 @@ public class FlatOptionPaneUI
|
|||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
|
showIcon = UIManager.getBoolean( "OptionPane.showIcon" );
|
||||||
iconMessageGap = UIManager.getInt( "OptionPane.iconMessageGap" );
|
iconMessageGap = UIManager.getInt( "OptionPane.iconMessageGap" );
|
||||||
messagePadding = UIManager.getInt( "OptionPane.messagePadding" );
|
messagePadding = UIManager.getInt( "OptionPane.messagePadding" );
|
||||||
maxCharactersPerLine = UIManager.getInt( "OptionPane.maxCharactersPerLine" );
|
maxCharactersPerLine = UIManager.getInt( "OptionPane.maxCharactersPerLine" );
|
||||||
focusWidth = UIManager.getInt( "Component.focusWidth" );
|
focusWidth = UIManager.getInt( "Component.focusWidth" );
|
||||||
|
sameSizeButtons = FlatUIUtils.getUIBoolean( "OptionPane.sameSizeButtons", true );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -110,6 +122,24 @@ public class FlatOptionPaneUI
|
|||||||
updateChildPanels( optionPane );
|
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
|
@Override
|
||||||
public Dimension getMinimumOptionPaneSize() {
|
public Dimension getMinimumOptionPaneSize() {
|
||||||
return UIScale.scale( super.getMinimumOptionPaneSize() );
|
return UIScale.scale( super.getMinimumOptionPaneSize() );
|
||||||
@@ -127,7 +157,7 @@ public class FlatOptionPaneUI
|
|||||||
|
|
||||||
// set icon-message gap
|
// set icon-message gap
|
||||||
if( iconMessageGap > 0 ) {
|
if( iconMessageGap > 0 ) {
|
||||||
Component iconMessageSeparator = findByName( messageArea, "OptionPane.separator" );
|
Component iconMessageSeparator = SwingUtils.getComponentByName( messageArea, "OptionPane.separator" );
|
||||||
if( iconMessageSeparator != null )
|
if( iconMessageSeparator != null )
|
||||||
iconMessageSeparator.setPreferredSize( new Dimension( UIScale.scale( iconMessageGap ), 1 ) );
|
iconMessageSeparator.setPreferredSize( new Dimension( UIScale.scale( iconMessageGap ), 1 ) );
|
||||||
}
|
}
|
||||||
@@ -157,15 +187,40 @@ public class FlatOptionPaneUI
|
|||||||
cons.insets.bottom = UIScale.scale( messagePadding );
|
cons.insets.bottom = UIScale.scale( messagePadding );
|
||||||
|
|
||||||
// disable line wrapping for HTML
|
// disable line wrapping for HTML
|
||||||
if( msg instanceof String && BasicHTML.isHTMLString( (String) msg ) )
|
if( msg != null &&
|
||||||
|
!(msg instanceof Component) &&
|
||||||
|
!(msg instanceof Object[]) &&
|
||||||
|
!(msg instanceof Icon) )
|
||||||
|
{
|
||||||
|
msg = msg.toString();
|
||||||
|
if( BasicHTML.isHTMLString( (String) msg ) )
|
||||||
maxll = Integer.MAX_VALUE;
|
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 );
|
super.addMessageComponents( container, cons, msg, maxll, internallyCreated );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateChildPanels( Container c ) {
|
private void updateChildPanels( Container c ) {
|
||||||
for( Component child : c.getComponents() ) {
|
for( Component child : c.getComponents() ) {
|
||||||
if( child instanceof JPanel ) {
|
if( child.getClass() == JPanel.class ) {
|
||||||
JPanel panel = (JPanel)child;
|
JPanel panel = (JPanel)child;
|
||||||
|
|
||||||
// make sub-panel non-opaque for OptionPane.background
|
// make sub-panel non-opaque for OptionPane.background
|
||||||
@@ -174,53 +229,16 @@ public class FlatOptionPaneUI
|
|||||||
// use non-UIResource borders to avoid that they are replaced when switching LaF
|
// use non-UIResource borders to avoid that they are replaced when switching LaF
|
||||||
Border border = panel.getBorder();
|
Border border = panel.getBorder();
|
||||||
if( border instanceof UIResource )
|
if( border instanceof UIResource )
|
||||||
panel.setBorder( new NonUIResourceBorder( border ) );
|
panel.setBorder( FlatUIUtils.nonUIResource( border ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( child instanceof Container ) {
|
if( child instanceof Container )
|
||||||
updateChildPanels( (Container) child );
|
updateChildPanels( (Container) child );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private Component findByName( Container c, String name ) {
|
|
||||||
for( Component child : c.getComponents() ) {
|
|
||||||
if( name.equals( child.getName() ) )
|
|
||||||
return child;
|
|
||||||
|
|
||||||
if( child instanceof Container ) {
|
|
||||||
Component c2 = findByName( (Container) child, name );
|
|
||||||
if( c2 != null )
|
|
||||||
return c2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
//---- class NonUIResourceBorder ------------------------------------------
|
|
||||||
|
|
||||||
private static class NonUIResourceBorder
|
|
||||||
implements Border
|
|
||||||
{
|
|
||||||
private final Border delegate;
|
|
||||||
|
|
||||||
NonUIResourceBorder( Border delegate ) {
|
|
||||||
this.delegate = delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
protected boolean getSizeButtonsToSameWidth() {
|
||||||
delegate.paintBorder( c, g, x, y, width, height );
|
return sameSizeButtons;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Insets getBorderInsets( Component c ) {
|
|
||||||
return delegate.getBorderInsets( c );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isBorderOpaque() {
|
|
||||||
return delegate.isBorderOpaque();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,20 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JPanel;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicPanelUI;
|
import javax.swing.plaf.basic.BasicPanelUI;
|
||||||
|
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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JPanel}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JPanel}.
|
||||||
@@ -27,15 +38,128 @@ import javax.swing.plaf.basic.BasicPanelUI;
|
|||||||
*
|
*
|
||||||
* @uiDefault Panel.font Font unused
|
* @uiDefault Panel.font Font unused
|
||||||
* @uiDefault Panel.background Color only used if opaque
|
* @uiDefault Panel.background Color only used if opaque
|
||||||
* @uiDefault Panel.foreground Color
|
* @uiDefault Panel.foreground Color unused
|
||||||
* @uiDefault Panel.border Border
|
* @uiDefault Panel.border Border
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatPanelUI
|
public class FlatPanelUI
|
||||||
extends BasicPanelUI
|
extends BasicPanelUI
|
||||||
|
implements StyleableUI, PropertyChangeListener
|
||||||
{
|
{
|
||||||
|
// only used via styling (not in UI defaults)
|
||||||
|
/** @since 2 */ @Styleable protected int arc = -1;
|
||||||
|
|
||||||
|
private final boolean shared;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return FlatUIUtils.createSharedUI( FlatPanelUI.class, FlatPanelUI::new );
|
return FlatUIUtils.canUseSharedUI( c )
|
||||||
|
? FlatUIUtils.createSharedUI( FlatPanelUI.class, () -> new FlatPanelUI( true ) )
|
||||||
|
: new FlatPanelUI( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected FlatPanelUI( boolean shared ) {
|
||||||
|
this.shared = shared;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
c.addPropertyChangeListener( this );
|
||||||
|
|
||||||
|
installStyle( (JPanel) c );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uninstallUI( JComponent c ) {
|
||||||
|
super.uninstallUI( c );
|
||||||
|
|
||||||
|
c.removePropertyChangeListener( this );
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.0.1 */
|
||||||
|
@Override
|
||||||
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
switch( e.getPropertyName() ) {
|
||||||
|
case FlatClientProperties.STYLE:
|
||||||
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
|
JPanel c = (JPanel) e.getSource();
|
||||||
|
if( shared && FlatStylingSupport.hasStyleProperty( c ) ) {
|
||||||
|
// unshare component UI if necessary
|
||||||
|
// updateUI() invokes installStyle() from installUI()
|
||||||
|
c.updateUI();
|
||||||
|
} else
|
||||||
|
installStyle( c );
|
||||||
|
c.revalidate();
|
||||||
|
c.repaint();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle( JPanel c ) {
|
||||||
|
try {
|
||||||
|
applyStyle( c, FlatStylingSupport.getResolvedStyle( c, "Panel" ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( JPanel c, Object style ) {
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style,
|
||||||
|
(key, value) -> applyStyleProperty( c, key, value ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( JPanel 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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update( Graphics g, JComponent c ) {
|
||||||
|
// fill background
|
||||||
|
if( c.isOpaque() ) {
|
||||||
|
int width = c.getWidth();
|
||||||
|
int height = c.getHeight();
|
||||||
|
int arc = (this.arc >= 0)
|
||||||
|
? this.arc
|
||||||
|
: ((c.getBorder() instanceof FlatLineBorder)
|
||||||
|
? ((FlatLineBorder)c.getBorder()).getArc()
|
||||||
|
: 0);
|
||||||
|
|
||||||
|
// fill background with parent color to avoid garbage in rounded corners
|
||||||
|
if( arc > 0 )
|
||||||
|
FlatUIUtils.paintParentBackground( g, c );
|
||||||
|
|
||||||
|
g.setColor( c.getBackground() );
|
||||||
|
if( arc > 0 ) {
|
||||||
|
// fill rounded rectangle if having rounded corners
|
||||||
|
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
||||||
|
FlatUIUtils.paintComponentBackground( (Graphics2D) g, 0, 0, width, height,
|
||||||
|
0, UIScale.scale( arc ) );
|
||||||
|
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||||
|
} else
|
||||||
|
g.fillRect( 0, 0, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
paint( g, c );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,29 +17,38 @@
|
|||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Dimension;
|
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Rectangle;
|
||||||
import java.awt.Toolkit;
|
import java.awt.Toolkit;
|
||||||
import java.awt.event.FocusListener;
|
|
||||||
import java.awt.event.KeyAdapter;
|
import java.awt.event.KeyAdapter;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
import java.awt.event.KeyListener;
|
import java.awt.event.KeyListener;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.swing.Action;
|
||||||
|
import javax.swing.ActionMap;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JPasswordField;
|
||||||
|
import javax.swing.JToggleButton;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicPasswordFieldUI;
|
import javax.swing.text.DefaultEditorKit;
|
||||||
import javax.swing.text.Caret;
|
import javax.swing.text.Element;
|
||||||
import javax.swing.text.JTextComponent;
|
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.FlatClientProperties;
|
||||||
|
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}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JPasswordField}.
|
||||||
*
|
*
|
||||||
* <!-- BasicPasswordFieldUI -->
|
* <!-- BasicTextFieldUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault PasswordField.font Font
|
* @uiDefault PasswordField.font Font
|
||||||
* @uiDefault PasswordField.background Color
|
* @uiDefault PasswordField.background Color
|
||||||
@@ -52,74 +61,97 @@ 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.inactiveForeground Color used if not enabled (yes, this is confusing; this should be named disabledForeground)
|
||||||
* @uiDefault PasswordField.border Border
|
* @uiDefault PasswordField.border Border
|
||||||
* @uiDefault PasswordField.margin Insets
|
* @uiDefault PasswordField.margin Insets
|
||||||
* @uiDefault PasswordField.echoChar character
|
|
||||||
* @uiDefault PasswordField.caretBlinkRate int default is 500 milliseconds
|
* @uiDefault PasswordField.caretBlinkRate int default is 500 milliseconds
|
||||||
*
|
*
|
||||||
* <!-- FlatPasswordFieldUI -->
|
* <!-- FlatTextFieldUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault Component.minimumWidth int
|
* @uiDefault Component.minimumWidth int
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
* @uiDefault Component.isIntelliJTheme boolean
|
||||||
* @uiDefault PasswordField.placeholderForeground Color
|
* @uiDefault PasswordField.placeholderForeground Color
|
||||||
* @uiDefault PasswordField.focusedBackground Color optional
|
* @uiDefault PasswordField.focusedBackground Color optional
|
||||||
* @uiDefault PasswordField.showCapsLock boolean
|
* @uiDefault PasswordField.iconTextGap int optional, default is 4
|
||||||
* @uiDefault PasswordField.capsLockIcon Icon
|
|
||||||
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
|
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
|
||||||
* @uiDefault TextComponent.selectAllOnMouseClick boolean
|
* @uiDefault TextComponent.selectAllOnMouseClick boolean
|
||||||
*
|
*
|
||||||
|
* <!-- FlatPasswordFieldUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault PasswordField.echoChar character
|
||||||
|
* @uiDefault PasswordField.showCapsLock boolean
|
||||||
|
* @uiDefault PasswordField.showRevealButton boolean
|
||||||
|
* @uiDefault PasswordField.capsLockIcon Icon
|
||||||
|
* @uiDefault PasswordField.revealIcon Icon
|
||||||
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatPasswordFieldUI
|
public class FlatPasswordFieldUI
|
||||||
extends BasicPasswordFieldUI
|
extends FlatTextFieldUI
|
||||||
{
|
{
|
||||||
protected int minimumWidth;
|
// used to preserve reveal button state when switching theme
|
||||||
protected boolean isIntelliJTheme;
|
private static final String KEY_REVEAL_SELECTED = "FlatLaf.internal.FlatPasswordFieldUI.revealSelected";
|
||||||
protected Color placeholderForeground;
|
|
||||||
protected Color focusedBackground;
|
private Character echoChar;
|
||||||
protected boolean showCapsLock;
|
|
||||||
protected Icon capsLockIcon;
|
@Styleable protected boolean showCapsLock;
|
||||||
|
/** @since 2 */ @Styleable protected boolean showRevealButton;
|
||||||
|
protected Icon capsLockIcon;
|
||||||
|
/** @since 2 */ protected Icon revealIcon;
|
||||||
|
|
||||||
private FocusListener focusListener;
|
|
||||||
private KeyListener capsLockListener;
|
private KeyListener capsLockListener;
|
||||||
|
private boolean capsLockIconShared = true;
|
||||||
|
private JToggleButton revealButton;
|
||||||
|
private boolean uninstallEchoChar;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatPasswordFieldUI();
|
return new FlatPasswordFieldUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getPropertyPrefix() {
|
||||||
|
return "PasswordField";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installRevealButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uninstallUI( JComponent c ) {
|
||||||
|
uninstallRevealButton();
|
||||||
|
|
||||||
|
super.uninstallUI( c );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
String prefix = getPropertyPrefix();
|
String prefix = getPropertyPrefix();
|
||||||
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
echoChar = (Character) UIManager.get( prefix + ".echoChar" );
|
||||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
if( echoChar != null )
|
||||||
placeholderForeground = UIManager.getColor( prefix + ".placeholderForeground" );
|
LookAndFeel.installProperty( getComponent(), "echoChar", echoChar );
|
||||||
focusedBackground = UIManager.getColor( prefix + ".focusedBackground" );
|
|
||||||
showCapsLock = UIManager.getBoolean( "PasswordField.showCapsLock" );
|
showCapsLock = UIManager.getBoolean( "PasswordField.showCapsLock" );
|
||||||
|
showRevealButton = UIManager.getBoolean( "PasswordField.showRevealButton" );
|
||||||
capsLockIcon = UIManager.getIcon( "PasswordField.capsLockIcon" );
|
capsLockIcon = UIManager.getIcon( "PasswordField.capsLockIcon" );
|
||||||
|
revealIcon = UIManager.getIcon( "PasswordField.revealIcon" );
|
||||||
LookAndFeel.installProperty( getComponent(), "opaque", false );
|
capsLockIconShared = true;
|
||||||
|
|
||||||
MigLayoutVisualPadding.install( getComponent() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
placeholderForeground = null;
|
|
||||||
focusedBackground = null;
|
|
||||||
capsLockIcon = null;
|
capsLockIcon = null;
|
||||||
|
revealIcon = null;
|
||||||
MigLayoutVisualPadding.uninstall( getComponent() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installListeners() {
|
protected void installListeners() {
|
||||||
super.installListeners();
|
super.installListeners();
|
||||||
|
|
||||||
// necessary to update focus border and background
|
|
||||||
focusListener = new FlatUIUtils.RepaintFocusListener( getComponent(), null );
|
|
||||||
|
|
||||||
// update caps lock indicator
|
// update caps lock indicator
|
||||||
capsLockListener = new KeyAdapter() {
|
capsLockListener = new KeyAdapter() {
|
||||||
@Override
|
@Override
|
||||||
@@ -131,12 +163,13 @@ public class FlatPasswordFieldUI
|
|||||||
repaint( e );
|
repaint( e );
|
||||||
}
|
}
|
||||||
private void repaint( KeyEvent e ) {
|
private void repaint( KeyEvent e ) {
|
||||||
if( e.getKeyCode() == KeyEvent.VK_CAPS_LOCK )
|
if( e.getKeyCode() == KeyEvent.VK_CAPS_LOCK ) {
|
||||||
e.getComponent().repaint();
|
e.getComponent().repaint();
|
||||||
|
scrollCaretToVisible();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
getComponent().addFocusListener( focusListener );
|
|
||||||
getComponent().addKeyListener( capsLockListener );
|
getComponent().addKeyListener( capsLockListener );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,59 +177,216 @@ public class FlatPasswordFieldUI
|
|||||||
protected void uninstallListeners() {
|
protected void uninstallListeners() {
|
||||||
super.uninstallListeners();
|
super.uninstallListeners();
|
||||||
|
|
||||||
getComponent().removeFocusListener( focusListener );
|
|
||||||
getComponent().removeKeyListener( capsLockListener );
|
getComponent().removeKeyListener( capsLockListener );
|
||||||
focusListener = null;
|
|
||||||
capsLockListener = null;
|
capsLockListener = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Caret createCaret() {
|
protected void installKeyboardActions() {
|
||||||
return new FlatCaret( UIManager.getString( "TextComponent.selectAllOnFocusPolicy" ),
|
super.installKeyboardActions();
|
||||||
UIManager.getBoolean( "TextComponent.selectAllOnMouseClick" ) );
|
|
||||||
|
// 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";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void applyStyle( Object style ) {
|
||||||
|
boolean oldShowRevealButton = showRevealButton;
|
||||||
|
|
||||||
|
super.applyStyle( style );
|
||||||
|
|
||||||
|
if( showRevealButton != oldShowRevealButton ) {
|
||||||
|
uninstallRevealButton();
|
||||||
|
installRevealButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @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
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
if( key.equals( "capsLockIconColor" ) && capsLockIcon instanceof FlatCapsLockIcon )
|
||||||
|
return ((FlatCapsLockIcon)capsLockIcon).getStyleableValue( key );
|
||||||
|
|
||||||
|
return super.getStyleableValue( c, key );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View create( Element elem ) {
|
||||||
|
return new PasswordView( elem );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
protected void paintIcons( Graphics g, Rectangle r ) {
|
||||||
|
super.paintIcons( g, r );
|
||||||
|
|
||||||
|
if( isCapsLockVisible() )
|
||||||
|
paintCapsLock( g, r );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void paintCapsLock( Graphics g, Rectangle r ) {
|
||||||
|
JTextComponent c = getComponent();
|
||||||
|
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 boolean hasTrailingIcon() {
|
||||||
|
return super.hasTrailingIcon() || isCapsLockVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
protected int getTrailingIconWidth() {
|
||||||
|
return super.getTrailingIconWidth()
|
||||||
|
+ (isCapsLockVisible() ? capsLockIcon.getIconWidth() + UIScale.scale( iconTextGap ) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 1.4 */
|
||||||
|
protected boolean isCapsLockVisible() {
|
||||||
|
if( !showCapsLock )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return FlatUIUtils.isPermanentFocusOwner( getComponent() ) &&
|
||||||
|
Toolkit.getDefaultToolkit().getLockingKeyState( KeyEvent.VK_CAPS_LOCK );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installRevealButton() {
|
||||||
|
if( showRevealButton ) {
|
||||||
|
revealButton = createRevealButton();
|
||||||
|
updateRevealButton();
|
||||||
|
installLayout();
|
||||||
|
getComponent().add( revealButton );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected JToggleButton createRevealButton() {
|
||||||
|
JPasswordField c = (JPasswordField) getComponent();
|
||||||
|
JToggleButton button = new JToggleButton( revealIcon, !c.echoCharIsSet() );
|
||||||
|
button.setName( "PasswordField.revealButton" );
|
||||||
|
prepareLeadingOrTrailingComponent( button );
|
||||||
|
button.putClientProperty( FlatClientProperties.STYLE_CLASS, "inTextField revealButton" );
|
||||||
|
if( FlatClientProperties.clientPropertyBoolean( c, KEY_REVEAL_SELECTED, false ) ) {
|
||||||
|
button.setSelected( true );
|
||||||
|
updateEchoChar( true );
|
||||||
|
}
|
||||||
|
button.addActionListener( e -> {
|
||||||
|
boolean selected = button.isSelected();
|
||||||
|
updateEchoChar( selected );
|
||||||
|
c.putClientProperty( KEY_REVEAL_SELECTED, selected );
|
||||||
|
} );
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
protected void updateRevealButton() {
|
||||||
|
if( revealButton == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
JTextComponent c = getComponent();
|
||||||
|
boolean visible = c.isEnabled();
|
||||||
|
if( visible != revealButton.isVisible() ) {
|
||||||
|
revealButton.setVisible( visible );
|
||||||
|
c.revalidate();
|
||||||
|
c.repaint();
|
||||||
|
|
||||||
|
if( !visible ) {
|
||||||
|
revealButton.setSelected( false );
|
||||||
|
updateEchoChar( false );
|
||||||
|
getComponent().putClientProperty( KEY_REVEAL_SELECTED, null );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void propertyChange( PropertyChangeEvent e ) {
|
protected void propertyChange( PropertyChangeEvent e ) {
|
||||||
super.propertyChange( e );
|
super.propertyChange( e );
|
||||||
FlatTextFieldUI.propertyChange( getComponent(), e );
|
|
||||||
|
switch( e.getPropertyName() ) {
|
||||||
|
case "enabled":
|
||||||
|
updateRevealButton();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void updateEchoChar( boolean selected ) {
|
||||||
protected void paintSafely( Graphics g ) {
|
char newEchoChar = selected
|
||||||
FlatTextFieldUI.paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground );
|
? 0
|
||||||
FlatTextFieldUI.paintPlaceholder( g, getComponent(), placeholderForeground );
|
: (echoChar != null ? echoChar : '*');
|
||||||
paintCapsLock( g );
|
|
||||||
|
|
||||||
super.paintSafely( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ) );
|
JPasswordField c = (JPasswordField) getComponent();
|
||||||
}
|
if( newEchoChar == c.getEchoChar() )
|
||||||
|
|
||||||
protected void paintCapsLock( Graphics g ) {
|
|
||||||
if( !showCapsLock )
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
JTextComponent c = getComponent();
|
// set echo char
|
||||||
if( !FlatUIUtils.isPermanentFocusOwner( c ) ||
|
LookAndFeel.installProperty( c, "echoChar", newEchoChar );
|
||||||
!Toolkit.getDefaultToolkit().getLockingKeyState( KeyEvent.VK_CAPS_LOCK ) )
|
|
||||||
return;
|
|
||||||
|
|
||||||
int y = (c.getHeight() - capsLockIcon.getIconHeight()) / 2;
|
// check whether was able to set echo char via LookAndFeel.installProperty()
|
||||||
int x = c.getWidth() - capsLockIcon.getIconWidth() - y;
|
// if not, then echo char was explicitly changed via JPasswordField.setEchoChar()
|
||||||
capsLockIcon.paintIcon( c, g, x, y );
|
char actualEchoChar = c.getEchoChar();
|
||||||
|
if( actualEchoChar != newEchoChar ) {
|
||||||
|
if( selected && actualEchoChar != 0 ) {
|
||||||
|
// use explicitly set echo char
|
||||||
|
echoChar = actualEchoChar;
|
||||||
|
uninstallEchoChar = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
c.setEchoChar( newEchoChar );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void uninstallRevealButton() {
|
||||||
|
if( revealButton != null ) {
|
||||||
|
if( uninstallEchoChar && revealButton.isSelected() )
|
||||||
|
((JPasswordField)getComponent()).setEchoChar( echoChar );
|
||||||
|
|
||||||
|
getComponent().remove( revealButton );
|
||||||
|
revealButton = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintBackground( Graphics g ) {
|
protected JComponent[] getTrailingComponents() {
|
||||||
// background is painted elsewhere
|
return new JComponent[] { trailingComponent, revealButton, clearButton };
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
|
||||||
return FlatTextFieldUI.applyMinimumWidth( c, super.getPreferredSize( c ), minimumWidth );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Dimension getMinimumSize( JComponent c ) {
|
|
||||||
return FlatTextFieldUI.applyMinimumWidth( c, super.getMinimumSize( c ), minimumWidth );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,10 +16,12 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.AWTEvent;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
|
import java.awt.EventQueue;
|
||||||
import java.awt.GraphicsConfiguration;
|
import java.awt.GraphicsConfiguration;
|
||||||
import java.awt.GraphicsDevice;
|
import java.awt.GraphicsDevice;
|
||||||
import java.awt.GraphicsEnvironment;
|
import java.awt.GraphicsEnvironment;
|
||||||
@@ -33,7 +35,10 @@ import java.awt.Toolkit;
|
|||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
import java.awt.event.ComponentEvent;
|
import java.awt.event.ComponentEvent;
|
||||||
import java.awt.event.ComponentListener;
|
import java.awt.event.ComponentListener;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JLayeredPane;
|
import javax.swing.JLayeredPane;
|
||||||
@@ -44,6 +49,7 @@ import javax.swing.Popup;
|
|||||||
import javax.swing.PopupFactory;
|
import javax.swing.PopupFactory;
|
||||||
import javax.swing.RootPaneContainer;
|
import javax.swing.RootPaneContainer;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.ToolTipManager;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
@@ -60,8 +66,8 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
public class FlatPopupFactory
|
public class FlatPopupFactory
|
||||||
extends PopupFactory
|
extends PopupFactory
|
||||||
{
|
{
|
||||||
private Method java8getPopupMethod;
|
private MethodHandle java8getPopupMethod;
|
||||||
private Method java9getPopupMethod;
|
private MethodHandle java9getPopupMethod;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Popup getPopup( Component owner, Component contents, int x, int y )
|
public Popup getPopup( Component owner, Component contents, int x, int y )
|
||||||
@@ -120,6 +126,10 @@ public class FlatPopupFactory
|
|||||||
popupWindow.getGraphicsConfiguration() == owner.getGraphicsConfiguration() )
|
popupWindow.getGraphicsConfiguration() == owner.getGraphicsConfiguration() )
|
||||||
return popup;
|
return popup;
|
||||||
|
|
||||||
|
// avoid endless loop (should newer happen; PopupFactory cache size is 5)
|
||||||
|
if( ++count > 10 )
|
||||||
|
return popup;
|
||||||
|
|
||||||
// remove contents component from popup window
|
// remove contents component from popup window
|
||||||
if( popupWindow instanceof JWindow )
|
if( popupWindow instanceof JWindow )
|
||||||
((JWindow)popupWindow).getContentPane().removeAll();
|
((JWindow)popupWindow).getContentPane().removeAll();
|
||||||
@@ -127,10 +137,6 @@ public class FlatPopupFactory
|
|||||||
// dispose unused popup
|
// dispose unused popup
|
||||||
// (do not invoke popup.hide() because this would cache the popup window)
|
// (do not invoke popup.hide() because this would cache the popup window)
|
||||||
popupWindow.dispose();
|
popupWindow.dispose();
|
||||||
|
|
||||||
// avoid endless loop (should newer happen; PopupFactory cache size is 5)
|
|
||||||
if( ++count > 10 )
|
|
||||||
return popup;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,7 +145,7 @@ public class FlatPopupFactory
|
|||||||
* <p>
|
* <p>
|
||||||
* On a dual screen setup, where screens use different scale factors, it may happen
|
* On a dual screen setup, where screens use different scale factors, it may happen
|
||||||
* that the window location changes when showing a heavy weight popup window.
|
* that the window location changes when showing a heavy weight popup window.
|
||||||
* E.g. when opening an dialog on the secondary screen and making combobox popup visible.
|
* E.g. when opening a dialog on the secondary screen and making combobox popup visible.
|
||||||
* <p>
|
* <p>
|
||||||
* This is a workaround for https://bugs.openjdk.java.net/browse/JDK-8224608
|
* This is a workaround for https://bugs.openjdk.java.net/browse/JDK-8224608
|
||||||
*/
|
*/
|
||||||
@@ -188,23 +194,25 @@ public class FlatPopupFactory
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if( SystemInfo.isJava_9_orLater ) {
|
if( SystemInfo.isJava_9_orLater ) {
|
||||||
|
// Java 9: protected Popup getPopup( Component owner, Component contents, int x, int y, boolean isHeavyWeightPopup )
|
||||||
if( java9getPopupMethod == null ) {
|
if( java9getPopupMethod == null ) {
|
||||||
java9getPopupMethod = PopupFactory.class.getDeclaredMethod(
|
MethodType mt = MethodType.methodType( Popup.class, Component.class, Component.class, int.class, int.class, boolean.class );
|
||||||
"getPopup", Component.class, Component.class, int.class, int.class, boolean.class );
|
java9getPopupMethod = MethodHandles.lookup().findVirtual( PopupFactory.class, "getPopup", mt );
|
||||||
}
|
}
|
||||||
return (Popup) java9getPopupMethod.invoke( this, owner, contents, x, y, true );
|
return (Popup) java9getPopupMethod.invoke( this, owner, contents, x, y, true );
|
||||||
} else {
|
} else {
|
||||||
// Java 8
|
// Java 8: private Popup getPopup( Component owner, Component contents, int ownerX, int ownerY, int popupType )
|
||||||
if( java8getPopupMethod == null ) {
|
if( java8getPopupMethod == null ) {
|
||||||
java8getPopupMethod = PopupFactory.class.getDeclaredMethod(
|
Method m = PopupFactory.class.getDeclaredMethod(
|
||||||
"getPopup", Component.class, Component.class, int.class, int.class, int.class );
|
"getPopup", Component.class, Component.class, int.class, int.class, int.class );
|
||||||
java8getPopupMethod.setAccessible( true );
|
m.setAccessible( true );
|
||||||
|
java8getPopupMethod = MethodHandles.lookup().unreflect( m );
|
||||||
}
|
}
|
||||||
return (Popup) java8getPopupMethod.invoke( this, owner, contents, x, y, /*HEAVY_WEIGHT_POPUP*/ 2 );
|
return (Popup) java8getPopupMethod.invoke( this, owner, contents, x, y, /*HEAVY_WEIGHT_POPUP*/ 2 );
|
||||||
}
|
}
|
||||||
} catch( NoSuchMethodException | SecurityException | IllegalAccessException | InvocationTargetException ex ) {
|
} catch( Throwable ex ) {
|
||||||
// ignore
|
// fallback
|
||||||
return null;
|
return super.getPopup( owner, contents, x, y );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +226,7 @@ public class FlatPopupFactory
|
|||||||
* and corrects the y-location so that the tooltip is placed above the mouse location.
|
* 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 ) {
|
private Point fixToolTipLocation( Component owner, Component contents, int x, int y ) {
|
||||||
if( !(contents instanceof JToolTip) || !wasInvokedFromToolTipManager() )
|
if( !(contents instanceof JToolTip) || !wasInvokedFromToolTipManager() || hasTipLocation( owner ) )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
PointerInfo pointerInfo = MouseInfo.getPointerInfo();
|
PointerInfo pointerInfo = MouseInfo.getPointerInfo();
|
||||||
@@ -260,13 +268,36 @@ public class FlatPopupFactory
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean wasInvokedFromToolTipManager() {
|
private boolean wasInvokedFromToolTipManager() {
|
||||||
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
return StackUtils.wasInvokedFrom( ToolTipManager.class.getName(), "showTipWindow", 8 );
|
||||||
for( StackTraceElement stackTraceElement : stackTrace ) {
|
|
||||||
if( "javax.swing.ToolTipManager".equals( stackTraceElement.getClassName() ) &&
|
|
||||||
"showTipWindow".equals( stackTraceElement.getMethodName() ) )
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the owner component returns a tooltip location in
|
||||||
|
* JComponent.getToolTipLocation(MouseEvent).
|
||||||
|
*/
|
||||||
|
private boolean hasTipLocation( Component owner ) {
|
||||||
|
if( !(owner instanceof JComponent) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
AWTEvent e = EventQueue.getCurrentEvent();
|
||||||
|
MouseEvent me;
|
||||||
|
if( e instanceof MouseEvent )
|
||||||
|
me = (MouseEvent) e;
|
||||||
|
else {
|
||||||
|
// no mouse event available because a timer is used to show the tooltip
|
||||||
|
// --> create mouse event from current mouse location
|
||||||
|
PointerInfo pointerInfo = MouseInfo.getPointerInfo();
|
||||||
|
if( pointerInfo == null )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Point location = new Point( pointerInfo.getLocation());
|
||||||
|
SwingUtilities.convertPointFromScreen( location, owner );
|
||||||
|
me = new MouseEvent( owner, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(),
|
||||||
|
0, location.x, location.y, 0, false );
|
||||||
|
}
|
||||||
|
|
||||||
|
return me.getSource() == owner &&
|
||||||
|
((JComponent)owner).getToolTipLocation( me ) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class NonFlashingPopup ---------------------------------------------
|
//---- class NonFlashingPopup ---------------------------------------------
|
||||||
@@ -494,6 +525,9 @@ public class FlatPopupFactory
|
|||||||
JLayeredPane layeredPane = ((RootPaneContainer)window).getLayeredPane();
|
JLayeredPane layeredPane = ((RootPaneContainer)window).getLayeredPane();
|
||||||
layeredPane.add( dropShadowPanel, JLayeredPane.POPUP_LAYER, 0 );
|
layeredPane.add( dropShadowPanel, JLayeredPane.POPUP_LAYER, 0 );
|
||||||
|
|
||||||
|
moveMediumWeightDropShadow();
|
||||||
|
resizeMediumWeightDropShadow();
|
||||||
|
|
||||||
mediumPanelListener = new ComponentListener() {
|
mediumPanelListener = new ComponentListener() {
|
||||||
@Override
|
@Override
|
||||||
public void componentShown( ComponentEvent e ) {
|
public void componentShown( ComponentEvent e ) {
|
||||||
@@ -509,17 +543,12 @@ public class FlatPopupFactory
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void componentMoved( ComponentEvent e ) {
|
public void componentMoved( ComponentEvent e ) {
|
||||||
if( dropShadowPanel != null && mediumWeightPanel != null ) {
|
moveMediumWeightDropShadow();
|
||||||
Point location = mediumWeightPanel.getLocation();
|
|
||||||
Insets insets = dropShadowPanel.getInsets();
|
|
||||||
dropShadowPanel.setLocation( location.x - insets.left, location.y - insets.top );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void componentResized( ComponentEvent e ) {
|
public void componentResized( ComponentEvent e ) {
|
||||||
if( dropShadowPanel != null )
|
resizeMediumWeightDropShadow();
|
||||||
dropShadowPanel.setSize( FlatUIUtils.addInsets( mediumWeightPanel.getSize(), dropShadowPanel.getInsets() ) );
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
mediumWeightPanel.addComponentListener( mediumPanelListener );
|
mediumWeightPanel.addComponentListener( mediumPanelListener );
|
||||||
@@ -535,5 +564,18 @@ public class FlatPopupFactory
|
|||||||
parent.repaint( bounds.x, bounds.y, bounds.width, bounds.height );
|
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;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableBorder;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,12 +37,50 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*/
|
*/
|
||||||
public class FlatPopupMenuBorder
|
public class FlatPopupMenuBorder
|
||||||
extends FlatLineBorder
|
extends FlatLineBorder
|
||||||
|
implements StyleableBorder
|
||||||
{
|
{
|
||||||
|
private Color borderColor;
|
||||||
|
|
||||||
public FlatPopupMenuBorder() {
|
public FlatPopupMenuBorder() {
|
||||||
super( UIManager.getInsets( "PopupMenu.borderInsets" ),
|
super( UIManager.getInsets( "PopupMenu.borderInsets" ),
|
||||||
UIManager.getColor( "PopupMenu.borderColor" ) );
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
switch( key ) {
|
||||||
|
case "borderInsets": return getStyleableValue();
|
||||||
|
case "borderColor": return borderColor;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color getLineColor() {
|
||||||
|
return (borderColor != null) ? borderColor : super.getLineColor();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
if( c instanceof Container &&
|
if( c instanceof Container &&
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ import javax.swing.plaf.ComponentUI;
|
|||||||
*
|
*
|
||||||
* <!-- BasicSeparatorUI -->
|
* <!-- BasicSeparatorUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault PopupMenuSeparator.background Color unused
|
* @uiDefault Separator.background Color unused
|
||||||
* @uiDefault PopupMenuSeparator.foreground Color
|
* @uiDefault Separator.foreground Color
|
||||||
*
|
*
|
||||||
* <!-- FlatSeparatorUI -->
|
* <!-- FlatSeparatorUI -->
|
||||||
*
|
*
|
||||||
@@ -39,11 +39,24 @@ public class FlatPopupMenuSeparatorUI
|
|||||||
extends FlatSeparatorUI
|
extends FlatSeparatorUI
|
||||||
{
|
{
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return FlatUIUtils.createSharedUI( FlatPopupMenuSeparatorUI.class, FlatPopupMenuSeparatorUI::new );
|
return FlatUIUtils.canUseSharedUI( c )
|
||||||
|
? FlatUIUtils.createSharedUI( FlatPopupMenuSeparatorUI.class, () -> new FlatPopupMenuSeparatorUI( true ) )
|
||||||
|
: new FlatPopupMenuSeparatorUI( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected FlatPopupMenuSeparatorUI( boolean shared ) {
|
||||||
|
super( shared );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getPropertyPrefix() {
|
protected String getPropertyPrefix() {
|
||||||
return "PopupMenuSeparator";
|
return "PopupMenuSeparator";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
String getStyleType() {
|
||||||
|
return "PopupMenuSeparator";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,60 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
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.GraphicsConfiguration;
|
||||||
|
import java.awt.GraphicsDevice;
|
||||||
|
import java.awt.GraphicsEnvironment;
|
||||||
|
import java.awt.Insets;
|
||||||
|
import java.awt.LayoutManager;
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.Toolkit;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.event.MouseListener;
|
||||||
|
import java.awt.event.MouseWheelEvent;
|
||||||
|
import java.awt.event.MouseWheelListener;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import javax.swing.BoxLayout;
|
||||||
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JPopupMenu;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JViewport;
|
||||||
|
import javax.swing.MenuElement;
|
||||||
|
import javax.swing.MenuSelectionManager;
|
||||||
|
import javax.swing.Popup;
|
||||||
|
import javax.swing.PopupFactory;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.Timer;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.event.MenuKeyEvent;
|
||||||
|
import javax.swing.event.MenuKeyListener;
|
||||||
|
import javax.swing.event.PopupMenuEvent;
|
||||||
|
import javax.swing.event.PopupMenuListener;
|
||||||
|
import javax.swing.plaf.ButtonUI;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import javax.swing.plaf.UIResource;
|
||||||
|
import javax.swing.plaf.basic.BasicComboPopup;
|
||||||
|
import javax.swing.plaf.basic.BasicMenuItemUI;
|
||||||
import javax.swing.plaf.basic.BasicPopupMenuUI;
|
import javax.swing.plaf.basic.BasicPopupMenuUI;
|
||||||
|
import javax.swing.plaf.basic.DefaultMenuLayout;
|
||||||
|
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.JPopupMenu}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JPopupMenu}.
|
||||||
@@ -30,12 +81,380 @@ import javax.swing.plaf.basic.BasicPopupMenuUI;
|
|||||||
* @uiDefault PopupMenu.foreground Color
|
* @uiDefault PopupMenu.foreground Color
|
||||||
* @uiDefault PopupMenu.border Border
|
* @uiDefault PopupMenu.border Border
|
||||||
*
|
*
|
||||||
|
* <!-- FlatPopupMenuUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||||
|
* @uiDefault PopupMenu.scrollArrowColor Color
|
||||||
|
* @uiDefault PopupMenu.hoverScrollArrowBackground Color optional
|
||||||
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatPopupMenuUI
|
public class FlatPopupMenuUI
|
||||||
extends BasicPopupMenuUI
|
extends BasicPopupMenuUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
|
/** @since 2.1 */ @Styleable protected String arrowType;
|
||||||
|
/** @since 2.1 */ @Styleable protected Color scrollArrowColor;
|
||||||
|
/** @since 2.1 */ @Styleable protected Color hoverScrollArrowBackground;
|
||||||
|
|
||||||
|
private PropertyChangeListener propertyChangeListener;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
private AtomicBoolean borderShared;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatPopupMenuUI();
|
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
|
||||||
|
public void installDefaults() {
|
||||||
|
super.installDefaults();
|
||||||
|
|
||||||
|
arrowType = UIManager.getString( "Component.arrowType" );
|
||||||
|
scrollArrowColor = UIManager.getColor( "PopupMenu.scrollArrowColor" );
|
||||||
|
hoverScrollArrowBackground = UIManager.getColor( "PopupMenu.hoverScrollArrowBackground" );
|
||||||
|
|
||||||
|
LayoutManager layout = popupMenu.getLayout();
|
||||||
|
if( layout == null || layout instanceof UIResource )
|
||||||
|
popupMenu.setLayout( new FlatPopupMenuLayout( popupMenu, BoxLayout.Y_AXIS ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallDefaults() {
|
||||||
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
scrollArrowColor = null;
|
||||||
|
hoverScrollArrowBackground = 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() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, popupMenu.getBorder(), key );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Popup getPopup( JPopupMenu popup, int x, int y ) {
|
||||||
|
// do not add scroller to combobox popups or to popups that already have a scroll pane
|
||||||
|
if( popup instanceof BasicComboPopup ||
|
||||||
|
(popup.getComponentCount() > 0 && popup.getComponent( 0 ) instanceof JScrollPane) )
|
||||||
|
return super.getPopup( popup, x, y );
|
||||||
|
|
||||||
|
// do not add scroller if popup fits into screen
|
||||||
|
Dimension prefSize = popup.getPreferredSize();
|
||||||
|
int screenHeight = getScreenHeightAt( x, y );
|
||||||
|
if( prefSize.height <= screenHeight )
|
||||||
|
return super.getPopup( popup, x, y );
|
||||||
|
|
||||||
|
// create scroller
|
||||||
|
FlatPopupScroller scroller = new FlatPopupScroller( popup );
|
||||||
|
scroller.setPreferredSize( new Dimension( prefSize.width, screenHeight ) );
|
||||||
|
|
||||||
|
// create popup
|
||||||
|
PopupFactory popupFactory = PopupFactory.getSharedInstance();
|
||||||
|
return popupFactory.getPopup( popup.getInvoker(), scroller, x, y );
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getScreenHeightAt( int x, int y ) {
|
||||||
|
// find GraphicsConfiguration at popup location (similar to JPopupMenu.getCurrentGraphicsConfiguration())
|
||||||
|
GraphicsConfiguration gc = null;
|
||||||
|
for( GraphicsDevice device : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices() ) {
|
||||||
|
if( device.getType() == GraphicsDevice.TYPE_RASTER_SCREEN ) {
|
||||||
|
GraphicsConfiguration dgc = device.getDefaultConfiguration();
|
||||||
|
if( dgc.getBounds().contains( x, y ) ) {
|
||||||
|
gc = dgc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( gc == null && popupMenu.getInvoker() != null )
|
||||||
|
gc = popupMenu.getInvoker().getGraphicsConfiguration();
|
||||||
|
|
||||||
|
// compute screen height
|
||||||
|
// (always subtract screen insets because there is no API to detect whether
|
||||||
|
// the popup can overlap the taskbar; see JPopupMenu.canPopupOverlapTaskBar())
|
||||||
|
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
||||||
|
Rectangle screenBounds = (gc != null) ? gc.getBounds() : new Rectangle( toolkit.getScreenSize() );
|
||||||
|
Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets( gc );
|
||||||
|
return screenBounds.height - screenInsets.top - screenInsets.bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class FlatPopupMenuLayout ------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2.4
|
||||||
|
*/
|
||||||
|
protected static class FlatPopupMenuLayout
|
||||||
|
extends DefaultMenuLayout
|
||||||
|
{
|
||||||
|
public FlatPopupMenuLayout( Container target, int axis ) {
|
||||||
|
super( target, axis );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension preferredLayoutSize( Container target ) {
|
||||||
|
FlatMenuItemRenderer.clearClientProperties( target );
|
||||||
|
|
||||||
|
return super.preferredLayoutSize( target );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class FlatPopupScroller --------------------------------------------
|
||||||
|
|
||||||
|
private class FlatPopupScroller
|
||||||
|
extends JPanel
|
||||||
|
implements MouseWheelListener, PopupMenuListener, MenuKeyListener
|
||||||
|
{
|
||||||
|
private final JPopupMenu popup;
|
||||||
|
|
||||||
|
private final JScrollPane scrollPane;
|
||||||
|
private final JButton scrollUpButton;
|
||||||
|
private final JButton scrollDownButton;
|
||||||
|
private int unitIncrement;
|
||||||
|
|
||||||
|
FlatPopupScroller( JPopupMenu popup ) {
|
||||||
|
super( new BorderLayout() );
|
||||||
|
this.popup = popup;
|
||||||
|
|
||||||
|
// this panel is required to avoid that JPopupMenu.setLocation() will be invoked
|
||||||
|
// while scrolling, because this would call JPopupMenu.showPopup()
|
||||||
|
JPanel view = new JPanel( new BorderLayout() );
|
||||||
|
view.add( popup, BorderLayout.CENTER );
|
||||||
|
|
||||||
|
scrollPane = new JScrollPane( view, JScrollPane.VERTICAL_SCROLLBAR_NEVER, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER );
|
||||||
|
scrollPane.setBorder( null );
|
||||||
|
|
||||||
|
scrollUpButton = new ArrowButton( SwingConstants.NORTH );
|
||||||
|
scrollDownButton = new ArrowButton( SwingConstants.SOUTH );
|
||||||
|
|
||||||
|
add( scrollPane, BorderLayout.CENTER );
|
||||||
|
add( scrollUpButton, BorderLayout.NORTH );
|
||||||
|
add( scrollDownButton, BorderLayout.SOUTH );
|
||||||
|
|
||||||
|
setBackground( popup.getBackground() );
|
||||||
|
setBorder( popup.getBorder() );
|
||||||
|
popup.setBorder( null );
|
||||||
|
|
||||||
|
popup.addPopupMenuListener( this );
|
||||||
|
popup.addMouseWheelListener( this );
|
||||||
|
popup.addMenuKeyListener( this );
|
||||||
|
|
||||||
|
updateArrowButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
void scroll( int unitsToScroll ) {
|
||||||
|
if( unitIncrement == 0 )
|
||||||
|
unitIncrement = new JMenuItem( "X" ).getPreferredSize().height;
|
||||||
|
|
||||||
|
JViewport viewport = scrollPane.getViewport();
|
||||||
|
Point viewPosition = viewport.getViewPosition();
|
||||||
|
int newY = viewPosition.y + (unitIncrement * unitsToScroll);
|
||||||
|
if( newY < 0 )
|
||||||
|
newY = 0;
|
||||||
|
else
|
||||||
|
newY = Math.min( newY, viewport.getViewSize().height - viewport.getExtentSize().height );
|
||||||
|
viewport.setViewPosition( new Point( viewPosition.x, newY ) );
|
||||||
|
|
||||||
|
updateArrowButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateArrowButtons() {
|
||||||
|
JViewport viewport = scrollPane.getViewport();
|
||||||
|
Point viewPosition = viewport.getViewPosition();
|
||||||
|
|
||||||
|
scrollUpButton.setVisible( viewPosition.y > 0 );
|
||||||
|
scrollDownButton.setVisible( viewPosition.y < viewport.getViewSize().height - viewport.getExtentSize().height );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- interface PopupMenuListener ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void popupMenuWillBecomeInvisible( PopupMenuEvent e ) {
|
||||||
|
// restore popup border
|
||||||
|
popup.setBorder( getBorder() );
|
||||||
|
|
||||||
|
popup.removePopupMenuListener( this );
|
||||||
|
popup.removeMouseWheelListener( this );
|
||||||
|
popup.removeMenuKeyListener( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void popupMenuWillBecomeVisible( PopupMenuEvent e ) {}
|
||||||
|
@Override public void popupMenuCanceled( PopupMenuEvent e ) {}
|
||||||
|
|
||||||
|
//---- interface MouseWheelListener ----
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll when user rotates mouse wheel.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void mouseWheelMoved( MouseWheelEvent e ) {
|
||||||
|
// convert mouse location before scrolling
|
||||||
|
Point mouseLocation = SwingUtilities.convertPoint( (Component) e.getSource(), e.getPoint(), this );
|
||||||
|
|
||||||
|
// scroll
|
||||||
|
scroll( e.getUnitsToScroll() );
|
||||||
|
|
||||||
|
// select menu item at mouse location
|
||||||
|
Component c = SwingUtilities.getDeepestComponentAt( this, mouseLocation.x, mouseLocation.y );
|
||||||
|
if( c instanceof JMenuItem ) {
|
||||||
|
ButtonUI ui = ((JMenuItem)c).getUI();
|
||||||
|
if( ui instanceof BasicMenuItemUI )
|
||||||
|
MenuSelectionManager.defaultManager().setSelectedPath( ((BasicMenuItemUI)ui).getPath() );
|
||||||
|
}
|
||||||
|
|
||||||
|
// this avoids that the popup is closed when running on Java 8
|
||||||
|
// https://bugs.openjdk.java.net/browse/JDK-8075063
|
||||||
|
e.consume();
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- interface MenuKeyListener ----
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll when user presses Up or Down keys.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void menuKeyPressed( MenuKeyEvent e ) {
|
||||||
|
// use invokeLater() because menu selection is not yet updated because
|
||||||
|
// this listener is invoked before another listener that updates the menu selection
|
||||||
|
EventQueue.invokeLater( () -> {
|
||||||
|
if( !isDisplayable() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
MenuElement[] path = MenuSelectionManager.defaultManager().getSelectedPath();
|
||||||
|
if( path.length == 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// scroll selected menu item to visible area
|
||||||
|
Component c = path[path.length - 1].getComponent();
|
||||||
|
JViewport viewport = scrollPane.getViewport();
|
||||||
|
Point pt = SwingUtilities.convertPoint( c, 0, 0, viewport );
|
||||||
|
viewport.scrollRectToVisible( new Rectangle( pt, c.getSize() ) );
|
||||||
|
|
||||||
|
// update arrow buttons
|
||||||
|
boolean upVisible = scrollUpButton.isVisible();
|
||||||
|
updateArrowButtons();
|
||||||
|
if( !upVisible && scrollUpButton.isVisible() ) {
|
||||||
|
// if "up" button becomes visible, make sure that bottom menu item stays visible
|
||||||
|
Point viewPosition = viewport.getViewPosition();
|
||||||
|
int newY = viewPosition.y + scrollUpButton.getPreferredSize().height;
|
||||||
|
viewport.setViewPosition( new Point( viewPosition.x, newY ) );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void menuKeyTyped( MenuKeyEvent e ) {}
|
||||||
|
@Override public void menuKeyReleased( MenuKeyEvent e ) {}
|
||||||
|
|
||||||
|
//---- class ArrowButton ----------------------------------------------
|
||||||
|
|
||||||
|
private class ArrowButton
|
||||||
|
extends FlatArrowButton
|
||||||
|
implements MouseListener, ActionListener
|
||||||
|
{
|
||||||
|
private Timer timer;
|
||||||
|
|
||||||
|
ArrowButton( int direction ) {
|
||||||
|
super( direction, arrowType, scrollArrowColor, null, null, hoverScrollArrowBackground, null, null );
|
||||||
|
|
||||||
|
addMouseListener( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paint( Graphics g ) {
|
||||||
|
// always fill background to paint over border on HiDPI screens
|
||||||
|
g.setColor( popup.getBackground() );
|
||||||
|
g.fillRect( 0, 0, getWidth(), getHeight() );
|
||||||
|
|
||||||
|
super.paint( g );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- interface MouseListener ----
|
||||||
|
|
||||||
|
@Override public void mouseClicked( MouseEvent e ) {}
|
||||||
|
@Override public void mousePressed( MouseEvent e ) {}
|
||||||
|
@Override public void mouseReleased( MouseEvent e ) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseEntered( MouseEvent e ) {
|
||||||
|
if( timer == null )
|
||||||
|
timer = new Timer( 50, this );
|
||||||
|
timer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseExited( MouseEvent e ) {
|
||||||
|
if( timer != null )
|
||||||
|
timer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- interface ActionListener ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed( ActionEvent e ) {
|
||||||
|
if( timer != null && !isDisplayable() ) {
|
||||||
|
timer.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scroll( direction == SwingConstants.NORTH ? -1 : 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,13 +25,17 @@ import java.awt.Insets;
|
|||||||
import java.awt.geom.Area;
|
import java.awt.geom.Area;
|
||||||
import java.awt.geom.RoundRectangle2D;
|
import java.awt.geom.RoundRectangle2D;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JProgressBar;
|
import javax.swing.JProgressBar;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicProgressBarUI;
|
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.HiDPIUtils;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,17 +62,30 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*/
|
*/
|
||||||
public class FlatProgressBarUI
|
public class FlatProgressBarUI
|
||||||
extends BasicProgressBarUI
|
extends BasicProgressBarUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
protected int arc;
|
@Styleable protected int arc;
|
||||||
protected Dimension horizontalSize;
|
@Styleable protected Dimension horizontalSize;
|
||||||
protected Dimension verticalSize;
|
@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 PropertyChangeListener propertyChangeListener;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatProgressBarUI();
|
return new FlatProgressBarUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
@@ -80,6 +97,13 @@ public class FlatProgressBarUI
|
|||||||
verticalSize = UIManager.getDimension( "ProgressBar.verticalSize" );
|
verticalSize = UIManager.getDimension( "ProgressBar.verticalSize" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallDefaults() {
|
||||||
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installListeners() {
|
protected void installListeners() {
|
||||||
super.installListeners();
|
super.installListeners();
|
||||||
@@ -91,6 +115,13 @@ public class FlatProgressBarUI
|
|||||||
progressBar.revalidate();
|
progressBar.revalidate();
|
||||||
progressBar.repaint();
|
progressBar.repaint();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case STYLE:
|
||||||
|
case STYLE_CLASS:
|
||||||
|
installStyle();
|
||||||
|
progressBar.revalidate();
|
||||||
|
progressBar.repaint();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
progressBar.addPropertyChangeListener( propertyChangeListener );
|
progressBar.addPropertyChangeListener( propertyChangeListener );
|
||||||
@@ -104,11 +135,42 @@ public class FlatProgressBarUI
|
|||||||
propertyChangeListener = null;
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
Dimension size = super.getPreferredSize( 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
|
// recalculate progress height/width to make it smaller
|
||||||
Insets insets = progressBar.getInsets();
|
Insets insets = progressBar.getInsets();
|
||||||
FontMetrics fm = progressBar.getFontMetrics( progressBar.getFont() );
|
FontMetrics fm = progressBar.getFontMetrics( progressBar.getFont() );
|
||||||
@@ -151,7 +213,7 @@ public class FlatProgressBarUI
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
boolean horizontal = (progressBar.getOrientation() == JProgressBar.HORIZONTAL);
|
boolean horizontal = (progressBar.getOrientation() == JProgressBar.HORIZONTAL);
|
||||||
int arc = clientPropertyBoolean( c, PROGRESS_BAR_SQUARE, false )
|
int arc = clientPropertyBoolean( c, PROGRESS_BAR_SQUARE, square )
|
||||||
? 0
|
? 0
|
||||||
: Math.min( UIScale.scale( this.arc ), horizontal ? height : width );
|
: Math.min( UIScale.scale( this.arc ), horizontal ? height : width );
|
||||||
|
|
||||||
|
|||||||
@@ -18,11 +18,19 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import javax.swing.plaf.basic.BasicMenuItemUI;
|
||||||
import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI;
|
import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JRadioButtonMenuItem}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JRadioButtonMenuItem}.
|
||||||
@@ -52,15 +60,30 @@ import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI;
|
|||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="selectionBackground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="selectionForeground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="disabledForeground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorForeground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorSelectionForeground" )
|
||||||
|
|
||||||
public class FlatRadioButtonMenuItemUI
|
public class FlatRadioButtonMenuItemUI
|
||||||
extends BasicRadioButtonMenuItemUI
|
extends BasicRadioButtonMenuItemUI
|
||||||
|
implements StyleableUI, StyleableLookupProvider
|
||||||
{
|
{
|
||||||
private FlatMenuItemRenderer renderer;
|
private FlatMenuItemRenderer renderer;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatRadioButtonMenuItemUI();
|
return new FlatRadioButtonMenuItemUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
@@ -74,13 +97,59 @@ public class FlatRadioButtonMenuItemUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
FlatMenuItemRenderer.clearClientProperties( menuItem.getParent() );
|
||||||
renderer = null;
|
renderer = null;
|
||||||
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected FlatMenuItemRenderer createRenderer() {
|
protected FlatMenuItemRenderer createRenderer() {
|
||||||
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
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 ) {
|
||||||
|
return FlatMenuItemUI.applyStyleProperty( menuItem, this, renderer, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return FlatMenuItemUI.getStyleableInfos( this, renderer );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatMenuItemUI.getStyleableValue( this, renderer, key );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public MethodHandles.Lookup getLookupForStyling() {
|
||||||
|
// MethodHandles.lookup() is caller sensitive and must be invoked in this class,
|
||||||
|
// otherwise it is not possible to access protected fields in JRE superclass
|
||||||
|
return MethodHandles.lookup();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
|
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
|
||||||
return renderer.getPreferredMenuItemSize();
|
return renderer.getPreferredMenuItemSize();
|
||||||
|
|||||||
@@ -18,20 +18,34 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
import static com.formdev.flatlaf.util.UIScale.scale;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.Rectangle;
|
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 java.util.Objects;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
import javax.swing.CellRendererPane;
|
import javax.swing.CellRendererPane;
|
||||||
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import javax.swing.plaf.basic.BasicButtonListener;
|
||||||
import javax.swing.plaf.basic.BasicRadioButtonUI;
|
import javax.swing.plaf.basic.BasicRadioButtonUI;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.icons.FlatCheckBoxIcon;
|
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;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,16 +70,52 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*/
|
*/
|
||||||
public class FlatRadioButtonUI
|
public class FlatRadioButtonUI
|
||||||
extends BasicRadioButtonUI
|
extends BasicRadioButtonUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
protected int iconTextGap;
|
protected int iconTextGap;
|
||||||
protected Color disabledText;
|
@Styleable protected Color disabledText;
|
||||||
|
|
||||||
private Color defaultBackground;
|
private Color defaultBackground;
|
||||||
|
|
||||||
|
private final boolean shared;
|
||||||
|
private boolean iconShared = true;
|
||||||
private boolean defaults_initialized = false;
|
private boolean defaults_initialized = false;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return FlatUIUtils.createSharedUI( FlatRadioButtonUI.class, FlatRadioButtonUI::new );
|
return FlatUIUtils.canUseSharedUI( c ) && !FlatUIUtils.needsLightAWTPeer( 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 ) {
|
||||||
|
if( FlatUIUtils.needsLightAWTPeer( c ) )
|
||||||
|
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
|
||||||
|
else
|
||||||
|
installUIImpl( c );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installUIImpl( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
if( FlatUIUtils.isAWTPeer( c ) )
|
||||||
|
AWTPeerMouseExitedFix.install( c );
|
||||||
|
|
||||||
|
installStyle( (AbstractButton) c );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uninstallUI( JComponent c ) {
|
||||||
|
super.uninstallUI( c );
|
||||||
|
|
||||||
|
if( FlatUIUtils.isAWTPeer( c ) )
|
||||||
|
AWTPeerMouseExitedFix.uninstall( c );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -80,6 +130,7 @@ public class FlatRadioButtonUI
|
|||||||
|
|
||||||
defaultBackground = UIManager.getColor( prefix + "background" );
|
defaultBackground = UIManager.getColor( prefix + "background" );
|
||||||
|
|
||||||
|
iconShared = true;
|
||||||
defaults_initialized = true;
|
defaults_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,11 +144,98 @@ public class FlatRadioButtonUI
|
|||||||
protected void uninstallDefaults( AbstractButton b ) {
|
protected void uninstallDefaults( AbstractButton b ) {
|
||||||
super.uninstallDefaults( b );
|
super.uninstallDefaults( b );
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
|
||||||
MigLayoutVisualPadding.uninstall( b );
|
MigLayoutVisualPadding.uninstall( b );
|
||||||
defaults_initialized = false;
|
defaults_initialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Insets tempInsets = new Insets( 0, 0, 0, 0 );
|
@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 installStyle() 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
// style icon
|
||||||
|
if( key.startsWith( "icon." ) ) {
|
||||||
|
return (icon instanceof FlatCheckBoxIcon)
|
||||||
|
? ((FlatCheckBoxIcon)icon).getStyleableValue( key.substring( "icon.".length() ) )
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Insets tempInsets = new Insets( 0, 0, 0, 0 );
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
@@ -110,7 +248,7 @@ public class FlatRadioButtonUI
|
|||||||
if( focusWidth > 0 ) {
|
if( focusWidth > 0 ) {
|
||||||
// Increase preferred width and height if insets were explicitly reduced (e.g. with
|
// Increase preferred width and height if insets were explicitly reduced (e.g. with
|
||||||
// an EmptyBorder) and icon has a focus width, which is not included in icon size.
|
// an EmptyBorder) and icon has a focus width, which is not included in icon size.
|
||||||
// Otherwise the component may be too small and outer focus border may be cut off.
|
// Otherwise, the component may be too small and outer focus border may be cut off.
|
||||||
Insets insets = c.getInsets( tempInsets );
|
Insets insets = c.getInsets( tempInsets );
|
||||||
size.width += Math.max( focusWidth - insets.left, 0 ) + Math.max( focusWidth - insets.right, 0 );
|
size.width += Math.max( focusWidth - insets.left, 0 ) + Math.max( focusWidth - insets.right, 0 );
|
||||||
size.height += Math.max( focusWidth - insets.top, 0 ) + Math.max( focusWidth - insets.bottom, 0 );
|
size.height += Math.max( focusWidth - insets.top, 0 ) + Math.max( focusWidth - insets.bottom, 0 );
|
||||||
@@ -178,8 +316,97 @@ public class FlatRadioButtonUI
|
|||||||
|
|
||||||
private int getIconFocusWidth( JComponent c ) {
|
private int getIconFocusWidth( JComponent c ) {
|
||||||
AbstractButton b = (AbstractButton) c;
|
AbstractButton b = (AbstractButton) c;
|
||||||
return (b.getIcon() == null && getDefaultIcon() instanceof FlatCheckBoxIcon)
|
Icon icon = b.getIcon();
|
||||||
? UIScale.scale( ((FlatCheckBoxIcon)getDefaultIcon()).focusWidth )
|
if( icon == null )
|
||||||
|
icon = getDefaultIcon();
|
||||||
|
|
||||||
|
return (icon instanceof FlatCheckBoxIcon)
|
||||||
|
? Math.round( UIScale.scale( ((FlatCheckBoxIcon)icon).getFocusWidth() ) )
|
||||||
: 0;
|
: 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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class AWTPeerMouseExitedFix ----------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hack for missing mouse-exited event for java.awt.Checkbox on macOS (to fix hover effect).
|
||||||
|
*
|
||||||
|
* On macOS, AWT components internally use Swing components.
|
||||||
|
* This is implemented in class sun.lwawt.LWCheckboxPeer, which uses
|
||||||
|
* a container component CheckboxDelegate that has a JCheckBox and a JRadioButton
|
||||||
|
* as children. Only one of them is visible.
|
||||||
|
*
|
||||||
|
* The reason that mouse-exited event is not sent to the JCheckBox or JRadioButton
|
||||||
|
* is that sun.lwawt.LWComponentPeer.createDelegateEvent() uses
|
||||||
|
* SwingUtilities.getDeepestComponentAt() to find the event target,
|
||||||
|
* which finds the container component CheckboxDelegate,
|
||||||
|
* which receives the mouse-exited event.
|
||||||
|
*
|
||||||
|
* This class adds listeners and forwards the mouse-exited event
|
||||||
|
* from CheckboxDelegate to JCheckBox or JRadioButton.
|
||||||
|
*/
|
||||||
|
private static class AWTPeerMouseExitedFix
|
||||||
|
extends MouseAdapter
|
||||||
|
implements PropertyChangeListener
|
||||||
|
{
|
||||||
|
private final JComponent button;
|
||||||
|
|
||||||
|
static void install( JComponent button ) {
|
||||||
|
AWTPeerMouseExitedFix l = new AWTPeerMouseExitedFix( button );
|
||||||
|
button.addPropertyChangeListener( "ancestor", l );
|
||||||
|
Container parent = button.getParent();
|
||||||
|
if( parent != null )
|
||||||
|
parent.addMouseListener( l );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uninstall( JComponent button ) {
|
||||||
|
for( PropertyChangeListener l : button.getPropertyChangeListeners( "ancestor" ) ) {
|
||||||
|
if( l instanceof AWTPeerMouseExitedFix ) {
|
||||||
|
button.removePropertyChangeListener( "ancestor", l );
|
||||||
|
Container parent = button.getParent();
|
||||||
|
if( parent != null )
|
||||||
|
parent.removeMouseListener( (AWTPeerMouseExitedFix) l );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AWTPeerMouseExitedFix( JComponent button ) {
|
||||||
|
this.button = button;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
if( e.getOldValue() instanceof Component )
|
||||||
|
((Component)e.getOldValue()).removeMouseListener( this );
|
||||||
|
if( e.getNewValue() instanceof Component ) {
|
||||||
|
((Component)e.getNewValue()).removeMouseListener( this ); // avoid duplicate listeners
|
||||||
|
((Component)e.getNewValue()).addMouseListener( this );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseExited( MouseEvent e ) {
|
||||||
|
button.dispatchEvent( SwingUtilities.convertMouseEvent( e.getComponent(), e, button ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,9 +27,11 @@ import java.awt.Insets;
|
|||||||
import java.awt.LayoutManager;
|
import java.awt.LayoutManager;
|
||||||
import java.awt.LayoutManager2;
|
import java.awt.LayoutManager2;
|
||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
import java.awt.event.HierarchyEvent;
|
import java.awt.event.ComponentAdapter;
|
||||||
import java.awt.event.HierarchyListener;
|
import java.awt.event.ComponentEvent;
|
||||||
|
import java.awt.event.ComponentListener;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
@@ -38,6 +40,7 @@ import javax.swing.JLayeredPane;
|
|||||||
import javax.swing.JMenuBar;
|
import javax.swing.JMenuBar;
|
||||||
import javax.swing.JRootPane;
|
import javax.swing.JRootPane;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
import javax.swing.plaf.BorderUIResource;
|
import javax.swing.plaf.BorderUIResource;
|
||||||
@@ -63,6 +66,9 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*
|
*
|
||||||
* <!-- FlatWindowResizer -->
|
* <!-- FlatWindowResizer -->
|
||||||
*
|
*
|
||||||
|
* @uiDefault RootPane.font Font unused
|
||||||
|
* @uiDefault RootPane.background Color
|
||||||
|
* @uiDefault RootPane.foreground Color unused
|
||||||
* @uiDefault RootPane.borderDragThickness int
|
* @uiDefault RootPane.borderDragThickness int
|
||||||
* @uiDefault RootPane.cornerDragWidth int
|
* @uiDefault RootPane.cornerDragWidth int
|
||||||
* @uiDefault RootPane.honorFrameMinimumSizeOnResize boolean
|
* @uiDefault RootPane.honorFrameMinimumSizeOnResize boolean
|
||||||
@@ -81,7 +87,8 @@ public class FlatRootPaneUI
|
|||||||
|
|
||||||
private Object nativeWindowBorderData;
|
private Object nativeWindowBorderData;
|
||||||
private LayoutManager oldLayout;
|
private LayoutManager oldLayout;
|
||||||
private HierarchyListener hierarchyListener;
|
private PropertyChangeListener ancestorListener;
|
||||||
|
private ComponentListener componentListener;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatRootPaneUI();
|
return new FlatRootPaneUI();
|
||||||
@@ -122,8 +129,23 @@ public class FlatRootPaneUI
|
|||||||
protected void installDefaults( JRootPane c ) {
|
protected void installDefaults( JRootPane c ) {
|
||||||
super.installDefaults( 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
|
// Update background color of JFrame or JDialog parent to avoid bad border
|
||||||
// on HiDPI screens when switching from light to dark Laf.
|
// 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 JFrame is initialized in JFrame.frameInit() and
|
||||||
// the background of JDialog in JDialog.dialogInit(),
|
// the background of JDialog in JDialog.dialogInit(),
|
||||||
// but it was not updated when switching Laf.
|
// but it was not updated when switching Laf.
|
||||||
@@ -139,26 +161,55 @@ public class FlatRootPaneUI
|
|||||||
c.putClientProperty( "jetbrains.awt.windowDarkAppearance", FlatLaf.isLafDark() );
|
c.putClientProperty( "jetbrains.awt.windowDarkAppearance", FlatLaf.isLafDark() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallDefaults( JRootPane c ) {
|
||||||
|
super.uninstallDefaults( c );
|
||||||
|
|
||||||
|
// uninstall background, foreground and font because not all Lafs set them
|
||||||
|
if( c.isBackgroundSet() && c.getBackground() instanceof UIResource )
|
||||||
|
c.setBackground( null );
|
||||||
|
if( c.isForegroundSet() && c.getForeground() instanceof UIResource )
|
||||||
|
c.setForeground( null );
|
||||||
|
if( c.isFontSet() && c.getFont() instanceof UIResource )
|
||||||
|
c.setFont( null );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installListeners( JRootPane root ) {
|
protected void installListeners( JRootPane root ) {
|
||||||
super.installListeners( root );
|
super.installListeners( root );
|
||||||
|
|
||||||
if( SystemInfo.isJava_9_orLater ) {
|
if( SystemInfo.isJava_9_orLater ) {
|
||||||
// On HiDPI screens, where scaling is used, there may be white lines at the
|
// On HiDPI screens, where scaling is used, there may be white lines on the
|
||||||
// bottom and at the right side of the window when it is initially shown.
|
// bottom and on 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.
|
// 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
|
// Seems to be a rounding issue when Swing adds dirty region of window
|
||||||
// using RepaintManager.nativeAddDirtyRegion().
|
// using RepaintManager.nativeAddDirtyRegion().
|
||||||
hierarchyListener = e -> {
|
//
|
||||||
if( (e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0 &&
|
// Note: Not using a HierarchyListener here, which would be much easier,
|
||||||
rootPane.getParent() instanceof Window )
|
// 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
|
// add whole root pane to dirty regions when window is initially shown
|
||||||
rootPane.getParent().repaint( rootPane.getX(), rootPane.getY(),
|
root.getParent().repaint( root.getX(), root.getY(), root.getWidth(), root.getHeight() );
|
||||||
rootPane.getWidth(), rootPane.getHeight() );
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
root.addHierarchyListener( hierarchyListener );
|
}
|
||||||
|
((Window)newValue).addComponentListener( componentListener );
|
||||||
|
} else if( newValue == null && oldValue instanceof Window ) {
|
||||||
|
if( componentListener != null )
|
||||||
|
((Window)oldValue).removeComponentListener( componentListener );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
root.addPropertyChangeListener( "ancestor", ancestorListener );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,29 +218,29 @@ public class FlatRootPaneUI
|
|||||||
super.uninstallListeners( root );
|
super.uninstallListeners( root );
|
||||||
|
|
||||||
if( SystemInfo.isJava_9_orLater ) {
|
if( SystemInfo.isJava_9_orLater ) {
|
||||||
root.removeHierarchyListener( hierarchyListener );
|
if( componentListener != null ) {
|
||||||
hierarchyListener = null;
|
Window window = SwingUtilities.windowForComponent( root );
|
||||||
|
if( window != null )
|
||||||
|
window.removeComponentListener( componentListener );
|
||||||
|
componentListener = null;
|
||||||
|
}
|
||||||
|
root.removePropertyChangeListener( "ancestor", ancestorListener );
|
||||||
|
ancestorListener = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @since 1.1.2 */
|
||||||
* @since 1.1.2
|
|
||||||
*/
|
|
||||||
protected void installNativeWindowBorder() {
|
protected void installNativeWindowBorder() {
|
||||||
nativeWindowBorderData = FlatNativeWindowBorder.install( rootPane );
|
nativeWindowBorderData = FlatNativeWindowBorder.install( rootPane );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @since 1.1.2 */
|
||||||
* @since 1.1.2
|
|
||||||
*/
|
|
||||||
protected void uninstallNativeWindowBorder() {
|
protected void uninstallNativeWindowBorder() {
|
||||||
FlatNativeWindowBorder.uninstall( rootPane, nativeWindowBorderData );
|
FlatNativeWindowBorder.uninstall( rootPane, nativeWindowBorderData );
|
||||||
nativeWindowBorderData = null;
|
nativeWindowBorderData = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @since 1.1.2 */
|
||||||
* @since 1.1.2
|
|
||||||
*/
|
|
||||||
public static void updateNativeWindowBorder( JRootPane rootPane ) {
|
public static void updateNativeWindowBorder( JRootPane rootPane ) {
|
||||||
RootPaneUI rui = rootPane.getUI();
|
RootPaneUI rui = rootPane.getUI();
|
||||||
if( !(rui instanceof FlatRootPaneUI) )
|
if( !(rui instanceof FlatRootPaneUI) )
|
||||||
@@ -293,6 +344,11 @@ public class FlatRootPaneUI
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.TITLE_BAR_SHOW_ICON:
|
||||||
|
if( titlePane != null )
|
||||||
|
titlePane.updateIcon();
|
||||||
|
break;
|
||||||
|
|
||||||
case FlatClientProperties.TITLE_BAR_BACKGROUND:
|
case FlatClientProperties.TITLE_BAR_BACKGROUND:
|
||||||
case FlatClientProperties.TITLE_BAR_FOREGROUND:
|
case FlatClientProperties.TITLE_BAR_FOREGROUND:
|
||||||
if( titlePane != null )
|
if( titlePane != null )
|
||||||
@@ -308,6 +364,12 @@ public class FlatRootPaneUI
|
|||||||
((FlatRootPaneUI)ui).titlePane.isMenuBarEmbedded();
|
((FlatRootPaneUI)ui).titlePane.isMenuBarEmbedded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.4 */
|
||||||
|
protected static FlatTitlePane getTitlePane( JRootPane rootPane ) {
|
||||||
|
RootPaneUI ui = rootPane.getUI();
|
||||||
|
return ui instanceof FlatRootPaneUI ? ((FlatRootPaneUI)ui).titlePane : null;
|
||||||
|
}
|
||||||
|
|
||||||
//---- class FlatRootLayout -----------------------------------------------
|
//---- class FlatRootLayout -----------------------------------------------
|
||||||
|
|
||||||
protected class FlatRootLayout
|
protected class FlatRootLayout
|
||||||
@@ -342,7 +404,7 @@ public class FlatRootPaneUI
|
|||||||
? getSizeFunc.apply( rootPane.getContentPane() )
|
? getSizeFunc.apply( rootPane.getContentPane() )
|
||||||
: rootPane.getSize();
|
: 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;
|
int height = titlePaneSize.height + contentSize.height;
|
||||||
if( titlePane == null || !titlePane.isMenuBarEmbedded() ) {
|
if( titlePane == null || !titlePane.isMenuBarEmbedded() ) {
|
||||||
JMenuBar menuBar = rootPane.getJMenuBar();
|
JMenuBar menuBar = rootPane.getJMenuBar();
|
||||||
@@ -454,7 +516,7 @@ public class FlatRootPaneUI
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
Container parent = c.getParent();
|
Container parent = c.getParent();
|
||||||
boolean active = parent instanceof Window ? ((Window)parent).isActive() : false;
|
boolean active = parent instanceof Window && ((Window)parent).isActive();
|
||||||
|
|
||||||
g.setColor( FlatUIUtils.deriveColor( active ? activeBorderColor : inactiveBorderColor, baseBorderColor ) );
|
g.setColor( FlatUIUtils.deriveColor( active ? activeBorderColor : inactiveBorderColor, baseBorderColor ) );
|
||||||
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
|
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
|
||||||
@@ -466,9 +528,7 @@ public class FlatRootPaneUI
|
|||||||
|
|
||||||
protected boolean isWindowMaximized( Component c ) {
|
protected boolean isWindowMaximized( Component c ) {
|
||||||
Container parent = c.getParent();
|
Container parent = c.getParent();
|
||||||
return parent instanceof Frame
|
return parent instanceof Frame && (((Frame)parent).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0;
|
||||||
? (((Frame)parent).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0
|
|
||||||
: false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Border for various components (e.g. {@link javax.swing.JComboBox}).
|
* Border for various components (e.g. {@link javax.swing.JComboBox}).
|
||||||
@@ -29,7 +30,10 @@ import javax.swing.UIManager;
|
|||||||
public class FlatRoundBorder
|
public class FlatRoundBorder
|
||||||
extends FlatBorder
|
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
|
@Override
|
||||||
protected int getArc( Component c ) {
|
protected int getArc( Component c ) {
|
||||||
@@ -37,6 +41,8 @@ public class FlatRoundBorder
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Boolean roundRect = FlatUIUtils.isRoundRect( c );
|
Boolean roundRect = FlatUIUtils.isRoundRect( c );
|
||||||
|
if( roundRect == null )
|
||||||
|
roundRect = this.roundRect;
|
||||||
return roundRect != null ? (roundRect ? Short.MAX_VALUE : 0) : arc;
|
return roundRect != null ? (roundRect ? Short.MAX_VALUE : 0) : arc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.Container;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
@@ -24,6 +25,8 @@ import java.awt.Rectangle;
|
|||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import javax.swing.InputMap;
|
import javax.swing.InputMap;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
@@ -35,6 +38,13 @@ import javax.swing.UIManager;
|
|||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicScrollBarUI;
|
import javax.swing.plaf.basic.BasicScrollBarUI;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
||||||
|
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;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,7 +53,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* <!-- BasicScrollBarUI -->
|
* <!-- BasicScrollBarUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault ScrollBar.background Color
|
* @uiDefault ScrollBar.background Color
|
||||||
* @uiDefault ScrollBar.foreground Color
|
* @uiDefault ScrollBar.foreground Color unused
|
||||||
* @uiDefault ScrollBar.track Color
|
* @uiDefault ScrollBar.track Color
|
||||||
* @uiDefault ScrollBar.thumb Color
|
* @uiDefault ScrollBar.thumb Color
|
||||||
* @uiDefault ScrollBar.width int
|
* @uiDefault ScrollBar.width int
|
||||||
@@ -53,6 +63,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*
|
*
|
||||||
* <!-- FlatScrollBarUI -->
|
* <!-- FlatScrollBarUI -->
|
||||||
*
|
*
|
||||||
|
* @uiDefault ScrollBar.minimumButtonSize Dimension
|
||||||
* @uiDefault ScrollBar.trackInsets Insets
|
* @uiDefault ScrollBar.trackInsets Insets
|
||||||
* @uiDefault ScrollBar.thumbInsets Insets
|
* @uiDefault ScrollBar.thumbInsets Insets
|
||||||
* @uiDefault ScrollBar.trackArc int
|
* @uiDefault ScrollBar.trackArc int
|
||||||
@@ -72,35 +83,56 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
@StyleableField( cls=BasicScrollBarUI.class, key="track", fieldName="trackColor" )
|
||||||
|
@StyleableField( cls=BasicScrollBarUI.class, key="thumb", fieldName="thumbColor" )
|
||||||
|
@StyleableField( cls=BasicScrollBarUI.class, key="width", fieldName="scrollBarWidth" )
|
||||||
|
@StyleableField( cls=BasicScrollBarUI.class, key="minimumThumbSize" )
|
||||||
|
@StyleableField( cls=BasicScrollBarUI.class, key="maximumThumbSize" )
|
||||||
|
|
||||||
public class FlatScrollBarUI
|
public class FlatScrollBarUI
|
||||||
extends BasicScrollBarUI
|
extends BasicScrollBarUI
|
||||||
|
implements StyleableUI, StyleableLookupProvider
|
||||||
{
|
{
|
||||||
protected Insets trackInsets;
|
// overrides BasicScrollBarUI.supportsAbsolutePositioning (which is private)
|
||||||
protected Insets thumbInsets;
|
@Styleable protected boolean allowsAbsolutePositioning;
|
||||||
protected int trackArc;
|
|
||||||
protected int thumbArc;
|
|
||||||
protected Color hoverTrackColor;
|
|
||||||
protected Color hoverThumbColor;
|
|
||||||
protected boolean hoverThumbWithTrack;
|
|
||||||
protected Color pressedTrackColor;
|
|
||||||
protected Color pressedThumbColor;
|
|
||||||
protected boolean pressedThumbWithTrack;
|
|
||||||
|
|
||||||
protected boolean showButtons;
|
/** @since 2.1 */ @Styleable protected Dimension minimumButtonSize;
|
||||||
protected String arrowType;
|
@Styleable protected Insets trackInsets;
|
||||||
protected Color buttonArrowColor;
|
@Styleable protected Insets thumbInsets;
|
||||||
protected Color buttonDisabledArrowColor;
|
@Styleable protected int trackArc;
|
||||||
protected Color hoverButtonBackground;
|
@Styleable protected int thumbArc;
|
||||||
protected Color pressedButtonBackground;
|
@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;
|
private MouseAdapter hoverListener;
|
||||||
protected boolean hoverTrack;
|
protected boolean hoverTrack;
|
||||||
protected boolean hoverThumb;
|
protected boolean hoverThumb;
|
||||||
|
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
private boolean isAWTPeer;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatScrollBarUI();
|
return new FlatScrollBarUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installListeners() {
|
protected void installListeners() {
|
||||||
super.installListeners();
|
super.installListeners();
|
||||||
@@ -123,6 +155,9 @@ public class FlatScrollBarUI
|
|||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
|
allowsAbsolutePositioning = super.getSupportsAbsolutePositioning();
|
||||||
|
|
||||||
|
minimumButtonSize = UIManager.getDimension( "ScrollBar.minimumButtonSize" );
|
||||||
trackInsets = UIManager.getInsets( "ScrollBar.trackInsets" );
|
trackInsets = UIManager.getInsets( "ScrollBar.trackInsets" );
|
||||||
thumbInsets = UIManager.getInsets( "ScrollBar.thumbInsets" );
|
thumbInsets = UIManager.getInsets( "ScrollBar.thumbInsets" );
|
||||||
trackArc = UIManager.getInt( "ScrollBar.trackArc" );
|
trackArc = UIManager.getInt( "ScrollBar.trackArc" );
|
||||||
@@ -152,6 +187,7 @@ public class FlatScrollBarUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
minimumButtonSize = null;
|
||||||
trackInsets = null;
|
trackInsets = null;
|
||||||
thumbInsets = null;
|
thumbInsets = null;
|
||||||
hoverTrackColor = null;
|
hoverTrackColor = null;
|
||||||
@@ -163,6 +199,8 @@ public class FlatScrollBarUI
|
|||||||
buttonDisabledArrowColor = null;
|
buttonDisabledArrowColor = null;
|
||||||
hoverButtonBackground = null;
|
hoverButtonBackground = null;
|
||||||
pressedButtonBackground = null;
|
pressedButtonBackground = null;
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -177,6 +215,13 @@ public class FlatScrollBarUI
|
|||||||
scrollbar.repaint();
|
scrollbar.repaint();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.STYLE:
|
||||||
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
|
installStyle();
|
||||||
|
scrollbar.revalidate();
|
||||||
|
scrollbar.repaint();
|
||||||
|
break;
|
||||||
|
|
||||||
case "componentOrientation":
|
case "componentOrientation":
|
||||||
// this is missing in BasicScrollBarUI.Handler.propertyChange()
|
// this is missing in BasicScrollBarUI.Handler.propertyChange()
|
||||||
InputMap inputMap = (InputMap) UIManager.get( "ScrollBar.ancestorInputMap" );
|
InputMap inputMap = (InputMap) UIManager.get( "ScrollBar.ancestorInputMap" );
|
||||||
@@ -189,10 +234,85 @@ public class FlatScrollBarUI
|
|||||||
}
|
}
|
||||||
SwingUtilities.replaceUIInputMap( scrollbar, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap );
|
SwingUtilities.replaceUIInputMap( scrollbar, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "ancestor":
|
||||||
|
// check whether scroll bar is used as AWT peer on macOS
|
||||||
|
if( SystemInfo.isMacOS ) {
|
||||||
|
Container p = scrollbar.getParent();
|
||||||
|
for( int i = 0; i < 2 && p != null; i++, p = p.getParent() ) {
|
||||||
|
if( FlatUIUtils.isAWTPeer( p ) ) {
|
||||||
|
// Used to disable hover, which does not work correctly
|
||||||
|
// because scroll bars do not receive mouse exited event.
|
||||||
|
// The scroll pane, including its scroll bars, is not part
|
||||||
|
// of the component hierarchy and does not receive mouse events
|
||||||
|
// directly. Instead LWComponentPeer receives mouse events
|
||||||
|
// and delegates them to peers, but entered/exited events
|
||||||
|
// are sent only for the whole scroll pane.
|
||||||
|
// Exited event is only sent when mouse leaves scroll pane.
|
||||||
|
// If mouse enters/exits scroll bar, no entered/exited events are sent.
|
||||||
|
isAWTPeer = true;
|
||||||
|
|
||||||
|
// if dark theme is active, reinstall using light theme
|
||||||
|
if( FlatLaf.isLafDark() ) {
|
||||||
|
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> {
|
||||||
|
JScrollBar scrollbar = this.scrollbar;
|
||||||
|
uninstallUI( scrollbar );
|
||||||
|
installUI( scrollbar );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 ) {
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, scrollbar, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public MethodHandles.Lookup getLookupForStyling() {
|
||||||
|
// MethodHandles.lookup() is caller sensitive and must be invoked in this class,
|
||||||
|
// otherwise it is not possible to access protected fields in JRE superclass
|
||||||
|
return MethodHandles.lookup();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
return UIScale.scale( super.getPreferredSize( c ) );
|
return UIScale.scale( super.getPreferredSize( c ) );
|
||||||
@@ -209,9 +329,17 @@ public class FlatScrollBarUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isShowButtons() {
|
protected boolean isShowButtons() {
|
||||||
|
// check client property on scroll bar
|
||||||
Object showButtons = scrollbar.getClientProperty( FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS );
|
Object showButtons = scrollbar.getClientProperty( FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS );
|
||||||
if( showButtons == null && scrollbar.getParent() instanceof JScrollPane )
|
if( showButtons == null && scrollbar.getParent() instanceof JScrollPane ) {
|
||||||
showButtons = ((JScrollPane)scrollbar.getParent()).getClientProperty( FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS );
|
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;
|
return (showButtons != null) ? Objects.equals( showButtons, true ) : this.showButtons;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +398,7 @@ public class FlatScrollBarUI
|
|||||||
Color trackColor = FlatUIUtils.deriveColor( this.trackColor, c.getBackground() );
|
Color trackColor = FlatUIUtils.deriveColor( this.trackColor, c.getBackground() );
|
||||||
return (pressed && pressedTrackColor != null)
|
return (pressed && pressedTrackColor != null)
|
||||||
? FlatUIUtils.deriveColor( pressedTrackColor, trackColor )
|
? FlatUIUtils.deriveColor( pressedTrackColor, trackColor )
|
||||||
: ((hover && hoverTrackColor != null)
|
: ((hover && hoverTrackColor != null && !isAWTPeer)
|
||||||
? FlatUIUtils.deriveColor( hoverTrackColor, trackColor )
|
? FlatUIUtils.deriveColor( hoverTrackColor, trackColor )
|
||||||
: trackColor);
|
: trackColor);
|
||||||
}
|
}
|
||||||
@@ -280,7 +408,7 @@ public class FlatScrollBarUI
|
|||||||
Color thumbColor = FlatUIUtils.deriveColor( this.thumbColor, trackColor );
|
Color thumbColor = FlatUIUtils.deriveColor( this.thumbColor, trackColor );
|
||||||
return (pressed && pressedThumbColor != null)
|
return (pressed && pressedThumbColor != null)
|
||||||
? FlatUIUtils.deriveColor( pressedThumbColor, thumbColor )
|
? FlatUIUtils.deriveColor( pressedThumbColor, thumbColor )
|
||||||
: ((hover && hoverThumbColor != null)
|
: ((hover && hoverThumbColor != null && !isAWTPeer)
|
||||||
? FlatUIUtils.deriveColor( hoverThumbColor, thumbColor )
|
? FlatUIUtils.deriveColor( hoverThumbColor, thumbColor )
|
||||||
: thumbColor);
|
: thumbColor);
|
||||||
}
|
}
|
||||||
@@ -295,6 +423,11 @@ public class FlatScrollBarUI
|
|||||||
return UIScale.scale( FlatUIUtils.addInsets( super.getMaximumThumbSize(), thumbInsets ) );
|
return UIScale.scale( FlatUIUtils.addInsets( super.getMaximumThumbSize(), thumbInsets ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getSupportsAbsolutePositioning() {
|
||||||
|
return allowsAbsolutePositioning;
|
||||||
|
}
|
||||||
|
|
||||||
//---- class ScrollBarHoverListener ---------------------------------------
|
//---- class ScrollBarHoverListener ---------------------------------------
|
||||||
|
|
||||||
// using static field to disabling hover for other scroll bars
|
// using static field to disabling hover for other scroll bars
|
||||||
@@ -363,11 +496,27 @@ public class FlatScrollBarUI
|
|||||||
super( direction, type, foreground, disabledForeground,
|
super( direction, type, foreground, disabledForeground,
|
||||||
hoverForeground, hoverBackground, pressedForeground, pressedBackground );
|
hoverForeground, hoverBackground, pressedForeground, pressedBackground );
|
||||||
|
|
||||||
setArrowWidth( FlatArrowButton.DEFAULT_ARROW_WIDTH - 2 );
|
|
||||||
setFocusable( false );
|
setFocusable( false );
|
||||||
setRequestFocusEnabled( false );
|
setRequestFocusEnabled( false );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void updateStyle() {
|
||||||
|
updateStyle( arrowType, buttonArrowColor, buttonDisabledArrowColor,
|
||||||
|
null, hoverButtonBackground, null, pressedButtonBackground );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getArrowWidth() {
|
||||||
|
// scale arrow size depending on scroll bar width
|
||||||
|
// (6 is default arrow width; 10 is base scroll bar width)
|
||||||
|
int arrowWidth = Math.round( 6 * (scrollBarWidth / 10f) );
|
||||||
|
|
||||||
|
// compute arrow size that leaves equal space on both sides (arrow is centered)
|
||||||
|
arrowWidth = scrollBarWidth - (((scrollBarWidth - arrowWidth) / 2) * 2);
|
||||||
|
|
||||||
|
return arrowWidth;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Color deriveBackground( Color background ) {
|
protected Color deriveBackground( Color background ) {
|
||||||
return FlatUIUtils.deriveColor( background, scrollbar.getBackground() );
|
return FlatUIUtils.deriveColor( background, scrollbar.getBackground() );
|
||||||
@@ -376,8 +525,9 @@ public class FlatScrollBarUI
|
|||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize() {
|
public Dimension getPreferredSize() {
|
||||||
if( isShowButtons() ) {
|
if( isShowButtons() ) {
|
||||||
int w = UIScale.scale( scrollBarWidth );
|
int w = UIScale.scale( Math.max( scrollBarWidth, (minimumButtonSize != null) ? minimumButtonSize.width : 0 ) );
|
||||||
return new Dimension( w, w );
|
int h = UIScale.scale( Math.max( scrollBarWidth, (minimumButtonSize != null) ? minimumButtonSize.height : 0 ) );
|
||||||
|
return new Dimension( w, h );
|
||||||
} else
|
} else
|
||||||
return new Dimension();
|
return new Dimension();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ import java.awt.event.MouseWheelEvent;
|
|||||||
import java.awt.event.MouseWheelListener;
|
import java.awt.event.MouseWheelListener;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
@@ -46,6 +48,9 @@ import javax.swing.UIManager;
|
|||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicScrollPaneUI;
|
import javax.swing.plaf.basic.BasicScrollPaneUI;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
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}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JScrollPane}.
|
||||||
@@ -66,20 +71,36 @@ import com.formdev.flatlaf.FlatClientProperties;
|
|||||||
*/
|
*/
|
||||||
public class FlatScrollPaneUI
|
public class FlatScrollPaneUI
|
||||||
extends BasicScrollPaneUI
|
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 Handler handler;
|
||||||
|
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
private AtomicBoolean borderShared;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatScrollPaneUI();
|
return new FlatScrollPaneUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void installUI( JComponent c ) {
|
public void installUI( JComponent c ) {
|
||||||
|
if( FlatUIUtils.needsLightAWTPeer( c ) )
|
||||||
|
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
|
||||||
|
else
|
||||||
|
installUIImpl( c );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installUIImpl( JComponent c ) {
|
||||||
super.installUI( c );
|
super.installUI( c );
|
||||||
|
|
||||||
int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
||||||
LookAndFeel.installProperty( c, "opaque", focusWidth == 0 );
|
LookAndFeel.installProperty( c, "opaque", focusWidth == 0 );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
|
||||||
MigLayoutVisualPadding.install( scrollpane );
|
MigLayoutVisualPadding.install( scrollpane );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,6 +109,9 @@ public class FlatScrollPaneUI
|
|||||||
MigLayoutVisualPadding.uninstall( scrollpane );
|
MigLayoutVisualPadding.uninstall( scrollpane );
|
||||||
|
|
||||||
super.uninstallUI( c );
|
super.uninstallUI( c );
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
borderShared = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -273,6 +297,17 @@ public class FlatScrollPaneUI
|
|||||||
((JButton)corner).setFocusable( false );
|
((JButton)corner).setFocusable( false );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.OUTLINE:
|
||||||
|
scrollpane.repaint();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.STYLE:
|
||||||
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
|
installStyle();
|
||||||
|
scrollpane.revalidate();
|
||||||
|
scrollpane.repaint();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -283,6 +318,44 @@ public class FlatScrollPaneUI
|
|||||||
return handler;
|
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() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, scrollpane.getBorder(), key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void updateViewport( PropertyChangeEvent e ) {
|
protected void updateViewport( PropertyChangeEvent e ) {
|
||||||
super.updateViewport( e );
|
super.updateViewport( e );
|
||||||
@@ -332,9 +405,7 @@ public class FlatScrollPaneUI
|
|||||||
paint( g, c );
|
paint( g, c );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @since 1.3 */
|
||||||
* @since 1.3
|
|
||||||
*/
|
|
||||||
public static boolean isPermanentFocusOwner( JScrollPane scrollPane ) {
|
public static boolean isPermanentFocusOwner( JScrollPane scrollPane ) {
|
||||||
JViewport viewport = scrollPane.getViewport();
|
JViewport viewport = scrollPane.getViewport();
|
||||||
Component view = (viewport != null) ? viewport.getView() : null;
|
Component view = (viewport != null) ? viewport.getView() : null;
|
||||||
|
|||||||
@@ -21,11 +21,18 @@ import java.awt.Dimension;
|
|||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JSeparator;
|
import javax.swing.JSeparator;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicSeparatorUI;
|
import javax.swing.plaf.basic.BasicSeparatorUI;
|
||||||
|
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.JSeparator}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JSeparator}.
|
||||||
@@ -45,15 +52,36 @@ import javax.swing.plaf.basic.BasicSeparatorUI;
|
|||||||
*/
|
*/
|
||||||
public class FlatSeparatorUI
|
public class FlatSeparatorUI
|
||||||
extends BasicSeparatorUI
|
extends BasicSeparatorUI
|
||||||
|
implements StyleableUI, PropertyChangeListener
|
||||||
{
|
{
|
||||||
protected int height;
|
@Styleable protected int height;
|
||||||
protected int stripeWidth;
|
@Styleable protected int stripeWidth;
|
||||||
protected int stripeIndent;
|
@Styleable protected int stripeIndent;
|
||||||
|
|
||||||
|
private final boolean shared;
|
||||||
private boolean defaults_initialized = false;
|
private boolean defaults_initialized = false;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return FlatUIUtils.createSharedUI( FlatSeparatorUI.class, FlatSeparatorUI::new );
|
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
|
@Override
|
||||||
@@ -73,13 +101,81 @@ public class FlatSeparatorUI
|
|||||||
@Override
|
@Override
|
||||||
protected void uninstallDefaults( JSeparator s ) {
|
protected void uninstallDefaults( JSeparator s ) {
|
||||||
super.uninstallDefaults( s );
|
super.uninstallDefaults( s );
|
||||||
|
|
||||||
defaults_initialized = false;
|
defaults_initialized = false;
|
||||||
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getPropertyPrefix() {
|
@Override
|
||||||
|
protected void installListeners( JSeparator s ) {
|
||||||
|
super.installListeners( s );
|
||||||
|
|
||||||
|
s.addPropertyChangeListener( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallListeners( JSeparator s ) {
|
||||||
|
super.uninstallListeners( s );
|
||||||
|
|
||||||
|
s.removePropertyChangeListener( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.0.1 */
|
||||||
|
@Override
|
||||||
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
switch( e.getPropertyName() ) {
|
||||||
|
case FlatClientProperties.STYLE:
|
||||||
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
|
JSeparator s = (JSeparator) e.getSource();
|
||||||
|
if( shared && FlatStylingSupport.hasStyleProperty( s ) ) {
|
||||||
|
// unshare component UI if necessary
|
||||||
|
// updateUI() invokes installStyle() from installUI()
|
||||||
|
s.updateUI();
|
||||||
|
} else
|
||||||
|
installStyle( s );
|
||||||
|
s.revalidate();
|
||||||
|
s.repaint();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @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";
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paint( Graphics g, JComponent c ) {
|
public void paint( Graphics g, JComponent c ) {
|
||||||
Graphics2D g2 = (Graphics2D) g.create();
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
|
|||||||
@@ -18,15 +18,19 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Font;
|
||||||
import java.awt.FontMetrics;
|
import java.awt.FontMetrics;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Insets;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.Shape;
|
import java.awt.Shape;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.awt.geom.Ellipse2D;
|
import java.awt.geom.Ellipse2D;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
import java.awt.geom.RoundRectangle2D;
|
import java.awt.geom.RoundRectangle2D;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JSlider;
|
import javax.swing.JSlider;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
@@ -34,7 +38,11 @@ import javax.swing.SwingUtilities;
|
|||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicSliderUI;
|
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.HiDPIUtils;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,6 +65,8 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault Slider.trackWidth int
|
* @uiDefault Slider.trackWidth int
|
||||||
* @uiDefault Slider.thumbSize Dimension
|
* @uiDefault Slider.thumbSize Dimension
|
||||||
* @uiDefault Slider.focusWidth int
|
* @uiDefault Slider.focusWidth int
|
||||||
|
* @uiDefault Slider.thumbBorderWidth int or float
|
||||||
|
*
|
||||||
* @uiDefault Slider.trackValueColor Color optional; defaults to Slider.thumbColor
|
* @uiDefault Slider.trackValueColor Color optional; defaults to Slider.thumbColor
|
||||||
* @uiDefault Slider.trackColor Color
|
* @uiDefault Slider.trackColor Color
|
||||||
* @uiDefault Slider.thumbColor Color
|
* @uiDefault Slider.thumbColor Color
|
||||||
@@ -73,23 +83,26 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*/
|
*/
|
||||||
public class FlatSliderUI
|
public class FlatSliderUI
|
||||||
extends BasicSliderUI
|
extends BasicSliderUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
protected int trackWidth;
|
@Styleable protected int trackWidth;
|
||||||
protected Dimension thumbSize;
|
@Styleable protected Dimension thumbSize;
|
||||||
protected int focusWidth;
|
@Styleable protected int focusWidth;
|
||||||
|
/** @since 2 */ @Styleable protected float thumbBorderWidth;
|
||||||
|
|
||||||
protected Color trackValueColor;
|
@Styleable protected Color trackValueColor;
|
||||||
protected Color trackColor;
|
@Styleable protected Color trackColor;
|
||||||
protected Color thumbColor;
|
@Styleable protected Color thumbColor;
|
||||||
protected Color thumbBorderColor;
|
@Styleable protected Color thumbBorderColor;
|
||||||
protected Color focusBaseColor;
|
protected Color focusBaseColor;
|
||||||
protected Color focusedColor;
|
@Styleable protected Color focusedColor;
|
||||||
protected Color focusedThumbBorderColor;
|
@Styleable protected Color focusedThumbBorderColor;
|
||||||
protected Color hoverThumbColor;
|
@Styleable protected Color hoverThumbColor;
|
||||||
protected Color pressedThumbColor;
|
@Styleable protected Color pressedThumbColor;
|
||||||
protected Color disabledTrackColor;
|
@Styleable protected Color disabledTrackColor;
|
||||||
protected Color disabledThumbColor;
|
@Styleable protected Color disabledThumbColor;
|
||||||
protected Color disabledThumbBorderColor;
|
@Styleable protected Color disabledThumbBorderColor;
|
||||||
|
@Styleable protected Color tickColor;
|
||||||
|
|
||||||
private Color defaultBackground;
|
private Color defaultBackground;
|
||||||
private Color defaultForeground;
|
private Color defaultForeground;
|
||||||
@@ -98,6 +111,7 @@ public class FlatSliderUI
|
|||||||
protected boolean thumbPressed;
|
protected boolean thumbPressed;
|
||||||
|
|
||||||
private Object[] oldRenderingHints;
|
private Object[] oldRenderingHints;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatSliderUI();
|
return new FlatSliderUI();
|
||||||
@@ -107,6 +121,13 @@ public class FlatSliderUI
|
|||||||
super( null );
|
super( null );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults( JSlider slider ) {
|
protected void installDefaults( JSlider slider ) {
|
||||||
super.installDefaults( slider );
|
super.installDefaults( slider );
|
||||||
@@ -121,6 +142,7 @@ public class FlatSliderUI
|
|||||||
thumbSize = new Dimension( thumbWidth, thumbWidth );
|
thumbSize = new Dimension( thumbWidth, thumbWidth );
|
||||||
}
|
}
|
||||||
focusWidth = FlatUIUtils.getUIInt( "Slider.focusWidth", 4 );
|
focusWidth = FlatUIUtils.getUIInt( "Slider.focusWidth", 4 );
|
||||||
|
thumbBorderWidth = FlatUIUtils.getUIFloat( "Slider.thumbBorderWidth", 1 );
|
||||||
|
|
||||||
trackValueColor = FlatUIUtils.getUIColor( "Slider.trackValueColor", "Slider.thumbColor" );
|
trackValueColor = FlatUIUtils.getUIColor( "Slider.trackValueColor", "Slider.thumbColor" );
|
||||||
trackColor = UIManager.getColor( "Slider.trackColor" );
|
trackColor = UIManager.getColor( "Slider.trackColor" );
|
||||||
@@ -134,6 +156,7 @@ public class FlatSliderUI
|
|||||||
disabledTrackColor = UIManager.getColor( "Slider.disabledTrackColor" );
|
disabledTrackColor = UIManager.getColor( "Slider.disabledTrackColor" );
|
||||||
disabledThumbColor = UIManager.getColor( "Slider.disabledThumbColor" );
|
disabledThumbColor = UIManager.getColor( "Slider.disabledThumbColor" );
|
||||||
disabledThumbBorderColor = FlatUIUtils.getUIColor( "Slider.disabledThumbBorderColor", "Component.disabledBorderColor" );
|
disabledThumbBorderColor = FlatUIUtils.getUIColor( "Slider.disabledThumbBorderColor", "Component.disabledBorderColor" );
|
||||||
|
tickColor = FlatUIUtils.getUIColor( "Slider.tickColor", Color.BLACK ); // see BasicSliderUI.paintTicks()
|
||||||
|
|
||||||
defaultBackground = UIManager.getColor( "Slider.background" );
|
defaultBackground = UIManager.getColor( "Slider.background" );
|
||||||
defaultForeground = UIManager.getColor( "Slider.foreground" );
|
defaultForeground = UIManager.getColor( "Slider.foreground" );
|
||||||
@@ -155,9 +178,12 @@ public class FlatSliderUI
|
|||||||
disabledTrackColor = null;
|
disabledTrackColor = null;
|
||||||
disabledThumbColor = null;
|
disabledThumbColor = null;
|
||||||
disabledThumbBorderColor = null;
|
disabledThumbBorderColor = null;
|
||||||
|
tickColor = null;
|
||||||
|
|
||||||
defaultBackground = null;
|
defaultBackground = null;
|
||||||
defaultForeground = null;
|
defaultForeground = null;
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -165,6 +191,43 @@ public class FlatSliderUI
|
|||||||
return new FlatTrackListener();
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getBaseline( JComponent c, int width, int height ) {
|
public int getBaseline( JComponent c, int width, int height ) {
|
||||||
if( c == null )
|
if( c == null )
|
||||||
@@ -176,9 +239,27 @@ public class FlatSliderUI
|
|||||||
if( slider.getOrientation() == JSlider.VERTICAL )
|
if( slider.getOrientation() == JSlider.VERTICAL )
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
// use default font (instead of slider font) because the slider font size
|
||||||
|
// may be different to label font size, but we want to 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
|
// compute a baseline so that the track is vertically centered
|
||||||
FontMetrics fm = slider.getFontMetrics( slider.getFont() );
|
return trackY + Math.round( (trackHeight - fm.getHeight()) / 2f ) + fm.getAscent() - 1;
|
||||||
return trackRect.y + Math.round( (trackRect.height - fm.getHeight()) / 2f ) + fm.getAscent() - 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -306,6 +387,19 @@ debug*/
|
|||||||
((Graphics2D)g).fill( track );
|
((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
|
@Override
|
||||||
public void paintThumb( Graphics g ) {
|
public void paintThumb( Graphics g ) {
|
||||||
Color thumbColor = getThumbColor();
|
Color thumbColor = getThumbColor();
|
||||||
@@ -321,11 +415,11 @@ debug*/
|
|||||||
Color focusedColor = FlatUIUtils.deriveColor( this.focusedColor,
|
Color focusedColor = FlatUIUtils.deriveColor( this.focusedColor,
|
||||||
(foreground != defaultForeground) ? foreground : focusBaseColor );
|
(foreground != defaultForeground) ? foreground : focusBaseColor );
|
||||||
|
|
||||||
paintThumb( g, slider, thumbRect, isRoundThumb(), color, borderColor, focusedColor, focusWidth );
|
paintThumb( g, slider, thumbRect, isRoundThumb(), color, borderColor, focusedColor, thumbBorderWidth, focusWidth );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void paintThumb( Graphics g, JSlider slider, Rectangle thumbRect, boolean roundThumb,
|
public static void paintThumb( Graphics g, JSlider slider, Rectangle thumbRect, boolean roundThumb,
|
||||||
Color thumbColor, Color thumbBorderColor, Color focusedColor, int focusWidth )
|
Color thumbColor, Color thumbBorderColor, Color focusedColor, float thumbBorderWidth, int focusWidth )
|
||||||
{
|
{
|
||||||
double systemScaleFactor = UIScale.getSystemScaleFactor( (Graphics2D) g );
|
double systemScaleFactor = UIScale.getSystemScaleFactor( (Graphics2D) g );
|
||||||
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
|
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
|
||||||
@@ -334,18 +428,20 @@ debug*/
|
|||||||
(g2d, x2, y2, width2, height2, scaleFactor) -> {
|
(g2d, x2, y2, width2, height2, scaleFactor) -> {
|
||||||
paintThumbImpl( g, slider, x2, y2, width2, height2,
|
paintThumbImpl( g, slider, x2, y2, width2, height2,
|
||||||
roundThumb, thumbColor, thumbBorderColor, focusedColor,
|
roundThumb, thumbColor, thumbBorderColor, focusedColor,
|
||||||
|
(float) (thumbBorderWidth * scaleFactor),
|
||||||
(float) (focusWidth * scaleFactor) );
|
(float) (focusWidth * scaleFactor) );
|
||||||
} );
|
} );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
paintThumbImpl( g, slider, thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height,
|
paintThumbImpl( g, slider, thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height,
|
||||||
roundThumb, thumbColor, thumbBorderColor, focusedColor, focusWidth );
|
roundThumb, thumbColor, thumbBorderColor, focusedColor, thumbBorderWidth, focusWidth );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void paintThumbImpl( Graphics g, JSlider slider, int x, int y, int width, int height,
|
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 focusWidth )
|
boolean roundThumb, Color thumbColor, Color thumbBorderColor, Color focusedColor,
|
||||||
|
float thumbBorderWidth, float focusWidth )
|
||||||
{
|
{
|
||||||
int fw = Math.round( UIScale.scale( focusWidth ) );
|
int fw = Math.round( UIScale.scale( focusWidth ) );
|
||||||
int tx = x + fw;
|
int tx = x + fw;
|
||||||
@@ -367,7 +463,7 @@ debug*/
|
|||||||
((Graphics2D)g).fill( createRoundThumbShape( tx, ty, tw, th ) );
|
((Graphics2D)g).fill( createRoundThumbShape( tx, ty, tw, th ) );
|
||||||
|
|
||||||
// paint thumb background
|
// paint thumb background
|
||||||
float lw = UIScale.scale( 1f );
|
float lw = UIScale.scale( thumbBorderWidth );
|
||||||
g.setColor( thumbColor );
|
g.setColor( thumbColor );
|
||||||
((Graphics2D)g).fill( createRoundThumbShape( tx + lw, ty + lw,
|
((Graphics2D)g).fill( createRoundThumbShape( tx + lw, ty + lw,
|
||||||
tw - lw - lw, th - lw - lw ) );
|
tw - lw - lw, th - lw - lw ) );
|
||||||
@@ -408,7 +504,7 @@ debug*/
|
|||||||
g2.fill( createDirectionalThumbShape( fw, fw, tw, th, 0 ) );
|
g2.fill( createDirectionalThumbShape( fw, fw, tw, th, 0 ) );
|
||||||
|
|
||||||
// paint thumb background
|
// paint thumb background
|
||||||
float lw = UIScale.scale( 1f );
|
float lw = UIScale.scale( thumbBorderWidth );
|
||||||
g2.setColor( thumbColor );
|
g2.setColor( thumbColor );
|
||||||
g2.fill( createDirectionalThumbShape( fw + lw, fw + lw,
|
g2.fill( createDirectionalThumbShape( fw + lw, fw + lw,
|
||||||
tw - lw - lw, th - lw - lw - (lw * 0.4142f), 0 ) );
|
tw - lw - lw, th - lw - lw - (lw * 0.4142f), 0 ) );
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import java.awt.Color;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
|
import java.awt.FontMetrics;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
@@ -32,6 +33,8 @@ import java.awt.event.FocusListener;
|
|||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JSpinner;
|
import javax.swing.JSpinner;
|
||||||
import javax.swing.JTextField;
|
import javax.swing.JTextField;
|
||||||
@@ -42,6 +45,9 @@ import javax.swing.plaf.ComponentUI;
|
|||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicSpinnerUI;
|
import javax.swing.plaf.basic.BasicSpinnerUI;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
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}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JSpinner}.
|
||||||
@@ -62,12 +68,13 @@ import com.formdev.flatlaf.FlatClientProperties;
|
|||||||
* @uiDefault Spinner.buttonStyle String button (default) or none
|
* @uiDefault Spinner.buttonStyle String button (default) or none
|
||||||
* @uiDefault Component.arrowType String chevron (default) or triangle
|
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
* @uiDefault Component.isIntelliJTheme boolean
|
||||||
* @uiDefault Component.borderColor Color
|
|
||||||
* @uiDefault Component.disabledBorderColor Color
|
|
||||||
* @uiDefault Spinner.disabledBackground Color
|
* @uiDefault Spinner.disabledBackground Color
|
||||||
* @uiDefault Spinner.disabledForeground Color
|
* @uiDefault Spinner.disabledForeground Color
|
||||||
* @uiDefault Spinner.focusedBackground Color optional
|
* @uiDefault Spinner.focusedBackground Color optional
|
||||||
* @uiDefault Spinner.buttonBackground Color
|
* @uiDefault Spinner.buttonBackground Color optional
|
||||||
|
* @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.buttonArrowColor Color
|
||||||
* @uiDefault Spinner.buttonDisabledArrowColor Color
|
* @uiDefault Spinner.buttonDisabledArrowColor Color
|
||||||
* @uiDefault Spinner.buttonHoverArrowColor Color
|
* @uiDefault Spinner.buttonHoverArrowColor Color
|
||||||
@@ -78,29 +85,41 @@ import com.formdev.flatlaf.FlatClientProperties;
|
|||||||
*/
|
*/
|
||||||
public class FlatSpinnerUI
|
public class FlatSpinnerUI
|
||||||
extends BasicSpinnerUI
|
extends BasicSpinnerUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
private Handler handler;
|
private Handler handler;
|
||||||
|
|
||||||
protected int minimumWidth;
|
@Styleable protected int minimumWidth;
|
||||||
protected String buttonStyle;
|
@Styleable protected String buttonStyle;
|
||||||
protected String arrowType;
|
@Styleable protected String arrowType;
|
||||||
protected boolean isIntelliJTheme;
|
protected boolean isIntelliJTheme;
|
||||||
protected Color borderColor;
|
@Styleable protected Color disabledBackground;
|
||||||
protected Color disabledBorderColor;
|
@Styleable protected Color disabledForeground;
|
||||||
protected Color disabledBackground;
|
@Styleable protected Color focusedBackground;
|
||||||
protected Color disabledForeground;
|
@Styleable protected Color buttonBackground;
|
||||||
protected Color focusedBackground;
|
/** @since 2 */ @Styleable protected float buttonSeparatorWidth;
|
||||||
protected Color buttonBackground;
|
/** @since 2 */ @Styleable protected Color buttonSeparatorColor;
|
||||||
protected Color buttonArrowColor;
|
/** @since 2 */ @Styleable protected Color buttonDisabledSeparatorColor;
|
||||||
protected Color buttonDisabledArrowColor;
|
@Styleable protected Color buttonArrowColor;
|
||||||
protected Color buttonHoverArrowColor;
|
@Styleable protected Color buttonDisabledArrowColor;
|
||||||
protected Color buttonPressedArrowColor;
|
@Styleable protected Color buttonHoverArrowColor;
|
||||||
protected Insets padding;
|
@Styleable protected Color buttonPressedArrowColor;
|
||||||
|
@Styleable protected Insets padding;
|
||||||
|
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
private AtomicBoolean borderShared;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatSpinnerUI();
|
return new FlatSpinnerUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
@@ -111,21 +130,19 @@ public class FlatSpinnerUI
|
|||||||
buttonStyle = UIManager.getString( "Spinner.buttonStyle" );
|
buttonStyle = UIManager.getString( "Spinner.buttonStyle" );
|
||||||
arrowType = UIManager.getString( "Component.arrowType" );
|
arrowType = UIManager.getString( "Component.arrowType" );
|
||||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
||||||
borderColor = UIManager.getColor( "Component.borderColor" );
|
|
||||||
disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
|
|
||||||
disabledBackground = UIManager.getColor( "Spinner.disabledBackground" );
|
disabledBackground = UIManager.getColor( "Spinner.disabledBackground" );
|
||||||
disabledForeground = UIManager.getColor( "Spinner.disabledForeground" );
|
disabledForeground = UIManager.getColor( "Spinner.disabledForeground" );
|
||||||
focusedBackground = UIManager.getColor( "Spinner.focusedBackground" );
|
focusedBackground = UIManager.getColor( "Spinner.focusedBackground" );
|
||||||
buttonBackground = UIManager.getColor( "Spinner.buttonBackground" );
|
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" );
|
buttonArrowColor = UIManager.getColor( "Spinner.buttonArrowColor" );
|
||||||
buttonDisabledArrowColor = UIManager.getColor( "Spinner.buttonDisabledArrowColor" );
|
buttonDisabledArrowColor = UIManager.getColor( "Spinner.buttonDisabledArrowColor" );
|
||||||
buttonHoverArrowColor = UIManager.getColor( "Spinner.buttonHoverArrowColor" );
|
buttonHoverArrowColor = UIManager.getColor( "Spinner.buttonHoverArrowColor" );
|
||||||
buttonPressedArrowColor = UIManager.getColor( "Spinner.buttonPressedArrowColor" );
|
buttonPressedArrowColor = UIManager.getColor( "Spinner.buttonPressedArrowColor" );
|
||||||
padding = UIManager.getInsets( "Spinner.padding" );
|
padding = UIManager.getInsets( "Spinner.padding" );
|
||||||
|
|
||||||
// scale
|
|
||||||
padding = scale( padding );
|
|
||||||
|
|
||||||
MigLayoutVisualPadding.install( spinner );
|
MigLayoutVisualPadding.install( spinner );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,18 +150,21 @@ public class FlatSpinnerUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
borderColor = null;
|
|
||||||
disabledBorderColor = null;
|
|
||||||
disabledBackground = null;
|
disabledBackground = null;
|
||||||
disabledForeground = null;
|
disabledForeground = null;
|
||||||
focusedBackground = null;
|
focusedBackground = null;
|
||||||
buttonBackground = null;
|
buttonBackground = null;
|
||||||
|
buttonSeparatorColor = null;
|
||||||
|
buttonDisabledSeparatorColor = null;
|
||||||
buttonArrowColor = null;
|
buttonArrowColor = null;
|
||||||
buttonDisabledArrowColor = null;
|
buttonDisabledArrowColor = null;
|
||||||
buttonHoverArrowColor = null;
|
buttonHoverArrowColor = null;
|
||||||
buttonPressedArrowColor = null;
|
buttonPressedArrowColor = null;
|
||||||
padding = null;
|
padding = null;
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
borderShared = null;
|
||||||
|
|
||||||
MigLayoutVisualPadding.uninstall( spinner );
|
MigLayoutVisualPadding.uninstall( spinner );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,17 +194,45 @@ public class FlatSpinnerUI
|
|||||||
return handler;
|
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() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, spinner.getBorder(), key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected JComponent createEditor() {
|
protected JComponent createEditor() {
|
||||||
JComponent editor = super.createEditor();
|
JComponent editor = super.createEditor();
|
||||||
|
configureEditor( editor );
|
||||||
// explicitly make non-opaque
|
|
||||||
editor.setOpaque( false );
|
|
||||||
JTextField textField = getEditorTextField( editor );
|
|
||||||
if( textField != null )
|
|
||||||
textField.setOpaque( false );
|
|
||||||
|
|
||||||
updateEditorColors();
|
|
||||||
return editor;
|
return editor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,8 +240,21 @@ public class FlatSpinnerUI
|
|||||||
protected void replaceEditor( JComponent oldEditor, JComponent newEditor ) {
|
protected void replaceEditor( JComponent oldEditor, JComponent newEditor ) {
|
||||||
super.replaceEditor( oldEditor, newEditor );
|
super.replaceEditor( oldEditor, newEditor );
|
||||||
|
|
||||||
|
configureEditor( newEditor );
|
||||||
|
|
||||||
removeEditorFocusListener( oldEditor );
|
removeEditorFocusListener( oldEditor );
|
||||||
addEditorFocusListener( newEditor );
|
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();
|
updateEditorColors();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,6 +270,12 @@ public class FlatSpinnerUI
|
|||||||
textField.removeFocusListener( getHandler() );
|
textField.removeFocusListener( getHandler() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateEditorPadding() {
|
||||||
|
JTextField textField = getEditorTextField( spinner.getEditor() );
|
||||||
|
if( textField != null )
|
||||||
|
textField.putClientProperty( FlatClientProperties.TEXT_FIELD_PADDING, padding );
|
||||||
|
}
|
||||||
|
|
||||||
private void updateEditorColors() {
|
private void updateEditorColors() {
|
||||||
JTextField textField = getEditorTextField( spinner.getEditor() );
|
JTextField textField = getEditorTextField( spinner.getEditor() );
|
||||||
if( textField != null ) {
|
if( textField != null ) {
|
||||||
@@ -226,17 +293,13 @@ public class FlatSpinnerUI
|
|||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @since 1.3 */
|
||||||
* @since 1.3
|
|
||||||
*/
|
|
||||||
public static boolean isPermanentFocusOwner( JSpinner spinner ) {
|
public static boolean isPermanentFocusOwner( JSpinner spinner ) {
|
||||||
if( FlatUIUtils.isPermanentFocusOwner( spinner ) )
|
if( FlatUIUtils.isPermanentFocusOwner( spinner ) )
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
JTextField textField = getEditorTextField( spinner.getEditor() );
|
JTextField textField = getEditorTextField( spinner.getEditor() );
|
||||||
return (textField != null)
|
return textField != null && FlatUIUtils.isPermanentFocusOwner( textField );
|
||||||
? FlatUIUtils.isPermanentFocusOwner( textField )
|
|
||||||
: false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Color getBackground( boolean enabled ) {
|
protected Color getBackground( boolean enabled ) {
|
||||||
@@ -287,6 +350,15 @@ public class FlatSpinnerUI
|
|||||||
return button;
|
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
|
@Override
|
||||||
public void update( Graphics g, JComponent c ) {
|
public void update( Graphics g, JComponent c ) {
|
||||||
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
|
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
|
||||||
@@ -317,7 +389,7 @@ public class FlatSpinnerUI
|
|||||||
boolean isLeftToRight = spinner.getComponentOrientation().isLeftToRight();
|
boolean isLeftToRight = spinner.getComponentOrientation().isLeftToRight();
|
||||||
|
|
||||||
// paint arrow buttons background
|
// paint arrow buttons background
|
||||||
if( enabled ) {
|
if( enabled && buttonBackground != null ) {
|
||||||
g2.setColor( buttonBackground );
|
g2.setColor( buttonBackground );
|
||||||
Shape oldClip = g2.getClip();
|
Shape oldClip = g2.getClip();
|
||||||
if( isLeftToRight )
|
if( isLeftToRight )
|
||||||
@@ -329,11 +401,14 @@ public class FlatSpinnerUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// paint vertical line between value and arrow buttons
|
// paint vertical line between value and arrow buttons
|
||||||
g2.setColor( enabled ? borderColor : disabledBorderColor );
|
Color separatorColor = enabled ? buttonSeparatorColor : buttonDisabledSeparatorColor;
|
||||||
float lw = scale( 1f );
|
if( separatorColor != null && buttonSeparatorWidth > 0 ) {
|
||||||
|
g2.setColor( separatorColor );
|
||||||
|
float lw = scale( buttonSeparatorWidth );
|
||||||
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
|
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
|
||||||
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2) ) );
|
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2) ) );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
paint( g, c );
|
paint( g, c );
|
||||||
|
|
||||||
@@ -373,9 +448,10 @@ public class FlatSpinnerUI
|
|||||||
@Override
|
@Override
|
||||||
public Dimension preferredLayoutSize( Container parent ) {
|
public Dimension preferredLayoutSize( Container parent ) {
|
||||||
Insets insets = parent.getInsets();
|
Insets insets = parent.getInsets();
|
||||||
|
Insets padding = scale( FlatSpinnerUI.this.padding );
|
||||||
Dimension editorSize = (editor != null) ? editor.getPreferredSize() : new Dimension( 0, 0 );
|
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
|
// the arrow buttons width is the same as the inner height so that the arrow buttons area is square
|
||||||
int minimumWidth = FlatUIUtils.minimumWidth( spinner, FlatSpinnerUI.this.minimumWidth );
|
int minimumWidth = FlatUIUtils.minimumWidth( spinner, FlatSpinnerUI.this.minimumWidth );
|
||||||
int innerHeight = editorSize.height + padding.top + padding.bottom;
|
int innerHeight = editorSize.height + padding.top + padding.bottom;
|
||||||
float focusWidth = FlatUIUtils.getBorderFocusWidth( spinner );
|
float focusWidth = FlatUIUtils.getBorderFocusWidth( spinner );
|
||||||
@@ -397,15 +473,19 @@ public class FlatSpinnerUI
|
|||||||
|
|
||||||
if( nextButton == null && previousButton == null ) {
|
if( nextButton == null && previousButton == null ) {
|
||||||
if( editor != null )
|
if( editor != null )
|
||||||
editor.setBounds( FlatUIUtils.subtractInsets( r, padding ) );
|
editor.setBounds( r );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle editorRect = new Rectangle( r );
|
Rectangle editorRect = new Rectangle( r );
|
||||||
Rectangle buttonsRect = new Rectangle( r );
|
Rectangle buttonsRect = new Rectangle( r );
|
||||||
|
|
||||||
|
// 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)
|
// make button area square (if spinner has preferred height)
|
||||||
int buttonsWidth = parent.getPreferredSize().height - insets.top - insets.bottom;
|
int buttonsWidth = Math.min( parent.getPreferredSize().height - insets.top - insets.bottom, maxButtonWidth );
|
||||||
buttonsRect.width = buttonsWidth;
|
buttonsRect.width = buttonsWidth;
|
||||||
|
|
||||||
if( parent.getComponentOrientation().isLeftToRight() ) {
|
if( parent.getComponentOrientation().isLeftToRight() ) {
|
||||||
@@ -417,7 +497,7 @@ public class FlatSpinnerUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
if( editor != null )
|
if( editor != null )
|
||||||
editor.setBounds( FlatUIUtils.subtractInsets( editorRect, padding ) );
|
editor.setBounds( editorRect );
|
||||||
|
|
||||||
int nextHeight = (buttonsRect.height / 2) + (buttonsRect.height % 2); // round up
|
int nextHeight = (buttonsRect.height / 2) + (buttonsRect.height % 2); // round up
|
||||||
if( nextButton != null )
|
if( nextButton != null )
|
||||||
@@ -462,12 +542,20 @@ public class FlatSpinnerUI
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case FlatClientProperties.COMPONENT_ROUND_RECT:
|
case FlatClientProperties.COMPONENT_ROUND_RECT:
|
||||||
|
case FlatClientProperties.OUTLINE:
|
||||||
spinner.repaint();
|
spinner.repaint();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FlatClientProperties.MINIMUM_WIDTH:
|
case FlatClientProperties.MINIMUM_WIDTH:
|
||||||
spinner.revalidate();
|
spinner.revalidate();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.STYLE:
|
||||||
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
|
installStyle();
|
||||||
|
spinner.revalidate();
|
||||||
|
spinner.repaint();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import java.awt.Graphics;
|
|||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JSplitPane;
|
import javax.swing.JSplitPane;
|
||||||
@@ -32,6 +34,11 @@ import javax.swing.UIManager;
|
|||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicSplitPaneDivider;
|
import javax.swing.plaf.basic.BasicSplitPaneDivider;
|
||||||
import javax.swing.plaf.basic.BasicSplitPaneUI;
|
import javax.swing.plaf.basic.BasicSplitPaneUI;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
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;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,6 +53,13 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault SplitPaneDivider.border Border
|
* @uiDefault SplitPaneDivider.border Border
|
||||||
* @uiDefault SplitPaneDivider.draggingColor Color only used if continuousLayout is false
|
* @uiDefault SplitPaneDivider.draggingColor Color only used if continuousLayout is false
|
||||||
*
|
*
|
||||||
|
* <!-- BasicSplitPaneDivider -->
|
||||||
|
*
|
||||||
|
* @uiDefault SplitPane.oneTouchButtonSize int
|
||||||
|
* @uiDefault SplitPane.oneTouchButtonOffset int
|
||||||
|
* @uiDefault SplitPane.centerOneTouchButtons boolean
|
||||||
|
* @uiDefault SplitPane.supportsOneTouchButtons boolean optional; default is true
|
||||||
|
*
|
||||||
* <!-- JSplitPane -->
|
* <!-- JSplitPane -->
|
||||||
*
|
*
|
||||||
* @uiDefault SplitPane.continuousLayout boolean
|
* @uiDefault SplitPane.continuousLayout boolean
|
||||||
@@ -66,16 +80,27 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*/
|
*/
|
||||||
public class FlatSplitPaneUI
|
public class FlatSplitPaneUI
|
||||||
extends BasicSplitPaneUI
|
extends BasicSplitPaneUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
protected String arrowType;
|
@Styleable protected String arrowType;
|
||||||
protected Color oneTouchArrowColor;
|
@Styleable protected Color oneTouchArrowColor;
|
||||||
protected Color oneTouchHoverArrowColor;
|
@Styleable protected Color oneTouchHoverArrowColor;
|
||||||
protected Color oneTouchPressedArrowColor;
|
@Styleable protected Color oneTouchPressedArrowColor;
|
||||||
|
|
||||||
|
private PropertyChangeListener propertyChangeListener;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatSplitPaneUI();
|
return new FlatSplitPaneUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
arrowType = UIManager.getString( "Component.arrowType" );
|
arrowType = UIManager.getString( "Component.arrowType" );
|
||||||
@@ -96,6 +121,24 @@ public class FlatSplitPaneUI
|
|||||||
oneTouchArrowColor = null;
|
oneTouchArrowColor = null;
|
||||||
oneTouchHoverArrowColor = null;
|
oneTouchHoverArrowColor = null;
|
||||||
oneTouchPressedArrowColor = 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
|
@Override
|
||||||
@@ -103,16 +146,64 @@ public class FlatSplitPaneUI
|
|||||||
return new FlatSplitPaneDivider( this );
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
if( divider instanceof FlatSplitPaneDivider ) {
|
||||||
|
Object value = ((FlatSplitPaneDivider)divider).getStyleableValue( key );
|
||||||
|
if( value != null )
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
//---- class FlatSplitPaneDivider -----------------------------------------
|
//---- class FlatSplitPaneDivider -----------------------------------------
|
||||||
|
|
||||||
protected class FlatSplitPaneDivider
|
protected class FlatSplitPaneDivider
|
||||||
extends BasicSplitPaneDivider
|
extends BasicSplitPaneDivider
|
||||||
{
|
{
|
||||||
protected final String style = UIManager.getString( "SplitPaneDivider.style" );
|
@Styleable protected String style = UIManager.getString( "SplitPaneDivider.style" );
|
||||||
protected final Color gripColor = UIManager.getColor( "SplitPaneDivider.gripColor" );
|
@Styleable protected Color gripColor = UIManager.getColor( "SplitPaneDivider.gripColor" );
|
||||||
protected final int gripDotCount = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotCount", 3 );
|
@Styleable protected int gripDotCount = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotCount", 3 );
|
||||||
protected final int gripDotSize = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotSize", 3 );
|
@Styleable protected int gripDotSize = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotSize", 3 );
|
||||||
protected final int gripGap = FlatUIUtils.getUIInt( "SplitPaneDivider.gripGap", 2 );
|
@Styleable protected int gripGap = FlatUIUtils.getUIInt( "SplitPaneDivider.gripGap", 2 );
|
||||||
|
|
||||||
protected FlatSplitPaneDivider( BasicSplitPaneUI ui ) {
|
protected FlatSplitPaneDivider( BasicSplitPaneUI ui ) {
|
||||||
super( ui );
|
super( ui );
|
||||||
@@ -120,6 +211,28 @@ public class FlatSplitPaneUI
|
|||||||
setLayout( new FlatDividerLayout() );
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateStyle() {
|
||||||
|
if( leftButton instanceof FlatOneTouchButton )
|
||||||
|
((FlatOneTouchButton)leftButton).updateStyle();
|
||||||
|
if( rightButton instanceof FlatOneTouchButton )
|
||||||
|
((FlatOneTouchButton)rightButton).updateStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setDividerSize( int newSize ) {
|
public void setDividerSize( int newSize ) {
|
||||||
super.setDividerSize( UIScale.scale( newSize ) );
|
super.setDividerSize( UIScale.scale( newSize ) );
|
||||||
@@ -142,7 +255,7 @@ public class FlatSplitPaneUI
|
|||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
case JSplitPane.DIVIDER_LOCATION_PROPERTY:
|
case JSplitPane.DIVIDER_LOCATION_PROPERTY:
|
||||||
// necessary to show/hide one-touch buttons on expand/collapse
|
// necessary to show/hide one-touch buttons on expand/collapse
|
||||||
revalidate();
|
doLayout();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,6 +313,11 @@ public class FlatSplitPaneUI
|
|||||||
this.left = left;
|
this.left = left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void updateStyle() {
|
||||||
|
updateStyle( arrowType, oneTouchArrowColor, null,
|
||||||
|
oneTouchHoverArrowColor, null, oneTouchPressedArrowColor, null );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDirection() {
|
public int getDirection() {
|
||||||
return (orientation == JSplitPane.VERTICAL_SPLIT)
|
return (orientation == JSplitPane.VERTICAL_SPLIT)
|
||||||
@@ -247,7 +365,7 @@ public class FlatSplitPaneUI
|
|||||||
if( leftButton == null || rightButton == null || !splitPane.isOneTouchExpandable() )
|
if( leftButton == null || rightButton == null || !splitPane.isOneTouchExpandable() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// increase side of buttons, which makes them easier to hit by the user
|
// increase size of buttons, which makes them easier to hit by the user
|
||||||
// and avoids cut arrows at small divider sizes
|
// and avoids cut arrows at small divider sizes
|
||||||
int extraSize = UIScale.scale( 4 );
|
int extraSize = UIScale.scale( 4 );
|
||||||
if( orientation == JSplitPane.VERTICAL_SPLIT ) {
|
if( orientation == JSplitPane.VERTICAL_SPLIT ) {
|
||||||
@@ -262,10 +380,19 @@ public class FlatSplitPaneUI
|
|||||||
|
|
||||||
// hide buttons if not applicable
|
// hide buttons if not applicable
|
||||||
boolean leftCollapsed = isLeftCollapsed();
|
boolean leftCollapsed = isLeftCollapsed();
|
||||||
if( leftCollapsed )
|
boolean rightCollapsed = isRightCollapsed();
|
||||||
rightButton.setLocation( leftButton.getLocation() );
|
if( leftCollapsed || rightCollapsed ) {
|
||||||
leftButton.setVisible( !leftCollapsed );
|
leftButton.setVisible( !leftCollapsed );
|
||||||
rightButton.setVisible( !isRightCollapsed() );
|
rightButton.setVisible( !rightCollapsed );
|
||||||
|
} else {
|
||||||
|
Object expandableSide = splitPane.getClientProperty( FlatClientProperties.SPLIT_PANE_EXPANDABLE_SIDE );
|
||||||
|
leftButton.setVisible( expandableSide == null || !FlatClientProperties.SPLIT_PANE_EXPANDABLE_SIDE_LEFT.equals( expandableSide ) );
|
||||||
|
rightButton.setVisible( expandableSide == null || !FlatClientProperties.SPLIT_PANE_EXPANDABLE_SIDE_RIGHT.equals( expandableSide ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// move right button if left button is hidden
|
||||||
|
if( !leftButton.isVisible() )
|
||||||
|
rightButton.setLocation( leftButton.getLocation() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,893 @@
|
|||||||
|
/*
|
||||||
|
* 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.Repeatable;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that a field in the specified (super) class
|
||||||
|
* is intended to be used by FlatLaf styling support.
|
||||||
|
* <p>
|
||||||
|
* Use this annotation, instead of {@link Styleable}, to style fields
|
||||||
|
* in superclasses, where it is not possible to use {@link Styleable}.
|
||||||
|
* <p>
|
||||||
|
* Classes using this annotation may implement {@link StyleableLookupProvider}
|
||||||
|
* to give access to protected fields (in JRE) in modular applications.
|
||||||
|
*
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Repeatable(StyleableFields.class)
|
||||||
|
public @interface StyleableField {
|
||||||
|
Class<?> cls();
|
||||||
|
String key();
|
||||||
|
String fieldName() default "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container annotation for {@link StyleableField}.
|
||||||
|
*
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface StyleableFields {
|
||||||
|
StyleableField[] value();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public interface StyleableUI {
|
||||||
|
Map<String, Class<?>> getStyleableInfos( JComponent c );
|
||||||
|
/** @since 2.5 */ Object getStyleableValue( JComponent c, String key );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public interface StyleableBorder {
|
||||||
|
Object applyStyleProperty( String key, Object value );
|
||||||
|
Map<String, Class<?>> getStyleableInfos();
|
||||||
|
/** @since 2.5 */ Object getStyleableValue( String key );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
public interface StyleableLookupProvider {
|
||||||
|
MethodHandles.Lookup getLookupForStyling();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 an 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 = keyToFieldName( key );
|
||||||
|
|
||||||
|
return applyToField( obj, fieldName, key, value, field -> {
|
||||||
|
Styleable styleable = field.getAnnotation( Styleable.class );
|
||||||
|
return styleable != null && styleable.dot() == (fieldName != key);
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String keyToFieldName( String key ) {
|
||||||
|
int dotIndex = key.indexOf( '.' );
|
||||||
|
if( dotIndex < 0 )
|
||||||
|
return key;
|
||||||
|
|
||||||
|
// remove first dot in key and change subsequent character to uppercase
|
||||||
|
return key.substring( 0, dotIndex )
|
||||||
|
+ Character.toUpperCase( key.charAt( dotIndex + 1 ) )
|
||||||
|
+ key.substring( dotIndex + 2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 ) )
|
||||||
|
return applyToField( f, obj, value, false );
|
||||||
|
} catch( NoSuchFieldException ex ) {
|
||||||
|
// field not found in class --> try superclass
|
||||||
|
}
|
||||||
|
|
||||||
|
for( StyleableField styleableField : cls.getAnnotationsByType( StyleableField.class ) ) {
|
||||||
|
if( key.equals( styleableField.key() ) )
|
||||||
|
return applyToField( getStyleableField( styleableField ), obj, value, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
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 Object applyToField( Field f, Object obj, Object value, boolean useMethodHandles ) {
|
||||||
|
checkValidField( f );
|
||||||
|
|
||||||
|
if( useMethodHandles && obj instanceof StyleableLookupProvider ) {
|
||||||
|
try {
|
||||||
|
// use method handles to access protected fields in JRE in modular applications
|
||||||
|
MethodHandles.Lookup lookup = ((StyleableLookupProvider)obj).getLookupForStyling();
|
||||||
|
|
||||||
|
// get old value and set new value
|
||||||
|
Object oldValue = lookup.unreflectGetter( f ).invoke( obj );
|
||||||
|
lookup.unreflectSetter( f ).invoke( obj, convertToEnum( value, f.getType() ) );
|
||||||
|
return oldValue;
|
||||||
|
} catch( Throwable ex ) {
|
||||||
|
throw newFieldAccessFailed( f, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 newFieldAccessFailed( f, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object getFieldValue( Field f, Object obj, boolean useMethodHandles ) {
|
||||||
|
checkValidField( f );
|
||||||
|
|
||||||
|
if( useMethodHandles && obj instanceof StyleableLookupProvider ) {
|
||||||
|
// use method handles to access protected fields in JRE in modular applications
|
||||||
|
try {
|
||||||
|
MethodHandles.Lookup lookup = ((StyleableLookupProvider)obj).getLookupForStyling();
|
||||||
|
return lookup.unreflectGetter( f ).invoke( obj );
|
||||||
|
} catch( Throwable ex ) {
|
||||||
|
throw newFieldAccessFailed( f, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
f.setAccessible( true );
|
||||||
|
return f.get( obj );
|
||||||
|
} catch( IllegalAccessException ex ) {
|
||||||
|
throw newFieldAccessFailed( f, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IllegalArgumentException newFieldAccessFailed( Field f, Throwable ex ) {
|
||||||
|
return new IllegalArgumentException( "failed to access field '" + f.getDeclaringClass().getName() + "." + f.getName() + "'", ex );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkValidField( Field f ) {
|
||||||
|
if( !isValidField( f ) )
|
||||||
|
throw new IllegalArgumentException( "field '" + f.getDeclaringClass().getName() + "." + f.getName() + "' is final or static" );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isValidField( Field f ) {
|
||||||
|
int modifiers = f.getModifiers();
|
||||||
|
return (modifiers & (Modifier.FINAL|Modifier.STATIC)) == 0 && !f.isSynthetic();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Field getStyleableField( StyleableField styleableField ) {
|
||||||
|
String fieldName = styleableField.fieldName();
|
||||||
|
if( fieldName.isEmpty() )
|
||||||
|
fieldName = styleableField.key();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return styleableField.cls().getDeclaredField( fieldName );
|
||||||
|
} catch( NoSuchFieldException ex ) {
|
||||||
|
throw new IllegalArgumentException( "field '" + styleableField.cls().getName() + "." + fieldName + "' not found", ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 an 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(;;) {
|
||||||
|
// find fields annotated with 'Styleable'
|
||||||
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// get fields specified in 'StyleableField' annotation
|
||||||
|
for( StyleableField styleableField : cls.getAnnotationsByType( StyleableField.class ) ) {
|
||||||
|
String name = styleableField.key();
|
||||||
|
|
||||||
|
// for the case that the same field name is used in a class and in
|
||||||
|
// one of its superclasses, do not process field in superclass
|
||||||
|
if( processedFields.contains( name ) )
|
||||||
|
continue;
|
||||||
|
processedFields.add( name );
|
||||||
|
|
||||||
|
Field f = getStyleableField( styleableField );
|
||||||
|
infos.put( name, f.getType() );
|
||||||
|
}
|
||||||
|
|
||||||
|
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() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object getAnnotatedStyleableValue( Object obj, String key ) {
|
||||||
|
String fieldName = keyToFieldName( key );
|
||||||
|
Class<?> cls = obj.getClass();
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
try {
|
||||||
|
// find field annotated with 'Styleable'
|
||||||
|
Field f = cls.getDeclaredField( fieldName );
|
||||||
|
Styleable styleable = f.getAnnotation( Styleable.class );
|
||||||
|
if( styleable != null ) {
|
||||||
|
if( styleable.dot() != (fieldName != key) )
|
||||||
|
throw new IllegalArgumentException( "'Styleable.dot' on field '" + fieldName + "' does not match key '" + key + "'" );
|
||||||
|
if( styleable.type() != Void.class )
|
||||||
|
throw new IllegalArgumentException( "'Styleable.type' on field '" + fieldName + "' not supported" );
|
||||||
|
|
||||||
|
return getFieldValue( f, obj, false );
|
||||||
|
}
|
||||||
|
} catch( NoSuchFieldException ex ) {
|
||||||
|
// field not found in class --> try superclass
|
||||||
|
}
|
||||||
|
|
||||||
|
// find field specified in 'StyleableField' annotation
|
||||||
|
for( StyleableField styleableField : cls.getAnnotationsByType( StyleableField.class ) ) {
|
||||||
|
if( key.equals( styleableField.key() ) )
|
||||||
|
return getFieldValue( getStyleableField( styleableField ), obj, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
cls = cls.getSuperclass();
|
||||||
|
if( cls == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
String superclassName = cls.getName();
|
||||||
|
if( superclassName.startsWith( "java." ) || superclassName.startsWith( "javax." ) )
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object getAnnotatedStyleableValue( Object obj, Border border, String key ) {
|
||||||
|
if( border instanceof StyleableBorder ) {
|
||||||
|
Object value = ((StyleableBorder)border).getStyleableValue( key );
|
||||||
|
if( value != null )
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return getAnnotatedStyleableValue( obj, key );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- 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
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user