mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2026-02-11 06:27:13 -06:00
Compare commits
560 Commits
fonts/robo
...
c995b4cbdf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c995b4cbdf | ||
|
|
b251ff76e8 | ||
|
|
ea2a985481 | ||
|
|
3ca7ebbfee | ||
|
|
7e6dc269c5 | ||
|
|
7680c3a817 | ||
|
|
0e3cb95791 | ||
|
|
3f0b5d5a0e | ||
|
|
6e3633cca3 | ||
|
|
4f1e5cdb05 | ||
|
|
070cf9c40d | ||
|
|
5b0f13110a | ||
|
|
d7a5c353fe | ||
|
|
9e8b8697d1 | ||
|
|
58e073a05b | ||
|
|
1c6e8774cf | ||
|
|
1e724029ae | ||
|
|
9fdaa827b5 | ||
|
|
b9441050b2 | ||
|
|
8239cdd540 | ||
|
|
08419d6135 | ||
|
|
3a72232ae3 | ||
|
|
e61499e7c6 | ||
|
|
d3e6c7af14 | ||
|
|
ff11a11d28 | ||
|
|
855c41bf4a | ||
|
|
2b587d4dba | ||
|
|
5fabfc7051 | ||
|
|
db4173dd06 | ||
|
|
03b7a1c29e | ||
|
|
60968f77eb | ||
|
|
8bafa37b4a | ||
|
|
04602ac227 | ||
|
|
02636b260a | ||
|
|
c8e2e78955 | ||
|
|
19c86cf1f7 | ||
|
|
7ebc1b27c1 | ||
|
|
d4827b6ddf | ||
|
|
02f7cb8972 | ||
|
|
10677d469f | ||
|
|
df8212b49e | ||
|
|
c583a21bf7 | ||
|
|
5263125a04 | ||
|
|
056da35758 | ||
|
|
3ccaacfb00 | ||
|
|
bdb7438672 | ||
|
|
299250a710 | ||
|
|
1e2a75a19c | ||
|
|
0fb4c811f6 | ||
|
|
0d4946230e | ||
|
|
960f9d86c1 | ||
|
|
015645e173 | ||
|
|
36d5685f4c | ||
|
|
ddc8d6e29c | ||
|
|
119b4a922d | ||
|
|
5e4f00f0c8 | ||
|
|
15cbf28a0d | ||
|
|
f8e53c9064 | ||
|
|
b3c9638e47 | ||
|
|
d079741f94 | ||
|
|
c051ad5f72 | ||
|
|
1ed7aeaa45 | ||
|
|
2ac7234c32 | ||
|
|
6f63982054 | ||
|
|
d388158de7 | ||
|
|
8cfe1ca597 | ||
|
|
e7a766bf8f | ||
|
|
97988e90b4 | ||
|
|
f71dbb2647 | ||
|
|
04ad21b5b6 | ||
|
|
ff722c0b34 | ||
|
|
34b19f00e4 | ||
|
|
286ce15146 | ||
|
|
abfaf86cd5 | ||
|
|
1eee35035d | ||
|
|
bc4c7b25d3 | ||
|
|
0863e289a1 | ||
|
|
5c2d8ba555 | ||
|
|
0f27125107 | ||
|
|
f2882370de | ||
|
|
6715886b24 | ||
|
|
4945378dd3 | ||
|
|
b178450e81 | ||
|
|
e3ffdd3b7c | ||
|
|
5326971287 | ||
|
|
cd34c08dc9 | ||
|
|
edee73e0ea | ||
|
|
54a53fb527 | ||
|
|
62b96fbccd | ||
|
|
42cbb0666d | ||
|
|
1465fbaabc | ||
|
|
5575854e68 | ||
|
|
640f2ba9a2 | ||
|
|
b221fd1894 | ||
|
|
b64ab09b88 | ||
|
|
2870ee5c51 | ||
|
|
23f8ce867b | ||
|
|
835a1f155b | ||
|
|
3925f198d9 | ||
|
|
35e86ba772 | ||
|
|
dade1cba5a | ||
|
|
dcb4c09387 | ||
|
|
3e8b213367 | ||
|
|
202a0d159b | ||
|
|
5d247f6269 | ||
|
|
d8e59f2cf3 | ||
|
|
d81bcd5254 | ||
|
|
666b99971d | ||
|
|
0ba7798cbd | ||
|
|
c486f695f2 | ||
|
|
0bc2513c46 | ||
|
|
bc3504378b | ||
|
|
94fc75dc78 | ||
|
|
681c0cd4fe | ||
|
|
f7495a0a5b | ||
|
|
dd44d3ed2d | ||
|
|
86a4d0ab12 | ||
|
|
b43c3a9e00 | ||
|
|
babc8aa55d | ||
|
|
5dc88a6210 | ||
|
|
d612b9f4b8 | ||
|
|
9b1ae5c74a | ||
|
|
143f96360b | ||
|
|
f5e6b90e02 | ||
|
|
f36886aeb3 | ||
|
|
d26eb2674f | ||
|
|
68b8769d0d | ||
|
|
7f37e884d3 | ||
|
|
a4dc1b4151 | ||
|
|
022a67929a | ||
|
|
f7c867fb97 | ||
|
|
f0685d179e | ||
|
|
c8eaf5f587 | ||
|
|
ed69049c08 | ||
|
|
ae4037ee82 | ||
|
|
411a2f6d29 | ||
|
|
f24b3a6022 | ||
|
|
ebacad2d04 | ||
|
|
76f436726f | ||
|
|
6c8f813e53 | ||
|
|
5f6cc719ad | ||
|
|
00858002de | ||
|
|
072cc3c488 | ||
|
|
1f594b2ba8 | ||
|
|
03e5f8623e | ||
|
|
c32c00a5eb | ||
|
|
3a8a55a545 | ||
|
|
6c49b8bc4d | ||
|
|
cca9707f6b | ||
|
|
54d6959533 | ||
|
|
112116556d | ||
|
|
f30dd876e4 | ||
|
|
c6872d48b3 | ||
|
|
3283cfe22f | ||
|
|
aecb496142 | ||
|
|
5e78b21df7 | ||
|
|
1e3e4d7c61 | ||
|
|
2ef87dc789 | ||
|
|
b808f6e803 | ||
|
|
f3ca3a001a | ||
|
|
d524536575 | ||
|
|
0a4c01cd40 | ||
|
|
28904c34cc | ||
|
|
91f19bf94c | ||
|
|
0ea188f8db | ||
|
|
6c77b81277 | ||
|
|
d513ec497b | ||
|
|
07fc190b5f | ||
|
|
078e59a443 | ||
|
|
d49282dfe8 | ||
|
|
ddee4ef526 | ||
|
|
f11f68282b | ||
|
|
ac0cceb09b | ||
|
|
a238fd4505 | ||
|
|
8dc6242889 | ||
|
|
d17fffb82a | ||
|
|
0ad3180b10 | ||
|
|
c73fd51704 | ||
|
|
251198c66d | ||
|
|
d7462bd424 | ||
|
|
9af7f95197 | ||
|
|
2e16ded5d4 | ||
|
|
91e8d04a9f | ||
|
|
9453d55abd | ||
|
|
641fada6c4 | ||
|
|
a303cd2dec | ||
|
|
2b810addd8 | ||
|
|
63272a03cf | ||
|
|
49a0a83eca | ||
|
|
516bd80702 | ||
|
|
80ba75fdeb | ||
|
|
7027821c00 | ||
|
|
a3a49cef73 | ||
|
|
abf77d5399 | ||
|
|
6404b8de2a | ||
|
|
19055d5a18 | ||
|
|
c12adf12e7 | ||
|
|
b9c68fbe77 | ||
|
|
7bdfd49921 | ||
|
|
58fa2a5085 | ||
|
|
a400799db5 | ||
|
|
2a8e487c1f | ||
|
|
145631fd43 | ||
|
|
6a774d8c70 | ||
|
|
bfd746f981 | ||
|
|
3af54b7215 | ||
|
|
3ba9fc6c1c | ||
|
|
0a9ecd66a9 | ||
|
|
6991d6729e | ||
|
|
304cb0d57b | ||
|
|
41332de275 | ||
|
|
ef61ae504b | ||
|
|
f96baf1bc2 | ||
|
|
1462636e97 | ||
|
|
7e59a7f4af | ||
|
|
e9a21848bc | ||
|
|
1dcb251ecb | ||
|
|
3f33543cee | ||
|
|
84bd2088f2 | ||
|
|
4f4a3132c5 | ||
|
|
e064c934cb | ||
|
|
16fc3cabf2 | ||
|
|
7e002ff6c2 | ||
|
|
323c0c62c3 | ||
|
|
ff5bd301cc | ||
|
|
c37712b0f0 | ||
|
|
ee9e238592 | ||
|
|
da5d6fa157 | ||
|
|
d471f08b15 | ||
|
|
b97424f767 | ||
|
|
a20cfa6db3 | ||
|
|
6ac6698ecf | ||
|
|
8004d2761a | ||
|
|
25c2bbc851 | ||
|
|
33e37a7167 | ||
|
|
c29a276188 | ||
|
|
d1694aa8bd | ||
|
|
570cf6fc51 | ||
|
|
8eab86e489 | ||
|
|
566568f61a | ||
|
|
56a73a4d17 | ||
|
|
656d25b75e | ||
|
|
dcdc80ade3 | ||
|
|
09f2d65d5e | ||
|
|
b304d46f7e | ||
|
|
3391f971ec | ||
|
|
778fed27a5 | ||
|
|
1755dbc877 | ||
|
|
4e6f538519 | ||
|
|
a6ecb0ef85 | ||
|
|
438ec6ac5c | ||
|
|
8089e66642 | ||
|
|
d27e0561f2 | ||
|
|
97b21bfa8b | ||
|
|
ec4343ed30 | ||
|
|
948decb3b5 | ||
|
|
d510fee7f6 | ||
|
|
70b7a3d662 | ||
|
|
b142a6f31e | ||
|
|
14705a9b30 | ||
|
|
32b0f1ba10 | ||
|
|
cbffdf4900 | ||
|
|
1238da5e54 | ||
|
|
cba203be09 | ||
|
|
d89c6156b9 | ||
|
|
e06475b3b7 | ||
|
|
5ff99bd45e | ||
|
|
127dd6ac41 | ||
|
|
9e05384513 | ||
|
|
9ffda72ae3 | ||
|
|
72a4c00e72 | ||
|
|
c95e95ef67 | ||
|
|
0c0d4bffbf | ||
|
|
2a494b1d60 | ||
|
|
1463723e52 | ||
|
|
9ade48d078 | ||
|
|
7ba8274fd4 | ||
|
|
238443074c | ||
|
|
0decbec595 | ||
|
|
0eb77c7f72 | ||
|
|
f05df0db0a | ||
|
|
13fbaf1f74 | ||
|
|
969d2642de | ||
|
|
17ce6d39b4 | ||
|
|
261d2b1fe8 | ||
|
|
a54aeb3838 | ||
|
|
cc4f9a9db5 | ||
|
|
a311bac89b | ||
|
|
029f273dd9 | ||
|
|
bbbdd7e4d3 | ||
|
|
3f3ef6b24f | ||
|
|
8c3dfd4a36 | ||
|
|
af57599df9 | ||
|
|
bde25f6ac8 | ||
|
|
c989b97ffa | ||
|
|
5f5c225300 | ||
|
|
36e4071b7f | ||
|
|
1068884bce | ||
|
|
32d102dbc9 | ||
|
|
4e1f092b98 | ||
|
|
bd60a18ff4 | ||
|
|
3b3d7d76eb | ||
|
|
ec76448e9f | ||
|
|
872c84974c | ||
|
|
5dd2008969 | ||
|
|
55ddac2bc7 | ||
|
|
a62dd22f83 | ||
|
|
a503879858 | ||
|
|
14f19dd735 | ||
|
|
9a727f68ce | ||
|
|
d26819d268 | ||
|
|
44752cc9aa | ||
|
|
11e0757387 | ||
|
|
1f1ebc3c44 | ||
|
|
4be97b6ea6 | ||
|
|
3facca5499 | ||
|
|
bfbd25012a | ||
|
|
063fff2ab4 | ||
|
|
fbdc8d5b99 | ||
|
|
625c0a3321 | ||
|
|
2972300112 | ||
|
|
a8e71895ee | ||
|
|
d7a76081e3 | ||
|
|
fd925a6718 | ||
|
|
4fc890a77c | ||
|
|
b804463b73 | ||
|
|
8f161b4b5a | ||
|
|
c6338169f3 | ||
|
|
6cea24ed9e | ||
|
|
3d8eb9eb66 | ||
|
|
a84aceb1ba | ||
|
|
8adb7e3021 | ||
|
|
bc0d5dc9b5 | ||
|
|
1d935d6659 | ||
|
|
445466acd0 | ||
|
|
30af74f806 | ||
|
|
16ddd100d3 | ||
|
|
c946ec170d | ||
|
|
ca514dd76e | ||
|
|
cf44a5c50b | ||
|
|
91b8c02c7f | ||
|
|
ca3b2b4b07 | ||
|
|
722dde63df | ||
|
|
c85baf4dc6 | ||
|
|
96b7770ab2 | ||
|
|
0c00117820 | ||
|
|
3465fa68b4 | ||
|
|
28278a75a7 | ||
|
|
f68a871dd6 | ||
|
|
93d424cfe1 | ||
|
|
a1adde0888 | ||
|
|
13528b49cb | ||
|
|
f3be3f2d1c | ||
|
|
241fe855cc | ||
|
|
ea2447dcb7 | ||
|
|
f40baed65e | ||
|
|
eed11d211b | ||
|
|
19f27a8d56 | ||
|
|
cf3fa17666 | ||
|
|
6fdc56f2d3 | ||
|
|
9f17a5b26d | ||
|
|
fa53e90847 | ||
|
|
50c630f403 | ||
|
|
5630c161ea | ||
|
|
7d16ff9e79 | ||
|
|
a9ea9daec3 | ||
|
|
45bdd40dce | ||
|
|
a2bca88eec | ||
|
|
c0dd02ee13 | ||
|
|
97495a6093 | ||
|
|
4ad45088c4 | ||
|
|
d6d1d4b1b7 | ||
|
|
6e453c170f | ||
|
|
beb2deee52 | ||
|
|
9c69043b2c | ||
|
|
4df34b3f9d | ||
|
|
ee01756188 | ||
|
|
0386aaa18b | ||
|
|
92c4230cde | ||
|
|
26165999e0 | ||
|
|
38b2641078 | ||
|
|
4e19169312 | ||
|
|
46de81c1c9 | ||
|
|
9bf4da7acf | ||
|
|
6f32236fb7 | ||
|
|
c25d857e78 | ||
|
|
8cc2925fd0 | ||
|
|
2b87d3c4db | ||
|
|
7f6f366744 | ||
|
|
b1fdbde5cd | ||
|
|
417f0f5f1c | ||
|
|
ec7673790c | ||
|
|
7d0bdf3b9e | ||
|
|
2ef5270095 | ||
|
|
61ba011c3b | ||
|
|
8d8b9f3e98 | ||
|
|
69899ec29f | ||
|
|
5063621c95 | ||
|
|
030177f739 | ||
|
|
808f5a6381 | ||
|
|
9602b191a9 | ||
|
|
34bd2d781c | ||
|
|
cf364c1264 | ||
|
|
a997820bb6 | ||
|
|
b8fabd59c0 | ||
|
|
97d290795e | ||
|
|
2a237ff5fc | ||
|
|
13a418f74e | ||
|
|
5c56dbfed6 | ||
|
|
0d2f37e1da | ||
|
|
0494c2161c | ||
|
|
635a620439 | ||
|
|
0a7c76ec72 | ||
|
|
9ad8fb38e8 | ||
|
|
1dbe968952 | ||
|
|
460b6492cb | ||
|
|
67b0faa9ae | ||
|
|
5553425a1a | ||
|
|
8ff516e43a | ||
|
|
b6207bafde | ||
|
|
b9f43fd560 | ||
|
|
c617d9f569 | ||
|
|
9efb9761c6 | ||
|
|
03f9115fbf | ||
|
|
a2859cedb5 | ||
|
|
0c604b1023 | ||
|
|
cdee0594f8 | ||
|
|
808833d749 | ||
|
|
581c64b601 | ||
|
|
40418607e5 | ||
|
|
5436ea88d8 | ||
|
|
7bec5ec6dc | ||
|
|
c953ff84d0 | ||
|
|
96cd207df3 | ||
|
|
7a75f62a6a | ||
|
|
61e5fe58c2 | ||
|
|
1a3baba702 | ||
|
|
58dc14bb46 | ||
|
|
a5b7e04943 | ||
|
|
22f2aa5475 | ||
|
|
d4e9cb12be | ||
|
|
75da361480 | ||
|
|
7488bcb7b0 | ||
|
|
1b1a9be107 | ||
|
|
db2f94aa53 | ||
|
|
810146b993 | ||
|
|
93091662ab | ||
|
|
d349227fbf | ||
|
|
c9423e3aa8 | ||
|
|
b9737ca4f1 | ||
|
|
4b4990635d | ||
|
|
afaa2c8c78 | ||
|
|
f506ef0d4f | ||
|
|
d30fe66cac | ||
|
|
270e998e86 | ||
|
|
c395386c05 | ||
|
|
4f1207b0db | ||
|
|
dc3878e290 | ||
|
|
be2876149d | ||
|
|
52bae9dfb0 | ||
|
|
bb636bac3f | ||
|
|
502b18fa86 | ||
|
|
e0a5450264 | ||
|
|
5ffb23c37f | ||
|
|
b75f22b7bd | ||
|
|
35fa3197c8 | ||
|
|
f03725ae36 | ||
|
|
2b640e2129 | ||
|
|
2a983f5c03 | ||
|
|
cacc5daa14 | ||
|
|
593502287d | ||
|
|
7a9bdf9be0 | ||
|
|
170c22c5ed | ||
|
|
7e8fa58bd7 | ||
|
|
046200625c | ||
|
|
710ed55152 | ||
|
|
ce527329a6 | ||
|
|
b455dd41ab | ||
|
|
b47ed94f40 | ||
|
|
f1351a2093 | ||
|
|
c1c5e81df0 | ||
|
|
8e3c8ba6c5 | ||
|
|
dfe4404a17 | ||
|
|
b3fb63c9f5 | ||
|
|
9db3dfff26 | ||
|
|
3c9051e7de | ||
|
|
798a6d061c | ||
|
|
19afbe99d9 | ||
|
|
4715d8d16c | ||
|
|
193da2bc4d | ||
|
|
799f8efe22 | ||
|
|
f6062e1ec4 | ||
|
|
c790778a46 | ||
|
|
4344f1b3a0 | ||
|
|
d520b30500 | ||
|
|
11c02e5f50 | ||
|
|
aa4c6ee9da | ||
|
|
98f8557392 | ||
|
|
6f6a860887 | ||
|
|
38695e9e16 | ||
|
|
242c478cb3 | ||
|
|
f003e835bd | ||
|
|
267defb321 | ||
|
|
4392c7627b | ||
|
|
fde65b2730 | ||
|
|
e908362f0a | ||
|
|
a40b837634 | ||
|
|
b391465fbf | ||
|
|
bad0428f5b | ||
|
|
97018df2f9 | ||
|
|
9d84501bc8 | ||
|
|
e9fb2b3fdc | ||
|
|
f60250fd8a | ||
|
|
5fc3cae28a | ||
|
|
e7935be85b | ||
|
|
89363b2ea1 | ||
|
|
e84390ee46 | ||
|
|
65a0f467ae | ||
|
|
4afb150106 | ||
|
|
0f6702217e | ||
|
|
13a0097858 | ||
|
|
01c830ad92 | ||
|
|
dce4f4623c | ||
|
|
d530624362 | ||
|
|
2e878b62d1 | ||
|
|
d27a246dfe | ||
|
|
778def118a | ||
|
|
bc5587477b | ||
|
|
03a775cd31 | ||
|
|
875083a924 | ||
|
|
f6fc925c9e | ||
|
|
74e1972781 | ||
|
|
2f5c54bb49 | ||
|
|
465798ee3d | ||
|
|
425f3acced | ||
|
|
546382e471 | ||
|
|
7e91d78633 | ||
|
|
136e1e4e30 | ||
|
|
f5f6850172 | ||
|
|
28cdde3f17 | ||
|
|
29b801e13d | ||
|
|
1435469ee5 | ||
|
|
4a0bd2c09f | ||
|
|
f8d67f863f | ||
|
|
0291dd5416 | ||
|
|
9014435d4d | ||
|
|
07ad467c73 | ||
|
|
35e23574cf | ||
|
|
9b62b8395f | ||
|
|
45e7022deb | ||
|
|
32dce16363 | ||
|
|
e34b5eafe1 | ||
|
|
4e1e749094 | ||
|
|
ede9293377 | ||
|
|
9101324a1f | ||
|
|
4b844353ee | ||
|
|
2134c19c58 | ||
|
|
c974784ebb | ||
|
|
5eb6961023 | ||
|
|
07cbd8b97b |
@@ -7,6 +7,11 @@ charset = latin1
|
|||||||
indent_style = tab
|
indent_style = tab
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
|
||||||
|
[{*.yaml,*.yml}]
|
||||||
|
end_of_line = lf
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
[*.java]
|
[*.java]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
ij_continuation_indent_size = 4
|
ij_continuation_indent_size = 4
|
||||||
|
|||||||
32
.github/actions/cache-gradle/action.yml
vendored
Normal file
32
.github/actions/cache-gradle/action.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# https://docs.github.com/en/actions/tutorials/create-actions/create-a-composite-action#creating-a-composite-action-within-the-same-repository
|
||||||
|
|
||||||
|
# cache uses two files:
|
||||||
|
#
|
||||||
|
# - gradle-wrapper-<os>-<arch>-<hash>:
|
||||||
|
# contains the Gradle wrapper/distribution (~/.gradle/wrapper)
|
||||||
|
# and is updated when the Gradle version changed
|
||||||
|
# - gradle-caches-<os>-<arch>-<hash>:
|
||||||
|
# contains the Gradle caches (~/.gradle/caches), buildSrc/build and buildSrc/.gradle
|
||||||
|
# and is updated when Gradle related files were changed
|
||||||
|
# buildSrc/.gradle is needed so that buildSrc tasks are UP-TO-DATE
|
||||||
|
|
||||||
|
name: 'Cache Gradle'
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: Cache '.gradle/wrapper'
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
key: gradle-wrapper-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles( 'gradle/wrapper/gradle-wrapper.properties' ) }}
|
||||||
|
path: |
|
||||||
|
~/.gradle/wrapper
|
||||||
|
|
||||||
|
- name: Cache '.gradle/caches' and 'buildSrc/build'
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
key: gradle-caches-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles( '**/*.gradle*', 'gradle/**', 'gradle.properties', 'buildSrc/src/**' ) }}
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
buildSrc/build
|
||||||
|
buildSrc/.gradle
|
||||||
162
.github/workflows/ci.yml
vendored
162
.github/workflows/ci.yml
vendored
@@ -9,45 +9,64 @@ on:
|
|||||||
- '*'
|
- '*'
|
||||||
tags:
|
tags:
|
||||||
- '[0-9]*'
|
- '[0-9]*'
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
- '.*'
|
||||||
|
- '**/.settings/**'
|
||||||
|
- 'flatlaf-core/svg/**'
|
||||||
|
- 'flatlaf-natives/**'
|
||||||
|
- 'flatlaf-testing/dumps/**'
|
||||||
|
- 'flatlaf-testing/misc/**'
|
||||||
|
- 'images/**'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
strategy:
|
steps:
|
||||||
matrix:
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: gradle/actions/wrapper-validation@v4
|
||||||
|
|
||||||
|
- name: Setup Java 21
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
java-version: 21
|
||||||
|
distribution: temurin # pre-installed on ubuntu-latest
|
||||||
|
|
||||||
|
- name: Cache Gradle
|
||||||
|
uses: ./.github/actions/cache-gradle
|
||||||
|
|
||||||
|
|
||||||
# test against
|
# test against
|
||||||
# - Java 8 (minimum requirement)
|
# - Java 8 (minimum requirement)
|
||||||
# - Java LTS versions (11, 17, ...)
|
# - Java LTS versions (11, 17, ...)
|
||||||
# - lastest Java version(s)
|
# - latest Java version(s)
|
||||||
java:
|
|
||||||
- 8
|
|
||||||
- 11 # LTS
|
|
||||||
- 17 # LTS
|
|
||||||
toolchain: [""]
|
|
||||||
include:
|
|
||||||
- java: 17
|
|
||||||
toolchain: 19 # latest
|
|
||||||
|
|
||||||
steps:
|
- name: Build with Java 11 LTS
|
||||||
- uses: actions/checkout@v3
|
if: github.repository == 'JFormDesigner/FlatLaf'
|
||||||
|
run: ./gradlew build clean -Dtoolchain=11
|
||||||
|
|
||||||
- uses: gradle/wrapper-validation-action@v1
|
- name: Build with Java 17 LTS
|
||||||
if: matrix.java == '8'
|
if: github.repository == 'JFormDesigner/FlatLaf'
|
||||||
|
run: ./gradlew build clean -Dtoolchain=17
|
||||||
|
|
||||||
- name: Setup Java ${{ matrix.java }}
|
- name: Build with Java 21 LTS
|
||||||
uses: actions/setup-java@v3
|
if: github.repository == 'JFormDesigner/FlatLaf'
|
||||||
with:
|
run: ./gradlew build clean -Dtoolchain=21
|
||||||
java-version: ${{ matrix.java }}
|
|
||||||
distribution: adopt # Java 8 and 11 are pre-installed on ubuntu-latest
|
|
||||||
cache: gradle
|
|
||||||
|
|
||||||
- name: Build with Gradle
|
- name: Build with Java 25 LTS
|
||||||
run: ./gradlew build -Dtoolchain=${{ matrix.toolchain }}
|
if: github.repository == 'JFormDesigner/FlatLaf'
|
||||||
|
run: ./gradlew build clean -Dtoolchain=25
|
||||||
|
|
||||||
- name: Upload artifacts
|
|
||||||
uses: actions/upload-artifact@v3
|
# build with Java 8 for snapshot
|
||||||
if: matrix.java == '11'
|
|
||||||
|
- name: Build with Java 8
|
||||||
|
run: ./gradlew build
|
||||||
|
|
||||||
|
- name: Upload artifacts to GitHub Actions
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: FlatLaf-build-artifacts
|
name: FlatLaf-build-artifacts
|
||||||
path: |
|
path: |
|
||||||
@@ -57,40 +76,15 @@ jobs:
|
|||||||
!**/*-sources.jar
|
!**/*-sources.jar
|
||||||
|
|
||||||
|
|
||||||
snapshot:
|
- name: Publish snapshot to Sonatype Central
|
||||||
runs-on: ubuntu-latest
|
if: |
|
||||||
needs: build
|
github.repository == 'JFormDesigner/FlatLaf' &&
|
||||||
if: |
|
github.event_name == 'push' &&
|
||||||
github.event_name == 'push' &&
|
(github.ref == 'refs/heads/main' || startsWith( github.ref, 'refs/heads/develop-' ))
|
||||||
(github.ref == 'refs/heads/main' || startsWith( github.ref, 'refs/heads/develop-' )) &&
|
run: ./gradlew publish -PskipFonts -Dorg.gradle.internal.publish.checksums.insecure=true -Dorg.gradle.parallel=false
|
||||||
github.repository == 'JFormDesigner/FlatLaf'
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Setup Java 11
|
|
||||||
uses: actions/setup-java@v3
|
|
||||||
with:
|
|
||||||
java-version: 11
|
|
||||||
distribution: adopt # pre-installed on ubuntu-latest
|
|
||||||
cache: gradle
|
|
||||||
|
|
||||||
- name: Publish snapshot to oss.sonatype.org
|
|
||||||
run: ./gradlew publish :flatlaf-theme-editor:build -PskipFonts -Dorg.gradle.internal.publish.checksums.insecure=true
|
|
||||||
env:
|
env:
|
||||||
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
|
SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
|
||||||
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
|
SONATYPE_PASSWORD: ${{ secrets.SONATYPE_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:
|
||||||
@@ -102,41 +96,31 @@ jobs:
|
|||||||
github.repository == 'JFormDesigner/FlatLaf'
|
github.repository == 'JFormDesigner/FlatLaf'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Java 11
|
- name: Setup Java 21
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 21
|
||||||
distribution: adopt # pre-installed on ubuntu-latest
|
distribution: temurin # pre-installed on ubuntu-latest
|
||||||
cache: gradle
|
|
||||||
|
|
||||||
- name: Release a new stable version to Maven Central
|
- name: Cache Gradle
|
||||||
run: ./gradlew publish :flatlaf-demo:build :flatlaf-theme-editor:build -PskipFonts -Prelease
|
uses: ./.github/actions/cache-gradle
|
||||||
|
|
||||||
|
- name: Release a new stable version to Maven Central and build demo and theme editor
|
||||||
|
run: ./gradlew publishToSonatype closeSonatypeStagingRepository :flatlaf-demo:build :flatlaf-theme-editor:build -PskipFonts -Prelease -Dorg.gradle.parallel=false
|
||||||
env:
|
env:
|
||||||
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
|
SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
|
||||||
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
|
SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
|
||||||
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
|
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
|
||||||
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
|
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
|
||||||
|
|
||||||
- name: Upload demo
|
- name: Install lftp
|
||||||
uses: sebastianpopp/ftp-action@releases/v2
|
run: sudo apt-get -y install lftp
|
||||||
with:
|
|
||||||
host: ${{ secrets.FTP_SERVER }}
|
|
||||||
user: ${{ secrets.FTP_USERNAME }}
|
|
||||||
password: ${{ secrets.FTP_PASSWORD }}
|
|
||||||
forceSsl: true
|
|
||||||
localDir: "flatlaf-demo/build/libs"
|
|
||||||
remoteDir: "."
|
|
||||||
options: "--only-newer --no-recursion --verbose=1"
|
|
||||||
|
|
||||||
- name: Upload theme editor
|
- name: Upload demo and theme editor
|
||||||
uses: sebastianpopp/ftp-action@releases/v2
|
run: >
|
||||||
with:
|
lftp -c "set ftp:ssl-force true;
|
||||||
host: ${{ secrets.FTP_SERVER }}
|
open -u ${{ secrets.FTP_USERNAME }},${{ secrets.FTP_PASSWORD }} ${{ secrets.FTP_SERVER }};
|
||||||
user: ${{ secrets.FTP_USERNAME }}
|
mput flatlaf-demo/build/libs/flatlaf-demo-*.jar;
|
||||||
password: ${{ secrets.FTP_PASSWORD }}
|
mput flatlaf-theme-editor/build/libs/flatlaf-theme-editor-*.jar"
|
||||||
forceSsl: true
|
|
||||||
localDir: "flatlaf-theme-editor/build/libs"
|
|
||||||
remoteDir: "."
|
|
||||||
options: "--only-newer --no-recursion --verbose=1"
|
|
||||||
|
|||||||
40
.github/workflows/error-prone.yml
vendored
Normal file
40
.github/workflows/error-prone.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
|
||||||
|
# https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle
|
||||||
|
|
||||||
|
name: Error Prone
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
tags:
|
||||||
|
- '[0-9]*'
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
- '.*'
|
||||||
|
- '**/.settings/**'
|
||||||
|
- 'flatlaf-core/svg/**'
|
||||||
|
- 'flatlaf-natives/**'
|
||||||
|
- 'flatlaf-testing/dumps/**'
|
||||||
|
- 'flatlaf-testing/misc/**'
|
||||||
|
- 'images/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
error-prone:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.repository == 'JFormDesigner/FlatLaf'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Java 21
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
java-version: 21
|
||||||
|
distribution: temurin # pre-installed on ubuntu-latest
|
||||||
|
|
||||||
|
- name: Cache Gradle
|
||||||
|
uses: ./.github/actions/cache-gradle
|
||||||
|
|
||||||
|
- name: Check with Error Prone
|
||||||
|
run: ./gradlew errorprone
|
||||||
27
.github/workflows/fonts.yml
vendored
27
.github/workflows/fonts.yml
vendored
@@ -13,6 +13,8 @@ on:
|
|||||||
- 'flatlaf-fonts/**'
|
- 'flatlaf-fonts/**'
|
||||||
- '.github/workflows/fonts.yml'
|
- '.github/workflows/fonts.yml'
|
||||||
- 'gradle/wrapper/gradle-wrapper.properties'
|
- 'gradle/wrapper/gradle-wrapper.properties'
|
||||||
|
- '!**.md'
|
||||||
|
- '!**/.settings/**'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Fonts:
|
Fonts:
|
||||||
@@ -22,6 +24,7 @@ jobs:
|
|||||||
- inter
|
- inter
|
||||||
- jetbrains-mono
|
- jetbrains-mono
|
||||||
- roboto
|
- roboto
|
||||||
|
- roboto-mono
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: |
|
if: |
|
||||||
@@ -29,31 +32,33 @@ jobs:
|
|||||||
github.repository == 'JFormDesigner/FlatLaf'
|
github.repository == 'JFormDesigner/FlatLaf'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Java 11
|
- name: Setup Java 21
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 21
|
||||||
distribution: adopt # pre-installed on ubuntu-latest
|
distribution: temurin # pre-installed on ubuntu-latest
|
||||||
cache: gradle
|
|
||||||
|
- name: Cache Gradle
|
||||||
|
uses: ./.github/actions/cache-gradle
|
||||||
|
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
run: ./gradlew :flatlaf-fonts-${{ matrix.font }}:build
|
run: ./gradlew :flatlaf-fonts-${{ matrix.font }}:build
|
||||||
if: startsWith( github.ref, format( 'refs/tags/fonts/{0}-', matrix.font ) ) != true
|
if: startsWith( github.ref, format( 'refs/tags/fonts/{0}-', matrix.font ) ) != true
|
||||||
|
|
||||||
- name: Publish snapshot to oss.sonatype.org
|
- name: Publish snapshot to Sonatype Central
|
||||||
run: ./gradlew :flatlaf-fonts-${{ matrix.font }}:publish -Dorg.gradle.internal.publish.checksums.insecure=true
|
run: ./gradlew :flatlaf-fonts-${{ matrix.font }}:publish -Dorg.gradle.internal.publish.checksums.insecure=true
|
||||||
env:
|
env:
|
||||||
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
|
SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
|
||||||
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
|
SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
|
||||||
if: github.ref == 'refs/heads/main' || startsWith( github.ref, 'refs/heads/develop-' )
|
if: github.ref == 'refs/heads/main' || startsWith( github.ref, 'refs/heads/develop-' )
|
||||||
|
|
||||||
- name: Release a new stable version to Maven Central
|
- name: Release a new stable version to Maven Central
|
||||||
run: ./gradlew :flatlaf-fonts-${{ matrix.font }}:build :flatlaf-fonts-${{ matrix.font }}:publish -Prelease
|
run: ./gradlew :flatlaf-fonts-${{ matrix.font }}:build :flatlaf-fonts-${{ matrix.font }}:publish -Prelease
|
||||||
env:
|
env:
|
||||||
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
|
SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
|
||||||
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
|
SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
|
||||||
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
|
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
|
||||||
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
|
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
|
||||||
if: startsWith( github.ref, format( 'refs/tags/fonts/{0}-', matrix.font ) )
|
if: startsWith( github.ref, format( 'refs/tags/fonts/{0}-', matrix.font ) )
|
||||||
|
|||||||
105
.github/workflows/natives.yml
vendored
105
.github/workflows/natives.yml
vendored
@@ -13,38 +13,119 @@ on:
|
|||||||
- 'flatlaf-natives/**'
|
- 'flatlaf-natives/**'
|
||||||
- '.github/workflows/natives.yml'
|
- '.github/workflows/natives.yml'
|
||||||
- 'gradle/wrapper/gradle-wrapper.properties'
|
- 'gradle/wrapper/gradle-wrapper.properties'
|
||||||
|
- '!**.md'
|
||||||
|
- '!**/.settings/**'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Natives:
|
Natives:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os:
|
os:
|
||||||
- windows
|
- windows-latest
|
||||||
- ubuntu
|
- macos-latest
|
||||||
|
- ubuntu-latest
|
||||||
|
- ubuntu-24.04-arm
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}-latest
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: gradle/wrapper-validation-action@v1
|
- uses: gradle/actions/wrapper-validation@v4
|
||||||
|
|
||||||
- name: Setup Java 11
|
- name: apt update (Linux)
|
||||||
uses: actions/setup-java@v3
|
if: matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm'
|
||||||
|
run: sudo apt-get update
|
||||||
|
|
||||||
|
- name: install libxt-dev and libgtk-3-dev (Linux)
|
||||||
|
if: matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm'
|
||||||
|
run: sudo apt-get install libxt-dev libgtk-3-dev
|
||||||
|
|
||||||
|
# - name: Download libgtk-3.so for arm64 (Linux)
|
||||||
|
# if: matrix.os == 'ubuntu-latest'
|
||||||
|
# working-directory: flatlaf-natives/flatlaf-natives-linux/lib/aarch64
|
||||||
|
# run: |
|
||||||
|
# pwd
|
||||||
|
# ls -l /usr/lib/x86_64-linux-gnu/libgtk*
|
||||||
|
# wget --no-verbose https://ports.ubuntu.com/pool/main/g/gtk%2b3.0/libgtk-3-0_3.24.18-1ubuntu1_arm64.deb
|
||||||
|
# ls -l
|
||||||
|
# ar -x libgtk-3-0_3.24.18-1ubuntu1_arm64.deb data.tar.xz
|
||||||
|
# tar -xvf data.tar.xz --wildcards --to-stdout "./usr/lib/aarch64-linux-gnu/libgtk-3.so.0.*" > libgtk-3.so
|
||||||
|
# rm libgtk-3-0_3.24.18-1ubuntu1_arm64.deb data.tar.xz
|
||||||
|
# ls -l
|
||||||
|
|
||||||
|
# - name: install g++-aarch64-linux-gnu (Linux)
|
||||||
|
# if: matrix.os == 'ubuntu-latest'
|
||||||
|
# run: sudo apt-get install g++-aarch64-linux-gnu
|
||||||
|
|
||||||
|
- name: Setup Java 21
|
||||||
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 21
|
||||||
distribution: adopt
|
distribution: temurin
|
||||||
cache: gradle
|
|
||||||
|
- name: Cache Gradle
|
||||||
|
uses: ./.github/actions/cache-gradle
|
||||||
|
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
# --no-daemon is necessary on Windows otherwise caching Gradle would fail with:
|
# --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
|
# tar.exe: Couldn't open ~/.gradle/caches/modules-2/modules-2.lock: Permission denied
|
||||||
run: ./gradlew build-natives --no-daemon
|
run: ./gradlew build-natives --no-daemon
|
||||||
|
|
||||||
|
- name: Sign Windows DLLs
|
||||||
|
if: matrix.os == 'windows-latest'
|
||||||
|
uses: skymatic/code-sign-action@v3
|
||||||
|
with:
|
||||||
|
certificate: '${{ secrets.CODE_SIGN_CERT_BASE64 }}'
|
||||||
|
password: '${{ secrets.CODE_SIGN_CERT_PASSWORD }}'
|
||||||
|
certificatesha1: '${{ secrets.CODE_SIGN_CERT_SHA1 }}'
|
||||||
|
folder: 'flatlaf-core/src/main/resources/com/formdev/flatlaf/natives'
|
||||||
|
|
||||||
|
- name: Sign macOS natives
|
||||||
|
if: matrix.os == 'DISABLED--macos-latest'
|
||||||
|
env:
|
||||||
|
CERT_BASE64: ${{ secrets.CODE_SIGN_CERT_BASE64 }}
|
||||||
|
CERT_PASSWORD: ${{ secrets.CODE_SIGN_CERT_PASSWORD }}
|
||||||
|
CERT_IDENTITY: ${{ secrets.CODE_SIGN_CERT_IDENTITY }}
|
||||||
|
run: |
|
||||||
|
# https://docs.github.com/en/actions/use-cases-and-examples/deploying/installing-an-apple-certificate-on-macos-runners-for-xcode-development
|
||||||
|
# create variables
|
||||||
|
CERTIFICATE_PATH=$RUNNER_TEMP/cert.p12
|
||||||
|
KEYCHAIN_PATH=$RUNNER_TEMP/signing.keychain-db
|
||||||
|
KEYCHAIN_PASSWORD=$CERT_PASSWORD
|
||||||
|
# decode certificate
|
||||||
|
printenv CERT_BASE64 | base64 --decode > $CERTIFICATE_PATH
|
||||||
|
# create temporary keychain
|
||||||
|
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
||||||
|
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
|
||||||
|
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
||||||
|
# import certificate to keychain
|
||||||
|
security import $CERTIFICATE_PATH -P "$CERT_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
|
||||||
|
# set partition list (required for codesign)
|
||||||
|
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
||||||
|
# add keychain to keychain search list
|
||||||
|
security list-keychains -d user -s $KEYCHAIN_PATH
|
||||||
|
# sign code
|
||||||
|
codesign --sign "$CERT_IDENTITY" --force --verbose=4 --timestamp \
|
||||||
|
flatlaf-core/src/main/resources/com/formdev/flatlaf/natives/libflatlaf-macos-*.dylib
|
||||||
|
codesign --display --verbose=4 flatlaf-core/src/main/resources/com/formdev/flatlaf/natives/libflatlaf-macos-*.dylib
|
||||||
|
# cleanup
|
||||||
|
security delete-keychain $KEYCHAIN_PATH
|
||||||
|
|
||||||
|
- name: Set artifacts pattern
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
case ${{ matrix.os }} in
|
||||||
|
windows-latest) echo "artifactPattern=flatlaf-windows-*.dll" >> $GITHUB_ENV ;;
|
||||||
|
macos-latest) echo "artifactPattern=libflatlaf-macos-*.dylib" >> $GITHUB_ENV ;;
|
||||||
|
ubuntu-latest) echo "artifactPattern=libflatlaf-linux-x86_64.so" >> $GITHUB_ENV ;;
|
||||||
|
ubuntu-24.04-arm) echo "artifactPattern=libflatlaf-linux-arm64.so" >> $GITHUB_ENV ;;
|
||||||
|
esac
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: FlatLaf-natives-build-artifacts-${{ matrix.os }}
|
name: FlatLaf-natives-build-artifacts-${{ matrix.os }}
|
||||||
path: |
|
path: |
|
||||||
flatlaf-core/src/main/resources/com/formdev/flatlaf/natives
|
flatlaf-core/src/main/resources/com/formdev/flatlaf/natives/${{ env.artifactPattern }}
|
||||||
flatlaf-natives/flatlaf-natives-*/build
|
flatlaf-natives/flatlaf-natives-*/build
|
||||||
|
|||||||
39
.github/workflows/pr-snapshots.yml
vendored
Normal file
39
.github/workflows/pr-snapshots.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
|
||||||
|
|
||||||
|
name: PR Snapshots
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
- '.*'
|
||||||
|
- '**/.settings/**'
|
||||||
|
- 'flatlaf-core/svg/**'
|
||||||
|
- 'flatlaf-testing/dumps/**'
|
||||||
|
- 'flatlaf-testing/misc/**'
|
||||||
|
- 'images/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
snapshot:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.repository == 'JFormDesigner/FlatLaf'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Java 21
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
java-version: 21
|
||||||
|
distribution: temurin # pre-installed on ubuntu-latest
|
||||||
|
|
||||||
|
- name: Cache Gradle
|
||||||
|
uses: ./.github/actions/cache-gradle
|
||||||
|
|
||||||
|
- name: Publish PR snapshot to Sonatype Central
|
||||||
|
run: >
|
||||||
|
./gradlew publish -PskipFonts -Dorg.gradle.internal.publish.checksums.insecure=true -Dorg.gradle.parallel=false
|
||||||
|
-Pgithub.event.pull_request.number=${{ github.event.pull_request.number }}
|
||||||
|
env:
|
||||||
|
SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
|
||||||
|
SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,6 +5,7 @@ build/
|
|||||||
.project
|
.project
|
||||||
.settings/
|
.settings/
|
||||||
.idea/
|
.idea/
|
||||||
|
.consulo/
|
||||||
out/
|
out/
|
||||||
*.iml
|
*.iml
|
||||||
*.ipr
|
*.ipr
|
||||||
|
|||||||
688
CHANGELOG.md
688
CHANGELOG.md
@@ -1,6 +1,692 @@
|
|||||||
FlatLaf Change Log
|
FlatLaf Change Log
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
## 3.7.1-SNAPSHOT
|
||||||
|
|
||||||
|
- ComboBox: Added UI property `ComboBox.buttonFocusedEditableBackground`. (issue
|
||||||
|
#1068)
|
||||||
|
- Popup: Fixed scrolling popup painting issue on Windows 10 when a glass pane is
|
||||||
|
visible and frame is maximized. (issue #1071)
|
||||||
|
- Slider: Styling `thumbSize` or `focusWidth` did not update slider size/layout.
|
||||||
|
(PR #1074)
|
||||||
|
- ToolBar: Grip disappeared when switching between Look and Feels. (issue #1075)
|
||||||
|
- Extras:
|
||||||
|
- UI defaults inspector: Fixed NPE if color of `FlatLineBorder` is null. Also
|
||||||
|
use `FlatLineBorder` line color as cell background color in "Value" column.
|
||||||
|
(PR #1080)
|
||||||
|
|
||||||
|
|
||||||
|
## 3.7
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- System File Chooser allows using **operating system file dialogs** in Java
|
||||||
|
Swing applications. (PR #988)
|
||||||
|
- Zooming API. (PR #1051)
|
||||||
|
- Icons:
|
||||||
|
- Support scaling Laf icons (checkbox, radiobutton, etc). (issue #1061)
|
||||||
|
- Scale checkbox and radiobutton icons when using
|
||||||
|
[text styles](https://www.formdev.com/flatlaf/typography/#text_styles)
|
||||||
|
`large`, `medium`, `small` and `mini`.
|
||||||
|
- TabbedPane: Added icon-only tab mode, which shows tab icons but hides tab
|
||||||
|
titles. Tab titles are used in "Show Hidden Tabs" popup menu. (set client
|
||||||
|
property `JTabbedPane.tabWidthMode` to `"iconOnly"`)
|
||||||
|
- TabbedPane: In scroll tab layout, propagate mouse wheel events to ancestors.
|
||||||
|
This allows mouse wheel scrolling if JTabbedPane is inside a JScrollPane. (PR
|
||||||
|
#1030)
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- CheckBox and RadioButton: Fixed styling of custom icon. Also fixed focus width
|
||||||
|
(and preferred size) if using custom icon. (PR #1060)
|
||||||
|
- TabbedPane: In "Show Hidden Tabs" popup menu, do not show text "x. Tab" if tab
|
||||||
|
has icon but no title. (issue #1062)
|
||||||
|
- TextField: Fixed wrong leading/trailing icon placement if border is set to
|
||||||
|
`null`. (issue #1047)
|
||||||
|
- Extras: UI defaults inspector: Exclude inspector window from being blocked by
|
||||||
|
modal dialogs. (issue #1048)
|
||||||
|
- JideButton, JideToggleButton, JideSplitButton and JideToggleSplitButton: Paint
|
||||||
|
border in button style `TOOLBAR_STYLE` if in selected state. (issue #1045)
|
||||||
|
- IntelliJ Themes: Fixed problem when using theme instance more than once when
|
||||||
|
switching to that theme. (issue #990)
|
||||||
|
|
||||||
|
|
||||||
|
## 3.6.2
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- If using `FlatLaf.registerCustomDefaultsSource( "com.myapp.themes" )` and
|
||||||
|
named Java modules, it is no longer necessary to add `opens com.myapp.themes;`
|
||||||
|
to `module-info.java`. (issue #1026)
|
||||||
|
- Extras: Made animated theme change (class `FlatAnimatedLafChange`) smoother.
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Tree and List: Fixed painting of rounded drop backgrounds. (issue #1023)
|
||||||
|
- Popup: Showing tooltip in inactive window brought that window to front (made
|
||||||
|
it active) and potentially hid the previously active window. (issue #1037)
|
||||||
|
- Popup: No longer reuse popup windows for menus to avoid immediately closing
|
||||||
|
dialogs on ChromeOS. (issue #1029)
|
||||||
|
- macOS: Fixed window "flashing" when switching from a light to a dark theme (or
|
||||||
|
vice versa). Especially when using animated theme changer (see
|
||||||
|
[FlatLaf Extras](flatlaf-extras)).
|
||||||
|
|
||||||
|
#### Incompatibilities
|
||||||
|
|
||||||
|
- FlatLaf properties files are now loaded using the UTF-8 character encoding
|
||||||
|
instead of ISO 8859-1. In usual properties files you will not notice any
|
||||||
|
difference because they use only ASCII characters, but if you've put localized
|
||||||
|
(non-English) texts (e.g. German umlauts) into your properties files, you need
|
||||||
|
to convert them to UTF-8. Properties files created with the FlatLaf Theme
|
||||||
|
Editor already use UTF-8, including in older versions. (issue #1031)
|
||||||
|
|
||||||
|
|
||||||
|
## 3.6.1
|
||||||
|
|
||||||
|
- Extras: Support JSVG 2.0.0. Minimum JSVG version is now 1.6.0. (issue #997)
|
||||||
|
- FlatLaf window decorations (Windows 10/11 only): Improved diagonal window
|
||||||
|
resizing on top-left and top-right window corners. Top window resize area now
|
||||||
|
also covers iconify/maximize/close buttons. (issue #1015)
|
||||||
|
- ToggleButton: Styling `selectedForeground` did not work if `foreground` is
|
||||||
|
also styled. (issue #1017)
|
||||||
|
- JideSplitButton: Fixed updating popup when switching theme. (issue #1000)
|
||||||
|
- IntelliJ Themes: Fixed logging false errors when loading 3rd party
|
||||||
|
`.theme.json` files. (issue #990)
|
||||||
|
- Linux: Popups appeared in wrong position on multi-screen setup if primary
|
||||||
|
display is located below or right to secondary display. (see
|
||||||
|
[NetBeans issue #8532](https://github.com/apache/netbeans/issues/8532))
|
||||||
|
- macOS: Fixed popup flickering after theme change. (issue #1009)
|
||||||
|
- macOS with JetBrains Runtime: Fixed sometimes empty popups. (issue #1019)
|
||||||
|
|
||||||
|
|
||||||
|
## 3.6
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- macOS: Re-enabled rounded popup border (see PR #772) on macOS 14.4+ (was
|
||||||
|
disabled in 3.5.x).
|
||||||
|
- Increased contrast of text for better readability: (PR #972; issue #762)
|
||||||
|
- In **FlatLaf Dark**, **FlatLaf Darcula** and many dark IntelliJ themes, made
|
||||||
|
all text colors brighter.
|
||||||
|
- In **FlatLaf Light**, **FlatLaf IntelliJ** and many light IntelliJ themes,
|
||||||
|
made disabled text colors slightly darker.
|
||||||
|
- In **FlatLaf macOS Light**, made disabled text colors darker.
|
||||||
|
- In **FlatLaf macOS Dark**, made text colors of "default" button and selected
|
||||||
|
ToggleButton lighter.
|
||||||
|
- CheckBox: Support styling indeterminate state of
|
||||||
|
[tri-state check boxes](https://www.javadoc.io/doc/com.formdev/flatlaf-extras/latest/com/formdev/flatlaf/extras/components/FlatTriStateCheckBox.html).
|
||||||
|
(PR #936; issue #919)
|
||||||
|
- List: Support for alternate row highlighting. (PR #939)
|
||||||
|
- Tree: Support for alternate row highlighting. (PR #903)
|
||||||
|
- Tree: Support wide cell renderer. (issue #922)
|
||||||
|
- ScrollBar: Use rounded thumb also on Windows (as on macOS and Linux) and made
|
||||||
|
thumb slightly darker/lighter. (issue #918)
|
||||||
|
- Extras: `FlatSVGIcon` color filters now can access painting component to
|
||||||
|
implement component state based color mappings. (issue #906)
|
||||||
|
- Linux:
|
||||||
|
- Rounded iconify/maximize/close buttons if using FlatLaf window decorations.
|
||||||
|
(PR #971)
|
||||||
|
- Added `libflatlaf-linux-arm64.so` for Linux on ARM64. (issue #899)
|
||||||
|
- Use X11 window manager events to resize window, if FlatLaf window
|
||||||
|
decorations are enabled. This gives FlatLaf windows a more "native" feeling.
|
||||||
|
(issue #866)
|
||||||
|
- IntelliJ Themes:
|
||||||
|
- Updated to latest versions and fixed various issues.
|
||||||
|
- Support customizing through properties files. (issue #824)
|
||||||
|
- SwingX: Support `JXTipOfTheDay` component. (issue #980)
|
||||||
|
- Support key prefixes for Linux desktop environments (e.g. `[gnome]`, `[kde]`
|
||||||
|
or `[xfce]`) in properties files. (issue #974)
|
||||||
|
- Support custom key prefixes (e.g. `[win10]` or `[test]`) in properties files.
|
||||||
|
(issue #649)
|
||||||
|
- Support multi-prefixed keys (e.g. `[dark][gnome]TitlePane.buttonBackground`).
|
||||||
|
The value is only used if all prefixes match current platform/theme.
|
||||||
|
- Support new component border color to indicate success state (set client
|
||||||
|
property `JComponent.outline` to `success`). (PR #982, issue #945)
|
||||||
|
- Fonts: Updated **Inter** to
|
||||||
|
[v4.1](https://github.com/rsms/inter/releases/tag/v4.1).
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Button: Fixed background and foreground colors for `borderless` and
|
||||||
|
`toolBarButton` style default buttons (`JButton.isDefaultButton()` is `true`).
|
||||||
|
(issue #947)
|
||||||
|
- FileChooser: Improved performance when navigating to large directories with
|
||||||
|
thousands of files. (issue #953)
|
||||||
|
- PopupFactory: Fixed NPE on Windows 10 when `owner` is `null`. (issue #952)
|
||||||
|
- Popup: On Windows 10, drop shadow of heavy-weight popup was not updated if
|
||||||
|
popup moved/resized. (issue #942)
|
||||||
|
- FlatLaf window decorations:
|
||||||
|
- Minimize and maximize icons were not shown for custom scale factors less
|
||||||
|
than 100% (e.g. `-Dflatlaf.uiScale=75%`). (issue #951)
|
||||||
|
- Linux: Fixed occasional maximizing of window when single-clicking the
|
||||||
|
window's title bar. (issue #637)
|
||||||
|
- Styling: MigLayout visual padding was not updated after applying style to
|
||||||
|
Button, ComboBox, Spinner, TextField (and subclasses) and ToggleButton. (issue
|
||||||
|
#965)
|
||||||
|
- Linux: Popups (menus and combobox lists) were not hidden when window is moved,
|
||||||
|
resized, maximized, restored, iconified or switched to another window. (issue
|
||||||
|
#962)
|
||||||
|
- Fixed loading FlatLaf UI delegate classes when using FlatLaf in special
|
||||||
|
application where multiple class loaders are involved. E.g. in Eclipse plugin
|
||||||
|
or in LibreOffice extension. (issues #955 and #851)
|
||||||
|
- HTML: Fixed rendering of `<hr noshade>` in dark themes. (issue #932)
|
||||||
|
- TextComponents: `selectAllOnFocusPolicy` related changes:
|
||||||
|
- No longer select all text if selection (or caret position) was changed by
|
||||||
|
application and `selectAllOnFocusPolicy` is `once` (the default). (issue
|
||||||
|
#983)
|
||||||
|
- FormattedTextField and Spinner: `selectAllOnFocusPolicy = once` behaves now
|
||||||
|
as `always` (was `never` before), which means that all text is selected when
|
||||||
|
component gains focus. This is because of special behavior of
|
||||||
|
`JFormattedTextField` that did not allow implementation of `once`.
|
||||||
|
- Client property `JTextField.selectAllOnFocusPolicy` now also works on
|
||||||
|
(editable) `JComboBox` and on `JSpinner`.
|
||||||
|
- Added client property `JTextField.selectAllOnMouseClick` to override UI
|
||||||
|
property `TextComponent.selectAllOnMouseClick`. (issue #961)
|
||||||
|
- For `selectAllOnMouseClick = true`, clicking with the mouse into the text
|
||||||
|
field, to focus it, now always selects all text, even if
|
||||||
|
`selectAllOnFocusPolicy` is `once`.
|
||||||
|
|
||||||
|
#### Incompatibilities
|
||||||
|
|
||||||
|
- IntelliJ Themes:
|
||||||
|
- Theme prefix in `IntelliJTheme$ThemeLaf.properties` changed from
|
||||||
|
`[theme-name]` to `{theme-name}`.
|
||||||
|
- Renamed classes in package
|
||||||
|
`com.formdev.flatlaf.intellijthemes.materialthemeuilite` from `Flat<theme>`
|
||||||
|
to `FlatMT<theme>`.
|
||||||
|
- Removed `Gruvbox Dark Medium` and `Gruvbox Dark Soft` themes.
|
||||||
|
- Prefixed keys in properties files (e.g. `[dark]Button.background` or
|
||||||
|
`[win]Button.arc`) are now handled earlier than before. In previous versions,
|
||||||
|
prefixed keys always had higher priority than unprefixed keys and did always
|
||||||
|
overwrite unprefixed keys. Now prefixed keys are handled in same order as
|
||||||
|
unprefixed keys, which means that if a key is prefixed and unprefixed (e.g.
|
||||||
|
`[win]Button.arc` and `Button.arc`), the one which is last specified in
|
||||||
|
properties file is used.\
|
||||||
|
Following worked in previous versions, but now `Button.arc` is always `6`:
|
||||||
|
~~~properties
|
||||||
|
[win]Button.arc = 12
|
||||||
|
Button.arc = 6
|
||||||
|
~~~
|
||||||
|
This works in new (and old) versions:
|
||||||
|
~~~properties
|
||||||
|
Button.arc = 6
|
||||||
|
[win]Button.arc = 12
|
||||||
|
~~~
|
||||||
|
|
||||||
|
|
||||||
|
## 3.5.4
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- HTML: Fixed NPE when using HTML text on a component with `null` font. (issue
|
||||||
|
#930; PR #931; regression in 3.5)
|
||||||
|
- Linux: Fixed NPE when using FlatLaf window decorations and switching theme.
|
||||||
|
(issue #933; regression in 3.5.3)
|
||||||
|
|
||||||
|
|
||||||
|
## 3.5.3
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- HTML: Fixed wrong rendering if HTML text contains `<style>` tag with
|
||||||
|
attributes (e.g. `<style type='text/css'>`). (issue #905; regression in 3.5.1)
|
||||||
|
- FlatLaf window decorations:
|
||||||
|
- Windows: Fixed possible deadlock with TabbedPane in window title area in
|
||||||
|
"full window content" mode. (issue #909)
|
||||||
|
- Windows: Fixed wrong layout in maximized frame after changing screen scale
|
||||||
|
factor. (issue #904)
|
||||||
|
- Linux: Fixed continuous cursor toggling between resize and standard cursor
|
||||||
|
when resizing window. (issue #907)
|
||||||
|
- Fixed sometimes broken window moving with SplitPane in window title area in
|
||||||
|
"full window content" mode. (issue #926)
|
||||||
|
- Popup: On Windows 10, fixed misplaced popup drop shadow. (issue #911;
|
||||||
|
regression in 3.5)
|
||||||
|
- Popup: Fixed NPE if `GraphicsConfiguration` is `null` on Windows. (issue #921)
|
||||||
|
- Theme Editor: Fixed using color picker on secondary screen.
|
||||||
|
- Fixed detection of Windows 11 if custom exe launcher does not specify Windows
|
||||||
|
10+ compatibility in application manifest. (issue #916)
|
||||||
|
- Linux: Fixed slightly different font size (or letter width) used to paint HTML
|
||||||
|
text when default font family is _Cantarell_ (e.g. on Fedora). (issue #912)
|
||||||
|
|
||||||
|
#### Other Changes
|
||||||
|
|
||||||
|
- Class `FlatPropertiesLaf` now supports FlatLaf macOS themes as base themes.
|
||||||
|
|
||||||
|
|
||||||
|
## 3.5.2
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Windows: Fixed repaint issues (ghosting) on some systems (probably depending
|
||||||
|
on graphics card/driver). This is done by setting Java system property
|
||||||
|
`sun.java2d.d3d.onscreen` to `false` (but only if `sun.java2d.d3d.onscreen`,
|
||||||
|
`sun.java2d.d3d` and `sun.java2d.noddraw` are not yet set), which disables
|
||||||
|
usage of Windows Direct3D (DirectX) onscreen surfaces. Component rendering
|
||||||
|
still uses Direct3D. (issue #887)
|
||||||
|
- FlatLaf window decorations:
|
||||||
|
- Iconify/maximize/close buttons did not fill whole title bar height, if some
|
||||||
|
custom component in menu bar increases title bar height. (issue #897)
|
||||||
|
- Windows: Fixed possible application freeze when using custom component that
|
||||||
|
overrides `Component.contains(int x, int y)` and invokes
|
||||||
|
`SwingUtilities.convertPoint()` (or similar) from the overridden method.
|
||||||
|
(issue #878)
|
||||||
|
- TextComponents: Fixed too fast scrolling in multi-line text components when
|
||||||
|
using touchpads (e.g. on macOS). (issue #892)
|
||||||
|
- ToolBar: Fixed endless loop if button in Toolbar has focus and is made
|
||||||
|
invisible. (issue #884)
|
||||||
|
|
||||||
|
#### Other Changes
|
||||||
|
|
||||||
|
- FlatLaf window decorations: Added client property `JRootPane.titleBarHeight`
|
||||||
|
to allow specifying a (larger) preferred height for the title bar. (issue
|
||||||
|
#897)
|
||||||
|
- Added system property `flatlaf.useRoundedPopupBorder` to allow disabling
|
||||||
|
native rounded popup borders on Windows 11 and macOS. On macOS 14.4+, where
|
||||||
|
rounded popup borders are disabled since FlatLaf 3.5 because of occasional
|
||||||
|
problems, you can use this to enable rounded popup borders (at your risk).
|
||||||
|
|
||||||
|
|
||||||
|
## 3.5.1
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- HTML: Fixed occasional cutoff wrapped text when using multi-line text in HTML
|
||||||
|
tags `<h1>`...`<h6>`, `<code>`, `<kbd>`, `<big>`, `<small>` or `<samp>`.
|
||||||
|
(issue #873; regression in 3.5)
|
||||||
|
- Popup: Fixed `UnsupportedOperationException: PERPIXEL_TRANSLUCENT translucency
|
||||||
|
is not supported` exception on Haiku OS when showing popup (partly) outside of
|
||||||
|
window. (issue #869)
|
||||||
|
- HiDPI: Fixed occasional wrong repaint areas when using
|
||||||
|
`HiDPIUtils.installHiDPIRepaintManager()`. (see PR #864)
|
||||||
|
- Added system property `flatlaf.useSubMenuSafeTriangle` to allow disabling
|
||||||
|
submenu safe triangle (PR #490) for
|
||||||
|
[SWTSwing](https://github.com/Chrriis/SWTSwing). (issue #870)
|
||||||
|
|
||||||
|
|
||||||
|
## 3.5
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Table: Support rounded selection. (PR #856)
|
||||||
|
- Button and ToggleButton: Added border colors for pressed and selected states.
|
||||||
|
(issue #848)
|
||||||
|
- Label: Support painting background with rounded corners. (issue #842)
|
||||||
|
- Popup: Fixed flicker of popups (e.g. tooltips) while they are moving (e.g.
|
||||||
|
following mouse pointer). (issues #832 and #672)
|
||||||
|
- FileChooser: Wrap shortcuts in scroll pane. (issue #828)
|
||||||
|
- Theme Editor: On macOS, use larger window title bar. (PR #779)
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- macOS: Disabled rounded popup border (see PR #772) on macOS 14.4+ because it
|
||||||
|
may freeze the application and crash the macOS WindowServer process (reports
|
||||||
|
vary from Finder restarts to OS restarts). This is a temporary change until a
|
||||||
|
solution is found. See NetBeans issues
|
||||||
|
[apache/netbeans#7560](https://github.com/apache/netbeans/issues/7560#issuecomment-2226439215)
|
||||||
|
and
|
||||||
|
[apache/netbeans#6647](https://github.com/apache/netbeans/issues/6647#issuecomment-2070124442).
|
||||||
|
- FlatLaf window decorations: Window top border on Windows 10 in "full window
|
||||||
|
content" mode was not fully repainted when activating or deactivating window.
|
||||||
|
(issue #809)
|
||||||
|
- Button and ToggleButton: UI properties `[Toggle]Button.selectedForeground` and
|
||||||
|
`[Toggle]Button.pressedForeground` did not work for HTML text. (issue #848)
|
||||||
|
- HTML: Fixed font sizes for HTML tags `<h1>`...`<h6>`, `<code>`, `<kbd>`,
|
||||||
|
`<big>`, `<small>` and `<samp>` in HTML text for components Button, CheckBox,
|
||||||
|
RadioButton, MenuItem (and subclasses), JideLabel, JideButton, JXBusyLabel and
|
||||||
|
JXHyperlink. Also fixed for Label and ToolTip if using Java 11+.
|
||||||
|
- ScrollPane: Fixed/improved border painting at 125% - 175% scaling to avoid
|
||||||
|
different border thicknesses. (issue #743)
|
||||||
|
- Table: Fixed painting of alternating rows below table if auto-resize mode is
|
||||||
|
`JTable.AUTO_RESIZE_OFF` and table width is smaller than scroll pane (was not
|
||||||
|
updated when table width changed and was painted on wrong side in
|
||||||
|
right-to-left component orientation).
|
||||||
|
- Theme Editor: Fixed occasional empty window on startup on macOS.
|
||||||
|
- FlatLaf window decorations: Fixed black line sometimes painted on top of
|
||||||
|
(native) window border on Windows 11. (issue #852)
|
||||||
|
- HiDPI: Fixed incomplete component paintings at 125% or 175% scaling on Windows
|
||||||
|
where sometimes a 1px wide area at the right or bottom component edge is not
|
||||||
|
repainted. E.g. ScrollPane focus indicator border. (issues #860 and #582)
|
||||||
|
|
||||||
|
#### Incompatibilities
|
||||||
|
|
||||||
|
- ProgressBar: Log warning (including stack trace) when uninstalling
|
||||||
|
indeterminate progress bar UI or using `JProgressBar.setIndeterminate(false)`
|
||||||
|
not on AWT thread, because this may throw NPE in `FlatProgressBarUI.paint()`.
|
||||||
|
(issues #841 and #830)
|
||||||
|
- Panel: Rounded background of panel with rounded corners is now painted even if
|
||||||
|
panel is not opaque. (issue #840)
|
||||||
|
|
||||||
|
|
||||||
|
## 3.4.1
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- SplitPane: Update divider when client property `JSplitPane.expandableSide`
|
||||||
|
changed.
|
||||||
|
- TabbedPane: Fixed swapped back and forward scroll buttons when using
|
||||||
|
`TabbedPane.scrollButtonsPlacement = trailing` (regression in FlatLaf 3.3).
|
||||||
|
- Fixed missing window top border on Windows 10 in "full window content" mode.
|
||||||
|
(issue #809)
|
||||||
|
- Extras:
|
||||||
|
- `FlatSVGIcon` color filters now support linear gradients. (PR #817)
|
||||||
|
- `FlatSVGIcon`: Use log level `CONFIG` instead of `SEVERE` and allow
|
||||||
|
disabling logging. (issue #823)
|
||||||
|
- Added support for `JSplitPane.expandableSide` client property to
|
||||||
|
`FlatSplitPane`.
|
||||||
|
- Native libraries: Added API version check to test whether native library
|
||||||
|
matches the JAR (bad builds could e.g. ship a newer JAR with an older
|
||||||
|
incompatible native library) and to test whether native methods can be invoked
|
||||||
|
(some security software allows loading native library but blocks method
|
||||||
|
invocation).
|
||||||
|
- macOS: Fixed crash when running in WebSwing. (issue #826; regression in 3.4)
|
||||||
|
|
||||||
|
#### Incompatibilities
|
||||||
|
|
||||||
|
- File names of custom properties files for nested Laf classes now must include
|
||||||
|
name of enclosing class name. E.g. nested Laf class `IntelliJTheme.ThemeLaf`
|
||||||
|
used `ThemeLaf.properties` in previous versions, but now needs to be named
|
||||||
|
`IntelliJTheme$ThemeLaf.properties`.
|
||||||
|
|
||||||
|
|
||||||
|
## 3.4
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- FlatLaf window decorations (Windows 10/11 and Linux): Support "full window
|
||||||
|
content" mode, which allows you to extend the content into the window title
|
||||||
|
bar. (PR #801)
|
||||||
|
- macOS: Support larger window title bar close/minimize/zoom buttons spacing in
|
||||||
|
[full window content](https://www.formdev.com/flatlaf/macos/#full_window_content)
|
||||||
|
mode and introduced "buttons placeholder". (PR #779)
|
||||||
|
- Native libraries:
|
||||||
|
- System property `flatlaf.nativeLibraryPath` now supports loading native
|
||||||
|
libraries named the same as on Maven central.
|
||||||
|
- Published `flatlaf-<version>-no-natives.jar` to Maven Central. This JAR is
|
||||||
|
equal to `flatlaf-<version>.jar`, except that it does not contain the
|
||||||
|
FlatLaf native libraries. The Maven "classifier" to use this JAR is
|
||||||
|
`no-natives`. You need to distribute the FlatLaf native libraries with your
|
||||||
|
application.
|
||||||
|
See https://www.formdev.com/flatlaf/native-libraries/ for more details.
|
||||||
|
- Improved log messages for loading fails.
|
||||||
|
- Fonts: Updated **Inter** to
|
||||||
|
[v4.0](https://github.com/rsms/inter/releases/tag/v4.0).
|
||||||
|
- Table: Select all text in cell editor when starting editing using `F2` key on
|
||||||
|
Windows or Linux. (issue #652)
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- macOS: Setting window background (of undecorated window) to translucent color
|
||||||
|
(alpha < 255) did not show the window translucent. (issue #705)
|
||||||
|
- JIDE CommandMenuBar: Fixed `ClassCastException` when JIDE command bar displays
|
||||||
|
`JideMenu` in popup. (PR #794)
|
||||||
|
|
||||||
|
|
||||||
|
## 3.3
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- macOS (10.14+): Popups (`JPopupMenu`, `JComboBox`, `JToolTip`, etc.) now use
|
||||||
|
native macOS rounded borders. (PR #772; issue #715)
|
||||||
|
- Native libraries: Added `libflatlaf-macos-arm64.dylib` and
|
||||||
|
`libflatlaf-macos-x86_64.dylib`. See also
|
||||||
|
https://www.formdev.com/flatlaf/native-libraries/.
|
||||||
|
- ScrollPane: Support rounded border. (PR #713)
|
||||||
|
- SplitPane: Support divider hover and pressed background colors. (PR #788)
|
||||||
|
- TabbedPane: Support vertical tabs. (PR #758, issue #633)
|
||||||
|
- TabbedPane: Paint rounded tab area background for rounded cards. (issue #717)
|
||||||
|
- ToolBar: Added styling properties `separatorWidth` and `separatorColor`.
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Button and ToggleButton: Selected buttons did not use explicitly set
|
||||||
|
foreground color. (issue #756)
|
||||||
|
- FileChooser: Catch NPE in Java 21 when getting icon for `.exe` files that use
|
||||||
|
default Windows exe icon. (see
|
||||||
|
[JDK-8320692](https://bugs.openjdk.org/browse/JDK-8320692))
|
||||||
|
- OptionPane: Fixed styling custom panel background in `JOptionPane`. (issue
|
||||||
|
#761)
|
||||||
|
- ScrollPane: Styling ScrollPane border properties did not work if view
|
||||||
|
component is a Table.
|
||||||
|
- Table:
|
||||||
|
- Switching theme looses table grid and intercell spacing. (issues #733 and
|
||||||
|
#750)
|
||||||
|
- Fixed background of `boolean` columns when using alternating row colors.
|
||||||
|
(issue #780)
|
||||||
|
- Fixed border arc of components in complex table cell editors. (issue #786)
|
||||||
|
- TableHeader:
|
||||||
|
- No longer temporary replace header cell renderer while painting. This avoids
|
||||||
|
a `StackOverflowError` in case that custom renderer does this too. (see
|
||||||
|
[NetBeans issue #6835](https://github.com/apache/netbeans/issues/6835)) This
|
||||||
|
also improves compatibility with custom table header implementations.
|
||||||
|
- Header cell renderer background/foreground colors were not restored after
|
||||||
|
hover if renderer uses `null` for background/foreground. (PR #790)
|
||||||
|
- TabbedPane:
|
||||||
|
- Avoid unnecessary repainting whole tabbed pane content area when layouting
|
||||||
|
leading/trailing components.
|
||||||
|
- Avoid unnecessary repainting of selected tab on temporary changes.
|
||||||
|
- Fixed "endless" layouting and repainting when using nested tabbed panes (top
|
||||||
|
and bottom tab placement) and RSyntaxTextArea (with enabled line-wrapping)
|
||||||
|
as tab content. (see
|
||||||
|
[jadx issue #2030](https://github.com/skylot/jadx/issues/2030))
|
||||||
|
- Fixed broken rendering after resizing window to minimum size and then
|
||||||
|
increasing size again. (issue #767)
|
||||||
|
|
||||||
|
#### Incompatibilities
|
||||||
|
|
||||||
|
- Removed support for JetBrains custom decorations, which required
|
||||||
|
[JetBrains Runtime](https://github.com/JetBrains/JetBrainsRuntime/wiki) (JBR)
|
||||||
|
8 or 11. It did not work for JBR 17. System property
|
||||||
|
`flatlaf.useJetBrainsCustomDecorations` is now ignored. **Note**: FlatLaf
|
||||||
|
window decorations continue to work with JBR.
|
||||||
|
|
||||||
|
|
||||||
|
## 3.2.5
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Popup: Fixed NPE if popup invoker is `null` on Windows 10. (issue #753;
|
||||||
|
regression in 3.2.1 in fix for #626)
|
||||||
|
|
||||||
|
|
||||||
|
## 3.2.4
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Popup: Fixed NPE if popup invoker is `null` on Linux with Wayland and Java 21.
|
||||||
|
(issue #752; regression in 3.2.3)
|
||||||
|
|
||||||
|
|
||||||
|
## 3.2.3
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Popup: Popups that request focus were not shown on Linux with Wayland and Java 21.
|
||||||
|
(issue #752)
|
||||||
|
|
||||||
|
|
||||||
|
## 3.2.2
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Button: Fixed painting icon and text at wrong location when using HTML text,
|
||||||
|
left/right vertical alignment and running in Java 19+. (issue #746)
|
||||||
|
- CheckBox and RadioButton: Fixed cut off right side when border is removed and
|
||||||
|
horizontal alignment is set to `right`. (issue #734)
|
||||||
|
- TabbedPane: Fixed NPE when using focusable component as tab component and
|
||||||
|
switching theme. (issue #745)
|
||||||
|
|
||||||
|
|
||||||
|
## 3.2.1
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Fixed memory leak in
|
||||||
|
`MultiResolutionImageSupport.create(int,Dimension[],Function<Dimension,Image>)`,
|
||||||
|
which caches images created by the producer function. Used by
|
||||||
|
`FlatSVGIcon.getImage()` and `FlatSVGUtils.createWindowIconImages()`. If you
|
||||||
|
use one of these methods, it is **strongly recommended** to upgrade to this
|
||||||
|
version, because if the returned image is larger and painted very often it may
|
||||||
|
result in an out-of-memory situation. (issue #726)
|
||||||
|
- FileChooser: Fixed occasional NPE in `FlatShortcutsPanel` on Windows. (issue
|
||||||
|
#718)
|
||||||
|
- TextField: Fixed placeholder text painting, which did not respect horizontal
|
||||||
|
alignment property of `JTextField`. (issue #721)
|
||||||
|
- Popup: Fixed drop shadow if popup overlaps a heavyweight component. (Windows
|
||||||
|
10 only; issue #626)
|
||||||
|
|
||||||
|
|
||||||
|
## 3.2
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- TabbedPane: Support rounded underline selection and rounded card tabs. (PR
|
||||||
|
#703)
|
||||||
|
- FlatLaf window decorations:
|
||||||
|
- Support for Windows on ARM 64-bit. (issue #443, PR #707)
|
||||||
|
- Support toolbox-style "small" window title bar. (issue #659, PR #702)
|
||||||
|
- Extras: Class `FlatSVGIcon` now uses [JSVG](https://github.com/weisJ/jsvg)
|
||||||
|
library (instead of svgSalamander) for rendering. JSVG provides improved SVG
|
||||||
|
rendering and uses less memory compared to svgSalamander. (PR #684)
|
||||||
|
- ComboBox: Improved location of selected item in popup if list is large and
|
||||||
|
scrollable.
|
||||||
|
- FileChooser: Show localized text for all locales supported by Java's Metal
|
||||||
|
look and feel. (issue #680)
|
||||||
|
- Added system property `flatlaf.useNativeLibrary` to allow disabling loading of
|
||||||
|
FlatLaf native library. (issue #674)
|
||||||
|
- IntelliJ Themes:
|
||||||
|
- Reduced memory footprint by releasing Json data and ignoring IntelliJ UI
|
||||||
|
properties that are not used in FlatLaf.
|
||||||
|
- Updated "Hiberbee Dark" and "Gradianto" themes.
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Styling: Fixed scaling of some styling properties (`rowHeight` for Table and
|
||||||
|
Tree; `iconTextGap` for Button, CheckBox and RadioButton). (issue #682)
|
||||||
|
- Fixed `IllegalComponentStateException` when invoker is not showing in
|
||||||
|
`SubMenuUsabilityHelper`. (issue #692)
|
||||||
|
- macOS themes: Changing `@accentColor` variable in FlatLaf properties files did
|
||||||
|
not change all accent related colors for all components.
|
||||||
|
- IntelliJ Themes:
|
||||||
|
- "Light Owl" theme: Fixed wrong (unreadable) text color in selected menu
|
||||||
|
items, selected text in text components, and selection in ComboBox popup
|
||||||
|
list. (issue #687)
|
||||||
|
- "Gradianto Midnight Blue" theme: Fixed color of ScrollBar track, which was
|
||||||
|
not visible. (issue #686)
|
||||||
|
- "Monocai" theme: Fixed unreadable text color of default buttons. (issue
|
||||||
|
#693)
|
||||||
|
- "Vuesion" theme: Fixed foreground colors of disabled text.
|
||||||
|
- "Material UI Lite" themes: Fixed non-editable ComboBox button background.
|
||||||
|
- CheckBox and RadioButton: Fixed unselected icon colors for themes "Atom One
|
||||||
|
Light", "Cyan Light", "GitHub", "Light Owl", "Material Lighter" and
|
||||||
|
"Solarized Light".
|
||||||
|
- TabbedPane: Fixed focused tab background color for themes "Arc *", "Material
|
||||||
|
Design Dark", "Monocai", "One Dark", "Spacegray" and "Xcode-Dark". (issue
|
||||||
|
#697)
|
||||||
|
- TextComponents, ComboBox and Spinner: Fixed background colors of enabled
|
||||||
|
text components, to distinguish from disabled, for themes "Carbon", "Cobalt
|
||||||
|
2", "Gradianto *", "Gruvbox *", "Monocai", "Spacegray", "Vuesion",
|
||||||
|
"Xcode-Dark", "GitHub", and "Light Owl". (issue #528)
|
||||||
|
- Fixed wrong disabled text colors in "Dark Flat", "Hiberbee Dark", "Light
|
||||||
|
Flat", "Nord", "Solarized Dark" and "Solarized Light" themes.
|
||||||
|
- Fixed colors for selection background/foreground, Separator, Slider track
|
||||||
|
and ProgressBar background in various themes.
|
||||||
|
- Native Windows libraries: Fixed crash when running in Java 8 and newer Java
|
||||||
|
version is installed in `PATH` environment variable and using class
|
||||||
|
`SystemInfo` before AWT initialization. (issue #673)
|
||||||
|
- ComboBox: Fixed search in item list for text with spaces. (issue #691)
|
||||||
|
- FormattedTextField: On Linux, fixed `IllegalArgumentException: Invalid
|
||||||
|
location` if `JFormattedTextField.setDocument()` is invoked in a focus gained
|
||||||
|
listener on that formatted text field. (issue #698)
|
||||||
|
- PopupMenu: Make sure that popup menu does not overlap any operating system
|
||||||
|
task bar. (issue #701)
|
||||||
|
- FileChooser: Use system icons on Windows with Java 17.0.3 (and later) 32-bit.
|
||||||
|
Only Java 17 - 17.0.2 32-bit do not use system icons because of a bug in Java
|
||||||
|
32-bit that crashes the application. (PR #709)
|
||||||
|
- FileChooser: Fixed crash on Windows with Java 17 to 17.0.2 32-bit. Java 17
|
||||||
|
64-bit is not affected. (regression since FlatLaf 2.3; PR #522, see also issue
|
||||||
|
#403)
|
||||||
|
|
||||||
|
#### Incompatibilities
|
||||||
|
|
||||||
|
- Extras: Class `FlatSVGIcon` now uses [JSVG](https://github.com/weisJ/jsvg)
|
||||||
|
library for SVG rendering. You need to replace svgSalamander with JSVG in your
|
||||||
|
build scripts and distribute `jsvg.jar` with your application. Also replace
|
||||||
|
`com.kitfox.svg` with `com.github.weisj.jsvg` in `module-info.java` files.
|
||||||
|
- IntelliJ Themes: Removed all "Contrast" themes from "Material UI Lite".
|
||||||
|
|
||||||
|
|
||||||
|
## 3.1.1
|
||||||
|
|
||||||
|
- IntelliJ Themes:
|
||||||
|
- Fixed too large menu item paddings and too large table/tree row heights (all
|
||||||
|
"Material Theme UI Lite" themes; issue #667; regression in FlatLaf 3.1).
|
||||||
|
- Fixed too large tree row height in "Carbon", "Dark Purple", "Gray",
|
||||||
|
"Material Design Dark", "Monokai Pro", "One Dark" and "Spacegray" themes.
|
||||||
|
- Native libraries: Fixed `IllegalArgumentException: URI scheme is not "file"`
|
||||||
|
when using FlatLaf in WebStart. (issue #668; regression in FlatLaf 3.1)
|
||||||
|
|
||||||
|
|
||||||
|
## 3.1
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Windows 11: Popups (`JPopupMenu`, `JComboBox`, `JToolTip`, etc.) now use
|
||||||
|
native Windows 11 rounded borders and drop shadows. (PR #643)
|
||||||
|
- Fonts:
|
||||||
|
- Added **Roboto Mono** (https://fonts.google.com/specimen/Roboto+Mono). (PR
|
||||||
|
#639, issue #638)
|
||||||
|
- Updated **JetBrains Mono** to
|
||||||
|
[v2.304](https://github.com/JetBrains/JetBrainsMono/releases/tag/v2.304).
|
||||||
|
- Theme Editor: Support macOS light and dark themes.
|
||||||
|
- TabbedPane: Support hover and focused tab foreground colors. (issue #627)
|
||||||
|
- TabbedPane: `tabbedPane.getBackgroundAt(tabIndex)` now has higher priority
|
||||||
|
than `TabbedPane.focusColor` and `TabbedPane.selectedBackground`. If
|
||||||
|
`tabbedPane.setBackgroundAt(tabIndex)` is used to set a color for a single
|
||||||
|
tab, then this color is now used even if the tab is focused or selected.
|
||||||
|
- TableHeader: Support column hover and pressed background and foreground
|
||||||
|
colors. (issue #636)
|
||||||
|
- Native libraries: Made it easier to distribute FlatLaf native libraries
|
||||||
|
(Windows `.dll` and Linux `.so`) to avoid problems on operating systems with
|
||||||
|
enabled execution restrictions.
|
||||||
|
See https://www.formdev.com/flatlaf/native-libraries/ for more details. (issue #624)
|
||||||
|
- Published native libraries to Maven Central for easy using them as
|
||||||
|
dependencies in Gradle and Maven.
|
||||||
|
- If available, native libraries are now loaded from same location as
|
||||||
|
`flatlaf.jar`, otherwise they are extract from `flatlaf.jar` to temporary
|
||||||
|
folder and loaded from there.
|
||||||
|
- Windows DLLs are now digitally signed with FormDev Software GmbH
|
||||||
|
certificate.
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- FlatLaf window decorations:
|
||||||
|
- Fixed inconsistent size of glass pane depending on whether FlatLaf window
|
||||||
|
decorations are used (e.g. Windows 10/11) or not (e.g. macOS). Now the glass
|
||||||
|
pane no longer overlaps the FlatLaf window title bar. (issue #630)
|
||||||
|
- Linux: Fixed broken window resizing on multi-screen setups. (issue #632)
|
||||||
|
- Linux: Fixed behavior of maximize/restore button when tiling window to left
|
||||||
|
or right half of screen. (issue #647)
|
||||||
|
- IntelliJ Themes:
|
||||||
|
- Fixed default button hover background in "Solarized Light" theme. (issue
|
||||||
|
#628)
|
||||||
|
- Avoid that accent color affect some colors in some IntelliJ themes. (issue
|
||||||
|
#625)
|
||||||
|
- Updated "Hiberbee Dark" and "Material Theme UI Lite" themes.
|
||||||
|
- Styling: Fixed resolving of UI variables in styles that use other variables.
|
||||||
|
- MenuItem: Fixed horizontal alignment of icons. (issue #631)
|
||||||
|
- Table: Fixed potential performance issue with paint cell focus indicator
|
||||||
|
border. (issue #654)
|
||||||
|
- Tree: Fixed missing custom closed/opened/leaf icons of a custom
|
||||||
|
`DefaultTreeCellRenderer`. (issue #653; regression since implementing PR #609
|
||||||
|
in FlatLaf 3.0)
|
||||||
|
- Tree: Fixed truncated node text and too small painted non-wide node background
|
||||||
|
if custom cell renderer sets icon, but not disabled icon, and tree is
|
||||||
|
disabled. (issue #640)
|
||||||
|
- Fixed `HiDPIUtils.paintAtScale1x()`, which painted at wrong location if
|
||||||
|
graphics is rotated, is scaled and `x` or `y` parameters are not zero. (issue
|
||||||
|
#646)
|
||||||
|
|
||||||
|
|
||||||
## 3.0
|
## 3.0
|
||||||
|
|
||||||
#### New features and improvements
|
#### New features and improvements
|
||||||
@@ -506,7 +1192,7 @@ FlatLaf Change Log
|
|||||||
- Native window decorations (Windows 10 only):
|
- Native window decorations (Windows 10 only):
|
||||||
- Fixed occasional application crash in `flatlaf-windows.dll`. (issue #357)
|
- Fixed occasional application crash in `flatlaf-windows.dll`. (issue #357)
|
||||||
- When window is initially shown, fill background with window background color
|
- When window is initially shown, fill background with window background color
|
||||||
(instead of white), which avoids flickering in dark themes. (issue 339)
|
(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
|
- 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
|
space with the window background color (instead of black) before the layout
|
||||||
is updated.
|
is updated.
|
||||||
|
|||||||
346
README.md
346
README.md
@@ -6,7 +6,7 @@ Swing desktop applications.
|
|||||||
|
|
||||||
It looks almost flat (no shadows or gradients), clean, simple and elegant.
|
It looks almost flat (no shadows or gradients), clean, simple and elegant.
|
||||||
FlatLaf comes with **Light**, **Dark**, **IntelliJ** and **Darcula** themes,
|
FlatLaf comes with **Light**, **Dark**, **IntelliJ** and **Darcula** themes,
|
||||||
scales on **HiDPI** displays and runs on Java 8 or newer.
|
scales on **HiDPI** displays and runs on Java 8 or newer (LTS and latest).
|
||||||
|
|
||||||
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.
|
||||||
@@ -15,6 +15,11 @@ IntelliJ IDEA 2019.2+ and uses almost the same colors and icons.
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
macOS Themes
|
||||||
|
------------
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
IntelliJ Platform Themes
|
IntelliJ Platform Themes
|
||||||
------------------------
|
------------------------
|
||||||
@@ -28,14 +33,24 @@ FlatLaf can use 3rd party themes created for IntelliJ Platform (see
|
|||||||
Sponsors
|
Sponsors
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
### Current Sponsors
|
||||||
|
|
||||||
|
<a href="https://www.soptim.de/"><img src="https://www.formdev.com/flatlaf/sponsor/soptim.svg" width="200" alt="SOPTIM" title="SOPTIM - your expert in software solutions for the energy industry"></a>
|
||||||
|
|
||||||
|
<a href="https://exocharts.com/"><img src="https://www.formdev.com/flatlaf/sponsor/Exocharts.png" width="200" alt="Exocharts" title="Exocharts - Professional Grade OrderFlow"></a>
|
||||||
|
|
||||||
|
<!-- [](https://www.formdev.com/flatlaf/sponsor/) -->
|
||||||
|
|
||||||
|
[Become a Sponsor](https://www.formdev.com/flatlaf/sponsor/)
|
||||||
|
|
||||||
|
### Previous Sponsors
|
||||||
|
|
||||||
<a href="https://www.ej-technologies.com/"><img src="https://www.formdev.com/flatlaf/sponsor/ej-technologies.png" width="200" alt="ej-technologies" title="ej-technologies - Java APM, Java Profiler, Java Installer Builder"></a>
|
<a href="https://www.ej-technologies.com/"><img src="https://www.formdev.com/flatlaf/sponsor/ej-technologies.png" width="200" alt="ej-technologies" title="ej-technologies - Java APM, Java Profiler, Java Installer Builder"></a>
|
||||||
|
|
||||||
<a href="https://www.dbvis.com/"><img src="https://www.formdev.com/flatlaf/sponsor/dbvisualizer.svg" width="200" alt="DbVisualizer" title="DbVisualizer - SQL Client and Editor"></a>
|
<a href="https://www.dbvis.com/"><img src="https://www.formdev.com/flatlaf/sponsor/dbvisualizer.svg" width="200" alt="DbVisualizer" title="DbVisualizer - SQL Client and Editor"></a>
|
||||||
|
|
||||||
<a href="https://www.dscsag.com/"><img src="https://www.formdev.com/flatlaf/sponsor/DSC.png" height="48" alt="DSC Software AG" title="DSC Software AG - Your Companion for Integrative PLM"></a>
|
<a href="https://www.dscsag.com/"><img src="https://www.formdev.com/flatlaf/sponsor/DSC.png" height="48" alt="DSC Software AG" title="DSC Software AG - Your Companion for Integrative PLM"></a>
|
||||||
|
|
||||||
[Become a Sponsor](https://www.formdev.com/flatlaf/sponsor/)
|
|
||||||
|
|
||||||
Demo
|
Demo
|
||||||
----
|
----
|
||||||
|
|
||||||
@@ -57,18 +72,23 @@ build script:
|
|||||||
artifactId: flatlaf
|
artifactId: flatlaf
|
||||||
version: (see button below)
|
version: (see button below)
|
||||||
|
|
||||||
Otherwise download `flatlaf-<version>.jar` here:
|
Otherwise, download `flatlaf-<version>.jar` here:
|
||||||
|
|
||||||
[](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf)
|
[](https://central.sonatype.com/artifact/com.formdev/flatlaf)
|
||||||
|
|
||||||
|
See also
|
||||||
|
[Native Libraries distribution](https://www.formdev.com/flatlaf/native-libraries/)
|
||||||
|
for instructions on how to redistribute FlatLaf native libraries with your
|
||||||
|
application.
|
||||||
|
|
||||||
|
|
||||||
### Snapshots
|
### Snapshots
|
||||||
|
|
||||||
FlatLaf snapshot binaries are available on
|
FlatLaf snapshot binaries are available on
|
||||||
[Sonatype OSSRH](https://oss.sonatype.org/content/repositories/snapshots/com/formdev/flatlaf/).
|
[Sonatype Central](https://central.sonatype.com/service/rest/repository/browse/maven-snapshots/com/formdev/flatlaf/).
|
||||||
To access the latest snapshot, change the FlatLaf version in your dependencies
|
To access the latest snapshot, change the FlatLaf version in your dependencies
|
||||||
to `<version>-SNAPSHOT` (e.g. `0.27-SNAPSHOT`) and add the repository
|
to `<version>-SNAPSHOT` (e.g. `3.7-SNAPSHOT`) and add the repository
|
||||||
`https://oss.sonatype.org/content/repositories/snapshots/` to your build (see
|
`https://central.sonatype.com/repository/maven-snapshots/` to your build (see
|
||||||
[Maven](https://maven.apache.org/guides/mini/guide-multiple-repositories.html)
|
[Maven](https://maven.apache.org/guides/mini/guide-multiple-repositories.html)
|
||||||
and
|
and
|
||||||
[Gradle](https://docs.gradle.org/current/userguide/declaring_repositories.html#sec:declaring_custom_repository)
|
[Gradle](https://docs.gradle.org/current/userguide/declaring_repositories.html#sec:declaring_custom_repository)
|
||||||
@@ -131,7 +151,7 @@ details and downloads.
|
|||||||
Buzz
|
Buzz
|
||||||
----
|
----
|
||||||
|
|
||||||
- [What others say about FlatLaf on Twitter](https://twitter.com/search?f=live&q=flatlaf)
|
- [FlatLaf 3.1 (and 3.0) announcement on Reddit](https://www.reddit.com/r/java/comments/12xgrsu/flatlaf_31_and_30_swing_look_and_feel/)
|
||||||
- [FlatLaf 1.0 announcement on Reddit](https://www.reddit.com/r/java/comments/lsbcwe/flatlaf_10_swing_look_and_feel/)
|
- [FlatLaf 1.0 announcement on Reddit](https://www.reddit.com/r/java/comments/lsbcwe/flatlaf_10_swing_look_and_feel/)
|
||||||
- [FlatLaf announcement on Reddit](https://www.reddit.com/r/java/comments/dl0hu3/flatlaf_flat_look_and_feel/)
|
- [FlatLaf announcement on Reddit](https://www.reddit.com/r/java/comments/dl0hu3/flatlaf_flat_look_and_feel/)
|
||||||
|
|
||||||
@@ -139,121 +159,231 @@ Buzz
|
|||||||
Applications using FlatLaf
|
Applications using FlatLaf
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
- 
|
### Featured
|
||||||
|
|
||||||
|
-  [JFormDesigner](https://www.formdev.com/)
|
||||||
|
(**commercial**) - Java/Swing GUI Designer (from the FlatLaf creators)
|
||||||
|
- 
|
||||||
[JProfiler](https://www.ej-technologies.com/products/jprofiler/overview.html)
|
[JProfiler](https://www.ej-technologies.com/products/jprofiler/overview.html)
|
||||||
12 (**commercial**) - the award-winning all-in-one Java profiler
|
(**commercial**) - the award-winning all-in-one Java profiler
|
||||||
-  [JFormDesigner](https://www.formdev.com/) 8
|
- 
|
||||||
(**commercial**) - Java/Swing GUI Designer
|
|
||||||
-  [Jeyla Studio](https://www.jeylastudio.com/) - Salon
|
|
||||||
Software
|
|
||||||
-  [Fanurio](https://www.fanuriotimetracking.com/) 3.3.2
|
|
||||||
(**commercial**) - time tracking and billing for freelancers and teams
|
|
||||||
-  [Antares](https://www.antarescircuit.io/) - a free,
|
|
||||||
powerful platform for designing, simulating and explaining digital circuits
|
|
||||||
- 
|
|
||||||
[Logisim-evolution](https://github.com/logisim-evolution/logisim-evolution)
|
|
||||||
3.6 - Digital logic design tool and simulator
|
|
||||||
-  [Cinecred](https://loadingbyte.com/cinecred/) - create
|
|
||||||
beautiful film credit sequences
|
|
||||||
-  [tinyMediaManager](https://www.tinymediamanager.org/)
|
|
||||||
v4 (**commercial**) - a media management tool
|
|
||||||
-  [Weasis](https://nroduit.github.io/) - medical DICOM
|
|
||||||
viewer used in healthcare by hospitals, health networks, etc
|
|
||||||
- 
|
|
||||||
[Makelangelo Software](https://github.com/MarginallyClever/Makelangelo-software)
|
|
||||||
7.3.0 - for plotters, especially the wall-hanging polargraph
|
|
||||||
-  [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 and much more
|
|
||||||
- [jclasslib bytecode viewer](https://github.com/ingokegel/jclasslib) 5.5
|
|
||||||
- [KeyStore Explorer](https://keystore-explorer.org/) 5.4.3
|
|
||||||
- 
|
|
||||||
[install4j](https://www.ej-technologies.com/products/install4j/overview.html)
|
[install4j](https://www.ej-technologies.com/products/install4j/overview.html)
|
||||||
9.0 (**commercial**) - the powerful multi-platform Java installer builder
|
(**commercial**) - the powerful multi-platform Java installer builder
|
||||||
-  [DbVisualizer](https://www.dbvis.com/) 12.0
|
-  [DbVisualizer](https://www.dbvis.com/)
|
||||||
(**commercial**) - the universal database tool for developers, analysts and
|
(**commercial**) - the universal database tool for developers, analysts and
|
||||||
DBAs
|
DBAs
|
||||||
-  [MagicPlot](https://magicplot.com/) 3.0
|
-  [Apache NetBeans](https://netbeans.apache.org/) - IDE
|
||||||
(**commercial**) - Software for nonlinear fitting, plotting and data analysis
|
for Java, PHP, HTML and much more
|
||||||
- 
|
- 
|
||||||
[Thermo-Calc](https://thermocalc.com/products/thermo-calc/) 2021a
|
[Thermo-Calc](https://thermocalc.com/products/thermo-calc/) (**commercial**) -
|
||||||
(**commercial**) - Thermodynamics and Properties Software
|
Thermodynamics and Properties Software
|
||||||
-  [OWASP ZAP](https://www.zaproxy.org/) 2.10 - the worlds
|
|
||||||
most widely used web app scanner
|
### Data
|
||||||
|
|
||||||
|
-  [Ultorg](https://www.ultorg.com/) (**commercial**) - a
|
||||||
|
visual query system for relational databases
|
||||||
|
- [Jailer](https://github.com/Wisser/Jailer) - database subsetting and
|
||||||
|
relational data browsing tool
|
||||||
|
-  [MagicPlot](https://magicplot.com/) (**commercial**) -
|
||||||
|
Software for nonlinear fitting, plotting and data analysis
|
||||||
|
- [Constellation](https://www.constellation-app.com/) - Data Visualization and
|
||||||
|
Analytics (based on NetBeans platform)
|
||||||
|
- [Kafka Visualizer](https://github.com/kumait/kafkavisualizer) - Kafka GUI
|
||||||
|
client
|
||||||
|
- 
|
||||||
|
[RedisFront](https://github.com/dromara/RedisFront/blob/master/README_EN.md) -
|
||||||
|
Cross-platform redis GUI
|
||||||
|
- 
|
||||||
|
[Zettelkasten](https://github.com/Zettelkasten-Team/Zettelkasten) - knowledge
|
||||||
|
management tool
|
||||||
|
-  [QStudio](https://www.timestored.com/qstudio/) - free
|
||||||
|
SQL editor
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
-  [ZAP](https://www.zaproxy.org/) - the world's most
|
||||||
|
widely used web app scanner
|
||||||
- 
|
- 
|
||||||
[Burp Suite Professional and Community Edition](https://portswigger.net/burp/pro)
|
[Burp Suite Professional and Community Edition](https://portswigger.net/burp/pro)
|
||||||
2020.11.2 (**commercial**) - the leading software for web security testing
|
(**commercial**) - the leading software for web security testing
|
||||||
|
- [Ghidra](https://github.com/NationalSecurityAgency/ghidra) - a software
|
||||||
|
reverse engineering (SRE) framework
|
||||||
|
- [jadx](https://github.com/skylot/jadx) - Dex to Java decompiler
|
||||||
- [BurpCustomizer](https://github.com/CoreyD97/BurpCustomizer) - adds more
|
- [BurpCustomizer](https://github.com/CoreyD97/BurpCustomizer) - adds more
|
||||||
FlatLaf themes to Burp Suite
|
FlatLaf themes to Burp Suite
|
||||||
|
- [Total Validator](https://www.totalvalidator.com/) (**commercial**) - checks
|
||||||
|
your website
|
||||||
|
- [JPass](https://github.com/gaborbata/jpass) - password manager with strong
|
||||||
|
encryption
|
||||||
|
|
||||||
|
### Software Development
|
||||||
|
|
||||||
|
- [jclasslib bytecode viewer](https://github.com/ingokegel/jclasslib)
|
||||||
|
- [KeyStore Explorer](https://keystore-explorer.org/)
|
||||||
|
- [muCommander](https://github.com/mucommander/mucommander) - lightweight
|
||||||
|
cross-platform file manager
|
||||||
|
- [Guiffy](https://www.guiffy.com/) (**commercial**) - advanced cross-platform
|
||||||
|
Diff/Merge
|
||||||
|
- [HashGarten](https://github.com/jonelo/HashGarten) - cross-platform Swing GUI
|
||||||
|
for Jacksum
|
||||||
|
- [Pseudo Assembler IDE](https://github.com/tomasz-herman/PseudoAssemblerIDE) -
|
||||||
|
IDE for Pseudo-Assembler
|
||||||
|
- [Linotte](https://github.com/cpc6128/LangageLinotte) - French programming
|
||||||
|
language created to learn programming
|
||||||
|
- [lsfusion platform](https://github.com/lsfusion/platform) - information
|
||||||
|
systems development platform
|
||||||
|
-  [Consulo](https://github.com/consulo/consulo) - open
|
||||||
|
source cross-platform multi-language IDE (Java, .NET, JS, etc)
|
||||||
|
- [Convertigo](https://github.com/convertigo/convertigo) - low code & no code
|
||||||
|
mobile & web platform
|
||||||
|
-  [EduMIPS64](https://github.com/EduMIPS64/edumips64) -
|
||||||
|
visual MIPS64 CPU simulator
|
||||||
|
-  [Launch4j](https://launch4j.sourceforge.net/) -
|
||||||
|
cross-platform Java executable wrapper
|
||||||
|
|
||||||
|
### Electrical
|
||||||
|
|
||||||
|
- [Antares](https://www.antarescircuit.io/) - a free, powerful platform for
|
||||||
|
designing, simulating and explaining digital circuits
|
||||||
|
- [Logisim-evolution](https://github.com/logisim-evolution/logisim-evolution) -
|
||||||
|
Digital logic design tool and simulator
|
||||||
|
-  [OpenPnP](https://github.com/openpnp/openpnp) - SMT
|
||||||
|
Pick and Place Hardware and Software
|
||||||
|
- 
|
||||||
|
[TrainControl](https://github.com/bob123456678/TrainControl) - control Marklin
|
||||||
|
/ Trix / DCC digital model train layout
|
||||||
|
- [Makelangelo Software](https://github.com/MarginallyClever/Makelangelo-software) -
|
||||||
|
for plotters, especially the wall-hanging polargraph
|
||||||
|
- [GUIslice Builder](https://github.com/ImpulseAdventure/GUIslice-Builder) - GUI
|
||||||
|
builder for [GUIslice](https://github.com/ImpulseAdventure/GUIslice), a
|
||||||
|
lightweight GUI framework for embedded displays
|
||||||
|
- [ThunderFocus](https://github.com/marcocipriani01/ThunderFocus) -
|
||||||
|
Arduino-based telescope focuser
|
||||||
|
- [RemoteLight](https://github.com/Drumber/RemoteLight) - multifunctional LED
|
||||||
|
control software
|
||||||
|
|
||||||
|
### Media
|
||||||
|
|
||||||
|
-  [jAlbum](https://jalbum.net/) (**commercial**) -
|
||||||
|
creates photo album websites
|
||||||
|
- [MediathekView](https://mediathekview.de/) - search in media libraries of
|
||||||
|
various German broadcasters
|
||||||
|
-  [Pixelitor](https://github.com/lbalazscs/Pixelitor) -
|
||||||
|
image editor
|
||||||
|
- [Cinecred](https://loadingbyte.com/cinecred/) - create beautiful film credit
|
||||||
|
sequences
|
||||||
|
- [tinyMediaManager](https://www.tinymediamanager.org/) (**commercial**) - a
|
||||||
|
media management tool
|
||||||
|
- [Weasis](https://nroduit.github.io/) - medical DICOM viewer used in healthcare
|
||||||
|
by hospitals, health networks, etc
|
||||||
|
- [Shutter Encoder](https://www.shutterencoder.com/)
|
||||||
|
([source code](https://github.com/paulpacifico/shutter-encoder)) -
|
||||||
|
professional video converter and compression tool
|
||||||
|
- [Sound Analysis](https://github.com/tomasz-herman/SoundAnalysis) - analyze
|
||||||
|
sound files in time or frequency domain
|
||||||
|
- [Novel-Grabber](https://github.com/Flameish/Novel-Grabber) - download novels
|
||||||
|
from any webnovel and lightnovel site
|
||||||
|
- [lectureStudio](https://www.lecturestudio.org/) - digitize your lectures with
|
||||||
|
ease
|
||||||
|
-  [Nortantis](https://jandjheydorn.com/nortantis) -
|
||||||
|
fantasy map generator and editor
|
||||||
|
|
||||||
|
### Modelling / Planning
|
||||||
|
|
||||||
|
-  [OpenRocket](https://github.com/openrocket/openrocket) -
|
||||||
|
model-rocketry aerodynamics and trajectory simulation software
|
||||||
|
- 
|
||||||
|
[Warteschlangensimulator](https://github.com/A-Herzog/Warteschlangensimulator) -
|
||||||
|
discrete-event stochastic simulator
|
||||||
|
-  [Gephi](https://github.com/gephi/gephi) - the Open
|
||||||
|
Graph Viz Platform
|
||||||
|
- [Astah](https://astah.net/) (**commercial**) - create UML, ER Diagram,
|
||||||
|
Flowchart, Data Flow Diagram, Requirement Diagram, SysML diagrams and more
|
||||||
|
- [IGMAS+](https://www.gfz-potsdam.de/igmas) - Interactive Gravity and Magnetic
|
||||||
|
Application System
|
||||||
|
-  [StarPlan](https://www.progotec.de/) (**commercial**) -
|
||||||
|
die Stundenplan Software für Bildungseinrichtungen
|
||||||
|
-  [SSPlot](https://github.com/babaissarkar/ssplot) -
|
||||||
|
plotting utility for plotting CSV data
|
||||||
|
|
||||||
|
### Documents
|
||||||
|
|
||||||
|
- [Big Faceless (BFO) PDF Viewer](https://bfo.com/) (**commercial**) - Swing PDF
|
||||||
|
Viewer
|
||||||
|
- [PDF Studio](https://www.qoppa.com/pdfstudio/) (**commercial**) - create,
|
||||||
|
review and edit PDF documents
|
||||||
|
- [XMLmind XML Editor](https://www.xmlmind.com/xmleditor/) (**commercial**)
|
||||||
|
|
||||||
|
### Geo
|
||||||
|
|
||||||
-  [JOSM](https://josm.openstreetmap.de/) - an extensible
|
-  [JOSM](https://josm.openstreetmap.de/) - an extensible
|
||||||
editor for [OpenStreetMap](https://www.openstreetmap.org/) (requires FlatLaf
|
editor for [OpenStreetMap](https://www.openstreetmap.org/) (requires FlatLaf
|
||||||
JOSM plugin)
|
JOSM plugin)
|
||||||
-  [jAlbum](https://jalbum.net/) 21 (**commercial**) -
|
- [Mapton](https://mapton.org/)
|
||||||
creates photo album websites
|
([source code](https://github.com/trixon/mapton)) - some kind of map
|
||||||
- [PDF Studio](https://www.qoppa.com/pdfstudio/) 2021 (**commercial**) - create,
|
application (based on NetBeans platform)
|
||||||
review and edit PDF documents
|
- [MeteoInfo](https://github.com/meteoinfo/MeteoInfo) - GIS and scientific
|
||||||
- [XMLmind XML Editor](https://www.xmlmind.com/xmleditor/) 9.3 (**commercial**)
|
computation environment for meteorological community
|
||||||
- [Total Validator](https://www.totalvalidator.com/) 15 (**commercial**) -
|
|
||||||
checks your website
|
### Business / Legal
|
||||||
- [j-lawyer](https://github.com/jlawyerorg/j-lawyer-org) - Kanzleisoftware
|
|
||||||
- [MegaMek](https://github.com/MegaMek/megamek),
|
-  
|
||||||
[MegaMekLab](https://github.com/MegaMek/megameklab) and
|
[Lisheane ERP](https://www.lisheane.ch/) (**commercial**) - backoffice
|
||||||
[MekHQ](https://github.com/MegaMek/mekhq) v0.47.5+ - a sci-fi tabletop
|
applikation
|
||||||
BattleTech simulator suite handling battles, unit building, and campaigns
|
- 
|
||||||
- [GUIslice Builder](https://github.com/ImpulseAdventure/GUIslice-Builder)
|
[j-lawyer](https://github.com/jlawyerorg/j-lawyer-org) - Kanzleisoftware
|
||||||
0.13.b024 - GUI builder for
|
-  [Jeyla Studio](https://www.jeylastudio.com/) -
|
||||||
[GUIslice](https://github.com/ImpulseAdventure/GUIslice), a lightweight GUI
|
Salon Software
|
||||||
framework for embedded displays
|
- [Fanurio](https://www.fanuriotimetracking.com/) (**commercial**) - time
|
||||||
- [Rest Suite](https://github.com/supanadit/restsuite) - Rest API testing
|
tracking and billing for freelancers and teams
|
||||||
- [ControllerBuddy](https://github.com/bwRavencl/ControllerBuddy) - advanced
|
- [Jes](https://www.jes-eur.de) - Die Java-EÜR
|
||||||
gamepad mapping software
|
|
||||||
- [SpringRemote](https://github.com/HaleyWang/SpringRemote) - remote Linux SSH
|
|
||||||
connections manager
|
|
||||||
- [jEnTunnel](https://github.com/ggrandes/jentunnel) - manage SSH Tunnels made
|
|
||||||
easy
|
|
||||||
- [mendelson AS2](https://sourceforge.net/projects/mec-as2/),
|
- [mendelson AS2](https://sourceforge.net/projects/mec-as2/),
|
||||||
[AS4](https://sourceforge.net/projects/mendelson-as4/) and
|
[AS4](https://sourceforge.net/projects/mendelson-as4/) and
|
||||||
[OFTP2](https://sourceforge.net/projects/mendelson-oftp2/) (open-source) and
|
[OFTP2](https://sourceforge.net/projects/mendelson-oftp2/) (open-source) and
|
||||||
[mendelson AS2](https://mendelson-e-c.com/as2/),
|
[mendelson AS2](https://mendelson-e-c.com/as2/),
|
||||||
[AS4](https://mendelson-e-c.com/as4/) and
|
[AS4](https://mendelson-e-c.com/as4/) and
|
||||||
[OFTP2](https://mendelson-e-c.com/oftp2) (**commercial**)
|
[OFTP2](https://mendelson-e-c.com/oftp2) (**commercial**)
|
||||||
- [IGMAS+](https://www.gfz-potsdam.de/igmas) - Interactive Gravity and Magnetic
|
|
||||||
Application System
|
### Messaging
|
||||||
- [MeteoInfo](https://github.com/meteoinfo/MeteoInfo) 2.2 - GIS and scientific
|
|
||||||
computation environment for meteorological community
|
- [Spark](https://github.com/igniterealtime/Spark) - cross-platform IM client
|
||||||
- [lsfusion platform](https://github.com/lsfusion/platform) 4 - information
|
optimized for businesses and organizations
|
||||||
systems development platform
|
- [Chatty](https://github.com/chatty/chatty) - Twitch Chat Client
|
||||||
- [JPass](https://github.com/gaborbata/jpass) - password manager with strong
|
|
||||||
encryption
|
### Gaming
|
||||||
- [Jes - Die Java-EÜR](https://www.jes-eur.de)
|
|
||||||
- [Mapton](https://mapton.org/) 2.0
|
-  [BGBlitz](https://www.bgblitz.com/)
|
||||||
([source code](https://github.com/trixon/mapton)) - some kind of map
|
(**commercial**) - professional Backgammon
|
||||||
application (based on NetBeans platform)
|
-  [josé](https://peteschaefer.github.io/jose/) - a
|
||||||
- [Pseudo Assembler IDE](https://github.com/tomasz-herman/PseudoAssemblerIDE) -
|
graphical chess tool
|
||||||
IDE for Pseudo-Assembler
|
-  [MCreator](https://github.com/MCreator/MCreator) - make
|
||||||
- [Linotte](https://github.com/cpc6128/LangageLinotte) 3.1 - French programming
|
Minecraft Java Edition mods, Minecraft Bedrock Edition Add-Ons, and data packs
|
||||||
language created to learn programming
|
- [MapTool](https://github.com/RPTools/maptool) - virtual Tabletop for playing
|
||||||
- [MEKA](https://github.com/Waikato/meka) 1.9.3 - multi-label classifiers and
|
role-playing games
|
||||||
evaluation procedures using the Weka machine learning framework
|
- [MegaMek](https://github.com/MegaMek/megamek),
|
||||||
- [Shutter Encoder](https://www.shutterencoder.com/) 14.2
|
[MegaMekLab](https://github.com/MegaMek/megameklab) and
|
||||||
([source code](https://github.com/paulpacifico/shutter-encoder)) -
|
[MekHQ](https://github.com/MegaMek/mekhq) - a sci-fi tabletop BattleTech
|
||||||
professional video converter and compression tool (screenshots show **old**
|
simulator suite handling battles, unit building, and campaigns
|
||||||
look)
|
- [ControllerBuddy](https://github.com/bwRavencl/ControllerBuddy) - advanced
|
||||||
- [Sound Analysis](https://github.com/tomasz-herman/SoundAnalysis) - analyze
|
gamepad mapping software
|
||||||
sound files in time or frequency domain
|
|
||||||
- [RemoteLight](https://github.com/Drumber/RemoteLight) - multifunctional LED
|
### Utilities
|
||||||
control software
|
|
||||||
- [ThunderFocus](https://github.com/marcocipriani01/ThunderFocus) -
|
- [MooInfo](https://github.com/rememberber/MooInfo) - visual implementation of
|
||||||
Arduino-based telescope focuser
|
OSHI, to view information about the system and hardware
|
||||||
- [Novel-Grabber](https://github.com/Flameish/Novel-Grabber) - download novels
|
- [Linux Task Manager (LTM)](https://github.com/ajee10x/LTM-LinuxTaskManager) -
|
||||||
from any webnovel and lightnovel site
|
GUI for monitoring and managing various aspects of a Linux system
|
||||||
- [lectureStudio](https://www.lecturestudio.org/) 4.3.1060 - digitize your
|
- [Rest Suite](https://github.com/supanadit/restsuite) - Rest API testing
|
||||||
lectures with ease
|
- [SpringRemote](https://github.com/HaleyWang/SpringRemote) - remote Linux SSH
|
||||||
|
connections manager
|
||||||
|
- [jEnTunnel](https://github.com/ggrandes/jentunnel) - manage SSH Tunnels made
|
||||||
|
easy
|
||||||
- [Android Tool](https://github.com/fast-geek/Android-Tool) - makes popular adb
|
- [Android Tool](https://github.com/fast-geek/Android-Tool) - makes popular adb
|
||||||
and fastboot commands easier to use
|
and fastboot commands easier to use
|
||||||
- and more...
|
-  [Termora](https://github.com/TermoraDev/termora) -
|
||||||
|
Terminal emulator and SSH client
|
||||||
|
|
||||||
|
### Miscellaneous
|
||||||
|
|
||||||
|
- [MEKA](https://github.com/Waikato/meka) - multi-label classifiers and
|
||||||
|
evaluation procedures using the Weka machine learning framework
|
||||||
|
|||||||
142
build.gradle.kts
142
build.gradle.kts
@@ -14,22 +14,30 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
val releaseVersion = "3.0"
|
import io.github.gradlenexus.publishplugin.CloseNexusStagingRepository
|
||||||
val developmentVersion = "3.1-SNAPSHOT"
|
import net.ltgt.gradle.errorprone.errorprone
|
||||||
|
import org.gradle.kotlin.dsl.withType
|
||||||
|
|
||||||
version = if( rootProject.hasProperty( "release" ) ) releaseVersion else developmentVersion
|
|
||||||
|
|
||||||
allprojects {
|
// initialize version
|
||||||
|
group = "com.formdev"
|
||||||
|
version = property( if( hasProperty( "release" ) ) "flatlaf.releaseVersion" else "flatlaf.developmentVersion" ) as String
|
||||||
|
|
||||||
|
// for PR snapshots change version to 'PR-<pr_number>-SNAPSHOT'
|
||||||
|
val pullRequestNumber = findProperty( "github.event.pull_request.number" )
|
||||||
|
if( pullRequestNumber != null )
|
||||||
|
version = "PR-${pullRequestNumber}-SNAPSHOT"
|
||||||
|
|
||||||
|
// apply version to all subprojects
|
||||||
|
subprojects {
|
||||||
version = rootProject.version
|
version = rootProject.version
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check required Java version
|
|
||||||
if( JavaVersion.current() < JavaVersion.VERSION_1_8 )
|
// initialize toolchain version (default is Java 8)
|
||||||
throw RuntimeException( "Java 8 or later required (running ${System.getProperty( "java.version" )})" )
|
val toolchainJavaVersion: String by extra {
|
||||||
|
System.getProperty( "toolchain", "8" )
|
||||||
|
}
|
||||||
|
|
||||||
// log version, Gradle and Java versions
|
// log version, Gradle and Java versions
|
||||||
println()
|
println()
|
||||||
@@ -37,13 +45,20 @@ println( "----------------------------------------------------------------------
|
|||||||
println( "FlatLaf Version: ${version}" )
|
println( "FlatLaf Version: ${version}" )
|
||||||
println( "Gradle ${gradle.gradleVersion} at ${gradle.gradleHomeDir}" )
|
println( "Gradle ${gradle.gradleVersion} at ${gradle.gradleHomeDir}" )
|
||||||
println( "Java ${System.getProperty( "java.version" )}" )
|
println( "Java ${System.getProperty( "java.version" )}" )
|
||||||
val toolchainJavaVersion = System.getProperty( "toolchain" )
|
println( "Java toolchain ${toolchainJavaVersion}" )
|
||||||
if( !toolchainJavaVersion.isNullOrEmpty() )
|
|
||||||
println( "Java toolchain ${toolchainJavaVersion}" )
|
|
||||||
println()
|
println()
|
||||||
|
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
alias( libs.plugins.gradle.nexus.publish.plugin )
|
||||||
|
alias( libs.plugins.errorprone ) apply false
|
||||||
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
withType<JavaCompile>().configureEach {
|
withType<JavaCompile>().configureEach {
|
||||||
sourceCompatibility = "1.8"
|
sourceCompatibility = "1.8"
|
||||||
@@ -67,6 +82,10 @@ allprojects {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
withType<AbstractArchiveTask>().configureEach {
|
||||||
|
isPreserveFileTimestamps = true
|
||||||
|
}
|
||||||
|
|
||||||
withType<Javadoc>().configureEach {
|
withType<Javadoc>().configureEach {
|
||||||
options {
|
options {
|
||||||
this as StandardJavadocDocletOptions
|
this as StandardJavadocDocletOptions
|
||||||
@@ -79,6 +98,101 @@ allprojects {
|
|||||||
links( "https://docs.oracle.com/en/java/javase/11/docs/api/" )
|
links( "https://docs.oracle.com/en/java/javase/11/docs/api/" )
|
||||||
}
|
}
|
||||||
isFailOnError = false
|
isFailOnError = false
|
||||||
|
|
||||||
|
// use Java 25 to generate javadoc
|
||||||
|
val javaToolchains = (project as ExtensionAware).extensions.getByName("javaToolchains") as JavaToolchainService
|
||||||
|
javadocTool.set( javaToolchains.javadocToolFor {
|
||||||
|
languageVersion.set( JavaLanguageVersion.of( 25 ) )
|
||||||
|
} )
|
||||||
|
}
|
||||||
|
|
||||||
|
// mark some publishing related tasks as not compatible with configuration cache
|
||||||
|
withType<Sign>().configureEach {
|
||||||
|
notCompatibleWithConfigurationCache( "not compatible" )
|
||||||
|
}
|
||||||
|
withType<PublishToMavenRepository>().configureEach {
|
||||||
|
notCompatibleWithConfigurationCache( "not compatible" )
|
||||||
|
}
|
||||||
|
withType<CloseNexusStagingRepository>().configureEach {
|
||||||
|
notCompatibleWithConfigurationCache( "not compatible" )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---- Error Prone ----
|
||||||
|
|
||||||
|
tasks.register( "errorprone" ) {
|
||||||
|
group = "verification"
|
||||||
|
tasks.withType<JavaCompile>().forEach {
|
||||||
|
dependsOn( it )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val useErrorProne = gradle.startParameter.taskNames.contains( "errorprone" )
|
||||||
|
if( useErrorProne ) {
|
||||||
|
plugins.withType<JavaPlugin> {
|
||||||
|
apply( plugin = libs.plugins.errorprone.get().pluginId )
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
"errorprone"( libs.errorprone )
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType<JavaCompile>().configureEach {
|
||||||
|
options.compilerArgs.add( "-Werror" )
|
||||||
|
options.errorprone {
|
||||||
|
disable(
|
||||||
|
"ReferenceEquality", // reports usage of '==' for objects
|
||||||
|
"StringSplitter", // reports String.split()
|
||||||
|
"JavaTimeDefaultTimeZone", // reports Year.now()
|
||||||
|
"MissingSummary", // reports `/** @since 2 */`
|
||||||
|
"InvalidBlockTag", // reports @uiDefault in javadoc
|
||||||
|
"AlreadyChecked", // reports false positives
|
||||||
|
"InlineMeSuggester", // suggests using Error Prone annotations for deprecated methods
|
||||||
|
"TypeParameterUnusedInFormals",
|
||||||
|
"UnsynchronizedOverridesSynchronized",
|
||||||
|
"NonApiType", // reports ArrayList/HashSet in parameter or return type
|
||||||
|
)
|
||||||
|
when( project.name ) {
|
||||||
|
"flatlaf-intellij-themes" -> disable(
|
||||||
|
"MutablePublicArray", // reports FlatAllIJThemes.INFOS
|
||||||
|
)
|
||||||
|
"flatlaf-theme-editor" -> disable(
|
||||||
|
"CatchAndPrintStackTrace",
|
||||||
|
)
|
||||||
|
"flatlaf-testing" -> disable(
|
||||||
|
"CatchAndPrintStackTrace",
|
||||||
|
"JdkObsolete", // reports Hashtable used for JSlider.setLabelTable()
|
||||||
|
"JavaUtilDate", // reports usage of class Date
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error Prone requires at lease Java 11
|
||||||
|
val java = (project as ExtensionAware).extensions.getByName("java") as JavaPluginExtension
|
||||||
|
val javaToolchains = (project as ExtensionAware).extensions.getByName("javaToolchains") as JavaToolchainService
|
||||||
|
if( java.toolchain.languageVersion.get().asInt() < 11 ) {
|
||||||
|
javaCompiler.set( javaToolchains.compilerFor {
|
||||||
|
languageVersion.set( JavaLanguageVersion.of( 11 ) )
|
||||||
|
} )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nexusPublishing {
|
||||||
|
repositories {
|
||||||
|
sonatype {
|
||||||
|
// see https://central.sonatype.org/publish/publish-portal-ossrh-staging-api/
|
||||||
|
nexusUrl = uri( "https://ossrh-staging-api.central.sonatype.com/service/local/" )
|
||||||
|
snapshotRepositoryUrl = uri( "https://central.sonatype.com/repository/maven-snapshots/" )
|
||||||
|
|
||||||
|
// get from gradle.properties
|
||||||
|
val sonatypeUsername: String? by project
|
||||||
|
val sonatypePassword: String? by project
|
||||||
|
|
||||||
|
username = System.getenv( "SONATYPE_USERNAME" ) ?: sonatypeUsername
|
||||||
|
password = System.getenv( "SONATYPE_PASSWORD" ) ?: sonatypePassword
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public class ReorderJarEntries
|
|||||||
// 1st pass: copy .properties files
|
// 1st pass: copy .properties files
|
||||||
copyFiles( zipOutStream, jarFile, name -> name.endsWith( ".properties" ) );
|
copyFiles( zipOutStream, jarFile, name -> name.endsWith( ".properties" ) );
|
||||||
|
|
||||||
// 2st pass: copy other files
|
// 2nd pass: copy other files
|
||||||
copyFiles( zipOutStream, jarFile, name -> !name.endsWith( ".properties" ) );
|
copyFiles( zipOutStream, jarFile, name -> !name.endsWith( ".properties" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import org.gradle.kotlin.dsl.support.serviceOf
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
`cpp-library`
|
`cpp-library`
|
||||||
}
|
}
|
||||||
@@ -27,7 +29,7 @@ library {
|
|||||||
}
|
}
|
||||||
with( linkTask.get() ) {
|
with( linkTask.get() ) {
|
||||||
if( name.contains( "Release" ) )
|
if( name.contains( "Release" ) )
|
||||||
debuggable.set( false )
|
debuggable = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,9 +39,14 @@ tasks {
|
|||||||
doFirst {
|
doFirst {
|
||||||
println( "Used Tool Chain:" )
|
println( "Used Tool Chain:" )
|
||||||
println( " - ${toolChain.get()}" )
|
println( " - ${toolChain.get()}" )
|
||||||
println( "Available Tool Chains:" )
|
}
|
||||||
toolChains.forEach {
|
|
||||||
println( " - $it" )
|
if( !project.gradle.serviceOf<BuildFeatures>().configurationCache.active.get() ) {
|
||||||
|
doFirst {
|
||||||
|
println( "Available Tool Chains:" )
|
||||||
|
toolChains.forEach {
|
||||||
|
println( " - $it" )
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,14 @@ plugins {
|
|||||||
java
|
java
|
||||||
}
|
}
|
||||||
|
|
||||||
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
|
// for Eclipse IDE project import, exclude if:
|
||||||
|
// - plugin "eclipse" is applied; e.g. if running in Eclipse IDE with buildship plugin
|
||||||
|
// - no taskNames specified at command line; e.g. if buildship synchronizes projects
|
||||||
|
val exclude =
|
||||||
|
rootProject.plugins.hasPlugin( "eclipse" ) &&
|
||||||
|
gradle.startParameter.taskNames.isEmpty()
|
||||||
|
|
||||||
|
if( !exclude ) {
|
||||||
sourceSets {
|
sourceSets {
|
||||||
create( "java9" ) {
|
create( "java9" ) {
|
||||||
java {
|
java {
|
||||||
@@ -35,6 +42,13 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
|
|||||||
named<JavaCompile>( "compileJava9Java" ) {
|
named<JavaCompile>( "compileJava9Java" ) {
|
||||||
sourceCompatibility = "9"
|
sourceCompatibility = "9"
|
||||||
targetCompatibility = "9"
|
targetCompatibility = "9"
|
||||||
|
|
||||||
|
// if global toolchain is Java 8, then use Java 11 to build
|
||||||
|
if( java.toolchain.languageVersion.get().asInt() < 9 ) {
|
||||||
|
javaCompiler.set( javaToolchains.compilerFor {
|
||||||
|
languageVersion.set( JavaLanguageVersion.of( 11 ) )
|
||||||
|
} )
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ tasks {
|
|||||||
// depend on :flatlaf-core:compileJava because it generates the JNI headers
|
// depend on :flatlaf-core:compileJava because it generates the JNI headers
|
||||||
dependsOn( ":flatlaf-core:compileJava" )
|
dependsOn( ":flatlaf-core:compileJava" )
|
||||||
|
|
||||||
from( project( ":flatlaf-core" ).buildDir.resolve( "generated/jni-headers" ) )
|
from( project( ":flatlaf-core" ).layout.buildDirectory.dir( "generated/jni-headers" ) )
|
||||||
into( "src/main/headers" )
|
into( "src/main/headers" )
|
||||||
include( extension.headers )
|
include( extension.headers )
|
||||||
filter<org.apache.tools.ant.filters.FixCrLfFilter>(
|
filter<org.apache.tools.ant.filters.FixCrLfFilter>(
|
||||||
|
|||||||
@@ -29,7 +29,14 @@ plugins {
|
|||||||
java
|
java
|
||||||
}
|
}
|
||||||
|
|
||||||
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
|
// for Eclipse IDE project import, exclude if:
|
||||||
|
// - plugin "eclipse" is applied; e.g. if running in Eclipse IDE with buildship plugin
|
||||||
|
// - no taskNames specified at command line; e.g. if buildship synchronizes projects
|
||||||
|
val exclude =
|
||||||
|
rootProject.plugins.hasPlugin( "eclipse" ) &&
|
||||||
|
gradle.startParameter.taskNames.isEmpty()
|
||||||
|
|
||||||
|
if( !exclude ) {
|
||||||
sourceSets {
|
sourceSets {
|
||||||
create( "module-info" ) {
|
create( "module-info" ) {
|
||||||
java {
|
java {
|
||||||
@@ -38,10 +45,11 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
|
|||||||
setSrcDirs( listOf( "src/main/module-info", "src/main/java", "src/main/java9" ) )
|
setSrcDirs( listOf( "src/main/module-info", "src/main/java", "src/main/java9" ) )
|
||||||
|
|
||||||
// exclude Java 8 source file if an equally named Java 9+ source file exists
|
// exclude Java 8 source file if an equally named Java 9+ source file exists
|
||||||
|
val projectDir = projectDir // necessary for configuration cache
|
||||||
exclude {
|
exclude {
|
||||||
if( it.isDirectory )
|
if( it.isDirectory )
|
||||||
return@exclude false
|
return@exclude false
|
||||||
val java9file = file( "${projectDir}/src/main/java9/${it.path}" )
|
val java9file = File( "${projectDir}/src/main/java9/${it.path}" )
|
||||||
java9file.exists() && java9file != it.file
|
java9file.exists() && java9file != it.file
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,6 +66,13 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
|
|||||||
options.compilerArgs.add( "--module-path" )
|
options.compilerArgs.add( "--module-path" )
|
||||||
options.compilerArgs.add( configurations.runtimeClasspath.get().asPath
|
options.compilerArgs.add( configurations.runtimeClasspath.get().asPath
|
||||||
+ File.pathSeparator + configurations.compileClasspath.get().asPath )
|
+ File.pathSeparator + configurations.compileClasspath.get().asPath )
|
||||||
|
|
||||||
|
// if global toolchain is Java 8, then use Java 11 to build
|
||||||
|
if( java.toolchain.languageVersion.get().asInt() < 9 ) {
|
||||||
|
javaCompiler.set( javaToolchains.compilerFor {
|
||||||
|
languageVersion.set( JavaLanguageVersion.of( 11 ) )
|
||||||
|
} )
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
|
|||||||
@@ -15,10 +15,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
open class NativeArtifact( val fileName: String, val classifier: String, val extension: String ) {}
|
||||||
|
|
||||||
open class PublishExtension {
|
open class PublishExtension {
|
||||||
var artifactId: String? = null
|
var artifactId: String? = null
|
||||||
var name: String? = null
|
var name: String? = null
|
||||||
var description: String? = null
|
var description: String? = null
|
||||||
|
var nativeArtifacts: List<NativeArtifact>? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
val extension = project.extensions.create<PublishExtension>( "flatlafPublish" )
|
val extension = project.extensions.create<PublishExtension>( "flatlafPublish" )
|
||||||
@@ -41,54 +44,44 @@ publishing {
|
|||||||
|
|
||||||
pom {
|
pom {
|
||||||
afterEvaluate {
|
afterEvaluate {
|
||||||
this@pom.name.set( extension.name )
|
this@pom.name = extension.name
|
||||||
this@pom.description.set( extension.description )
|
this@pom.description = extension.description
|
||||||
}
|
}
|
||||||
url.set( "https://github.com/JFormDesigner/FlatLaf" )
|
url = "https://github.com/JFormDesigner/FlatLaf"
|
||||||
|
|
||||||
licenses {
|
licenses {
|
||||||
license {
|
license {
|
||||||
name.set( "The Apache License, Version 2.0" )
|
name = "The Apache License, Version 2.0"
|
||||||
url.set( "https://www.apache.org/licenses/LICENSE-2.0.txt" )
|
url = "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
developers {
|
developers {
|
||||||
developer {
|
developer {
|
||||||
name.set( "Karl Tauber" )
|
name = "Karl Tauber"
|
||||||
organization.set( "FormDev Software GmbH" )
|
organization = "FormDev Software GmbH"
|
||||||
organizationUrl.set( "https://www.formdev.com/" )
|
organizationUrl = "https://www.formdev.com/"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scm {
|
scm {
|
||||||
connection.set( "scm:git:git://github.com/JFormDesigner/FlatLaf.git" )
|
connection = "scm:git:git://github.com/JFormDesigner/FlatLaf.git"
|
||||||
url.set( "https://github.com/JFormDesigner/FlatLaf" )
|
url = "https://github.com/JFormDesigner/FlatLaf"
|
||||||
}
|
}
|
||||||
|
|
||||||
issueManagement {
|
issueManagement {
|
||||||
system.set( "GitHub" )
|
system = "GitHub"
|
||||||
url.set( "https://github.com/JFormDesigner/FlatLaf/issues" )
|
url = "https://github.com/JFormDesigner/FlatLaf/issues"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
afterEvaluate {
|
||||||
maven {
|
extension.nativeArtifacts?.forEach {
|
||||||
name = "OSSRH"
|
artifact( file( it.fileName ) ) {
|
||||||
|
classifier = it.classifier
|
||||||
val releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
|
extension = it.extension
|
||||||
val snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots/"
|
}
|
||||||
url = uri( if( rootProject.hasProperty( "release" ) ) releasesRepoUrl else snapshotsRepoUrl )
|
}
|
||||||
|
|
||||||
credentials {
|
|
||||||
// get from gradle.properties
|
|
||||||
val ossrhUsername: String? by project
|
|
||||||
val ossrhPassword: String? by project
|
|
||||||
|
|
||||||
username = System.getenv( "OSSRH_USERNAME" ) ?: ossrhUsername
|
|
||||||
password = System.getenv( "OSSRH_PASSWORD" ) ?: ossrhPassword
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,3 +103,23 @@ signing {
|
|||||||
tasks.withType<Sign>().configureEach {
|
tasks.withType<Sign>().configureEach {
|
||||||
onlyIf { rootProject.hasProperty( "release" ) }
|
onlyIf { rootProject.hasProperty( "release" ) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
// check whether parallel build is enabled
|
||||||
|
withType<AbstractPublishToMaven>().configureEach {
|
||||||
|
doFirst {
|
||||||
|
if( System.getProperty( "org.gradle.parallel" ) == "true" )
|
||||||
|
throw RuntimeException( "Publishing does not work correctly with enabled parallel build. Disable parallel build with VM option '-Dorg.gradle.parallel=false'." )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
register( "publishToSonatypeAndCloseStagingRepo" ) {
|
||||||
|
group = "publishing"
|
||||||
|
description = "Publish to Sonatype Maven Central and close staging repository"
|
||||||
|
|
||||||
|
dependsOn(
|
||||||
|
"publishToSonatype",
|
||||||
|
":closeSonatypeStagingRepository"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,9 +18,8 @@ plugins {
|
|||||||
java
|
java
|
||||||
}
|
}
|
||||||
|
|
||||||
val toolchainJavaVersion = System.getProperty( "toolchain" )
|
val toolchainJavaVersion: String by rootProject.extra
|
||||||
if( !toolchainJavaVersion.isNullOrEmpty() ) {
|
|
||||||
java.toolchain {
|
java.toolchain {
|
||||||
languageVersion.set( JavaLanguageVersion.of( toolchainJavaVersion ) )
|
languageVersion = JavaLanguageVersion.of( toolchainJavaVersion )
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
2
flatlaf-core/.settings/org.eclipse.core.resources.prefs
Normal file
2
flatlaf-core/.settings/org.eclipse.core.resources.prefs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
eclipse.preferences.version=1
|
||||||
|
encoding/<project>=ISO-8859-1
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
eclipse.preferences.version=1
|
eclipse.preferences.version=1
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
|
||||||
|
org.eclipse.jdt.core.compiler.compliance=1.8
|
||||||
|
org.eclipse.jdt.core.compiler.source=1.8
|
||||||
org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false
|
org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false
|
||||||
org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647
|
org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647
|
||||||
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
|
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import Flatlaf_publish_gradle.NativeArtifact
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
`java-library`
|
`java-library`
|
||||||
`flatlaf-toolchain`
|
`flatlaf-toolchain`
|
||||||
@@ -25,12 +27,11 @@ plugins {
|
|||||||
val sigtest = configurations.create( "sigtest" )
|
val sigtest = configurations.create( "sigtest" )
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testImplementation( "org.junit.jupiter:junit-jupiter-api:5.7.2" )
|
testImplementation( libs.junit )
|
||||||
testImplementation( "org.junit.jupiter:junit-jupiter-params" )
|
testRuntimeOnly( libs.junit.launcher )
|
||||||
testRuntimeOnly( "org.junit.jupiter:junit-jupiter-engine" )
|
|
||||||
|
|
||||||
// https://github.com/jtulach/netbeans-apitest
|
// https://github.com/jtulach/netbeans-apitest
|
||||||
sigtest( "org.netbeans.tools:sigtest-maven-plugin:1.7" )
|
sigtest( libs.sigtest )
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
@@ -41,11 +42,11 @@ java {
|
|||||||
tasks {
|
tasks {
|
||||||
compileJava {
|
compileJava {
|
||||||
// generate JNI headers
|
// generate JNI headers
|
||||||
options.headerOutputDirectory.set( buildDir.resolve( "generated/jni-headers" ) )
|
options.headerOutputDirectory = layout.buildDirectory.dir( "generated/jni-headers" )
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
archiveBaseName.set( "flatlaf" )
|
archiveBaseName = "flatlaf"
|
||||||
|
|
||||||
doLast {
|
doLast {
|
||||||
ReorderJarEntries.reorderJarEntries( outputs.files.singleFile );
|
ReorderJarEntries.reorderJarEntries( outputs.files.singleFile );
|
||||||
@@ -53,11 +54,32 @@ tasks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
named<Jar>( "sourcesJar" ) {
|
named<Jar>( "sourcesJar" ) {
|
||||||
archiveBaseName.set( "flatlaf" )
|
archiveBaseName = "flatlaf"
|
||||||
}
|
}
|
||||||
|
|
||||||
named<Jar>( "javadocJar" ) {
|
named<Jar>( "javadocJar" ) {
|
||||||
archiveBaseName.set( "flatlaf" )
|
archiveBaseName = "flatlaf"
|
||||||
|
}
|
||||||
|
|
||||||
|
register<Zip>( "jarNoNatives" ) {
|
||||||
|
group = "build"
|
||||||
|
dependsOn( "jar" )
|
||||||
|
|
||||||
|
archiveBaseName = "flatlaf"
|
||||||
|
archiveClassifier = "no-natives"
|
||||||
|
archiveExtension = "jar"
|
||||||
|
destinationDirectory = layout.buildDirectory.dir( "libs" )
|
||||||
|
|
||||||
|
from( zipTree( jar.get().archiveFile.get().asFile ) )
|
||||||
|
exclude( "com/formdev/flatlaf/natives/**" )
|
||||||
|
}
|
||||||
|
|
||||||
|
withType<AbstractPublishToMaven>().configureEach {
|
||||||
|
dependsOn( "jarNoNatives" )
|
||||||
|
}
|
||||||
|
|
||||||
|
withType<Sign>().configureEach {
|
||||||
|
dependsOn( "jarNoNatives" )
|
||||||
}
|
}
|
||||||
|
|
||||||
check {
|
check {
|
||||||
@@ -68,7 +90,7 @@ tasks {
|
|||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
||||||
|
|
||||||
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 )
|
if( java.toolchain.languageVersion.get().asInt() >= 9 )
|
||||||
jvmArgs( listOf( "--add-opens", "java.desktop/javax.swing.plaf.basic=ALL-UNNAMED" ) )
|
jvmArgs( listOf( "--add-opens", "java.desktop/javax.swing.plaf.basic=ALL-UNNAMED" ) )
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,21 +98,31 @@ tasks {
|
|||||||
group = "verification"
|
group = "verification"
|
||||||
dependsOn( "jar" )
|
dependsOn( "jar" )
|
||||||
|
|
||||||
|
// necessary for configuration cache
|
||||||
|
val classpath = sigtest.asPath
|
||||||
|
val signatureFile = "${project.name}-sigtest.txt"
|
||||||
|
val jarPath = jar.get().outputs.files.asPath
|
||||||
|
val version = version
|
||||||
|
|
||||||
doLast {
|
doLast {
|
||||||
ant.withGroovyBuilder {
|
ant.withGroovyBuilder {
|
||||||
"taskdef"(
|
"taskdef"(
|
||||||
"name" to "sigtest",
|
"name" to "sigtest",
|
||||||
"classname" to "org.netbeans.apitest.Sigtest",
|
"classname" to "org.netbeans.apitest.Sigtest",
|
||||||
"classpath" to sigtest.asPath )
|
"classpath" to classpath )
|
||||||
|
|
||||||
"sigtest"(
|
"sigtest"(
|
||||||
"action" to "generate",
|
"action" to "generate",
|
||||||
"fileName" to "${project.name}-sigtest.txt",
|
"fileName" to signatureFile,
|
||||||
"classpath" to jar.get().outputs.files.asPath,
|
"classpath" to jarPath,
|
||||||
"packages" to "com.formdev.flatlaf,com.formdev.flatlaf.themes,com.formdev.flatlaf.util",
|
"packages" to "com.formdev.flatlaf,com.formdev.flatlaf.themes,com.formdev.flatlaf.util",
|
||||||
"version" to version,
|
"version" to version,
|
||||||
"release" to "1.8", // Java version
|
"release" to "1.8", // Java version
|
||||||
"failonerror" to "true" )
|
"failonerror" to "true" )
|
||||||
|
|
||||||
|
"fixcrlf"(
|
||||||
|
"file" to signatureFile,
|
||||||
|
"eol" to "lf" )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,17 +131,23 @@ tasks {
|
|||||||
group = "verification"
|
group = "verification"
|
||||||
dependsOn( "jar" )
|
dependsOn( "jar" )
|
||||||
|
|
||||||
|
// necessary for configuration cache
|
||||||
|
val classpath = sigtest.asPath
|
||||||
|
val signatureFile = "${project.name}-sigtest.txt"
|
||||||
|
val jarPath = jar.get().outputs.files.asPath
|
||||||
|
val version = version
|
||||||
|
|
||||||
doLast {
|
doLast {
|
||||||
ant.withGroovyBuilder {
|
ant.withGroovyBuilder {
|
||||||
"taskdef"(
|
"taskdef"(
|
||||||
"name" to "sigtest",
|
"name" to "sigtest",
|
||||||
"classname" to "org.netbeans.apitest.Sigtest",
|
"classname" to "org.netbeans.apitest.Sigtest",
|
||||||
"classpath" to sigtest.asPath )
|
"classpath" to classpath )
|
||||||
|
|
||||||
"sigtest"(
|
"sigtest"(
|
||||||
"action" to "check",
|
"action" to "check",
|
||||||
"fileName" to "${project.name}-sigtest.txt",
|
"fileName" to signatureFile,
|
||||||
"classpath" to jar.get().outputs.files.asPath,
|
"classpath" to jarPath,
|
||||||
"packages" to "com.formdev.flatlaf,com.formdev.flatlaf.util",
|
"packages" to "com.formdev.flatlaf,com.formdev.flatlaf.util",
|
||||||
"version" to version,
|
"version" to version,
|
||||||
"release" to "1.8", // Java version
|
"release" to "1.8", // Java version
|
||||||
@@ -123,4 +161,17 @@ flatlafPublish {
|
|||||||
artifactId = "flatlaf"
|
artifactId = "flatlaf"
|
||||||
name = "FlatLaf"
|
name = "FlatLaf"
|
||||||
description = "Flat Look and Feel"
|
description = "Flat Look and Feel"
|
||||||
|
|
||||||
|
val natives = "src/main/resources/com/formdev/flatlaf/natives"
|
||||||
|
nativeArtifacts = listOf(
|
||||||
|
NativeArtifact( tasks.getByName( "jarNoNatives" ).outputs.files.asPath, "no-natives", "jar" ),
|
||||||
|
|
||||||
|
NativeArtifact( "${natives}/flatlaf-windows-x86.dll", "windows-x86", "dll" ),
|
||||||
|
NativeArtifact( "${natives}/flatlaf-windows-x86_64.dll", "windows-x86_64", "dll" ),
|
||||||
|
NativeArtifact( "${natives}/flatlaf-windows-arm64.dll", "windows-arm64", "dll" ),
|
||||||
|
NativeArtifact( "${natives}/libflatlaf-macos-arm64.dylib", "macos-arm64", "dylib" ),
|
||||||
|
NativeArtifact( "${natives}/libflatlaf-macos-x86_64.dylib", "macos-x86_64", "dylib" ),
|
||||||
|
NativeArtifact( "${natives}/libflatlaf-linux-x86_64.so", "linux-x86_64", "so" ),
|
||||||
|
NativeArtifact( "${natives}/libflatlaf-linux-arm64.so", "linux-arm64", "so" ),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#Signature file v4.1
|
#Signature file v4.1
|
||||||
#Version 2.6
|
#Version 3.7
|
||||||
|
|
||||||
CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties
|
CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties
|
||||||
fld public final static java.lang.String BUTTON_TYPE = "JButton.buttonType"
|
fld public final static java.lang.String BUTTON_TYPE = "JButton.buttonType"
|
||||||
@@ -12,15 +12,25 @@ fld public final static java.lang.String BUTTON_TYPE_TOOLBAR_BUTTON = "toolBarBu
|
|||||||
fld public final static java.lang.String COMPONENT_FOCUS_OWNER = "JComponent.focusOwner"
|
fld public final static java.lang.String COMPONENT_FOCUS_OWNER = "JComponent.focusOwner"
|
||||||
fld public final static java.lang.String COMPONENT_ROUND_RECT = "JComponent.roundRect"
|
fld public final static java.lang.String COMPONENT_ROUND_RECT = "JComponent.roundRect"
|
||||||
fld public final static java.lang.String COMPONENT_TITLE_BAR_CAPTION = "JComponent.titleBarCaption"
|
fld public final static java.lang.String COMPONENT_TITLE_BAR_CAPTION = "JComponent.titleBarCaption"
|
||||||
|
fld public final static java.lang.String FULL_WINDOW_CONTENT = "FlatLaf.fullWindowContent"
|
||||||
|
fld public final static java.lang.String FULL_WINDOW_CONTENT_BUTTONS_BOUNDS = "FlatLaf.fullWindowContent.buttonsBounds"
|
||||||
|
fld public final static java.lang.String FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER = "FlatLaf.fullWindowContent.buttonsPlaceholder"
|
||||||
|
fld public final static java.lang.String GLASS_PANE_FULL_HEIGHT = "JRootPane.glassPaneFullHeight"
|
||||||
|
fld public final static java.lang.String MACOS_WINDOW_BUTTONS_SPACING = "FlatLaf.macOS.windowButtonsSpacing"
|
||||||
|
fld public final static java.lang.String MACOS_WINDOW_BUTTONS_SPACING_LARGE = "large"
|
||||||
|
fld public final static java.lang.String MACOS_WINDOW_BUTTONS_SPACING_MEDIUM = "medium"
|
||||||
fld public final static java.lang.String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded"
|
fld public final static java.lang.String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded"
|
||||||
fld public final static java.lang.String MINIMUM_HEIGHT = "JComponent.minimumHeight"
|
fld public final static java.lang.String MINIMUM_HEIGHT = "JComponent.minimumHeight"
|
||||||
fld public final static java.lang.String MINIMUM_WIDTH = "JComponent.minimumWidth"
|
fld public final static java.lang.String MINIMUM_WIDTH = "JComponent.minimumWidth"
|
||||||
fld public final static java.lang.String OUTLINE = "JComponent.outline"
|
fld public final static java.lang.String OUTLINE = "JComponent.outline"
|
||||||
fld public final static java.lang.String OUTLINE_ERROR = "error"
|
fld public final static java.lang.String OUTLINE_ERROR = "error"
|
||||||
|
fld public final static java.lang.String OUTLINE_SUCCESS = "success"
|
||||||
fld public final static java.lang.String OUTLINE_WARNING = "warning"
|
fld public final static java.lang.String OUTLINE_WARNING = "warning"
|
||||||
fld public final static java.lang.String PLACEHOLDER_TEXT = "JTextField.placeholderText"
|
fld public final static java.lang.String PLACEHOLDER_TEXT = "JTextField.placeholderText"
|
||||||
|
fld public final static java.lang.String POPUP_BORDER_CORNER_RADIUS = "Popup.borderCornerRadius"
|
||||||
fld public final static java.lang.String POPUP_DROP_SHADOW_PAINTED = "Popup.dropShadowPainted"
|
fld public final static java.lang.String POPUP_DROP_SHADOW_PAINTED = "Popup.dropShadowPainted"
|
||||||
fld public final static java.lang.String POPUP_FORCE_HEAVY_WEIGHT = "Popup.forceHeavyWeight"
|
fld public final static java.lang.String POPUP_FORCE_HEAVY_WEIGHT = "Popup.forceHeavyWeight"
|
||||||
|
fld public final static java.lang.String POPUP_ROUNDED_BORDER_WIDTH = "Popup.roundedBorderWidth"
|
||||||
fld public final static java.lang.String PROGRESS_BAR_LARGE_HEIGHT = "JProgressBar.largeHeight"
|
fld public final static java.lang.String PROGRESS_BAR_LARGE_HEIGHT = "JProgressBar.largeHeight"
|
||||||
fld public final static java.lang.String PROGRESS_BAR_SQUARE = "JProgressBar.square"
|
fld public final static java.lang.String PROGRESS_BAR_SQUARE = "JProgressBar.square"
|
||||||
fld public final static java.lang.String SCROLL_BAR_SHOW_BUTTONS = "JScrollBar.showButtons"
|
fld public final static java.lang.String SCROLL_BAR_SHOW_BUTTONS = "JScrollBar.showButtons"
|
||||||
@@ -31,6 +41,7 @@ fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY = "JTextFiel
|
|||||||
fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_ALWAYS = "always"
|
fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_ALWAYS = "always"
|
||||||
fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_NEVER = "never"
|
fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_NEVER = "never"
|
||||||
fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_ONCE = "once"
|
fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_ONCE = "once"
|
||||||
|
fld public final static java.lang.String SELECT_ALL_ON_MOUSE_CLICK = "JTextField.selectAllOnMouseClick"
|
||||||
fld public final static java.lang.String SPLIT_PANE_EXPANDABLE_SIDE = "JSplitPane.expandableSide"
|
fld public final static java.lang.String SPLIT_PANE_EXPANDABLE_SIDE = "JSplitPane.expandableSide"
|
||||||
fld public final static java.lang.String SPLIT_PANE_EXPANDABLE_SIDE_LEFT = "left"
|
fld public final static java.lang.String SPLIT_PANE_EXPANDABLE_SIDE_LEFT = "left"
|
||||||
fld public final static java.lang.String SPLIT_PANE_EXPANDABLE_SIDE_RIGHT = "right"
|
fld public final static java.lang.String SPLIT_PANE_EXPANDABLE_SIDE_RIGHT = "right"
|
||||||
@@ -65,12 +76,18 @@ fld public final static java.lang.String TABBED_PANE_TAB_CLOSE_TOOLTIPTEXT = "JT
|
|||||||
fld public final static java.lang.String TABBED_PANE_TAB_HEIGHT = "JTabbedPane.tabHeight"
|
fld public final static java.lang.String TABBED_PANE_TAB_HEIGHT = "JTabbedPane.tabHeight"
|
||||||
fld public final static java.lang.String TABBED_PANE_TAB_ICON_PLACEMENT = "JTabbedPane.tabIconPlacement"
|
fld public final static java.lang.String TABBED_PANE_TAB_ICON_PLACEMENT = "JTabbedPane.tabIconPlacement"
|
||||||
fld public final static java.lang.String TABBED_PANE_TAB_INSETS = "JTabbedPane.tabInsets"
|
fld public final static java.lang.String TABBED_PANE_TAB_INSETS = "JTabbedPane.tabInsets"
|
||||||
|
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION = "JTabbedPane.tabRotation"
|
||||||
|
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION_AUTO = "auto"
|
||||||
|
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION_LEFT = "left"
|
||||||
|
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION_NONE = "none"
|
||||||
|
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION_RIGHT = "right"
|
||||||
fld public final static java.lang.String TABBED_PANE_TAB_TYPE = "JTabbedPane.tabType"
|
fld public final static java.lang.String TABBED_PANE_TAB_TYPE = "JTabbedPane.tabType"
|
||||||
fld public final static java.lang.String TABBED_PANE_TAB_TYPE_CARD = "card"
|
fld public final static java.lang.String TABBED_PANE_TAB_TYPE_CARD = "card"
|
||||||
fld public final static java.lang.String TABBED_PANE_TAB_TYPE_UNDERLINED = "underlined"
|
fld public final static java.lang.String TABBED_PANE_TAB_TYPE_UNDERLINED = "underlined"
|
||||||
fld public final static java.lang.String TABBED_PANE_TAB_WIDTH_MODE = "JTabbedPane.tabWidthMode"
|
fld public final static java.lang.String TABBED_PANE_TAB_WIDTH_MODE = "JTabbedPane.tabWidthMode"
|
||||||
fld public final static java.lang.String TABBED_PANE_TAB_WIDTH_MODE_COMPACT = "compact"
|
fld public final static java.lang.String TABBED_PANE_TAB_WIDTH_MODE_COMPACT = "compact"
|
||||||
fld public final static java.lang.String TABBED_PANE_TAB_WIDTH_MODE_EQUAL = "equal"
|
fld public final static java.lang.String TABBED_PANE_TAB_WIDTH_MODE_EQUAL = "equal"
|
||||||
|
fld public final static java.lang.String TABBED_PANE_TAB_WIDTH_MODE_ICON_ONLY = "iconOnly"
|
||||||
fld public final static java.lang.String TABBED_PANE_TAB_WIDTH_MODE_PREFERRED = "preferred"
|
fld public final static java.lang.String TABBED_PANE_TAB_WIDTH_MODE_PREFERRED = "preferred"
|
||||||
fld public final static java.lang.String TABBED_PANE_TRAILING_COMPONENT = "JTabbedPane.trailingComponent"
|
fld public final static java.lang.String TABBED_PANE_TRAILING_COMPONENT = "JTabbedPane.trailingComponent"
|
||||||
fld public final static java.lang.String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground"
|
fld public final static java.lang.String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground"
|
||||||
@@ -86,10 +103,18 @@ fld public final static java.lang.String TEXT_FIELD_TRAILING_COMPONENT = "JTextF
|
|||||||
fld public final static java.lang.String TEXT_FIELD_TRAILING_ICON = "JTextField.trailingIcon"
|
fld public final static java.lang.String TEXT_FIELD_TRAILING_ICON = "JTextField.trailingIcon"
|
||||||
fld public final static java.lang.String TITLE_BAR_BACKGROUND = "JRootPane.titleBarBackground"
|
fld public final static java.lang.String TITLE_BAR_BACKGROUND = "JRootPane.titleBarBackground"
|
||||||
fld public final static java.lang.String TITLE_BAR_FOREGROUND = "JRootPane.titleBarForeground"
|
fld public final static java.lang.String TITLE_BAR_FOREGROUND = "JRootPane.titleBarForeground"
|
||||||
|
fld public final static java.lang.String TITLE_BAR_HEIGHT = "JRootPane.titleBarHeight"
|
||||||
|
fld public final static java.lang.String TITLE_BAR_SHOW_CLOSE = "JRootPane.titleBarShowClose"
|
||||||
fld public final static java.lang.String TITLE_BAR_SHOW_ICON = "JRootPane.titleBarShowIcon"
|
fld public final static java.lang.String TITLE_BAR_SHOW_ICON = "JRootPane.titleBarShowIcon"
|
||||||
|
fld public final static java.lang.String TITLE_BAR_SHOW_ICONIFFY = "JRootPane.titleBarShowIconify"
|
||||||
|
fld public final static java.lang.String TITLE_BAR_SHOW_MAXIMIZE = "JRootPane.titleBarShowMaximize"
|
||||||
|
fld public final static java.lang.String TITLE_BAR_SHOW_TITLE = "JRootPane.titleBarShowTitle"
|
||||||
fld public final static java.lang.String TREE_PAINT_SELECTION = "JTree.paintSelection"
|
fld public final static java.lang.String TREE_PAINT_SELECTION = "JTree.paintSelection"
|
||||||
|
fld public final static java.lang.String TREE_WIDE_CELL_RENDERER = "JTree.wideCellRenderer"
|
||||||
fld public final static java.lang.String TREE_WIDE_SELECTION = "JTree.wideSelection"
|
fld public final static java.lang.String TREE_WIDE_SELECTION = "JTree.wideSelection"
|
||||||
fld public final static java.lang.String USE_WINDOW_DECORATIONS = "JRootPane.useWindowDecorations"
|
fld public final static java.lang.String USE_WINDOW_DECORATIONS = "JRootPane.useWindowDecorations"
|
||||||
|
fld public final static java.lang.String WINDOW_STYLE = "Window.style"
|
||||||
|
fld public final static java.lang.String WINDOW_STYLE_SMALL = "small"
|
||||||
meth public static <%0 extends java.lang.Object> {%%0} clientProperty(javax.swing.JComponent,java.lang.String,{%%0},java.lang.Class<{%%0}>)
|
meth public static <%0 extends java.lang.Object> {%%0} clientProperty(javax.swing.JComponent,java.lang.String,{%%0},java.lang.Class<{%%0}>)
|
||||||
meth public static boolean clientPropertyBoolean(javax.swing.JComponent,java.lang.String,boolean)
|
meth public static boolean clientPropertyBoolean(javax.swing.JComponent,java.lang.String,boolean)
|
||||||
meth public static boolean clientPropertyEquals(javax.swing.JComponent,java.lang.String,java.lang.Object)
|
meth public static boolean clientPropertyEquals(javax.swing.JComponent,java.lang.String,java.lang.Object)
|
||||||
@@ -195,9 +220,18 @@ meth public static boolean isUseNativeWindowDecorations()
|
|||||||
meth public static boolean setup(javax.swing.LookAndFeel)
|
meth public static boolean setup(javax.swing.LookAndFeel)
|
||||||
meth public static boolean supportsNativeWindowDecorations()
|
meth public static boolean supportsNativeWindowDecorations()
|
||||||
meth public static java.lang.Object parseDefaultsValue(java.lang.String,java.lang.String,java.lang.Class<?>)
|
meth public static java.lang.Object parseDefaultsValue(java.lang.String,java.lang.String,java.lang.Class<?>)
|
||||||
|
meth public static java.lang.String getPreferredFontFamily()
|
||||||
|
meth public static java.lang.String getPreferredLightFontFamily()
|
||||||
|
meth public static java.lang.String getPreferredMonospacedFontFamily()
|
||||||
|
meth public static java.lang.String getPreferredSemiboldFontFamily()
|
||||||
|
meth public static java.lang.String getUIKeyLightOrDarkPrefix(boolean)
|
||||||
meth public static java.util.Map<java.lang.String,java.lang.Class<?>> getStyleableInfos(javax.swing.JComponent)
|
meth public static java.util.Map<java.lang.String,java.lang.Class<?>> getStyleableInfos(javax.swing.JComponent)
|
||||||
meth public static java.util.Map<java.lang.String,java.lang.String> getGlobalExtraDefaults()
|
meth public static java.util.Map<java.lang.String,java.lang.String> getGlobalExtraDefaults()
|
||||||
|
meth public static java.util.Set<java.lang.String> getUIKeyPlatformPrefixes()
|
||||||
|
meth public static java.util.Set<java.lang.String> getUIKeySpecialPrefixes()
|
||||||
|
meth public static java.util.function.Function<java.lang.String,java.awt.Color> getSystemColorGetter()
|
||||||
meth public static javax.swing.UIDefaults$ActiveValue createActiveFontValue(float)
|
meth public static javax.swing.UIDefaults$ActiveValue createActiveFontValue(float)
|
||||||
|
meth public static void disableWindowsD3Donscreen()
|
||||||
meth public static void hideMnemonics()
|
meth public static void hideMnemonics()
|
||||||
meth public static void initIconColors(javax.swing.UIDefaults,boolean)
|
meth public static void initIconColors(javax.swing.UIDefaults,boolean)
|
||||||
meth public static void installLafInfo(java.lang.String,java.lang.Class<? extends javax.swing.LookAndFeel>)
|
meth public static void installLafInfo(java.lang.String,java.lang.Class<? extends javax.swing.LookAndFeel>)
|
||||||
@@ -209,6 +243,11 @@ meth public static void repaintAllFramesAndDialogs()
|
|||||||
meth public static void revalidateAndRepaintAllFramesAndDialogs()
|
meth public static void revalidateAndRepaintAllFramesAndDialogs()
|
||||||
meth public static void runWithUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>,java.lang.Runnable)
|
meth public static void runWithUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>,java.lang.Runnable)
|
||||||
meth public static void setGlobalExtraDefaults(java.util.Map<java.lang.String,java.lang.String>)
|
meth public static void setGlobalExtraDefaults(java.util.Map<java.lang.String,java.lang.String>)
|
||||||
|
meth public static void setPreferredFontFamily(java.lang.String)
|
||||||
|
meth public static void setPreferredLightFontFamily(java.lang.String)
|
||||||
|
meth public static void setPreferredMonospacedFontFamily(java.lang.String)
|
||||||
|
meth public static void setPreferredSemiboldFontFamily(java.lang.String)
|
||||||
|
meth public static void setSystemColorGetter(java.util.function.Function<java.lang.String,java.awt.Color>)
|
||||||
meth public static void setUseNativeWindowDecorations(boolean)
|
meth public static void setUseNativeWindowDecorations(boolean)
|
||||||
meth public static void showMnemonics(java.awt.Component)
|
meth public static void showMnemonics(java.awt.Component)
|
||||||
meth public static void unregisterCustomDefaultsSource(java.io.File)
|
meth public static void unregisterCustomDefaultsSource(java.io.File)
|
||||||
@@ -223,7 +262,7 @@ meth public void setExtraDefaults(java.util.Map<java.lang.String,java.lang.Strin
|
|||||||
meth public void uninitialize()
|
meth public void uninitialize()
|
||||||
meth public void unregisterUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>)
|
meth public void unregisterUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>)
|
||||||
supr javax.swing.plaf.basic.BasicLookAndFeel
|
supr javax.swing.plaf.basic.BasicLookAndFeel
|
||||||
hfds DESKTOPFONTHINTS,aquaLoaded,customDefaultsSources,desktopPropertyListener,desktopPropertyName,desktopPropertyName2,extraDefaults,getUIMethod,getUIMethodInitialized,globalExtraDefaults,mnemonicHandler,oldPopupFactory,postInitialization,subMenuUsabilityHelperInstalled,uiDefaultsGetters,updateUIPending
|
hfds DESKTOPFONTHINTS,aquaLoaded,customDefaultsSources,desktopPropertyListener,desktopPropertyName,desktopPropertyName2,extraDefaults,globalExtraDefaults,linuxPopupMenuCanceler,mnemonicHandler,oldPopupFactory,postInitialization,preferredFontFamily,preferredLightFontFamily,preferredMonospacedFontFamily,preferredSemiboldFontFamily,subMenuUsabilityHelperInstalled,systemColorGetter,uiDefaultsGetters,uiKeyPlatformPrefixes,uiKeySpecialPrefixes,updateUIPending
|
||||||
hcls ActiveFont,FlatUIDefaults,ImageIconUIResource
|
hcls ActiveFont,FlatUIDefaults,ImageIconUIResource
|
||||||
|
|
||||||
CLSS public abstract interface static com.formdev.flatlaf.FlatLaf$DisabledIconProvider
|
CLSS public abstract interface static com.formdev.flatlaf.FlatLaf$DisabledIconProvider
|
||||||
@@ -259,11 +298,17 @@ CLSS public abstract interface com.formdev.flatlaf.FlatSystemProperties
|
|||||||
fld public final static java.lang.String ANIMATION = "flatlaf.animation"
|
fld public final static java.lang.String ANIMATION = "flatlaf.animation"
|
||||||
fld public final static java.lang.String MENUBAR_EMBEDDED = "flatlaf.menuBarEmbedded"
|
fld public final static java.lang.String MENUBAR_EMBEDDED = "flatlaf.menuBarEmbedded"
|
||||||
fld public final static java.lang.String NATIVE_LIBRARY_PATH = "flatlaf.nativeLibraryPath"
|
fld public final static java.lang.String NATIVE_LIBRARY_PATH = "flatlaf.nativeLibraryPath"
|
||||||
|
fld public final static java.lang.String REUSE_VISIBLE_POPUP_WINDOW = "flatlaf.reuseVisiblePopupWindow"
|
||||||
fld public final static java.lang.String UI_SCALE = "flatlaf.uiScale"
|
fld public final static java.lang.String UI_SCALE = "flatlaf.uiScale"
|
||||||
fld public final static java.lang.String UI_SCALE_ALLOW_SCALE_DOWN = "flatlaf.uiScale.allowScaleDown"
|
fld public final static java.lang.String UI_SCALE_ALLOW_SCALE_DOWN = "flatlaf.uiScale.allowScaleDown"
|
||||||
fld public final static java.lang.String UI_SCALE_ENABLED = "flatlaf.uiScale.enabled"
|
fld public final static java.lang.String UI_SCALE_ENABLED = "flatlaf.uiScale.enabled"
|
||||||
fld public final static java.lang.String UPDATE_UI_ON_SYSTEM_FONT_CHANGE = "flatlaf.updateUIOnSystemFontChange"
|
fld public final static java.lang.String UPDATE_UI_ON_SYSTEM_FONT_CHANGE = "flatlaf.updateUIOnSystemFontChange"
|
||||||
fld public final static java.lang.String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations"
|
fld public final static java.lang.String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations"
|
||||||
|
anno 0 java.lang.Deprecated()
|
||||||
|
fld public final static java.lang.String USE_NATIVE_LIBRARY = "flatlaf.useNativeLibrary"
|
||||||
|
fld public final static java.lang.String USE_ROUNDED_POPUP_BORDER = "flatlaf.useRoundedPopupBorder"
|
||||||
|
fld public final static java.lang.String USE_SUB_MENU_SAFE_TRIANGLE = "flatlaf.useSubMenuSafeTriangle"
|
||||||
|
fld public final static java.lang.String USE_SYSTEM_FILE_CHOOSER = "flatlaf.useSystemFileChooser"
|
||||||
fld public final static java.lang.String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection"
|
fld public final static java.lang.String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection"
|
||||||
fld public final static java.lang.String USE_UBUNTU_FONT = "flatlaf.useUbuntuFont"
|
fld public final static java.lang.String USE_UBUNTU_FONT = "flatlaf.useUbuntuFont"
|
||||||
fld public final static java.lang.String USE_WINDOW_DECORATIONS = "flatlaf.useWindowDecorations"
|
fld public final static java.lang.String USE_WINDOW_DECORATIONS = "flatlaf.useWindowDecorations"
|
||||||
@@ -282,7 +327,7 @@ meth public static boolean setup(java.io.InputStream)
|
|||||||
meth public static com.formdev.flatlaf.FlatLaf createLaf(com.formdev.flatlaf.IntelliJTheme)
|
meth public static com.formdev.flatlaf.FlatLaf createLaf(com.formdev.flatlaf.IntelliJTheme)
|
||||||
meth public static com.formdev.flatlaf.FlatLaf createLaf(java.io.InputStream) throws java.io.IOException
|
meth public static com.formdev.flatlaf.FlatLaf createLaf(java.io.InputStream) throws java.io.IOException
|
||||||
supr java.lang.Object
|
supr java.lang.Object
|
||||||
hfds checkboxDuplicateColors,checkboxKeyMapping,colors,icons,isMaterialUILite,namedColors,ui,uiKeyCopying,uiKeyInverseMapping,uiKeyMapping
|
hfds checkboxDuplicateColors,checkboxKeyMapping,jsonColors,jsonIcons,jsonUI,namedColors,uiKeyCopying,uiKeyDoNotOverride,uiKeyExcludesContains,uiKeyExcludesStartsWith,uiKeyInverseMapping,uiKeyMapping
|
||||||
|
|
||||||
CLSS public static com.formdev.flatlaf.IntelliJTheme$ThemeLaf
|
CLSS public static com.formdev.flatlaf.IntelliJTheme$ThemeLaf
|
||||||
outer com.formdev.flatlaf.IntelliJTheme
|
outer com.formdev.flatlaf.IntelliJTheme
|
||||||
@@ -295,6 +340,26 @@ meth public java.lang.String getName()
|
|||||||
supr com.formdev.flatlaf.FlatLaf
|
supr com.formdev.flatlaf.FlatLaf
|
||||||
hfds theme
|
hfds theme
|
||||||
|
|
||||||
|
CLSS public com.formdev.flatlaf.themes.FlatMacDarkLaf
|
||||||
|
cons public init()
|
||||||
|
fld public final static java.lang.String NAME = "FlatLaf macOS Dark"
|
||||||
|
meth public boolean isDark()
|
||||||
|
meth public java.lang.String getDescription()
|
||||||
|
meth public java.lang.String getName()
|
||||||
|
meth public static boolean setup()
|
||||||
|
meth public static void installLafInfo()
|
||||||
|
supr com.formdev.flatlaf.FlatDarkLaf
|
||||||
|
|
||||||
|
CLSS public com.formdev.flatlaf.themes.FlatMacLightLaf
|
||||||
|
cons public init()
|
||||||
|
fld public final static java.lang.String NAME = "FlatLaf macOS Light"
|
||||||
|
meth public boolean isDark()
|
||||||
|
meth public java.lang.String getDescription()
|
||||||
|
meth public java.lang.String getName()
|
||||||
|
meth public static boolean setup()
|
||||||
|
meth public static void installLafInfo()
|
||||||
|
supr com.formdev.flatlaf.FlatLightLaf
|
||||||
|
|
||||||
CLSS public abstract interface com.formdev.flatlaf.util.AnimatedIcon
|
CLSS public abstract interface com.formdev.flatlaf.util.AnimatedIcon
|
||||||
innr public static AnimationSupport
|
innr public static AnimationSupport
|
||||||
intf javax.swing.Icon
|
intf javax.swing.Icon
|
||||||
@@ -357,11 +422,13 @@ innr public static Fade
|
|||||||
innr public static HSLChange
|
innr public static HSLChange
|
||||||
innr public static HSLIncreaseDecrease
|
innr public static HSLIncreaseDecrease
|
||||||
innr public static Mix
|
innr public static Mix
|
||||||
|
innr public static Mix2
|
||||||
meth public !varargs static java.awt.Color applyFunctions(java.awt.Color,com.formdev.flatlaf.util.ColorFunctions$ColorFunction[])
|
meth public !varargs static java.awt.Color applyFunctions(java.awt.Color,com.formdev.flatlaf.util.ColorFunctions$ColorFunction[])
|
||||||
meth public static float clamp(float)
|
meth public static float clamp(float)
|
||||||
meth public static float luma(java.awt.Color)
|
meth public static float luma(java.awt.Color)
|
||||||
meth public static java.awt.Color darken(java.awt.Color,float)
|
meth public static java.awt.Color darken(java.awt.Color,float)
|
||||||
meth public static java.awt.Color desaturate(java.awt.Color,float)
|
meth public static java.awt.Color desaturate(java.awt.Color,float)
|
||||||
|
meth public static java.awt.Color fade(java.awt.Color,float)
|
||||||
meth public static java.awt.Color lighten(java.awt.Color,float)
|
meth public static java.awt.Color lighten(java.awt.Color,float)
|
||||||
meth public static java.awt.Color mix(java.awt.Color,java.awt.Color,float)
|
meth public static java.awt.Color mix(java.awt.Color,java.awt.Color,float)
|
||||||
meth public static java.awt.Color saturate(java.awt.Color,float)
|
meth public static java.awt.Color saturate(java.awt.Color,float)
|
||||||
@@ -417,6 +484,16 @@ meth public java.lang.String toString()
|
|||||||
meth public void apply(float[])
|
meth public void apply(float[])
|
||||||
supr java.lang.Object
|
supr java.lang.Object
|
||||||
|
|
||||||
|
CLSS public static com.formdev.flatlaf.util.ColorFunctions$Mix2
|
||||||
|
outer com.formdev.flatlaf.util.ColorFunctions
|
||||||
|
cons public init(java.awt.Color,float)
|
||||||
|
fld public final float weight
|
||||||
|
fld public final java.awt.Color color1
|
||||||
|
intf com.formdev.flatlaf.util.ColorFunctions$ColorFunction
|
||||||
|
meth public java.lang.String toString()
|
||||||
|
meth public void apply(float[])
|
||||||
|
supr java.lang.Object
|
||||||
|
|
||||||
CLSS public com.formdev.flatlaf.util.CubicBezierEasing
|
CLSS public com.formdev.flatlaf.util.CubicBezierEasing
|
||||||
cons public init(float,float,float,float)
|
cons public init(float,float,float,float)
|
||||||
fld public final static com.formdev.flatlaf.util.CubicBezierEasing EASE
|
fld public final static com.formdev.flatlaf.util.CubicBezierEasing EASE
|
||||||
@@ -437,6 +514,17 @@ meth public java.lang.String toString()
|
|||||||
supr javax.swing.plaf.ColorUIResource
|
supr javax.swing.plaf.ColorUIResource
|
||||||
hfds baseOfDefaultColorRGB,functions,hasBaseOfDefaultColor
|
hfds baseOfDefaultColorRGB,functions,hasBaseOfDefaultColor
|
||||||
|
|
||||||
|
CLSS public com.formdev.flatlaf.util.FontUtils
|
||||||
|
cons public init()
|
||||||
|
meth public static boolean installFont(java.net.URL)
|
||||||
|
meth public static java.awt.Font getCompositeFont(java.lang.String,int,int)
|
||||||
|
meth public static java.awt.Font[] getAllFonts()
|
||||||
|
meth public static java.lang.String[] getAvailableFontFamilyNames()
|
||||||
|
meth public static void loadFontFamily(java.lang.String)
|
||||||
|
meth public static void registerFontFamilyLoader(java.lang.String,java.lang.Runnable)
|
||||||
|
supr java.lang.Object
|
||||||
|
hfds loadersMap
|
||||||
|
|
||||||
CLSS public com.formdev.flatlaf.util.Graphics2DProxy
|
CLSS public com.formdev.flatlaf.util.Graphics2DProxy
|
||||||
cons public init(java.awt.Graphics2D)
|
cons public init(java.awt.Graphics2D)
|
||||||
meth public boolean drawImage(java.awt.Image,int,int,int,int,int,int,int,int,java.awt.Color,java.awt.image.ImageObserver)
|
meth public boolean drawImage(java.awt.Image,int,int,int,int,int,int,int,int,java.awt.Color,java.awt.image.ImageObserver)
|
||||||
@@ -565,15 +653,32 @@ hfds alpha,hsl,rgb
|
|||||||
|
|
||||||
CLSS public com.formdev.flatlaf.util.HiDPIUtils
|
CLSS public com.formdev.flatlaf.util.HiDPIUtils
|
||||||
cons public init()
|
cons public init()
|
||||||
|
innr public abstract interface static DirtyRegionCallback
|
||||||
innr public abstract interface static Painter
|
innr public abstract interface static Painter
|
||||||
|
innr public static HiDPIRepaintManager
|
||||||
meth public static float computeTextYCorrection(java.awt.Graphics2D)
|
meth public static float computeTextYCorrection(java.awt.Graphics2D)
|
||||||
meth public static java.awt.Graphics2D createGraphicsTextYCorrection(java.awt.Graphics2D)
|
meth public static java.awt.Graphics2D createGraphicsTextYCorrection(java.awt.Graphics2D)
|
||||||
|
meth public static void addDirtyRegion(javax.swing.JComponent,int,int,int,int,com.formdev.flatlaf.util.HiDPIUtils$DirtyRegionCallback)
|
||||||
meth public static void drawStringUnderlineCharAtWithYCorrection(javax.swing.JComponent,java.awt.Graphics2D,java.lang.String,int,int,int)
|
meth public static void drawStringUnderlineCharAtWithYCorrection(javax.swing.JComponent,java.awt.Graphics2D,java.lang.String,int,int,int)
|
||||||
meth public static void drawStringWithYCorrection(javax.swing.JComponent,java.awt.Graphics2D,java.lang.String,int,int)
|
meth public static void drawStringWithYCorrection(javax.swing.JComponent,java.awt.Graphics2D,java.lang.String,int,int)
|
||||||
|
meth public static void installHiDPIRepaintManager()
|
||||||
meth public static void paintAtScale1x(java.awt.Graphics2D,int,int,int,int,com.formdev.flatlaf.util.HiDPIUtils$Painter)
|
meth public static void paintAtScale1x(java.awt.Graphics2D,int,int,int,int,com.formdev.flatlaf.util.HiDPIUtils$Painter)
|
||||||
meth public static void paintAtScale1x(java.awt.Graphics2D,javax.swing.JComponent,com.formdev.flatlaf.util.HiDPIUtils$Painter)
|
meth public static void paintAtScale1x(java.awt.Graphics2D,javax.swing.JComponent,com.formdev.flatlaf.util.HiDPIUtils$Painter)
|
||||||
|
meth public static void repaint(java.awt.Component)
|
||||||
|
meth public static void repaint(java.awt.Component,int,int,int,int)
|
||||||
|
meth public static void repaint(java.awt.Component,java.awt.Rectangle)
|
||||||
supr java.lang.Object
|
supr java.lang.Object
|
||||||
hfds useTextYCorrection
|
hfds CORRECTION_INTER,CORRECTION_OPEN_SANS,CORRECTION_SEGOE_UI,CORRECTION_TAHOMA,SCALE_FACTORS,useDebugScaleFactor,useTextYCorrection
|
||||||
|
|
||||||
|
CLSS public abstract interface static com.formdev.flatlaf.util.HiDPIUtils$DirtyRegionCallback
|
||||||
|
outer com.formdev.flatlaf.util.HiDPIUtils
|
||||||
|
meth public abstract void addDirtyRegion(javax.swing.JComponent,int,int,int,int)
|
||||||
|
|
||||||
|
CLSS public static com.formdev.flatlaf.util.HiDPIUtils$HiDPIRepaintManager
|
||||||
|
outer com.formdev.flatlaf.util.HiDPIUtils
|
||||||
|
cons public init()
|
||||||
|
meth public void addDirtyRegion(javax.swing.JComponent,int,int,int,int)
|
||||||
|
supr javax.swing.RepaintManager
|
||||||
|
|
||||||
CLSS public abstract interface static com.formdev.flatlaf.util.HiDPIUtils$Painter
|
CLSS public abstract interface static com.formdev.flatlaf.util.HiDPIUtils$Painter
|
||||||
outer com.formdev.flatlaf.util.HiDPIUtils
|
outer com.formdev.flatlaf.util.HiDPIUtils
|
||||||
@@ -667,9 +772,116 @@ cons public init()
|
|||||||
meth public static <%0 extends java.awt.Component> {%%0} getComponentByName(java.awt.Container,java.lang.String)
|
meth public static <%0 extends java.awt.Component> {%%0} getComponentByName(java.awt.Container,java.lang.String)
|
||||||
supr java.lang.Object
|
supr java.lang.Object
|
||||||
|
|
||||||
|
CLSS public com.formdev.flatlaf.util.SystemFileChooser
|
||||||
|
cons public init()
|
||||||
|
cons public init(java.io.File)
|
||||||
|
cons public init(java.lang.String)
|
||||||
|
fld public final static int APPROVE_OPTION = 0
|
||||||
|
fld public final static int CANCEL_OPTION = 1
|
||||||
|
fld public final static int DIRECTORIES_ONLY = 1
|
||||||
|
fld public final static int FILES_ONLY = 0
|
||||||
|
fld public final static int OPEN_DIALOG = 0
|
||||||
|
fld public final static int SAVE_DIALOG = 1
|
||||||
|
fld public final static java.lang.String LINUX_OPTIONS_CLEAR = "linux.optionsClear"
|
||||||
|
fld public final static java.lang.String LINUX_OPTIONS_SET = "linux.optionsSet"
|
||||||
|
fld public final static java.lang.String MAC_FILTER_FIELD_LABEL = "mac.filterFieldLabel"
|
||||||
|
fld public final static java.lang.String MAC_MESSAGE = "mac.message"
|
||||||
|
fld public final static java.lang.String MAC_NAME_FIELD_LABEL = "mac.nameFieldLabel"
|
||||||
|
fld public final static java.lang.String MAC_OPTIONS_CLEAR = "mac.optionsClear"
|
||||||
|
fld public final static java.lang.String MAC_OPTIONS_SET = "mac.optionsSet"
|
||||||
|
fld public final static java.lang.String MAC_TREATS_FILE_PACKAGES_AS_DIRECTORIES = "mac.treatsFilePackagesAsDirectories"
|
||||||
|
fld public final static java.lang.String WINDOWS_DEFAULT_EXTENSION = "windows.defaultExtension"
|
||||||
|
fld public final static java.lang.String WINDOWS_DEFAULT_FOLDER = "windows.defaultFolder"
|
||||||
|
fld public final static java.lang.String WINDOWS_FILE_NAME_LABEL = "windows.fileNameLabel"
|
||||||
|
fld public final static java.lang.String WINDOWS_OPTIONS_CLEAR = "windows.optionsClear"
|
||||||
|
fld public final static java.lang.String WINDOWS_OPTIONS_SET = "windows.optionsSet"
|
||||||
|
innr public abstract interface static ApproveCallback
|
||||||
|
innr public abstract interface static StateStore
|
||||||
|
innr public abstract static ApproveContext
|
||||||
|
innr public abstract static FileFilter
|
||||||
|
innr public final static FileNameExtensionFilter
|
||||||
|
meth public <%0 extends java.lang.Object> {%%0} getPlatformProperty(java.lang.String)
|
||||||
|
meth public boolean isAcceptAllFileFilterUsed()
|
||||||
|
meth public boolean isDirectorySelectionEnabled()
|
||||||
|
meth public boolean isFileHidingEnabled()
|
||||||
|
meth public boolean isFileSelectionEnabled()
|
||||||
|
meth public boolean isMultiSelectionEnabled()
|
||||||
|
meth public boolean removeChoosableFileFilter(com.formdev.flatlaf.util.SystemFileChooser$FileFilter)
|
||||||
|
meth public com.formdev.flatlaf.util.SystemFileChooser$ApproveCallback getApproveCallback()
|
||||||
|
meth public com.formdev.flatlaf.util.SystemFileChooser$FileFilter getAcceptAllFileFilter()
|
||||||
|
meth public com.formdev.flatlaf.util.SystemFileChooser$FileFilter getFileFilter()
|
||||||
|
meth public com.formdev.flatlaf.util.SystemFileChooser$FileFilter[] getChoosableFileFilters()
|
||||||
|
meth public int getApproveButtonMnemonic()
|
||||||
|
meth public int getDialogType()
|
||||||
|
meth public int getFileSelectionMode()
|
||||||
|
meth public int showDialog(java.awt.Component,java.lang.String)
|
||||||
|
meth public int showOpenDialog(java.awt.Component)
|
||||||
|
meth public int showSaveDialog(java.awt.Component)
|
||||||
|
meth public java.io.File getCurrentDirectory()
|
||||||
|
meth public java.io.File getSelectedFile()
|
||||||
|
meth public java.io.File[] getSelectedFiles()
|
||||||
|
meth public java.lang.String getApproveButtonText()
|
||||||
|
meth public java.lang.String getDialogTitle()
|
||||||
|
meth public java.lang.String getStateStoreID()
|
||||||
|
meth public static com.formdev.flatlaf.util.SystemFileChooser$StateStore getStateStore()
|
||||||
|
meth public static void setStateStore(com.formdev.flatlaf.util.SystemFileChooser$StateStore)
|
||||||
|
meth public void addChoosableFileFilter(com.formdev.flatlaf.util.SystemFileChooser$FileFilter)
|
||||||
|
meth public void putPlatformProperty(java.lang.String,java.lang.Object)
|
||||||
|
meth public void resetChoosableFileFilters()
|
||||||
|
meth public void setAcceptAllFileFilterUsed(boolean)
|
||||||
|
meth public void setApproveButtonMnemonic(char)
|
||||||
|
meth public void setApproveButtonMnemonic(int)
|
||||||
|
meth public void setApproveButtonText(java.lang.String)
|
||||||
|
meth public void setApproveCallback(com.formdev.flatlaf.util.SystemFileChooser$ApproveCallback)
|
||||||
|
meth public void setCurrentDirectory(java.io.File)
|
||||||
|
meth public void setDialogTitle(java.lang.String)
|
||||||
|
meth public void setDialogType(int)
|
||||||
|
meth public void setFileFilter(com.formdev.flatlaf.util.SystemFileChooser$FileFilter)
|
||||||
|
meth public void setFileHidingEnabled(boolean)
|
||||||
|
meth public void setFileSelectionMode(int)
|
||||||
|
meth public void setMultiSelectionEnabled(boolean)
|
||||||
|
meth public void setSelectedFile(java.io.File)
|
||||||
|
meth public void setSelectedFiles(java.io.File[])
|
||||||
|
meth public void setStateStoreID(java.lang.String)
|
||||||
|
supr java.lang.Object
|
||||||
|
hfds acceptAllFileFilter,approveButtonMnemonic,approveButtonText,approveCallback,approveResult,currentDirectory,dialogTitle,dialogType,fileFilter,fileSelectionMode,filters,inMemoryStateStore,keepAcceptAllAtEnd,multiSelection,platformProperties,selectedFile,selectedFiles,stateStore,stateStoreID,useAcceptAllFileFilter,useFileHiding
|
||||||
|
hcls AcceptAllFileFilter,FileChooserProvider,LinuxFileChooserProvider,MacFileChooserProvider,SwingFileChooserProvider,SystemFileChooserProvider,WindowsFileChooserProvider
|
||||||
|
|
||||||
|
CLSS public abstract interface static com.formdev.flatlaf.util.SystemFileChooser$ApproveCallback
|
||||||
|
outer com.formdev.flatlaf.util.SystemFileChooser
|
||||||
|
meth public abstract int approve(java.io.File[],com.formdev.flatlaf.util.SystemFileChooser$ApproveContext)
|
||||||
|
|
||||||
|
CLSS public abstract static com.formdev.flatlaf.util.SystemFileChooser$ApproveContext
|
||||||
|
outer com.formdev.flatlaf.util.SystemFileChooser
|
||||||
|
cons public init()
|
||||||
|
meth public abstract !varargs int showMessageDialog(int,java.lang.String,java.lang.String,int,java.lang.String[])
|
||||||
|
supr java.lang.Object
|
||||||
|
|
||||||
|
CLSS public abstract static com.formdev.flatlaf.util.SystemFileChooser$FileFilter
|
||||||
|
outer com.formdev.flatlaf.util.SystemFileChooser
|
||||||
|
cons public init()
|
||||||
|
meth public abstract java.lang.String getDescription()
|
||||||
|
supr java.lang.Object
|
||||||
|
|
||||||
|
CLSS public final static com.formdev.flatlaf.util.SystemFileChooser$FileNameExtensionFilter
|
||||||
|
outer com.formdev.flatlaf.util.SystemFileChooser
|
||||||
|
cons public !varargs init(java.lang.String,java.lang.String[])
|
||||||
|
meth public java.lang.String getDescription()
|
||||||
|
meth public java.lang.String toString()
|
||||||
|
meth public java.lang.String[] getExtensions()
|
||||||
|
supr com.formdev.flatlaf.util.SystemFileChooser$FileFilter
|
||||||
|
hfds description,extensions
|
||||||
|
|
||||||
|
CLSS public abstract interface static com.formdev.flatlaf.util.SystemFileChooser$StateStore
|
||||||
|
outer com.formdev.flatlaf.util.SystemFileChooser
|
||||||
|
fld public final static java.lang.String KEY_CURRENT_DIRECTORY = "currentDirectory"
|
||||||
|
meth public abstract java.lang.String get(java.lang.String,java.lang.String)
|
||||||
|
meth public abstract void put(java.lang.String,java.lang.String)
|
||||||
|
|
||||||
CLSS public com.formdev.flatlaf.util.SystemInfo
|
CLSS public com.formdev.flatlaf.util.SystemInfo
|
||||||
cons public init()
|
cons public init()
|
||||||
fld public final static boolean isAARCH64
|
fld public final static boolean isAARCH64
|
||||||
|
fld public final static boolean isGNOME
|
||||||
fld public final static boolean isJava_11_orLater
|
fld public final static boolean isJava_11_orLater
|
||||||
fld public final static boolean isJava_12_orLater
|
fld public final static boolean isJava_12_orLater
|
||||||
fld public final static boolean isJava_15_orLater
|
fld public final static boolean isJava_15_orLater
|
||||||
@@ -686,6 +898,7 @@ fld public final static boolean isMacOS_10_11_ElCapitan_orLater
|
|||||||
fld public final static boolean isMacOS_10_14_Mojave_orLater
|
fld public final static boolean isMacOS_10_14_Mojave_orLater
|
||||||
fld public final static boolean isMacOS_10_15_Catalina_orLater
|
fld public final static boolean isMacOS_10_15_Catalina_orLater
|
||||||
fld public final static boolean isProjector
|
fld public final static boolean isProjector
|
||||||
|
fld public final static boolean isUnknownOS
|
||||||
fld public final static boolean isWebswing
|
fld public final static boolean isWebswing
|
||||||
fld public final static boolean isWinPE
|
fld public final static boolean isWinPE
|
||||||
fld public final static boolean isWindows
|
fld public final static boolean isWindows
|
||||||
@@ -701,13 +914,21 @@ supr java.lang.Object
|
|||||||
|
|
||||||
CLSS public com.formdev.flatlaf.util.UIScale
|
CLSS public com.formdev.flatlaf.util.UIScale
|
||||||
cons public init()
|
cons public init()
|
||||||
|
fld public final static java.lang.String PROP_USER_SCALE_FACTOR = "userScaleFactor"
|
||||||
|
fld public final static java.lang.String PROP_ZOOM_FACTOR = "zoomFactor"
|
||||||
meth public static boolean isSystemScalingEnabled()
|
meth public static boolean isSystemScalingEnabled()
|
||||||
|
meth public static boolean setZoomFactor(float)
|
||||||
|
meth public static boolean zoomIn()
|
||||||
|
meth public static boolean zoomOut()
|
||||||
|
meth public static boolean zoomReset()
|
||||||
meth public static double getSystemScaleFactor(java.awt.Graphics2D)
|
meth public static double getSystemScaleFactor(java.awt.Graphics2D)
|
||||||
meth public static double getSystemScaleFactor(java.awt.GraphicsConfiguration)
|
meth public static double getSystemScaleFactor(java.awt.GraphicsConfiguration)
|
||||||
meth public static float computeFontScaleFactor(java.awt.Font)
|
meth public static float computeFontScaleFactor(java.awt.Font)
|
||||||
meth public static float getUserScaleFactor()
|
meth public static float getUserScaleFactor()
|
||||||
|
meth public static float getZoomFactor()
|
||||||
meth public static float scale(float)
|
meth public static float scale(float)
|
||||||
meth public static float unscale(float)
|
meth public static float unscale(float)
|
||||||
|
meth public static float[] getSupportedZoomFactors()
|
||||||
meth public static int scale(int)
|
meth public static int scale(int)
|
||||||
meth public static int scale2(int)
|
meth public static int scale2(int)
|
||||||
meth public static int unscale(int)
|
meth public static int unscale(int)
|
||||||
@@ -717,8 +938,9 @@ meth public static javax.swing.plaf.FontUIResource applyCustomScaleFactor(javax.
|
|||||||
meth public static void addPropertyChangeListener(java.beans.PropertyChangeListener)
|
meth public static void addPropertyChangeListener(java.beans.PropertyChangeListener)
|
||||||
meth public static void removePropertyChangeListener(java.beans.PropertyChangeListener)
|
meth public static void removePropertyChangeListener(java.beans.PropertyChangeListener)
|
||||||
meth public static void scaleGraphics(java.awt.Graphics2D)
|
meth public static void scaleGraphics(java.awt.Graphics2D)
|
||||||
|
meth public static void setSupportedZoomFactors(float[])
|
||||||
supr java.lang.Object
|
supr java.lang.Object
|
||||||
hfds DEBUG,changeSupport,initialized,jreHiDPI,scaleFactor
|
hfds DEBUG,changeSupport,ignoreFontChange,inUnitTests,initialized,jreHiDPI,listenerInitialized,scaleFactor,supportedZoomFactors,unzoomedScaleFactor,zoomFactor
|
||||||
|
|
||||||
CLSS public java.awt.Color
|
CLSS public java.awt.Color
|
||||||
cons public init(float,float,float)
|
cons public init(float,float,float)
|
||||||
@@ -1073,6 +1295,31 @@ meth public void provideErrorFeedback(java.awt.Component)
|
|||||||
meth public void uninitialize()
|
meth public void uninitialize()
|
||||||
supr java.lang.Object
|
supr java.lang.Object
|
||||||
|
|
||||||
|
CLSS public javax.swing.RepaintManager
|
||||||
|
cons public init()
|
||||||
|
meth public boolean isCompletelyDirty(javax.swing.JComponent)
|
||||||
|
meth public boolean isDoubleBufferingEnabled()
|
||||||
|
meth public java.awt.Dimension getDoubleBufferMaximumSize()
|
||||||
|
meth public java.awt.Image getOffscreenBuffer(java.awt.Component,int,int)
|
||||||
|
meth public java.awt.Image getVolatileOffscreenBuffer(java.awt.Component,int,int)
|
||||||
|
meth public java.awt.Rectangle getDirtyRegion(javax.swing.JComponent)
|
||||||
|
meth public java.lang.String toString()
|
||||||
|
meth public static javax.swing.RepaintManager currentManager(java.awt.Component)
|
||||||
|
meth public static javax.swing.RepaintManager currentManager(javax.swing.JComponent)
|
||||||
|
meth public static void setCurrentManager(javax.swing.RepaintManager)
|
||||||
|
meth public void addDirtyRegion(java.applet.Applet,int,int,int,int)
|
||||||
|
meth public void addDirtyRegion(java.awt.Window,int,int,int,int)
|
||||||
|
meth public void addDirtyRegion(javax.swing.JComponent,int,int,int,int)
|
||||||
|
meth public void addInvalidComponent(javax.swing.JComponent)
|
||||||
|
meth public void markCompletelyClean(javax.swing.JComponent)
|
||||||
|
meth public void markCompletelyDirty(javax.swing.JComponent)
|
||||||
|
meth public void paintDirtyRegions()
|
||||||
|
meth public void removeInvalidComponent(javax.swing.JComponent)
|
||||||
|
meth public void setDoubleBufferMaximumSize(java.awt.Dimension)
|
||||||
|
meth public void setDoubleBufferingEnabled(boolean)
|
||||||
|
meth public void validateInvalidComponents()
|
||||||
|
supr java.lang.Object
|
||||||
|
|
||||||
CLSS public abstract javax.swing.border.AbstractBorder
|
CLSS public abstract javax.swing.border.AbstractBorder
|
||||||
cons public init()
|
cons public init()
|
||||||
intf java.io.Serializable
|
intf java.io.Serializable
|
||||||
|
|||||||
@@ -17,8 +17,12 @@
|
|||||||
package com.formdev.flatlaf;
|
package com.formdev.flatlaf;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.IllegalComponentStateException;
|
||||||
|
import java.awt.Window;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JFormattedTextField;
|
||||||
|
import javax.swing.JSpinner;
|
||||||
import javax.swing.SwingConstants;
|
import javax.swing.SwingConstants;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -31,7 +35,7 @@ public interface FlatClientProperties
|
|||||||
//---- JButton ------------------------------------------------------------
|
//---- JButton ------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies type of a button.
|
* Specifies type of button.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
|
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.String}<br>
|
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||||
@@ -100,6 +104,17 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String BUTTON_TYPE_BORDERLESS = "borderless";
|
String BUTTON_TYPE_BORDERLESS = "borderless";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether the button preferred size will be made square (quadratically).
|
||||||
|
* <p>
|
||||||
|
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
|
*/
|
||||||
|
String SQUARE_SIZE = "JButton.squareSize";
|
||||||
|
|
||||||
|
|
||||||
|
//---- JCheckBox ----------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies selected state of a checkbox.
|
* Specifies selected state of a checkbox.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -116,13 +131,6 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String SELECTED_STATE_INDETERMINATE = "indeterminate";
|
String SELECTED_STATE_INDETERMINATE = "indeterminate";
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies whether the button preferred size will be made square (quadratically).
|
|
||||||
* <p>
|
|
||||||
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
|
|
||||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
|
||||||
*/
|
|
||||||
String SQUARE_SIZE = "JButton.squareSize";
|
|
||||||
|
|
||||||
//---- JComponent ---------------------------------------------------------
|
//---- JComponent ---------------------------------------------------------
|
||||||
|
|
||||||
@@ -214,6 +222,7 @@ public interface FlatClientProperties
|
|||||||
* <strong>Allowed Values</strong>
|
* <strong>Allowed Values</strong>
|
||||||
* {@link #OUTLINE_ERROR},
|
* {@link #OUTLINE_ERROR},
|
||||||
* {@link #OUTLINE_WARNING},
|
* {@link #OUTLINE_WARNING},
|
||||||
|
* {@link #OUTLINE_SUCCESS},
|
||||||
* any color (type {@link java.awt.Color}) or
|
* any color (type {@link java.awt.Color}) or
|
||||||
* an array of two colors (type {@link java.awt.Color}[2]) where the first color
|
* an array of two colors (type {@link java.awt.Color}[2]) where the first color
|
||||||
* is for focused state and the second for unfocused state
|
* is for focused state and the second for unfocused state
|
||||||
@@ -234,6 +243,14 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String OUTLINE_WARNING = "warning";
|
String OUTLINE_WARNING = "warning";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paint the component border in another color (usually greenish) to indicate a success.
|
||||||
|
*
|
||||||
|
* @see #OUTLINE
|
||||||
|
* @since 3.6
|
||||||
|
*/
|
||||||
|
String OUTLINE_SUCCESS = "success";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies a callback that is invoked to check whether a component is permanent focus owner.
|
* Specifies a callback that is invoked to check whether a component is permanent focus owner.
|
||||||
* Used to paint focus indicators.
|
* Used to paint focus indicators.
|
||||||
@@ -254,20 +271,156 @@ 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
|
* Specifies whether a component shown in a window title bar area should behave as caption
|
||||||
* (left-click allows moving window, right-click shows window system menu).
|
* (left-click allows moving window, right-click shows window system menu).
|
||||||
* The component does not receive mouse pressed/released/clicked/dragged events,
|
* The caption component does not receive mouse pressed/released/clicked/dragged events,
|
||||||
* but it gets mouse entered/exited/moved events.
|
* but it gets mouse entered/exited/moved events.
|
||||||
* <p>
|
* <p>
|
||||||
|
* Since 3.4, this client property also supports using a function that can check
|
||||||
|
* whether a given location in the component should behave as caption.
|
||||||
|
* Useful for components that do not use mouse input on whole component bounds.
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* myComponent.putClientProperty( "JComponent.titleBarCaption",
|
||||||
|
* (Function<Point, Boolean>) pt -> {
|
||||||
|
* // parameter pt contains mouse location (in myComponent coordinates)
|
||||||
|
* // return true if the component is not interested in mouse input at the given location
|
||||||
|
* // return false if the component wants process mouse input at the given location
|
||||||
|
* // return null if the component children should be checked
|
||||||
|
* return ...; // check here
|
||||||
|
* } );
|
||||||
|
* }</pre>
|
||||||
|
* <b>Warning</b>:
|
||||||
|
* <ul>
|
||||||
|
* <li>This function is invoked often when mouse is moved over window title bar area
|
||||||
|
* and should therefore return quickly.
|
||||||
|
* <li>This function is invoked on 'AWT-Windows' thread (not 'AWT-EventQueue' thread)
|
||||||
|
* while processing Windows messages.
|
||||||
|
* It <b>must not</b> change any component property or layout because this could cause a dead lock.
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
|
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
* <strong>Value type</strong> {@link java.lang.Boolean} or {@link java.util.function.Function}<Point, Boolean>
|
||||||
*
|
*
|
||||||
* @since 2.5
|
* @since 2.5
|
||||||
*/
|
*/
|
||||||
String COMPONENT_TITLE_BAR_CAPTION = "JComponent.titleBarCaption";
|
String COMPONENT_TITLE_BAR_CAPTION = "JComponent.titleBarCaption";
|
||||||
|
|
||||||
|
|
||||||
|
//---- Panel --------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the panel as placeholder for the iconfify/maximize/close buttons
|
||||||
|
* in fullWindowContent mode. See {@link #FULL_WINDOW_CONTENT}.
|
||||||
|
* <p>
|
||||||
|
* If fullWindowContent mode is enabled, the preferred size of the panel is equal
|
||||||
|
* to the size of the iconfify/maximize/close buttons. Otherwise is is {@code 0,0}.
|
||||||
|
* <p>
|
||||||
|
* You're responsible to layout that panel at the top-left or top-right corner,
|
||||||
|
* depending on platform, where the iconfify/maximize/close buttons are located.
|
||||||
|
* <p>
|
||||||
|
* Syntax of the value string is: {@code "win|mac [horizontal|vertical] [zeroInFullScreen] [leftToRight|rightToLeft]"}.
|
||||||
|
* <p>
|
||||||
|
* The string must start with {@code "win"} (for Windows or Linux) or
|
||||||
|
* with {@code "mac"} (for macOS) and specifies the platform where the placeholder
|
||||||
|
* should be used. On macOS, you need the placeholder in the top-left corner,
|
||||||
|
* but on Windows/Linux you need it in the top-right corner. So if your application supports
|
||||||
|
* fullWindowContent mode on both platforms, you can add two placeholders to your layout
|
||||||
|
* and FlatLaf automatically uses only one of them. The other gets size {@code 0,0}.
|
||||||
|
* <p>
|
||||||
|
* Optionally, you can append following options to the value string (separated by space characters):
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code "horizontal"} - preferred height is zero
|
||||||
|
* <li>{@code "vertical"} - preferred width is zero
|
||||||
|
* <li>{@code "zeroInFullScreen"} - in full-screen mode on macOS, preferred size is {@code 0,0}
|
||||||
|
* <li>{@code "leftToRight"} - in right-to-left component orientation, preferred size is {@code 0,0}
|
||||||
|
* <li>{@code "rightToLeft"} - in left-to-right component orientation, preferred size is {@code 0,0}
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* Example for adding placeholder to top-left corner on macOS:
|
||||||
|
* <pre>{@code
|
||||||
|
* JPanel placeholder = new JPanel();
|
||||||
|
* placeholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "mac" );
|
||||||
|
*
|
||||||
|
* JToolBar toolBar = new JToolBar();
|
||||||
|
* // add tool bar items
|
||||||
|
*
|
||||||
|
* JPanel toolBarPanel = new JPanel( new BorderLayout() );
|
||||||
|
* toolBarPanel.add( placeholder, BorderLayout.WEST );
|
||||||
|
* toolBarPanel.add( toolBar, BorderLayout.CENTER );
|
||||||
|
*
|
||||||
|
* frame.getContentPane().add( toolBarPanel, BorderLayout.NORTH );
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* Or add placeholder as first item to the tool bar:
|
||||||
|
* <pre>{@code
|
||||||
|
* JPanel placeholder = new JPanel();
|
||||||
|
* placeholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "mac" );
|
||||||
|
*
|
||||||
|
* JToolBar toolBar = new JToolBar();
|
||||||
|
* toolBar.add( placeholder );
|
||||||
|
* // add tool bar items
|
||||||
|
*
|
||||||
|
* frame.getContentPane().add( toolBar, BorderLayout.NORTH );
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* If a tabbed pane is located at the top, you can add the placeholder
|
||||||
|
* as leading component to that tabbed pane:
|
||||||
|
* <pre>{@code
|
||||||
|
* JPanel placeholder = new JPanel();
|
||||||
|
* placeholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "mac" );
|
||||||
|
*
|
||||||
|
* tabbedPane.putClientProperty( FlatClientProperties.TABBED_PANE_LEADING_COMPONENT, placeholder );
|
||||||
|
* }</pre>
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JPanel}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.String}
|
||||||
|
*
|
||||||
|
* @since 3.4
|
||||||
|
*/
|
||||||
|
String FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER = "FlatLaf.fullWindowContent.buttonsPlaceholder";
|
||||||
|
|
||||||
|
|
||||||
//---- Popup --------------------------------------------------------------
|
//---- Popup --------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the popup border corner radius if the component is shown in a popup
|
||||||
|
* or if the component is the owner of another component that is shown in a popup.
|
||||||
|
* <p>
|
||||||
|
* Note that this is not available on all platforms since it requires special support.
|
||||||
|
* Supported platforms:
|
||||||
|
* <ul>
|
||||||
|
* <li><strong>Windows 11</strong>: Only two corner radiuses are supported
|
||||||
|
* by the OS: {@code DWMWCP_ROUND} is 8px and {@code DWMWCP_ROUNDSMALL} is 4px.
|
||||||
|
* If this value is {@code 1 - 4}, then {@code DWMWCP_ROUNDSMALL} is used.
|
||||||
|
* If it is {@code >= 5}, then {@code DWMWCP_ROUND} is used.
|
||||||
|
* <li><strong>macOS</strong> (10.14 and later): Any corner radius is supported.
|
||||||
|
* </ul>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Integer}<br>
|
||||||
|
*
|
||||||
|
* @since 3.1
|
||||||
|
*/
|
||||||
|
String POPUP_BORDER_CORNER_RADIUS = "Popup.borderCornerRadius";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the popup rounded border width if the component is shown in a popup
|
||||||
|
* or if the component is the owner of another component that is shown in a popup.
|
||||||
|
* <p>
|
||||||
|
* Only used if popup uses rounded border.
|
||||||
|
* <p>
|
||||||
|
* Note that this is not available on all platforms since it requires special support.
|
||||||
|
* Supported platforms:
|
||||||
|
* <ul>
|
||||||
|
* <li><strong>macOS</strong> (10.14 and later)
|
||||||
|
* </ul>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Integer} or {@link java.lang.Float}<br>
|
||||||
|
*
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
String POPUP_ROUNDED_BORDER_WIDTH = "Popup.roundedBorderWidth";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether a drop shadow is painted if the component is shown in a popup
|
* Specifies whether a drop shadow is painted if the component is shown in a popup
|
||||||
* or if the component is the owner of another component that is shown in a popup.
|
* or if the component is the owner of another component that is shown in a popup.
|
||||||
@@ -286,6 +439,7 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String POPUP_FORCE_HEAVY_WEIGHT = "Popup.forceHeavyWeight";
|
String POPUP_FORCE_HEAVY_WEIGHT = "Popup.forceHeavyWeight";
|
||||||
|
|
||||||
|
|
||||||
//---- JProgressBar -------------------------------------------------------
|
//---- JProgressBar -------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -304,6 +458,7 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String PROGRESS_BAR_SQUARE = "JProgressBar.square";
|
String PROGRESS_BAR_SQUARE = "JProgressBar.square";
|
||||||
|
|
||||||
|
|
||||||
//---- JRootPane ----------------------------------------------------------
|
//---- JRootPane ----------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -317,7 +472,7 @@ public interface FlatClientProperties
|
|||||||
* {@link FlatSystemProperties#USE_WINDOW_DECORATIONS}, but higher priority
|
* {@link FlatSystemProperties#USE_WINDOW_DECORATIONS}, but higher priority
|
||||||
* than UI default {@code TitlePane.useWindowDecorations}.
|
* than UI default {@code TitlePane.useWindowDecorations}.
|
||||||
* <p>
|
* <p>
|
||||||
* (requires Window 10)
|
* (requires Windows 10/11)
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
@@ -337,13 +492,55 @@ public interface FlatClientProperties
|
|||||||
* {@link FlatSystemProperties#MENUBAR_EMBEDDED}, but higher priority
|
* {@link FlatSystemProperties#MENUBAR_EMBEDDED}, but higher priority
|
||||||
* than UI default {@code TitlePane.menuBarEmbedded}.
|
* than UI default {@code TitlePane.menuBarEmbedded}.
|
||||||
* <p>
|
* <p>
|
||||||
* (requires Window 10)
|
* (requires Windows 10/11)
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
*/
|
*/
|
||||||
String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded";
|
String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether the content pane (and the glass pane) should be extended
|
||||||
|
* into the window title bar area
|
||||||
|
* (requires enabled window decorations). Default is {@code false}.
|
||||||
|
* <p>
|
||||||
|
* On macOS, use client property {@code apple.awt.fullWindowContent}
|
||||||
|
* (see <a href="https://www.formdev.com/flatlaf/macos/#full_window_content">macOS Full window content</a>).
|
||||||
|
* <p>
|
||||||
|
* Setting this enables/disables full window content
|
||||||
|
* for the {@code JFrame} or {@code JDialog} that contains the root pane.
|
||||||
|
* <p>
|
||||||
|
* If {@code true}, the content pane (and the glass pane) is extended into
|
||||||
|
* the title bar area. The window icon and title are hidden.
|
||||||
|
* Only the iconfify/maximize/close buttons stay visible in the upper right corner
|
||||||
|
* (and overlap the content pane).
|
||||||
|
* <p>
|
||||||
|
* The user can left-click-and-drag on the title bar area to move the window,
|
||||||
|
* except when clicking on a component that processes mouse events (e.g. buttons or menus).
|
||||||
|
* <p>
|
||||||
|
* (requires Windows 10/11)
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
|
*
|
||||||
|
* @since 3.4
|
||||||
|
*/
|
||||||
|
String FULL_WINDOW_CONTENT = "FlatLaf.fullWindowContent";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains the current bounds of the iconfify/maximize/close buttons
|
||||||
|
* (in root pane coordinates) if fullWindowContent mode is enabled.
|
||||||
|
* Otherwise its value is {@code null}.
|
||||||
|
* <p>
|
||||||
|
* <b>Note</b>: Do not set this client property. It is set by FlatLaf.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.awt.Rectangle}
|
||||||
|
*
|
||||||
|
* @since 3.4
|
||||||
|
*/
|
||||||
|
String FULL_WINDOW_CONTENT_BUTTONS_BOUNDS = "FlatLaf.fullWindowContent.buttonsBounds";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether the window icon should be shown in the window title bar
|
* Specifies whether the window icon should be shown in the window title bar
|
||||||
* (requires enabled window decorations). Default is UI property {@code TitlePane.showIcon}.
|
* (requires enabled window decorations). Default is UI property {@code TitlePane.showIcon}.
|
||||||
@@ -353,7 +550,7 @@ public interface FlatClientProperties
|
|||||||
* <p>
|
* <p>
|
||||||
* This client property has higher priority than UI default {@code TitlePane.showIcon}.
|
* This client property has higher priority than UI default {@code TitlePane.showIcon}.
|
||||||
* <p>
|
* <p>
|
||||||
* (requires Window 10)
|
* (requires Windows 10/11)
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
@@ -369,6 +566,8 @@ public interface FlatClientProperties
|
|||||||
* Setting this shows/hides the windows title
|
* Setting this shows/hides the windows title
|
||||||
* for the {@code JFrame} or {@code JDialog} that contains the root pane.
|
* for the {@code JFrame} or {@code JDialog} that contains the root pane.
|
||||||
* <p>
|
* <p>
|
||||||
|
* (requires Windows 10/11)
|
||||||
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
*
|
*
|
||||||
@@ -377,12 +576,14 @@ public interface FlatClientProperties
|
|||||||
String TITLE_BAR_SHOW_TITLE = "JRootPane.titleBarShowTitle";
|
String TITLE_BAR_SHOW_TITLE = "JRootPane.titleBarShowTitle";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether the "iconfify" button should be shown in the window title bar
|
* Specifies whether the "iconify" button should be shown in the window title bar
|
||||||
* (requires enabled window decorations). Default is {@code true}.
|
* (requires enabled window decorations). Default is {@code true}.
|
||||||
* <p>
|
* <p>
|
||||||
* Setting this shows/hides the "iconfify" button
|
* Setting this shows/hides the "iconify" button
|
||||||
* for the {@code JFrame} that contains the root pane.
|
* for the {@code JFrame} that contains the root pane.
|
||||||
* <p>
|
* <p>
|
||||||
|
* (requires Windows 10/11)
|
||||||
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
*
|
*
|
||||||
@@ -397,6 +598,8 @@ public interface FlatClientProperties
|
|||||||
* Setting this shows/hides the "maximize/restore" button
|
* Setting this shows/hides the "maximize/restore" button
|
||||||
* for the {@code JFrame} that contains the root pane.
|
* for the {@code JFrame} that contains the root pane.
|
||||||
* <p>
|
* <p>
|
||||||
|
* (requires Windows 10/11)
|
||||||
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
*
|
*
|
||||||
@@ -411,6 +614,8 @@ public interface FlatClientProperties
|
|||||||
* Setting this shows/hides the "close" button
|
* Setting this shows/hides the "close" button
|
||||||
* for the {@code JFrame} or {@code JDialog} that contains the root pane.
|
* for the {@code JFrame} or {@code JDialog} that contains the root pane.
|
||||||
* <p>
|
* <p>
|
||||||
|
* (requires Windows 10/11)
|
||||||
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
*
|
*
|
||||||
@@ -421,7 +626,7 @@ public interface FlatClientProperties
|
|||||||
/**
|
/**
|
||||||
* Background color of window title bar (requires enabled window decorations).
|
* Background color of window title bar (requires enabled window decorations).
|
||||||
* <p>
|
* <p>
|
||||||
* (requires Window 10)
|
* (requires Windows 10/11)
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
* <strong>Value type</strong> {@link java.awt.Color}
|
* <strong>Value type</strong> {@link java.awt.Color}
|
||||||
@@ -433,7 +638,7 @@ public interface FlatClientProperties
|
|||||||
/**
|
/**
|
||||||
* Foreground color of window title bar (requires enabled window decorations).
|
* Foreground color of window title bar (requires enabled window decorations).
|
||||||
* <p>
|
* <p>
|
||||||
* (requires Window 10)
|
* (requires Windows 10/11)
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
* <strong>Value type</strong> {@link java.awt.Color}
|
* <strong>Value type</strong> {@link java.awt.Color}
|
||||||
@@ -442,6 +647,62 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String TITLE_BAR_FOREGROUND = "JRootPane.titleBarForeground";
|
String TITLE_BAR_FOREGROUND = "JRootPane.titleBarForeground";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the preferred height of title bar (requires enabled window decorations).
|
||||||
|
* <p>
|
||||||
|
* (requires Windows 10/11)
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Integer}
|
||||||
|
*
|
||||||
|
* @since 3.5.2
|
||||||
|
*/
|
||||||
|
String TITLE_BAR_HEIGHT = "JRootPane.titleBarHeight";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether the glass pane should have full height and overlap the title bar,
|
||||||
|
* if FlatLaf window decorations are enabled. Default is {@code false}.
|
||||||
|
* <p>
|
||||||
|
* (requires Windows 10/11)
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
|
*
|
||||||
|
* @since 3.1
|
||||||
|
*/
|
||||||
|
String GLASS_PANE_FULL_HEIGHT = "JRootPane.glassPaneFullHeight";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the style of the window title bar.
|
||||||
|
* Besides the default title bar style, you can use a Utility-style title bar,
|
||||||
|
* which is smaller than the default title bar.
|
||||||
|
* <p>
|
||||||
|
* On Windows 10/11, this requires FlatLaf window decorations.
|
||||||
|
* On macOS, Java supports this out of the box.
|
||||||
|
* <p>
|
||||||
|
* Note that this client property must be set before the window becomes displayable.
|
||||||
|
* Otherwise, an {@link IllegalComponentStateException} is thrown.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||||
|
* <strong>Allowed Values</strong>
|
||||||
|
* {@link #WINDOW_STYLE_SMALL}
|
||||||
|
*
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
String WINDOW_STYLE = "Window.style";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The window has Utility-style title bar, which is smaller than default title bar.
|
||||||
|
* <p>
|
||||||
|
* This is the same as using {@link Window#setType}( {@link Window.Type#UTILITY} ).
|
||||||
|
*
|
||||||
|
* @see #WINDOW_STYLE
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
String WINDOW_STYLE_SMALL = "small";
|
||||||
|
|
||||||
|
|
||||||
//---- JScrollBar / JScrollPane -------------------------------------------
|
//---- JScrollBar / JScrollPane -------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -460,6 +721,7 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String SCROLL_PANE_SMOOTH_SCROLLING = "JScrollPane.smoothScrolling";
|
String SCROLL_PANE_SMOOTH_SCROLLING = "JScrollPane.smoothScrolling";
|
||||||
|
|
||||||
|
|
||||||
//---- JSplitPane ---------------------------------------------------------
|
//---- JSplitPane ---------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -494,6 +756,7 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String SPLIT_PANE_EXPANDABLE_SIDE_RIGHT = "right";
|
String SPLIT_PANE_EXPANDABLE_SIDE_RIGHT = "right";
|
||||||
|
|
||||||
|
|
||||||
//---- JTabbedPane --------------------------------------------------------
|
//---- JTabbedPane --------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -759,7 +1022,7 @@ public interface FlatClientProperties
|
|||||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Integer} or {@link java.lang.String}<br>
|
* <strong>Value type</strong> {@link java.lang.Integer} or {@link java.lang.String}<br>
|
||||||
* <strong>Allowed Values</strong>
|
* <strong>Allowed Values</strong>
|
||||||
* {@link SwingConstants#LEADING} (default)
|
* {@link SwingConstants#LEADING} (default),
|
||||||
* {@link SwingConstants#TRAILING},
|
* {@link SwingConstants#TRAILING},
|
||||||
* {@link SwingConstants#CENTER},
|
* {@link SwingConstants#CENTER},
|
||||||
* {@link #TABBED_PANE_ALIGN_LEADING} (default),
|
* {@link #TABBED_PANE_ALIGN_LEADING} (default),
|
||||||
@@ -823,8 +1086,9 @@ public interface FlatClientProperties
|
|||||||
* <strong>Value type</strong> {@link java.lang.String}<br>
|
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||||
* <strong>Allowed Values</strong>
|
* <strong>Allowed Values</strong>
|
||||||
* {@link #TABBED_PANE_TAB_WIDTH_MODE_PREFERRED} (default),
|
* {@link #TABBED_PANE_TAB_WIDTH_MODE_PREFERRED} (default),
|
||||||
* {@link #TABBED_PANE_TAB_WIDTH_MODE_EQUAL} or
|
* {@link #TABBED_PANE_TAB_WIDTH_MODE_EQUAL},
|
||||||
* {@link #TABBED_PANE_TAB_WIDTH_MODE_COMPACT}
|
* {@link #TABBED_PANE_TAB_WIDTH_MODE_COMPACT} or
|
||||||
|
* {@link #TABBED_PANE_TAB_WIDTH_MODE_ICON_ONLY}
|
||||||
*/
|
*/
|
||||||
String TABBED_PANE_TAB_WIDTH_MODE = "JTabbedPane.tabWidthMode";
|
String TABBED_PANE_TAB_WIDTH_MODE = "JTabbedPane.tabWidthMode";
|
||||||
|
|
||||||
@@ -850,6 +1114,14 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String TABBED_PANE_TAB_WIDTH_MODE_COMPACT = "compact";
|
String TABBED_PANE_TAB_WIDTH_MODE_COMPACT = "compact";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All tabs are smaller because they show only the tab icon, but no tab title.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TAB_WIDTH_MODE
|
||||||
|
* @since 3.7
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_WIDTH_MODE_ICON_ONLY = "iconOnly";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies the tab icon placement (relative to tab title).
|
* Specifies the tab icon placement (relative to tab title).
|
||||||
* <p>
|
* <p>
|
||||||
@@ -863,6 +1135,59 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String TABBED_PANE_TAB_ICON_PLACEMENT = "JTabbedPane.tabIconPlacement";
|
String TABBED_PANE_TAB_ICON_PLACEMENT = "JTabbedPane.tabIconPlacement";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the rotation of the tabs (title, icon, etc.).
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Integer} or {@link java.lang.String}<br>
|
||||||
|
* <strong>Allowed Values</strong>
|
||||||
|
* {@link SwingConstants#LEFT},
|
||||||
|
* {@link SwingConstants#RIGHT},
|
||||||
|
* {@link #TABBED_PANE_TAB_ROTATION_NONE} (default),
|
||||||
|
* {@link #TABBED_PANE_TAB_ROTATION_AUTO},
|
||||||
|
* {@link #TABBED_PANE_TAB_ROTATION_LEFT} or
|
||||||
|
* {@link #TABBED_PANE_TAB_ROTATION_RIGHT}
|
||||||
|
*
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_ROTATION = "JTabbedPane.tabRotation";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tabs are not rotated.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TAB_ROTATION
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_ROTATION_NONE = "none";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tabs are rotated depending on tab placement.
|
||||||
|
* <p>
|
||||||
|
* For top and bottom tab placement, the tabs are not rotated.<br>
|
||||||
|
* For left tab placement, the tabs are rotated counter-clockwise.<br>
|
||||||
|
* For right tab placement, the tabs are rotated clockwise.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TAB_ROTATION
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_ROTATION_AUTO = "auto";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tabs are rotated counter-clockwise.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TAB_ROTATION
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_ROTATION_LEFT = "left";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tabs are rotated clockwise.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TAB_ROTATION
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_ROTATION_RIGHT = "right";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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>
|
||||||
@@ -889,17 +1214,21 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String TABBED_PANE_TRAILING_COMPONENT = "JTabbedPane.trailingComponent";
|
String TABBED_PANE_TRAILING_COMPONENT = "JTabbedPane.trailingComponent";
|
||||||
|
|
||||||
|
|
||||||
//---- JTextField ---------------------------------------------------------
|
//---- JTextField ---------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether all text is selected when the text component gains focus.
|
* Specifies whether all text is selected when the text component gains focus.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
|
* <strong>Components</strong> {@link javax.swing.text.JTextComponent} (and subclasses),
|
||||||
|
* {@link javax.swing.JComboBox} (since 3.6) and {@link javax.swing.JSpinner} (since 3.6)<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.String}<br>
|
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||||
* <strong>Allowed Values</strong>
|
* <strong>Allowed Values</strong>
|
||||||
* {@link #SELECT_ALL_ON_FOCUS_POLICY_NEVER},
|
* {@link #SELECT_ALL_ON_FOCUS_POLICY_NEVER},
|
||||||
* {@link #SELECT_ALL_ON_FOCUS_POLICY_ONCE} (default) or
|
* {@link #SELECT_ALL_ON_FOCUS_POLICY_ONCE} (default) or
|
||||||
* {@link #SELECT_ALL_ON_FOCUS_POLICY_ALWAYS}
|
* {@link #SELECT_ALL_ON_FOCUS_POLICY_ALWAYS}
|
||||||
|
*
|
||||||
|
* @see #SELECT_ALL_ON_MOUSE_CLICK
|
||||||
*/
|
*/
|
||||||
String SELECT_ALL_ON_FOCUS_POLICY = "JTextField.selectAllOnFocusPolicy";
|
String SELECT_ALL_ON_FOCUS_POLICY = "JTextField.selectAllOnFocusPolicy";
|
||||||
|
|
||||||
@@ -914,6 +1243,12 @@ public interface FlatClientProperties
|
|||||||
* Select all text when the text component gains focus for the first time
|
* Select all text when the text component gains focus for the first time
|
||||||
* and selection was not modified (is at end of text).
|
* and selection was not modified (is at end of text).
|
||||||
* This is the default.
|
* This is the default.
|
||||||
|
* <p>
|
||||||
|
* <b>Limitations:</b>
|
||||||
|
* For {@link JFormattedTextField} and {@link JSpinner} this behaves
|
||||||
|
* as {@link #SELECT_ALL_ON_FOCUS_POLICY_ALWAYS}.
|
||||||
|
* This is because of special behavior of {@link JFormattedTextField}
|
||||||
|
* that did not allow implementation of {@code "once"}.
|
||||||
*
|
*
|
||||||
* @see #SELECT_ALL_ON_FOCUS_POLICY
|
* @see #SELECT_ALL_ON_FOCUS_POLICY
|
||||||
*/
|
*/
|
||||||
@@ -926,6 +1261,19 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String SELECT_ALL_ON_FOCUS_POLICY_ALWAYS = "always";
|
String SELECT_ALL_ON_FOCUS_POLICY_ALWAYS = "always";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether all text is selected when when clicking with the mouse
|
||||||
|
* into the text field (and if "select all on focus" policy is enabled).
|
||||||
|
* <p>
|
||||||
|
* <strong>Components</strong> {@link javax.swing.text.JTextComponent} (and subclasses),
|
||||||
|
* {@link javax.swing.JComboBox} and {@link javax.swing.JSpinner}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
|
*
|
||||||
|
* @see #SELECT_ALL_ON_FOCUS_POLICY
|
||||||
|
* @since 3.6
|
||||||
|
*/
|
||||||
|
String SELECT_ALL_ON_MOUSE_CLICK = "JTextField.selectAllOnMouseClick";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Placeholder text that is only painted if the text field is empty.
|
* Placeholder text that is only painted if the text field is empty.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -1058,6 +1406,7 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String TEXT_FIELD_CLEAR_CALLBACK = "JTextField.clearCallback";
|
String TEXT_FIELD_CLEAR_CALLBACK = "JTextField.clearCallback";
|
||||||
|
|
||||||
|
|
||||||
//---- JToggleButton ------------------------------------------------------
|
//---- JToggleButton ------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1065,8 +1414,8 @@ public interface FlatClientProperties
|
|||||||
* <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}<br>
|
* <strong>Value type</strong> {@link java.lang.Integer}<br>
|
||||||
* <strong>SupportedValues:</strong>
|
* <strong>Allowed Values</strong>
|
||||||
* {@link SwingConstants#BOTTOM} (default)
|
* {@link SwingConstants#BOTTOM} (default),
|
||||||
* {@link SwingConstants#TOP},
|
* {@link SwingConstants#TOP},
|
||||||
* {@link SwingConstants#LEFT} or
|
* {@link SwingConstants#LEFT} or
|
||||||
* {@link SwingConstants#RIGHT}
|
* {@link SwingConstants#RIGHT}
|
||||||
@@ -1099,16 +1448,27 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground";
|
String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground";
|
||||||
|
|
||||||
|
|
||||||
//---- JTree --------------------------------------------------------------
|
//---- JTree --------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override if a tree shows a wide selection. Default is {@code true}.
|
* Specifies whether tree shows a wide selection. Default is {@code true}.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JTree}<br>
|
* <strong>Component</strong> {@link javax.swing.JTree}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
*/
|
*/
|
||||||
String TREE_WIDE_SELECTION = "JTree.wideSelection";
|
String TREE_WIDE_SELECTION = "JTree.wideSelection";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether tree uses a wide cell renderer. Default is {@code false}.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTree}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
|
*
|
||||||
|
* @since 3.6
|
||||||
|
*/
|
||||||
|
String TREE_WIDE_CELL_RENDERER = "JTree.wideCellRenderer";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether tree item selection is painted. Default is {@code true}.
|
* Specifies whether tree item selection is painted. Default is {@code true}.
|
||||||
* If set to {@code false}, then the tree cell renderer is responsible for painting selection.
|
* If set to {@code false}, then the tree cell renderer is responsible for painting selection.
|
||||||
@@ -1118,6 +1478,45 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String TREE_PAINT_SELECTION = "JTree.paintSelection";
|
String TREE_PAINT_SELECTION = "JTree.paintSelection";
|
||||||
|
|
||||||
|
|
||||||
|
//---- macOS --------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the spacing around the macOS window close/minimize/zoom buttons.
|
||||||
|
* Useful if <a href="https://www.formdev.com/flatlaf/macos/#full_window_content">full window content</a>
|
||||||
|
* is enabled.
|
||||||
|
* <p>
|
||||||
|
* (requires macOS 10.14+ for "medium" spacing and macOS 11+ for "large" spacing, requires Java 17+)
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||||
|
* <strong>Allowed Values</strong>
|
||||||
|
* {@link #MACOS_WINDOW_BUTTONS_SPACING_MEDIUM} or
|
||||||
|
* {@link #MACOS_WINDOW_BUTTONS_SPACING_LARGE} (requires macOS 11+)
|
||||||
|
*
|
||||||
|
* @since 3.4
|
||||||
|
*/
|
||||||
|
String MACOS_WINDOW_BUTTONS_SPACING = "FlatLaf.macOS.windowButtonsSpacing";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add medium spacing around the macOS window close/minimize/zoom buttons.
|
||||||
|
*
|
||||||
|
* @see #MACOS_WINDOW_BUTTONS_SPACING
|
||||||
|
* @since 3.4
|
||||||
|
*/
|
||||||
|
String MACOS_WINDOW_BUTTONS_SPACING_MEDIUM = "medium";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add large spacing around the macOS window close/minimize/zoom buttons.
|
||||||
|
* <p>
|
||||||
|
* (requires macOS 11+; "medium" is used on older systems)
|
||||||
|
*
|
||||||
|
* @see #MACOS_WINDOW_BUTTONS_SPACING
|
||||||
|
* @since 3.4
|
||||||
|
*/
|
||||||
|
String MACOS_WINDOW_BUTTONS_SPACING_LARGE = "large";
|
||||||
|
|
||||||
|
|
||||||
//---- helper methods -----------------------------------------------------
|
//---- helper methods -----------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public abstract class FlatDefaultsAddon
|
|||||||
public InputStream getDefaults( Class<?> lafClass ) {
|
public InputStream getDefaults( Class<?> lafClass ) {
|
||||||
Class<?> addonClass = this.getClass();
|
Class<?> addonClass = this.getClass();
|
||||||
String propertiesName = '/' + addonClass.getPackage().getName().replace( '.', '/' )
|
String propertiesName = '/' + addonClass.getPackage().getName().replace( '.', '/' )
|
||||||
+ '/' + lafClass.getSimpleName() + ".properties";
|
+ '/' + UIDefaultsLoader.simpleClassName( lafClass ) + ".properties";
|
||||||
return addonClass.getResourceAsStream( propertiesName );
|
return addonClass.getResourceAsStream( propertiesName );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,8 @@ class FlatInputMaps
|
|||||||
}
|
}
|
||||||
|
|
||||||
modifyInputMap( defaults, "ComboBox.ancestorInputMap",
|
modifyInputMap( defaults, "ComboBox.ancestorInputMap",
|
||||||
"SPACE", "spacePopup",
|
// Space key still shows popup, but from FlatComboBoxUI.FlatKeySelectionManager
|
||||||
|
// "SPACE", "spacePopup",
|
||||||
|
|
||||||
"UP", mac( "selectPrevious2", "selectPrevious" ),
|
"UP", mac( "selectPrevious2", "selectPrevious" ),
|
||||||
"DOWN", mac( "selectNext2", "selectNext" ),
|
"DOWN", mac( "selectNext2", "selectNext" ),
|
||||||
@@ -70,7 +71,7 @@ class FlatInputMaps
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// join ltr and rtl bindings to fix up/down/etc keys in right-to-left component orientation
|
// join ltr and rtl bindings to fix up/down/etc. keys in right-to-left component orientation
|
||||||
Object[] bindings = (Object[]) defaults.get( "PopupMenu.selectedWindowInputMapBindings" );
|
Object[] bindings = (Object[]) defaults.get( "PopupMenu.selectedWindowInputMapBindings" );
|
||||||
Object[] rtlBindings = (Object[]) defaults.get( "PopupMenu.selectedWindowInputMapBindings.RightToLeft" );
|
Object[] rtlBindings = (Object[]) defaults.get( "PopupMenu.selectedWindowInputMapBindings.RightToLeft" );
|
||||||
if( bindings != null && rtlBindings != null ) {
|
if( bindings != null && rtlBindings != null ) {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import java.awt.Color;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
|
import java.awt.GraphicsEnvironment;
|
||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
import java.awt.RenderingHints;
|
import java.awt.RenderingHints;
|
||||||
import java.awt.Toolkit;
|
import java.awt.Toolkit;
|
||||||
@@ -30,15 +31,13 @@ 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.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -46,6 +45,7 @@ import java.util.MissingResourceException;
|
|||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.ServiceLoader;
|
import java.util.ServiceLoader;
|
||||||
|
import java.util.Set;
|
||||||
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 java.util.function.IntUnaryOperator;
|
||||||
@@ -71,12 +71,14 @@ 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;
|
||||||
import javax.swing.plaf.basic.BasicLookAndFeel;
|
import javax.swing.plaf.basic.BasicLookAndFeel;
|
||||||
|
import javax.swing.plaf.metal.MetalLookAndFeel;
|
||||||
import javax.swing.text.StyleContext;
|
import javax.swing.text.StyleContext;
|
||||||
import javax.swing.text.html.HTMLEditorKit;
|
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.FlatUIUtils;
|
||||||
|
import com.formdev.flatlaf.ui.JavaCompatibility2;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.util.FontUtils;
|
import com.formdev.flatlaf.util.FontUtils;
|
||||||
import com.formdev.flatlaf.util.GrayFilter;
|
import com.formdev.flatlaf.util.GrayFilter;
|
||||||
@@ -100,6 +102,8 @@ public abstract class FlatLaf
|
|||||||
private static Map<String, String> globalExtraDefaults;
|
private static Map<String, String> globalExtraDefaults;
|
||||||
private Map<String, String> extraDefaults;
|
private Map<String, String> extraDefaults;
|
||||||
private static Function<String, Color> systemColorGetter;
|
private static Function<String, Color> systemColorGetter;
|
||||||
|
private static Set<String> uiKeyPlatformPrefixes;
|
||||||
|
private static Set<String> uiKeySpecialPrefixes;
|
||||||
|
|
||||||
private String desktopPropertyName;
|
private String desktopPropertyName;
|
||||||
private String desktopPropertyName2;
|
private String desktopPropertyName2;
|
||||||
@@ -111,6 +115,7 @@ public abstract class FlatLaf
|
|||||||
private PopupFactory oldPopupFactory;
|
private PopupFactory oldPopupFactory;
|
||||||
private MnemonicHandler mnemonicHandler;
|
private MnemonicHandler mnemonicHandler;
|
||||||
private boolean subMenuUsabilityHelperInstalled;
|
private boolean subMenuUsabilityHelperInstalled;
|
||||||
|
private LinuxPopupMenuCanceler linuxPopupMenuCanceler;
|
||||||
|
|
||||||
private Consumer<UIDefaults> postInitialization;
|
private Consumer<UIDefaults> postInitialization;
|
||||||
private List<Function<Object, Object>> uiDefaultsGetters;
|
private List<Function<Object, Object>> uiDefaultsGetters;
|
||||||
@@ -120,6 +125,46 @@ public abstract class FlatLaf
|
|||||||
private static String preferredSemiboldFontFamily;
|
private static String preferredSemiboldFontFamily;
|
||||||
private static String preferredMonospacedFontFamily;
|
private static String preferredMonospacedFontFamily;
|
||||||
|
|
||||||
|
static {
|
||||||
|
// see disableWindowsD3Donscreen() for details
|
||||||
|
// https://github.com/JFormDesigner/FlatLaf/issues/887
|
||||||
|
if( SystemInfo.isWindows &&
|
||||||
|
System.getProperty( "sun.java2d.d3d.onscreen" ) == null &&
|
||||||
|
System.getProperty( "sun.java2d.d3d" ) == null &&
|
||||||
|
System.getProperty( "sun.java2d.noddraw" ) == null )
|
||||||
|
System.setProperty( "sun.java2d.d3d.onscreen", "false" );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable usage of Windows Direct3D (DirectX) onscreen surfaces because this may lead to
|
||||||
|
* repaint issues (ghosting) on some systems (probably depending on graphics card/driver).
|
||||||
|
* Problem occurs usually when a small heavy-weight popup window (menu, combobox, tooltip) is shown.
|
||||||
|
* <p>
|
||||||
|
* Sets system property {@code sun.java2d.d3d.onscreen} to {@code false},
|
||||||
|
* but only if {@code sun.java2d.d3d.onscreen}, {@code sun.java2d.d3d}
|
||||||
|
* and {@code sun.java2d.noddraw} are not yet set.
|
||||||
|
* <p>
|
||||||
|
* <strong>Note</strong>: Must be invoked very early before the graphics environment is created.
|
||||||
|
* <p>
|
||||||
|
* This method is automatically invoked when loading this class,
|
||||||
|
* which is usually before the graphics environment is created.
|
||||||
|
* E.g. when doing {@code FlatLightLaf.setup()} or
|
||||||
|
* {@code UIManager.setLookAndFeel( "com.formdev.flatlaf.FlatLightLaf" )}.
|
||||||
|
* <p>
|
||||||
|
* However, it may be invoked too late if you use some methods from {@link UIManager}
|
||||||
|
* of {@link GraphicsEnvironment} before setting look and feel.
|
||||||
|
* E.g. {@link UIManager#put(Object, Object)}.
|
||||||
|
* In that case invoke this method yourself very early.
|
||||||
|
* <p>
|
||||||
|
* <strong>Tip</strong>: How to find out when the graphics environment is created?
|
||||||
|
* Set a breakpoint at constructor of class {@link GraphicsEnvironment} and look at the stack.
|
||||||
|
*
|
||||||
|
* @since 3.5.2
|
||||||
|
*/
|
||||||
|
public static void disableWindowsD3Donscreen() {
|
||||||
|
// dummy method used to trigger invocation of "static {...}" block
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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)}.
|
||||||
@@ -182,17 +227,11 @@ public abstract class FlatLaf
|
|||||||
* 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/11 (see exception below)
|
* This method returns {@code true} on Windows 10/11 (see exception below)
|
||||||
* and on Linux, {@code false} otherwise.
|
* and on Linux, otherwise returns {@code false}.
|
||||||
|
* <p>
|
||||||
|
* Returns also {@code false} on Windows 10/11 if
|
||||||
|
* FlatLaf native window border support is available (requires Windows 10/11).
|
||||||
* <p>
|
* <p>
|
||||||
* Returns also {@code false} on Windows 10/11 if:
|
|
||||||
* <ul>
|
|
||||||
* <li>FlatLaf native window border support is available (requires Windows 10/11)</li>
|
|
||||||
* <li>running in
|
|
||||||
* <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>)
|
|
||||||
* and JBR supports custom window decorations
|
|
||||||
* </li>
|
|
||||||
* </ul>
|
|
||||||
* In these 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.
|
||||||
@@ -271,6 +310,10 @@ public abstract class FlatLaf
|
|||||||
// install submenu usability helper
|
// install submenu usability helper
|
||||||
subMenuUsabilityHelperInstalled = SubMenuUsabilityHelper.install();
|
subMenuUsabilityHelperInstalled = SubMenuUsabilityHelper.install();
|
||||||
|
|
||||||
|
// install Linux popup menu canceler
|
||||||
|
if( SystemInfo.isLinux )
|
||||||
|
linuxPopupMenuCanceler = new LinuxPopupMenuCanceler();
|
||||||
|
|
||||||
// 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:
|
||||||
@@ -325,6 +368,22 @@ public abstract class FlatLaf
|
|||||||
String.format( "a, address { color: #%06x; }", linkColor.getRGB() & 0xffffff ) );
|
String.format( "a, address { color: #%06x; }", linkColor.getRGB() & 0xffffff ) );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Initialize UIScale user scale factor immediately after FlatLaf was activated,
|
||||||
|
// which is necessary to ensure that UIScale.setZoomFactor(float)
|
||||||
|
// scales FlatLaf defaultDont correctly even if UIScale.scale() was not yet used.
|
||||||
|
// In other words: Without this, UIScale.setZoomFactor(float) would
|
||||||
|
// not work correctly if invoked between FlatLaf.setup() and crating UI.
|
||||||
|
PropertyChangeListener listener = new PropertyChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
if( "lookAndFeel".equals( e.getPropertyName() ) ) {
|
||||||
|
UIManager.removePropertyChangeListener( this );
|
||||||
|
UIScale.getUserScaleFactor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
UIManager.addPropertyChangeListener( listener );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -363,6 +422,12 @@ public abstract class FlatLaf
|
|||||||
subMenuUsabilityHelperInstalled = false;
|
subMenuUsabilityHelperInstalled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// uninstall Linux popup menu canceler
|
||||||
|
if( linuxPopupMenuCanceler != null ) {
|
||||||
|
linuxPopupMenuCanceler.uninstall();
|
||||||
|
linuxPopupMenuCanceler = null;
|
||||||
|
}
|
||||||
|
|
||||||
// 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;
|
||||||
@@ -391,7 +456,7 @@ public abstract class FlatLaf
|
|||||||
Method m = UIManager.class.getMethod( "createLookAndFeel", String.class );
|
Method m = UIManager.class.getMethod( "createLookAndFeel", String.class );
|
||||||
aquaLaf = (BasicLookAndFeel) m.invoke( null, "Mac OS X" );
|
aquaLaf = (BasicLookAndFeel) m.invoke( null, "Mac OS X" );
|
||||||
} else
|
} else
|
||||||
aquaLaf = (BasicLookAndFeel) Class.forName( aquaLafClassName ).getDeclaredConstructor().newInstance();
|
aquaLaf = Class.forName( aquaLafClassName ).asSubclass( BasicLookAndFeel.class ).getDeclaredConstructor().newInstance();
|
||||||
} catch( Exception ex ) {
|
} catch( Exception ex ) {
|
||||||
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to initialize Aqua look and feel '" + aquaLafClassName + "'.", ex );
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to initialize Aqua look and feel '" + aquaLafClassName + "'.", ex );
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
@@ -476,10 +541,10 @@ public abstract class FlatLaf
|
|||||||
|
|
||||||
// load defaults from properties
|
// load defaults from properties
|
||||||
List<Class<?>> lafClassesForDefaultsLoading = getLafClassesForDefaultsLoading();
|
List<Class<?>> lafClassesForDefaultsLoading = getLafClassesForDefaultsLoading();
|
||||||
if( lafClassesForDefaultsLoading != null )
|
if( lafClassesForDefaultsLoading == null )
|
||||||
UIDefaultsLoader.loadDefaultsFromProperties( lafClassesForDefaultsLoading, addons, getAdditionalDefaults(), isDark(), defaults );
|
lafClassesForDefaultsLoading = UIDefaultsLoader.getLafClassesForDefaultsLoading( getClass() );
|
||||||
else
|
UIDefaultsLoader.loadDefaultsFromProperties( lafClassesForDefaultsLoading, addons,
|
||||||
UIDefaultsLoader.loadDefaultsFromProperties( getClass(), addons, getAdditionalDefaults(), isDark(), defaults );
|
this::applyAdditionalProperties, getAdditionalDefaults(), isDark(), defaults );
|
||||||
|
|
||||||
// setup default font after loading defaults from properties
|
// setup default font after loading defaults from properties
|
||||||
// to allow defining "defaultFont" in properties
|
// to allow defining "defaultFont" in properties
|
||||||
@@ -496,9 +561,6 @@ public abstract class FlatLaf
|
|||||||
// initialize text antialiasing
|
// initialize text antialiasing
|
||||||
putAATextInfo( defaults );
|
putAATextInfo( defaults );
|
||||||
|
|
||||||
// apply additional defaults (e.g. from IntelliJ themes)
|
|
||||||
applyAdditionalDefaults( defaults );
|
|
||||||
|
|
||||||
// allow addons modifying UI defaults
|
// allow addons modifying UI defaults
|
||||||
for( FlatDefaultsAddon addon : addons )
|
for( FlatDefaultsAddon addon : addons )
|
||||||
addon.afterDefaultsLoading( this, defaults );
|
addon.afterDefaultsLoading( this, defaults );
|
||||||
@@ -508,6 +570,9 @@ public abstract class FlatLaf
|
|||||||
return UIScale.getUserScaleFactor();
|
return UIScale.getUserScaleFactor();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
// add lazy UI delegate class loading (if necessary)
|
||||||
|
addLazyUIdelegateClassLoading( defaults );
|
||||||
|
|
||||||
if( postInitialization != null ) {
|
if( postInitialization != null ) {
|
||||||
postInitialization.accept( defaults );
|
postInitialization.accept( defaults );
|
||||||
postInitialization = null;
|
postInitialization = null;
|
||||||
@@ -516,7 +581,8 @@ public abstract class FlatLaf
|
|||||||
return defaults;
|
return defaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
void applyAdditionalDefaults( UIDefaults defaults ) {
|
// apply additional properties (e.g. from IntelliJ themes)
|
||||||
|
void applyAdditionalProperties( Properties properties ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<Class<?>> getLafClassesForDefaultsLoading() {
|
protected List<Class<?>> getLafClassesForDefaultsLoading() {
|
||||||
@@ -543,7 +609,7 @@ public abstract class FlatLaf
|
|||||||
// which can happen in applications that use some plugin system
|
// which can happen in applications that use some plugin system
|
||||||
// and load FlatLaf in a plugin that uses its own classloader.
|
// and load FlatLaf in a plugin that uses its own classloader.
|
||||||
// (e.g. Apache NetBeans)
|
// (e.g. Apache NetBeans)
|
||||||
if( defaults.get( "FileChooser.fileNameHeaderText" ) != null )
|
if( defaults.get( "TabbedPane.moreTabsButtonToolTipText" ) != null )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// load FlatLaf resource bundle and add content to defaults
|
// load FlatLaf resource bundle and add content to defaults
|
||||||
@@ -581,10 +647,13 @@ public abstract class FlatLaf
|
|||||||
Object activeFont = new ActiveFont( null, null, -1, 0, 0, 0, 0 );
|
Object activeFont = new ActiveFont( null, null, -1, 0, 0, 0, 0 );
|
||||||
|
|
||||||
// override fonts
|
// override fonts
|
||||||
|
List<String> fontKeys = new ArrayList<>( 50 );
|
||||||
for( Object key : defaults.keySet() ) {
|
for( Object key : defaults.keySet() ) {
|
||||||
if( key instanceof String && (((String)key).endsWith( ".font" ) || ((String)key).endsWith( "Font" )) )
|
if( key instanceof String && (((String)key).endsWith( ".font" ) || ((String)key).endsWith( "Font" )) )
|
||||||
defaults.put( key, activeFont );
|
fontKeys.add( (String) key );
|
||||||
}
|
}
|
||||||
|
for( String key : fontKeys )
|
||||||
|
defaults.put( key, activeFont );
|
||||||
|
|
||||||
// add fonts that are not set in BasicLookAndFeel
|
// add fonts that are not set in BasicLookAndFeel
|
||||||
defaults.put( "RootPane.font", activeFont );
|
defaults.put( "RootPane.font", activeFont );
|
||||||
@@ -654,11 +723,22 @@ public abstract class FlatLaf
|
|||||||
uiFont = ((ActiveFont)defaultFont).derive( baseFont, fontSize -> {
|
uiFont = ((ActiveFont)defaultFont).derive( baseFont, fontSize -> {
|
||||||
return Math.round( fontSize * UIScale.computeFontScaleFactor( baseFont ) );
|
return Math.round( fontSize * UIScale.computeFontScaleFactor( baseFont ) );
|
||||||
} );
|
} );
|
||||||
}
|
} else if( defaultFont instanceof LazyValue )
|
||||||
|
uiFont = ActiveFont.toUIResource( (Font) ((LazyValue)defaultFont).createValue( defaults ) );
|
||||||
|
|
||||||
// increase font size if system property "flatlaf.uiScale" is set
|
// increase font size if system property "flatlaf.uiScale" is set
|
||||||
uiFont = UIScale.applyCustomScaleFactor( uiFont );
|
uiFont = UIScale.applyCustomScaleFactor( uiFont );
|
||||||
|
|
||||||
|
// apply zoom factor to font size
|
||||||
|
float zoomFactor = UIScale.getZoomFactor();
|
||||||
|
if( zoomFactor != 1 ) {
|
||||||
|
// see also UIScale.setZoomFactor()
|
||||||
|
int unzoomedFontSize = uiFont.getSize();
|
||||||
|
defaults.put( "defaultFont.unzoomedSize", unzoomedFontSize );
|
||||||
|
int newFontSize = Math.max( Math.round( unzoomedFontSize * zoomFactor ), 1 );
|
||||||
|
uiFont = new FontUIResource( uiFont.deriveFont( (float) newFontSize ) );
|
||||||
|
}
|
||||||
|
|
||||||
// set default font
|
// set default font
|
||||||
defaults.put( "defaultFont", uiFont );
|
defaults.put( "defaultFont", uiFont );
|
||||||
}
|
}
|
||||||
@@ -702,6 +782,53 @@ public abstract class FlatLaf
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle UI delegate classes if running in special application where multiple class loaders are involved.
|
||||||
|
* E.g. in Eclipse plugin or in LibreOffice extension.
|
||||||
|
* <p>
|
||||||
|
* Problem: Swing runs in Java's system classloader and FlatLaf is loaded in plugin classloader.
|
||||||
|
* When Swing tries to load UI delegate class in {@link UIDefaults#getUIClass(String, ClassLoader)},
|
||||||
|
* invoked from {@link UIDefaults#getUI(JComponent)}, it uses the component's classloader,
|
||||||
|
* which is Java's system classloader for core Swing components,
|
||||||
|
* and can not find FlatLaf UI delegates.
|
||||||
|
* <p>
|
||||||
|
* Solution: Add lazy values for UI delegate class names.
|
||||||
|
* Those lazy values use FlatLaf classloader to load UI delegate class.
|
||||||
|
* This is similar to what {@link UIDefaults#getUIClass(String, ClassLoader)} does.
|
||||||
|
* <p>
|
||||||
|
* Not using {@code defaults.put( "ClassLoader", FlatLaf.class.getClassLoader() )},
|
||||||
|
* which would work for FlatLaf UI delegates, but it would break custom
|
||||||
|
* UI delegates used in other classloaders.
|
||||||
|
*/
|
||||||
|
private static void addLazyUIdelegateClassLoading( UIDefaults defaults ) {
|
||||||
|
if( FlatLaf.class.getClassLoader() == ClassLoader.getSystemClassLoader() )
|
||||||
|
return; // not necessary
|
||||||
|
|
||||||
|
Map<String, LazyValue> map = new HashMap<>();
|
||||||
|
for( Map.Entry<Object, Object> e : defaults.entrySet() ) {
|
||||||
|
Object key = e.getKey();
|
||||||
|
Object value = e.getValue();
|
||||||
|
if( key instanceof String && ((String)key).endsWith( "UI" ) &&
|
||||||
|
value instanceof String && !defaults.containsKey( value ) )
|
||||||
|
{
|
||||||
|
String className = (String) value;
|
||||||
|
map.put( className, (LazyValue) t -> {
|
||||||
|
try {
|
||||||
|
Class<?> uiClass = FlatLaf.class.getClassLoader().loadClass( className );
|
||||||
|
if( ComponentUI.class.isAssignableFrom( uiClass ) )
|
||||||
|
return uiClass;
|
||||||
|
} catch( ClassNotFoundException ex ) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
// let UIDefaults.getUIClass() try to load UI delegate class
|
||||||
|
return null;
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defaults.putAll( map );
|
||||||
|
}
|
||||||
|
|
||||||
private void putAATextInfo( UIDefaults defaults ) {
|
private void putAATextInfo( UIDefaults defaults ) {
|
||||||
if ( SystemInfo.isMacOS && SystemInfo.isJetBrainsJVM ) {
|
if ( SystemInfo.isMacOS && SystemInfo.isJetBrainsJVM ) {
|
||||||
// The awt.font.desktophints property suggests sub-pixel anti-aliasing
|
// The awt.font.desktophints property suggests sub-pixel anti-aliasing
|
||||||
@@ -811,8 +938,7 @@ public abstract class FlatLaf
|
|||||||
* <p>
|
* <p>
|
||||||
* Invoke this method before setting the look and feel.
|
* Invoke this method before setting the look and feel.
|
||||||
* <p>
|
* <p>
|
||||||
* If using Java modules, the package must be opened in {@code module-info.java}.
|
* If using Java modules, it is not necessary to open the package 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")
|
||||||
*/
|
*/
|
||||||
@@ -859,9 +985,9 @@ public abstract class FlatLaf
|
|||||||
* <p>
|
* <p>
|
||||||
* See {@link #registerCustomDefaultsSource(String)} for details.
|
* See {@link #registerCustomDefaultsSource(String)} for details.
|
||||||
* <p>
|
* <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/" ) )}.
|
* E.g. {@code FlatLaf.registerCustomDefaultsSource( MyApp.class.getResource( "/com/myapp/themes/" ) )}.
|
||||||
|
* <p>
|
||||||
|
* If using Java modules, it is not necessary to open the package in {@code module-info.java}.
|
||||||
*
|
*
|
||||||
* @param packageUrl a package URL
|
* @param packageUrl a package URL
|
||||||
* @since 2
|
* @since 2
|
||||||
@@ -1026,6 +1152,92 @@ public abstract class FlatLaf
|
|||||||
FlatLaf.systemColorGetter = systemColorGetter;
|
FlatLaf.systemColorGetter = systemColorGetter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns UI key prefix, used in FlatLaf properties files, for light or dark themes.
|
||||||
|
* Return value is either {@code [light]} or {@code [dark]}.
|
||||||
|
*
|
||||||
|
* @since 3.6
|
||||||
|
*/
|
||||||
|
public static String getUIKeyLightOrDarkPrefix( boolean dark ) {
|
||||||
|
return dark ? "[dark]" : "[light]";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns set of UI key prefixes, used in FlatLaf properties files, for current platform.
|
||||||
|
* If UI keys in properties files start with a prefix (e.g. {@code [someprefix]Button.background}),
|
||||||
|
* then they are only used if that prefix is contained in this set
|
||||||
|
* (or is one of {@code [light]} or {@code [dark]} depending on current theme).
|
||||||
|
* <p>
|
||||||
|
* By default, the set contains one or more of following prefixes:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code [win]} on Windows
|
||||||
|
* <li>{@code [mac]} on macOS
|
||||||
|
* <li>{@code [linux]} on Linux
|
||||||
|
* <li>{@code [unknown]} on other platforms
|
||||||
|
* <li>{@code [gnome]} on Linux with GNOME desktop environment
|
||||||
|
* <li>{@code [kde]} on Linux with KDE desktop environment
|
||||||
|
* <li>on Linux, the value of the environment variable {@code XDG_CURRENT_DESKTOP},
|
||||||
|
* split at colons and converted to lower case (e.g. if value of {@code XDG_CURRENT_DESKTOP}
|
||||||
|
* is {@code ubuntu:GNOME}, then {@code [ubuntu]} and {@code [gnome]})
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* You can add own prefixes to the set.
|
||||||
|
* The prefixes must start with '[' and end with ']' characters, otherwise they will be ignored.
|
||||||
|
*
|
||||||
|
* @since 3.6
|
||||||
|
*/
|
||||||
|
public static Set<String> getUIKeyPlatformPrefixes() {
|
||||||
|
if( uiKeyPlatformPrefixes == null ) {
|
||||||
|
uiKeyPlatformPrefixes = new HashSet<>();
|
||||||
|
uiKeyPlatformPrefixes.add(
|
||||||
|
SystemInfo.isWindows ? "[win]" :
|
||||||
|
SystemInfo.isMacOS ? "[mac]" :
|
||||||
|
SystemInfo.isLinux ? "[linux]" : "[unknown]" );
|
||||||
|
|
||||||
|
// Linux
|
||||||
|
if( SystemInfo.isLinux ) {
|
||||||
|
if( SystemInfo.isGNOME )
|
||||||
|
uiKeyPlatformPrefixes.add( "[gnome]" );
|
||||||
|
else if( SystemInfo.isKDE )
|
||||||
|
uiKeyPlatformPrefixes.add( "[kde]" );
|
||||||
|
|
||||||
|
// add values from XDG_CURRENT_DESKTOP for other desktops
|
||||||
|
String desktop = System.getenv( "XDG_CURRENT_DESKTOP" );
|
||||||
|
if( desktop != null ) {
|
||||||
|
// XDG_CURRENT_DESKTOP is a colon-separated list of strings
|
||||||
|
// https://specifications.freedesktop.org/desktop-entry-spec/latest/recognized-keys.html#key-onlyshowin
|
||||||
|
// e.g. "ubuntu:GNOME" on Ubuntu 24.10 or "GNOME-Classic:GNOME" on CentOS 7
|
||||||
|
for( String desk : StringUtils.split( desktop.toLowerCase( Locale.ENGLISH ), ':', true, true ) )
|
||||||
|
uiKeyPlatformPrefixes.add( '[' + desk + ']' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uiKeyPlatformPrefixes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns set of special UI key prefixes, used in FlatLaf properties files.
|
||||||
|
* Unlike other prefixes, properties with special prefixes are preserved.
|
||||||
|
* You can access them using `UIManager`. E.g. `UIManager.get( "[someSpecialPrefix]someKey" )`.
|
||||||
|
* <p>
|
||||||
|
* By default, the set contains following special prefixes:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code [style]}
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* You can add own prefixes to the set.
|
||||||
|
* The prefixes must start with '[' and end with ']' characters, otherwise they will be ignored.
|
||||||
|
*
|
||||||
|
* @since 3.6
|
||||||
|
*/
|
||||||
|
public static Set<String> getUIKeySpecialPrefixes() {
|
||||||
|
if( uiKeySpecialPrefixes == null ) {
|
||||||
|
uiKeySpecialPrefixes = new HashSet<>();
|
||||||
|
uiKeySpecialPrefixes.add( "[style]" );
|
||||||
|
}
|
||||||
|
return uiKeySpecialPrefixes;
|
||||||
|
}
|
||||||
|
|
||||||
private static void reSetLookAndFeel() {
|
private static void reSetLookAndFeel() {
|
||||||
EventQueue.invokeLater( () -> {
|
EventQueue.invokeLater( () -> {
|
||||||
LookAndFeel lookAndFeel = UIManager.getLookAndFeel();
|
LookAndFeel lookAndFeel = UIManager.getLookAndFeel();
|
||||||
@@ -1291,8 +1503,8 @@ public abstract class FlatLaf
|
|||||||
* @since 2.5
|
* @since 2.5
|
||||||
*/
|
*/
|
||||||
public static Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
public static Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
StyleableUI ui = getStyleableUI( c );
|
ComponentUI ui = JavaCompatibility2.getUI( c );
|
||||||
return (ui != null) ? ui.getStyleableInfos( c ) : null;
|
return (ui instanceof StyleableUI) ? ((StyleableUI)ui).getStyleableInfos( c ) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1304,41 +1516,10 @@ public abstract class FlatLaf
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings( "unchecked" )
|
@SuppressWarnings( "unchecked" )
|
||||||
public static <T> T getStyleableValue( JComponent c, String key ) {
|
public static <T> T getStyleableValue( JComponent c, String key ) {
|
||||||
StyleableUI ui = getStyleableUI( c );
|
ComponentUI ui = JavaCompatibility2.getUI( c );
|
||||||
return (ui != null) ? (T) ui.getStyleableValue( c, key ) : null;
|
return (ui instanceof StyleableUI) ? (T) ((StyleableUI)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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the preferred font family to be used for (nearly) all fonts; or {@code null}.
|
* Returns the preferred font family to be used for (nearly) all fonts; or {@code null}.
|
||||||
*
|
*
|
||||||
@@ -1428,26 +1609,36 @@ public abstract class FlatLaf
|
|||||||
private class FlatUIDefaults
|
private class FlatUIDefaults
|
||||||
extends UIDefaults
|
extends UIDefaults
|
||||||
{
|
{
|
||||||
|
private UIDefaults metalDefaults;
|
||||||
|
|
||||||
FlatUIDefaults( int initialCapacity, float loadFactor ) {
|
FlatUIDefaults( int initialCapacity, float loadFactor ) {
|
||||||
super( initialCapacity, loadFactor );
|
super( initialCapacity, loadFactor );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object get( Object key ) {
|
public Object get( Object key ) {
|
||||||
Object value = getValue( key );
|
return get( key, null );
|
||||||
return (value != null) ? (value != NULL_VALUE ? value : null) : super.get( key );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object get( Object key, Locale l ) {
|
public Object get( Object key, Locale l ) {
|
||||||
Object value = getValue( key );
|
Object value = getFromUIDefaultsGetters( key );
|
||||||
return (value != null) ? (value != NULL_VALUE ? value : null) : super.get( key, l );
|
if( value != null )
|
||||||
|
return (value != NULL_VALUE) ? value : null;
|
||||||
|
|
||||||
|
value = super.get( key, l );
|
||||||
|
if( value != null )
|
||||||
|
return value;
|
||||||
|
|
||||||
|
// get file chooser texts from Metal
|
||||||
|
return (key instanceof String && ((String)key).startsWith( "FileChooser." ))
|
||||||
|
? getFromMetal( (String) key, l )
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object getValue( Object key ) {
|
private Object getFromUIDefaultsGetters( Object key ) {
|
||||||
// use local variable for getters to avoid potential multi-threading issues
|
// use local variable for getters to avoid potential multi-threading issues
|
||||||
List<Function<Object, Object>> uiDefaultsGetters = FlatLaf.this.uiDefaultsGetters;
|
List<Function<Object, Object>> uiDefaultsGetters = FlatLaf.this.uiDefaultsGetters;
|
||||||
|
|
||||||
if( uiDefaultsGetters == null )
|
if( uiDefaultsGetters == null )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@@ -1459,6 +1650,22 @@ public abstract class FlatLaf
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private synchronized Object getFromMetal( String key, Locale l ) {
|
||||||
|
if( metalDefaults == null ) {
|
||||||
|
metalDefaults = new MetalLookAndFeel() {
|
||||||
|
// avoid unnecessary initialization
|
||||||
|
@Override protected void initClassDefaults( UIDefaults table ) {}
|
||||||
|
@Override protected void initSystemColorDefaults( UIDefaults table ) {}
|
||||||
|
}.getDefaults();
|
||||||
|
|
||||||
|
// empty not needed defaults (to save memory) because we're only interested
|
||||||
|
// in resource bundle strings, which are stored in another internal map
|
||||||
|
metalDefaults.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return metalDefaults.get( key, l );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class ActiveFont ---------------------------------------------------
|
//---- class ActiveFont ---------------------------------------------------
|
||||||
@@ -1541,7 +1748,7 @@ public abstract class FlatLaf
|
|||||||
int newStyle = (style != -1)
|
int newStyle = (style != -1)
|
||||||
? style
|
? style
|
||||||
: (styleChange != 0)
|
: (styleChange != 0)
|
||||||
? baseStyle & ~((styleChange >> 16) & 0xffff) | (styleChange & 0xffff)
|
? (baseStyle & ~((styleChange >> 16) & 0xffff)) | (styleChange & 0xffff)
|
||||||
: baseStyle;
|
: baseStyle;
|
||||||
|
|
||||||
// new size
|
// new size
|
||||||
@@ -1588,7 +1795,7 @@ public abstract class FlatLaf
|
|||||||
return toUIResource( baseFont );
|
return toUIResource( baseFont );
|
||||||
}
|
}
|
||||||
|
|
||||||
private FontUIResource toUIResource( Font font ) {
|
private static FontUIResource toUIResource( Font font ) {
|
||||||
// make sure that font is a UIResource for LaF switching
|
// make sure that font is a UIResource for LaF switching
|
||||||
return (font instanceof FontUIResource)
|
return (font instanceof FontUIResource)
|
||||||
? (FontUIResource) font
|
? (FontUIResource) font
|
||||||
|
|||||||
@@ -20,15 +20,21 @@ import java.io.File;
|
|||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import com.formdev.flatlaf.themes.FlatMacDarkLaf;
|
||||||
|
import com.formdev.flatlaf.themes.FlatMacLightLaf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Flat LaF that is able to load UI defaults from properties passed to the constructor.
|
* A Flat LaF that is able to load UI defaults from properties passed to the constructor.
|
||||||
* <p>
|
* <p>
|
||||||
* Specify the base theme in the properties with {@code @baseTheme=<baseTheme>}.
|
* Specify the base theme in the properties with {@code @baseTheme=<baseTheme>}.
|
||||||
* Allowed values for {@code <baseTheme>} are {@code light} (the default), {@code dark},
|
* Allowed values for {@code <baseTheme>} are {@code light} (the default), {@code dark},
|
||||||
* {@code intellij} or {@code darcula}.
|
* {@code intellij}, {@code darcula}, {@code maclight} or {@code macdark}.
|
||||||
* <p>
|
* <p>
|
||||||
* The properties are applied after loading the base theme and may overwrite base properties.
|
* The properties are applied after loading the base theme and may overwrite base properties.
|
||||||
* All features of FlatLaf properties files are available.
|
* All features of FlatLaf properties files are available.
|
||||||
@@ -59,8 +65,8 @@ public class FlatPropertiesLaf
|
|||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
Properties properties = new Properties();
|
Properties properties = new Properties();
|
||||||
try( InputStream in2 = in ) {
|
try( Reader reader = new InputStreamReader( in, StandardCharsets.UTF_8 )) {
|
||||||
properties.load( in2 );
|
properties.load( reader );
|
||||||
}
|
}
|
||||||
return properties;
|
return properties;
|
||||||
}
|
}
|
||||||
@@ -70,7 +76,8 @@ public class FlatPropertiesLaf
|
|||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
|
|
||||||
baseTheme = properties.getProperty( "@baseTheme", "light" );
|
baseTheme = properties.getProperty( "@baseTheme", "light" );
|
||||||
dark = "dark".equalsIgnoreCase( baseTheme ) || "darcula".equalsIgnoreCase( baseTheme );
|
dark = "dark".equalsIgnoreCase( baseTheme ) || "darcula".equalsIgnoreCase( baseTheme ) ||
|
||||||
|
"macdark".equalsIgnoreCase( baseTheme );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -96,7 +103,7 @@ public class FlatPropertiesLaf
|
|||||||
protected ArrayList<Class<?>> getLafClassesForDefaultsLoading() {
|
protected ArrayList<Class<?>> getLafClassesForDefaultsLoading() {
|
||||||
ArrayList<Class<?>> lafClasses = new ArrayList<>();
|
ArrayList<Class<?>> lafClasses = new ArrayList<>();
|
||||||
lafClasses.add( FlatLaf.class );
|
lafClasses.add( FlatLaf.class );
|
||||||
switch( baseTheme.toLowerCase() ) {
|
switch( baseTheme.toLowerCase( Locale.ENGLISH ) ) {
|
||||||
default:
|
default:
|
||||||
case "light":
|
case "light":
|
||||||
lafClasses.add( FlatLightLaf.class );
|
lafClasses.add( FlatLightLaf.class );
|
||||||
@@ -115,6 +122,16 @@ public class FlatPropertiesLaf
|
|||||||
lafClasses.add( FlatDarkLaf.class );
|
lafClasses.add( FlatDarkLaf.class );
|
||||||
lafClasses.add( FlatDarculaLaf.class );
|
lafClasses.add( FlatDarculaLaf.class );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "maclight":
|
||||||
|
lafClasses.add( FlatLightLaf.class );
|
||||||
|
lafClasses.add( FlatMacLightLaf.class );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "macdark":
|
||||||
|
lafClasses.add( FlatDarkLaf.class );
|
||||||
|
lafClasses.add( FlatMacDarkLaf.class );
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return lafClasses;
|
return lafClasses;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,10 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf;
|
package com.formdev.flatlaf;
|
||||||
|
|
||||||
|
import javax.swing.JFileChooser;
|
||||||
|
import javax.swing.JPopupMenu;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
|
import com.formdev.flatlaf.util.SystemFileChooser;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -82,7 +85,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/11)
|
* (requires Windows 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
|
||||||
@@ -99,11 +102,14 @@ public interface FlatSystemProperties
|
|||||||
* 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/11)
|
* (requires Windows 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 false} (since v2; was {@code true} in v1)
|
* <strong>Default</strong> {@code false} (since v2; was {@code true} in v1)
|
||||||
|
*
|
||||||
|
* @deprecated No longer used since FlatLaf 3.3. Retained for API compatibility.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations";
|
String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -117,7 +123,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/11)
|
* (requires Windows 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
|
||||||
@@ -132,6 +138,37 @@ public interface FlatSystemProperties
|
|||||||
*/
|
*/
|
||||||
String ANIMATION = "flatlaf.animation";
|
String ANIMATION = "flatlaf.animation";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether native rounded popup borders should be used (if supported by operating system).
|
||||||
|
* <p>
|
||||||
|
* (requires Windows 11 or macOS)
|
||||||
|
* <p>
|
||||||
|
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||||
|
* <strong>Default</strong> {@code true}; except in FlatLaf 3.5.x on macOS 14.4+ where it was {@code false}
|
||||||
|
*
|
||||||
|
* @since 3.5.2
|
||||||
|
*/
|
||||||
|
String USE_ROUNDED_POPUP_BORDER = "flatlaf.useRoundedPopupBorder";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Species whether popup windows may be reused without (temporary) hiding them.
|
||||||
|
* E.g. if "moving" a tooltip to follow the mouse pointer, normally it is necessary
|
||||||
|
* to hide the tooltip and show it again at the new location, which causes some
|
||||||
|
* flicker with heavy-weight popup windows that FlatLaf uses on all platforms.
|
||||||
|
* <p>
|
||||||
|
* If {@code true}, hiding popup window is deferred for an event cycle,
|
||||||
|
* which allows reusing still visible popup window and avoids flicker when "moving" the popup.
|
||||||
|
* <p>
|
||||||
|
* Note that {@link JPopupMenu} popup windows (menus and combobox lists) are newer reused.
|
||||||
|
* <p>
|
||||||
|
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||||
|
* <strong>Default</strong> {@code true}
|
||||||
|
*
|
||||||
|
* @since 3.6.2
|
||||||
|
*/
|
||||||
|
String REUSE_VISIBLE_POPUP_WINDOW = "flatlaf.reuseVisiblePopupWindow";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether vertical text position is corrected when UI is scaled on HiDPI screens.
|
* Specifies whether vertical text position is corrected when UI is scaled on HiDPI screens.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -150,29 +187,78 @@ public interface FlatSystemProperties
|
|||||||
* <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 true}
|
||||||
|
*
|
||||||
* @since 2.5
|
* @since 2.5
|
||||||
*/
|
*/
|
||||||
String UPDATE_UI_ON_SYSTEM_FONT_CHANGE = "flatlaf.updateUIOnSystemFontChange";
|
String UPDATE_UI_ON_SYSTEM_FONT_CHANGE = "flatlaf.updateUIOnSystemFontChange";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies a directory in which the native FlatLaf libraries have been extracted.
|
* Specifies whether FlatLaf native library should be used.
|
||||||
|
* <p>
|
||||||
|
* Setting this to {@code false} disables loading native library,
|
||||||
|
* which also disables some features that depend on the native library.
|
||||||
|
* <p>
|
||||||
|
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||||
|
* <strong>Default</strong> {@code true}
|
||||||
|
*
|
||||||
|
* @since 3.2
|
||||||
|
*/
|
||||||
|
String USE_NATIVE_LIBRARY = "flatlaf.useNativeLibrary";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies a directory in which the FlatLaf native libraries are searched for.
|
||||||
* The path can be absolute or relative to current application working directory.
|
* 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.
|
* This can be used to avoid extraction of the native libraries to the temporary directory at runtime.
|
||||||
* <p>
|
* <p>
|
||||||
* If the value is {@code "system"}, then {@link System#loadLibrary(String)} is
|
* If the value is {@code "system"} (supported since FlatLaf 2.6),
|
||||||
* used to load the native library.
|
* then {@link System#loadLibrary(String)} is used to load the native library.
|
||||||
* Searches for the native library in classloader of caller
|
* This searches for the native library in classloader of caller
|
||||||
* (using {@link ClassLoader#findLibrary(String)}) and in paths specified
|
* (using {@link ClassLoader#findLibrary(String)}) and in paths specified
|
||||||
* in system properties {@code sun.boot.library.path} and {@code java.library.path}.
|
* in system properties {@code sun.boot.library.path} and {@code java.library.path}.
|
||||||
* (supported since FlatLaf 2.6)
|
|
||||||
* <p>
|
* <p>
|
||||||
* If the native library can not loaded from the given path (or via {@link System#loadLibrary(String)}),
|
* If the native library can not be loaded from the given path (or via {@link System#loadLibrary(String)}),
|
||||||
* then the embedded native library is extracted to the temporary directory and loaded from there.
|
* then the embedded native library is extracted to the temporary directory and loaded from there.
|
||||||
|
* <p>
|
||||||
|
* The file names of the native libraries must be either:
|
||||||
|
* <ul>
|
||||||
|
* <li>the same as in flatlaf.jar in package 'com/formdev/flatlaf/natives' (required for "system") or
|
||||||
|
* <li>when downloaded from Maven central then as described here:
|
||||||
|
* <a href="https://www.formdev.com/flatlaf/native-libraries/">https://www.formdev.com/flatlaf/native-libraries/</a>
|
||||||
|
* (requires FlatLaf 3.4)
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* <strong>Note</strong>: Since FlatLaf 3.1 it is recommended to download the
|
||||||
|
* FlatLaf native libraries from Maven central and distribute them with your
|
||||||
|
* application in the same directory as flatlaf.jar.
|
||||||
|
* Then it is <strong>not necessary</strong> to set this system property.
|
||||||
|
* See <a href="https://www.formdev.com/flatlaf/native-libraries/">https://www.formdev.com/flatlaf/native-libraries/</a>
|
||||||
|
* for details.
|
||||||
*
|
*
|
||||||
* @since 2
|
* @since 2
|
||||||
*/
|
*/
|
||||||
String NATIVE_LIBRARY_PATH = "flatlaf.nativeLibraryPath";
|
String NATIVE_LIBRARY_PATH = "flatlaf.nativeLibraryPath";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether safe triangle is used to improve usability of submenus.
|
||||||
|
* <p>
|
||||||
|
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||||
|
* <strong>Default</strong> {@code true}
|
||||||
|
*
|
||||||
|
* @since 3.5.1
|
||||||
|
*/
|
||||||
|
String USE_SUB_MENU_SAFE_TRIANGLE = "flatlaf.useSubMenuSafeTriangle";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether {@link SystemFileChooser} uses operating system file dialogs.
|
||||||
|
* If set to {@code false}, the {@link JFileChooser} is used instead.
|
||||||
|
* <p>
|
||||||
|
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||||
|
* <strong>Default</strong> {@code true}
|
||||||
|
*
|
||||||
|
* @since 3.7
|
||||||
|
*/
|
||||||
|
String USE_SYSTEM_FILE_CHOOSER = "flatlaf.useSystemFileChooser";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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}.
|
||||||
|
|||||||
@@ -16,25 +16,24 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf;
|
package com.formdev.flatlaf;
|
||||||
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javax.swing.UIDefaults;
|
|
||||||
import javax.swing.plaf.ColorUIResource;
|
|
||||||
import com.formdev.flatlaf.json.Json;
|
import com.formdev.flatlaf.json.Json;
|
||||||
import com.formdev.flatlaf.json.ParseException;
|
import com.formdev.flatlaf.json.ParseException;
|
||||||
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;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
@@ -59,13 +58,11 @@ public class IntelliJTheme
|
|||||||
public final boolean dark;
|
public final boolean dark;
|
||||||
public final String author;
|
public final String author;
|
||||||
|
|
||||||
private final boolean isMaterialUILite;
|
private final Map<String, String> jsonColors;
|
||||||
|
private final Map<String, Object> jsonUI;
|
||||||
|
private final Map<String, Object> jsonIcons;
|
||||||
|
|
||||||
private final Map<String, String> colors;
|
private Map<String, String> namedColors = Collections.emptyMap();
|
||||||
private final Map<String, Object> ui;
|
|
||||||
private final Map<String, Object> icons;
|
|
||||||
|
|
||||||
private Map<String, ColorUIResource> namedColors = Collections.emptyMap();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a IntelliJ .theme.json file from the given input stream,
|
* Loads a IntelliJ .theme.json file from the given input stream,
|
||||||
@@ -80,7 +77,7 @@ public class IntelliJTheme
|
|||||||
try {
|
try {
|
||||||
return FlatLaf.setup( createLaf( in ) );
|
return FlatLaf.setup( createLaf( in ) );
|
||||||
} catch( Exception ex ) {
|
} catch( Exception ex ) {
|
||||||
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to load IntelliJ theme", ex );
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to load IntelliJ theme", ex );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,179 +131,247 @@ public class IntelliJTheme
|
|||||||
dark = Boolean.parseBoolean( (String) json.get( "dark" ) );
|
dark = Boolean.parseBoolean( (String) json.get( "dark" ) );
|
||||||
author = (String) json.get( "author" );
|
author = (String) json.get( "author" );
|
||||||
|
|
||||||
isMaterialUILite = author.equals( "Mallowigi" );
|
jsonColors = (Map<String, String>) json.get( "colors" );
|
||||||
|
jsonUI = (Map<String, Object>) json.get( "ui" );
|
||||||
colors = (Map<String, String>) json.get( "colors" );
|
jsonIcons = (Map<String, Object>) json.get( "icons" );
|
||||||
ui = (Map<String, Object>) json.get( "ui" );
|
|
||||||
icons = (Map<String, Object>) json.get( "icons" );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyProperties( UIDefaults defaults ) {
|
private void applyProperties( Properties properties ) {
|
||||||
if( ui == null )
|
if( jsonUI == null )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
defaults.put( "Component.isIntelliJTheme", true );
|
put( properties, "Component.isIntelliJTheme", "true" );
|
||||||
|
|
||||||
// enable button shadows
|
// enable button shadows
|
||||||
defaults.put( "Button.paintShadow", true );
|
put( properties, "Button.paintShadow", "true" );
|
||||||
defaults.put( "Button.shadowWidth", dark ? 2 : 1 );
|
put( properties, "Button.shadowWidth", dark ? "2" : "1" );
|
||||||
|
|
||||||
Map<Object, Object> themeSpecificDefaults = removeThemeSpecificDefaults( defaults );
|
Map<String, String> themeSpecificProps = removeThemeSpecificProps( properties );
|
||||||
|
Set<String> jsonUIKeys = new HashSet<>();
|
||||||
|
|
||||||
loadNamedColors( defaults );
|
// Json node "colors"
|
||||||
|
loadNamedColors( properties, jsonUIKeys );
|
||||||
|
|
||||||
// convert Json "ui" structure to UI defaults
|
// convert Json "ui" structure to UI properties
|
||||||
ArrayList<Object> defaultsKeysCache = new ArrayList<>();
|
for( Map.Entry<String, Object> e : jsonUI.entrySet() )
|
||||||
Set<String> uiKeys = new HashSet<>();
|
apply( e.getKey(), e.getValue(), properties, jsonUIKeys );
|
||||||
for( Map.Entry<String, Object> e : ui.entrySet() )
|
|
||||||
apply( e.getKey(), e.getValue(), defaults, defaultsKeysCache, uiKeys );
|
|
||||||
|
|
||||||
applyColorPalette( defaults );
|
// set FlatLaf variables
|
||||||
applyCheckBoxColors( defaults );
|
copyIfSetInJson( properties, jsonUIKeys, "@background", "Panel.background", "*.background" );
|
||||||
|
copyIfSetInJson( properties, jsonUIKeys, "@foreground", "CheckBox.foreground", "*.foreground" );
|
||||||
|
copyIfSetInJson( properties, jsonUIKeys, "@accentBaseColor",
|
||||||
|
"ColorPalette.accent", // Material UI Lite, Hiberbee
|
||||||
|
"ColorPalette.accentColor", // Dracula, One Dark
|
||||||
|
"ProgressBar.foreground",
|
||||||
|
"*.selectionBackground" );
|
||||||
|
copyIfSetInJson( properties, jsonUIKeys, "@accentUnderlineColor", "*.underlineColor", "TabbedPane.underlineColor" );
|
||||||
|
copyIfSetInJson( properties, jsonUIKeys, "@selectionBackground", "*.selectionBackground" );
|
||||||
|
copyIfSetInJson( properties, jsonUIKeys, "@selectionForeground", "*.selectionForeground" );
|
||||||
|
copyIfSetInJson( properties, jsonUIKeys, "@selectionInactiveBackground", "*.selectionInactiveBackground" );
|
||||||
|
copyIfSetInJson( properties, jsonUIKeys, "@selectionInactiveForeground", "*.selectionInactiveForeground" );
|
||||||
|
|
||||||
|
// Json node "icons/ColorPalette"
|
||||||
|
applyIconsColorPalette( properties );
|
||||||
|
|
||||||
|
// apply "CheckBox.icon." colors
|
||||||
|
applyCheckBoxColors( properties );
|
||||||
|
|
||||||
// copy values
|
// copy values
|
||||||
for( Map.Entry<String, String> e : uiKeyCopying.entrySet() ) {
|
for( Map.Entry<String, String> e : uiKeyCopying.entrySet() ) {
|
||||||
Object value = defaults.get( e.getValue() );
|
Object value = properties.get( e.getValue() );
|
||||||
if( value != null )
|
if( value != null )
|
||||||
defaults.put( e.getKey(), value );
|
put( properties, 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" );
|
put( properties, "Button.disabledBackground", "@disabledBackground" );
|
||||||
defaults.put( "Button.disabledBackground", panelBackground );
|
put( properties, "ToggleButton.disabledBackground", "@disabledBackground" );
|
||||||
defaults.put( "ToggleButton.disabledBackground", panelBackground );
|
|
||||||
|
|
||||||
// fix Button borders
|
// fix Button
|
||||||
copyIfNotSet( defaults, "Button.focusedBorderColor", "Component.focusedBorderColor", uiKeys );
|
fixStartEnd( properties, jsonUIKeys, "Button.startBackground", "Button.endBackground", "Button.background" );
|
||||||
defaults.put( "Button.hoverBorderColor", defaults.get( "Button.focusedBorderColor" ) );
|
fixStartEnd( properties, jsonUIKeys, "Button.startBorderColor", "Button.endBorderColor", "Button.borderColor" );
|
||||||
defaults.put( "HelpButton.hoverBorderColor", defaults.get( "Button.focusedBorderColor" ) );
|
fixStartEnd( properties, jsonUIKeys, "Button.default.startBackground", "Button.default.endBackground", "Button.default.background" );
|
||||||
|
fixStartEnd( properties, jsonUIKeys, "Button.default.startBorderColor", "Button.default.endBorderColor", "Button.default.borderColor" );
|
||||||
// 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 helpButtonBorderColor = defaults.get( "Button.startBorderColor" );
|
|
||||||
if( helpButtonBackground == null )
|
|
||||||
helpButtonBackground = defaults.get( "Button.background" );
|
|
||||||
if( helpButtonBorderColor == null )
|
|
||||||
helpButtonBorderColor = defaults.get( "Button.borderColor" );
|
|
||||||
defaults.put( "HelpButton.background", helpButtonBackground );
|
|
||||||
defaults.put( "HelpButton.borderColor", helpButtonBorderColor );
|
|
||||||
defaults.put( "HelpButton.disabledBackground", panelBackground );
|
|
||||||
defaults.put( "HelpButton.disabledBorderColor", defaults.get( "Button.disabledBorderColor" ) );
|
|
||||||
defaults.put( "HelpButton.focusedBorderColor", defaults.get( "Button.focusedBorderColor" ) );
|
|
||||||
defaults.put( "HelpButton.focusedBackground", defaults.get( "Button.focusedBackground" ) );
|
|
||||||
|
|
||||||
// IDEA uses TextField.background for editable ComboBox and Spinner
|
// IDEA uses TextField.background for editable ComboBox and Spinner
|
||||||
defaults.put( "ComboBox.editableBackground", defaults.get( "TextField.background" ) );
|
Object textFieldBackground = get( properties, themeSpecificProps, "TextField.background" );
|
||||||
defaults.put( "Spinner.background", defaults.get( "TextField.background" ) );
|
put( properties, "ComboBox.editableBackground", textFieldBackground );
|
||||||
|
put( properties, "Spinner.background", textFieldBackground );
|
||||||
// Spinner arrow button always has same colors as ComboBox arrow button
|
|
||||||
defaults.put( "Spinner.buttonBackground", defaults.get( "ComboBox.buttonEditableBackground" ) );
|
|
||||||
defaults.put( "Spinner.buttonArrowColor", defaults.get( "ComboBox.buttonArrowColor" ) );
|
|
||||||
defaults.put( "Spinner.buttonDisabledArrowColor", defaults.get( "ComboBox.buttonDisabledArrowColor" ) );
|
|
||||||
|
|
||||||
// some themes specify colors for TextField.background, but forget to specify it for other components
|
// some themes specify colors for TextField.background, but forget to specify it for other components
|
||||||
// (probably because those components are not used in IntelliJ)
|
// (probably because those components are not used in IntelliJ IDEA)
|
||||||
if( uiKeys.contains( "TextField.background" ) ) {
|
putAll( properties, textFieldBackground,
|
||||||
Object textFieldBackground = defaults.get( "TextField.background" );
|
"EditorPane.background",
|
||||||
if( !uiKeys.contains( "FormattedTextField.background" ) )
|
"FormattedTextField.background",
|
||||||
defaults.put( "FormattedTextField.background", textFieldBackground );
|
"PasswordField.background",
|
||||||
if( !uiKeys.contains( "PasswordField.background" ) )
|
"TextArea.background",
|
||||||
defaults.put( "PasswordField.background", textFieldBackground );
|
"TextPane.background"
|
||||||
if( !uiKeys.contains( "EditorPane.background" ) )
|
);
|
||||||
defaults.put( "EditorPane.background", textFieldBackground );
|
putAll( properties, get( properties, themeSpecificProps, "TextField.selectionBackground" ),
|
||||||
if( !uiKeys.contains( "TextArea.background" ) )
|
"EditorPane.selectionBackground",
|
||||||
defaults.put( "TextArea.background", textFieldBackground );
|
"FormattedTextField.selectionBackground",
|
||||||
if( !uiKeys.contains( "TextPane.background" ) )
|
"PasswordField.selectionBackground",
|
||||||
defaults.put( "TextPane.background", textFieldBackground );
|
"TextArea.selectionBackground",
|
||||||
if( !uiKeys.contains( "Spinner.background" ) )
|
"TextPane.selectionBackground"
|
||||||
defaults.put( "Spinner.background", textFieldBackground );
|
);
|
||||||
}
|
putAll( properties, get( properties, themeSpecificProps, "TextField.selectionForeground" ),
|
||||||
|
"EditorPane.selectionForeground",
|
||||||
|
"FormattedTextField.selectionForeground",
|
||||||
|
"PasswordField.selectionForeground",
|
||||||
|
"TextArea.selectionForeground",
|
||||||
|
"TextPane.selectionForeground"
|
||||||
|
);
|
||||||
|
|
||||||
// fix ToggleButton
|
// fix disabled and not-editable backgrounds for text components, combobox and spinner
|
||||||
if( !uiKeys.contains( "ToggleButton.startBackground" ) && !uiKeys.contains( "*.startBackground" ) )
|
// (IntelliJ IDEA does not use those colors; instead it used background color of parent)
|
||||||
defaults.put( "ToggleButton.startBackground", defaults.get( "Button.startBackground" ) );
|
putAll( properties, "@disabledBackground",
|
||||||
if( !uiKeys.contains( "ToggleButton.endBackground" ) && !uiKeys.contains( "*.endBackground" ) )
|
"ComboBox.disabledBackground",
|
||||||
defaults.put( "ToggleButton.endBackground", defaults.get( "Button.endBackground" ) );
|
"EditorPane.disabledBackground", "EditorPane.inactiveBackground",
|
||||||
if( !uiKeys.contains( "ToggleButton.foreground" ) && uiKeys.contains( "Button.foreground" ) )
|
"FormattedTextField.disabledBackground", "FormattedTextField.inactiveBackground",
|
||||||
defaults.put( "ToggleButton.foreground", defaults.get( "Button.foreground" ) );
|
"PasswordField.disabledBackground", "PasswordField.inactiveBackground",
|
||||||
|
"Spinner.disabledBackground",
|
||||||
|
"TextArea.disabledBackground", "TextArea.inactiveBackground",
|
||||||
|
"TextField.disabledBackground", "TextField.inactiveBackground",
|
||||||
|
"TextPane.disabledBackground", "TextPane.inactiveBackground"
|
||||||
|
);
|
||||||
|
|
||||||
// fix DesktopPane background (use Panel.background and make it 5% darker/lighter)
|
// fix DesktopPane background (use Panel.background and make it 5% darker/lighter)
|
||||||
Color desktopBackgroundBase = defaults.getColor( "Panel.background" );
|
put( properties, "Desktop.background", dark ? "lighten($Panel.background,5%)" : "darken($Panel.background,5%)" );
|
||||||
Color desktopBackground = ColorFunctions.applyFunctions( desktopBackgroundBase,
|
|
||||||
new ColorFunctions.HSLIncreaseDecrease( 2, dark, 5, false, true ) );
|
|
||||||
defaults.put( "Desktop.background", new ColorUIResource( desktopBackground ) );
|
|
||||||
|
|
||||||
// fix List and Table background colors in Material UI Lite themes
|
|
||||||
if( isMaterialUILite ) {
|
|
||||||
defaults.put( "List.background", defaults.get( "Tree.background" ) );
|
|
||||||
defaults.put( "Table.background", defaults.get( "Tree.background" ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// limit tree row height
|
// limit tree row height
|
||||||
int rowHeight = defaults.getInt( "Tree.rowHeight" );
|
String rowHeightStr = (String) properties.get( "Tree.rowHeight" );
|
||||||
|
int rowHeight = (rowHeightStr != null) ? Integer.parseInt( rowHeightStr ) : 0;
|
||||||
if( rowHeight > 22 )
|
if( rowHeight > 22 )
|
||||||
defaults.put( "Tree.rowHeight", 22 );
|
put( properties, "Tree.rowHeight", "22" );
|
||||||
|
|
||||||
// apply theme specific UI defaults at the end to allow overwriting
|
// get (and remove) theme specific wildcard replacements, which override all other properties that end with same suffix
|
||||||
for( Map.Entry<Object, Object> e : themeSpecificDefaults.entrySet() ) {
|
HashMap<String, String> wildcardProps = new HashMap<>();
|
||||||
Object key = e.getKey();
|
Iterator<Map.Entry<String, String>> it = themeSpecificProps.entrySet().iterator();
|
||||||
Object value = e.getValue();
|
while( it.hasNext() ) {
|
||||||
|
Map.Entry<String, String> e = it.next();
|
||||||
|
String key = e.getKey();
|
||||||
|
if( key.startsWith( "*." ) ) {
|
||||||
|
wildcardProps.put( key, e.getValue() );
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// override properties with theme specific wildcard replacements
|
||||||
|
if( !wildcardProps.isEmpty() ) {
|
||||||
|
for( Map.Entry<String, String> e : wildcardProps.entrySet() )
|
||||||
|
applyWildcard( properties, e.getKey(), e.getValue() );
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply theme specific properties at the end to allow overwriting
|
||||||
|
for( Map.Entry<String, String> e : themeSpecificProps.entrySet() ) {
|
||||||
|
String key = e.getKey();
|
||||||
|
String value = e.getValue();
|
||||||
|
|
||||||
// append styles to existing styles
|
// append styles to existing styles
|
||||||
if( key instanceof String && ((String)key).startsWith( "[style]" ) ) {
|
if( key.startsWith( "[style]" ) ) {
|
||||||
Object oldValue = defaults.get( key );
|
String oldValue = (String) properties.get( key );
|
||||||
if( oldValue != null )
|
if( oldValue != null )
|
||||||
value = oldValue + "; " + value;
|
value = oldValue + "; " + value;
|
||||||
}
|
}
|
||||||
|
|
||||||
defaults.put( key, value );
|
put( properties, key, value );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<Object, Object> removeThemeSpecificDefaults( UIDefaults defaults ) {
|
private String get( Properties properties, Map<String, String> themeSpecificProps, String key ) {
|
||||||
// search for theme specific UI defaults keys
|
return themeSpecificProps.getOrDefault( key, (String) properties.get( key ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void put( Properties properties, Object key, Object value ) {
|
||||||
|
if( value != null )
|
||||||
|
properties.put( key, value );
|
||||||
|
else
|
||||||
|
properties.remove( key );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putAll( Properties properties, Object value, String... keys ) {
|
||||||
|
for( String key : keys )
|
||||||
|
put( properties, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyIfSetInJson( Properties properties, Set<String> jsonUIKeys, String destKey, String... srcKeys ) {
|
||||||
|
for( String srcKey : srcKeys ) {
|
||||||
|
if( jsonUIKeys.contains( srcKey ) ) {
|
||||||
|
Object value = properties.get( srcKey );
|
||||||
|
if( value != null ) {
|
||||||
|
put( properties, destKey, value );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fixStartEnd( Properties properties, Set<String> jsonUIKeys, String startKey, String endKey, String key ) {
|
||||||
|
if( jsonUIKeys.contains( startKey ) && jsonUIKeys.contains( endKey ) )
|
||||||
|
put( properties, key, "$" + startKey );
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> removeThemeSpecificProps( Properties properties ) {
|
||||||
|
// search for theme specific properties keys
|
||||||
ArrayList<String> themeSpecificKeys = new ArrayList<>();
|
ArrayList<String> themeSpecificKeys = new ArrayList<>();
|
||||||
for( Object key : defaults.keySet() ) {
|
for( Object key : properties.keySet() ) {
|
||||||
if( key instanceof String && ((String)key).startsWith( "[" ) && !((String)key).startsWith( "[style]" ) )
|
if( ((String)key).startsWith( "{" ) )
|
||||||
themeSpecificKeys.add( (String) key );
|
themeSpecificKeys.add( (String) key );
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove theme specific UI defaults and remember only those for current theme
|
// special prefixes (priority from highest to lowest)
|
||||||
Map<Object, Object> themeSpecificDefaults = new HashMap<>();
|
String currentThemePrefix = '{' + name.replace( ' ', '_' ) + '}';
|
||||||
String currentThemePrefix = '[' + name.replace( ' ', '_' ) + ']';
|
String currentThemeAndAuthorPrefix = '{' + name.replace( ' ', '_' ) + "---" + author.replace( ' ', '_' ) + '}';
|
||||||
String currentThemeAndAuthorPrefix = '[' + name.replace( ' ', '_' ) + "---" + author.replace( ' ', '_' ) + ']';
|
String currentAuthorPrefix = "{author-" + author.replace( ' ', '_' ) + '}';
|
||||||
String currentAuthorPrefix = "[author-" + author.replace( ' ', '_' ) + ']';
|
String lightOrDarkPrefix = dark ? "{*-dark}" : "{*-light}";
|
||||||
String allThemesPrefix = "[*]";
|
String allThemesPrefix = "{*}";
|
||||||
String[] prefixes = { currentThemePrefix, currentThemeAndAuthorPrefix, currentAuthorPrefix, allThemesPrefix };
|
String[] prefixes = { currentThemePrefix, currentThemeAndAuthorPrefix, currentAuthorPrefix, lightOrDarkPrefix, allThemesPrefix };
|
||||||
|
|
||||||
|
// collect values for special prefixes in its own maps
|
||||||
|
@SuppressWarnings( "unchecked" )
|
||||||
|
Map<String, String>[] maps = new Map[prefixes.length];
|
||||||
|
for( int i = 0; i < maps.length; i++ )
|
||||||
|
maps[i] = new HashMap<>();
|
||||||
|
|
||||||
|
// remove theme specific properties and remember only those for current theme
|
||||||
for( String key : themeSpecificKeys ) {
|
for( String key : themeSpecificKeys ) {
|
||||||
Object value = defaults.remove( key );
|
String value = (String) properties.remove( key );
|
||||||
for( String prefix : prefixes ) {
|
for( int i = 0; i < prefixes.length; i++ ) {
|
||||||
|
String prefix = prefixes[i];
|
||||||
if( key.startsWith( prefix ) ) {
|
if( key.startsWith( prefix ) ) {
|
||||||
themeSpecificDefaults.put( key.substring( prefix.length() ), value );
|
maps[i].put( key.substring( prefix.length() ), value );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return themeSpecificDefaults;
|
// copy values into single map (from lowest to highest priority)
|
||||||
|
Map<String, String> themeSpecificProps = new HashMap<>();
|
||||||
|
for( int i = maps.length - 1; i >= 0; i-- )
|
||||||
|
themeSpecificProps.putAll( maps[i] );
|
||||||
|
return themeSpecificProps;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* http://www.jetbrains.org/intellij/sdk/docs/reference_guide/ui_themes/themes_customize.html#defining-named-colors
|
* http://www.jetbrains.org/intellij/sdk/docs/reference_guide/ui_themes/themes_customize.html#defining-named-colors
|
||||||
*/
|
*/
|
||||||
private void loadNamedColors( UIDefaults defaults ) {
|
private void loadNamedColors( Properties properties, Set<String> jsonUIKeys ) {
|
||||||
if( colors == null )
|
if( jsonColors == null )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
namedColors = new HashMap<>();
|
namedColors = new HashMap<>();
|
||||||
|
|
||||||
for( Map.Entry<String, String> e : colors.entrySet() ) {
|
for( Map.Entry<String, String> e : jsonColors.entrySet() ) {
|
||||||
String value = e.getValue();
|
String value = e.getValue();
|
||||||
ColorUIResource color = parseColor( value );
|
if( canParseColor( value ) ) {
|
||||||
if( color != null ) {
|
|
||||||
String key = e.getKey();
|
String key = e.getKey();
|
||||||
namedColors.put( key, color );
|
namedColors.put( key, value );
|
||||||
defaults.put( "ColorPalette." + key, color );
|
|
||||||
|
String uiKey = "ColorPalette." + key;
|
||||||
|
put( properties, uiKey, value );
|
||||||
|
|
||||||
|
// this is only necessary for copyIfSetInJson() (used for accent color)
|
||||||
|
jsonUIKeys.add( uiKey );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -315,7 +380,7 @@ public class IntelliJTheme
|
|||||||
* http://www.jetbrains.org/intellij/sdk/docs/reference_guide/ui_themes/themes_customize.html#custom-ui-control-colors
|
* http://www.jetbrains.org/intellij/sdk/docs/reference_guide/ui_themes/themes_customize.html#custom-ui-control-colors
|
||||||
*/
|
*/
|
||||||
@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, Properties properties, Set<String> jsonUIKeys ) {
|
||||||
if( value instanceof Map ) {
|
if( value instanceof Map ) {
|
||||||
Map<String, Object> map = (Map<String, Object>)value;
|
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" ) ) {
|
if( map.containsKey( "os.default" ) || map.containsKey( "os.windows" ) || map.containsKey( "os.mac" ) || map.containsKey( "os.linux" ) ) {
|
||||||
@@ -323,21 +388,28 @@ public class IntelliJTheme
|
|||||||
: SystemInfo.isMacOS ? "os.mac"
|
: SystemInfo.isMacOS ? "os.mac"
|
||||||
: SystemInfo.isLinux ? "os.linux" : null;
|
: SystemInfo.isLinux ? "os.linux" : null;
|
||||||
if( osKey != null && map.containsKey( osKey ) )
|
if( osKey != null && map.containsKey( osKey ) )
|
||||||
apply( key, map.get( osKey ), defaults, defaultsKeysCache, uiKeys );
|
apply( key, map.get( osKey ), properties, jsonUIKeys );
|
||||||
else if( map.containsKey( "os.default" ) )
|
else if( map.containsKey( "os.default" ) )
|
||||||
apply( key, map.get( "os.default" ), defaults, defaultsKeysCache, uiKeys );
|
apply( key, map.get( "os.default" ), properties, jsonUIKeys );
|
||||||
} else {
|
} else {
|
||||||
for( Map.Entry<String, Object> e : map.entrySet() )
|
for( Map.Entry<String, Object> e : map.entrySet() )
|
||||||
apply( key + '.' + e.getKey(), e.getValue(), defaults, defaultsKeysCache, uiKeys );
|
apply( key + '.' + e.getKey(), e.getValue(), properties, jsonUIKeys );
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if( "".equals( value ) )
|
if( "".equals( value ) )
|
||||||
return; // ignore empty value
|
return; // ignore empty value
|
||||||
|
|
||||||
uiKeys.add( key );
|
// ignore some properties that affect sizes
|
||||||
|
if( key.endsWith( ".border" ) ||
|
||||||
|
key.endsWith( ".rowHeight" ) ||
|
||||||
|
key.equals( "ComboBox.padding" ) ||
|
||||||
|
key.equals( "Spinner.padding" ) ||
|
||||||
|
key.equals( "Tree.leftChildIndent" ) ||
|
||||||
|
key.equals( "Tree.rightChildIndent" ) )
|
||||||
|
return; // ignore
|
||||||
|
|
||||||
// fix ComboBox size and Spinner border in all Material UI Lite themes
|
// ignore icons
|
||||||
if( isMaterialUILite && (key.equals( "ComboBox.padding" ) || key.equals( "Spinner.border" )) )
|
if( key.endsWith( "Icon" ) )
|
||||||
return; // ignore
|
return; // ignore
|
||||||
|
|
||||||
// map keys
|
// map keys
|
||||||
@@ -345,10 +417,31 @@ public class IntelliJTheme
|
|||||||
if( key.isEmpty() )
|
if( key.isEmpty() )
|
||||||
return; // ignore key
|
return; // ignore key
|
||||||
|
|
||||||
String valueStr = value.toString();
|
// exclude properties (1st level)
|
||||||
|
int dot = key.indexOf( '.' );
|
||||||
|
if( dot > 0 && uiKeyExcludesStartsWith.contains( key.substring( 0, dot + 1 ) ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// exclude properties (2st level)
|
||||||
|
int dot2 = (dot > 0) ? key.indexOf( '.', dot + 1 ) : -1;
|
||||||
|
if( dot2 > 0 && uiKeyExcludesStartsWith.contains( key.substring( 0, dot2 + 1 ) ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// exclude properties (contains)
|
||||||
|
for( String s : uiKeyExcludesContains ) {
|
||||||
|
if( key.contains( s ) )
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( uiKeyDoNotOverride.contains( key ) && jsonUIKeys.contains( key ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
jsonUIKeys.add( key );
|
||||||
|
|
||||||
|
String valueStr = value.toString().trim();
|
||||||
|
|
||||||
// map named colors
|
// map named colors
|
||||||
Object uiValue = namedColors.get( valueStr );
|
String uiValue = namedColors.get( valueStr );
|
||||||
|
|
||||||
// parse value
|
// parse value
|
||||||
if( uiValue == null ) {
|
if( uiValue == null ) {
|
||||||
@@ -367,46 +460,64 @@ public class IntelliJTheme
|
|||||||
|
|
||||||
// parse value
|
// parse value
|
||||||
try {
|
try {
|
||||||
uiValue = UIDefaultsLoader.parseValue( key, valueStr, null );
|
UIDefaultsLoader.parseValue( key, valueStr, null );
|
||||||
|
uiValue = valueStr;
|
||||||
} catch( RuntimeException ex ) {
|
} catch( RuntimeException ex ) {
|
||||||
UIDefaultsLoader.logParseError( key, valueStr, ex, false );
|
UIDefaultsLoader.logParseError( key, valueStr, ex, true );
|
||||||
return; // ignore invalid value
|
return; // ignore invalid value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( key.startsWith( "*." ) ) {
|
// wildcards
|
||||||
// wildcard
|
if( applyWildcard( properties, key, uiValue ) )
|
||||||
String tail = key.substring( 1 );
|
return;
|
||||||
|
|
||||||
// because we can not iterate over the UI defaults keys while
|
put( properties, key, uiValue );
|
||||||
// modifying UI defaults in the same loop, we have to copy the keys
|
|
||||||
if( defaultsKeysCache.size() != defaults.size() ) {
|
|
||||||
defaultsKeysCache.clear();
|
|
||||||
Enumeration<Object> e = defaults.keys();
|
|
||||||
while( e.hasMoreElements() )
|
|
||||||
defaultsKeysCache.add( e.nextElement() );
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace all values in UI defaults that match the wildcard key
|
|
||||||
for( Object k : defaultsKeysCache ) {
|
|
||||||
if( k.equals( "Desktop.background" ) ||
|
|
||||||
k.equals( "DesktopIcon.background" ) )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if( k instanceof String ) {
|
|
||||||
// support replacing of mapped keys
|
|
||||||
// (e.g. set ComboBox.buttonEditableBackground to *.background
|
|
||||||
// because it is mapped from ComboBox.ArrowButton.background)
|
|
||||||
String km = uiKeyInverseMapping.getOrDefault( k, (String) k );
|
|
||||||
if( km.endsWith( tail ) && !((String)k).startsWith( "CheckBox.icon." ) )
|
|
||||||
defaults.put( k, uiValue );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
defaults.put( key, uiValue );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean applyWildcard( Properties properties, String key, String value ) {
|
||||||
|
if( !key.startsWith( "*." ) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
String tail = key.substring( 1 );
|
||||||
|
|
||||||
|
// because we can not iterate over the properties keys while
|
||||||
|
// modifying properties in the same loop, we have to copy the keys
|
||||||
|
String[] keys = properties.keySet().toArray( new String[properties.size()] );
|
||||||
|
|
||||||
|
// replace all values in properties that match the wildcard key
|
||||||
|
for( String k : keys ) {
|
||||||
|
if( k.startsWith( "*" ) ||
|
||||||
|
k.startsWith( "@" ) ||
|
||||||
|
k.startsWith( "HelpButton." ) ||
|
||||||
|
k.startsWith( "JX" ) ||
|
||||||
|
k.startsWith( "Jide" ) ||
|
||||||
|
k.startsWith( "ProgressBar.selection" ) ||
|
||||||
|
k.startsWith( "TitlePane." ) ||
|
||||||
|
k.startsWith( "ToggleButton.tab." ) ||
|
||||||
|
k.equals( "Desktop.background" ) ||
|
||||||
|
k.equals( "DesktopIcon.background" ) ||
|
||||||
|
k.equals( "TabbedPane.focusColor" ) ||
|
||||||
|
k.endsWith( ".hoverBackground" ) ||
|
||||||
|
k.endsWith( ".pressedBackground" ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// support replacing of mapped keys
|
||||||
|
// (e.g. set ComboBox.buttonEditableBackground to *.background
|
||||||
|
// because it is mapped from ComboBox.ArrowButton.background)
|
||||||
|
String km = uiKeyInverseMapping.getOrDefault( k, k );
|
||||||
|
if( km.endsWith( tail ) && !k.startsWith( "CheckBox.icon." ) )
|
||||||
|
put( properties, k, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: also add wildcards to properties and let UIDefaultsLoader
|
||||||
|
// process it on BasicLookAndFeel UI defaults
|
||||||
|
put( properties, key, value );
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private String fixColorIfValid( String newColorStr, String colorStr ) {
|
private String fixColorIfValid( String newColorStr, String colorStr ) {
|
||||||
try {
|
try {
|
||||||
// check whether it is valid
|
// check whether it is valid
|
||||||
@@ -418,11 +529,11 @@ public class IntelliJTheme
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyColorPalette( UIDefaults defaults ) {
|
private void applyIconsColorPalette( Properties properties ) {
|
||||||
if( icons == null )
|
if( jsonIcons == null )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Object palette = icons.get( "ColorPalette" );
|
Object palette = jsonIcons.get( "ColorPalette" );
|
||||||
if( !(palette instanceof Map) )
|
if( !(palette instanceof Map) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -431,44 +542,48 @@ public class IntelliJTheme
|
|||||||
for( Map.Entry<String, Object> e : colorPalette.entrySet() ) {
|
for( Map.Entry<String, Object> e : colorPalette.entrySet() ) {
|
||||||
String key = e.getKey();
|
String key = e.getKey();
|
||||||
Object value = e.getValue();
|
Object value = e.getValue();
|
||||||
if( key.startsWith( "Checkbox." ) || !(value instanceof String) )
|
if( key.startsWith( "Checkbox." ) || key.startsWith( "#" ) || !(value instanceof String) )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if( dark )
|
if( dark )
|
||||||
key = StringUtils.removeTrailing( key, ".Dark" );
|
key = StringUtils.removeTrailing( key, ".Dark" );
|
||||||
|
|
||||||
ColorUIResource color = toColor( (String) value );
|
String color = toColor( (String) value );
|
||||||
if( color != null )
|
if( color != null )
|
||||||
defaults.put( key, color );
|
put( properties, key, color );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ColorUIResource toColor( String value ) {
|
private String toColor( String value ) {
|
||||||
|
if( value.startsWith( "##" ) )
|
||||||
|
value = fixColorIfValid( value.substring( 1 ), value );
|
||||||
|
|
||||||
// map named colors
|
// map named colors
|
||||||
ColorUIResource color = namedColors.get( value );
|
String color = namedColors.get( value );
|
||||||
|
|
||||||
// parse color
|
// parse color
|
||||||
return (color != null) ? color : parseColor( value );
|
return (color != null) ? color : (canParseColor( value ) ? value : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ColorUIResource parseColor( String value ) {
|
private boolean canParseColor( String value ) {
|
||||||
try {
|
try {
|
||||||
return UIDefaultsLoader.parseColor( value );
|
return UIDefaultsLoader.parseColor( value ) != null;
|
||||||
} catch( IllegalArgumentException ex ) {
|
} catch( IllegalArgumentException ex ) {
|
||||||
return null;
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to parse color: '" + value + '\'', ex );
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Because IDEA uses SVGs for check boxes and radio buttons, the colors for
|
* Because IDEA uses SVGs for check boxes and radio buttons, the colors for
|
||||||
* this two components are specified in "icons > ColorPalette".
|
* these two components are specified in "icons > ColorPalette".
|
||||||
* FlatLaf uses vector icons and expects colors for the two components in UI defaults.
|
* FlatLaf uses vector icons and expects colors for the two components in properties.
|
||||||
*/
|
*/
|
||||||
private void applyCheckBoxColors( UIDefaults defaults ) {
|
private void applyCheckBoxColors( Properties properties ) {
|
||||||
if( icons == null )
|
if( jsonIcons == null )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Object palette = icons.get( "ColorPalette" );
|
Object palette = jsonIcons.get( "ColorPalette" );
|
||||||
if( !(palette instanceof Map) )
|
if( !(palette instanceof Map) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -481,18 +596,6 @@ public class IntelliJTheme
|
|||||||
if( !key.startsWith( "Checkbox." ) || !(value instanceof String) )
|
if( !key.startsWith( "Checkbox." ) || !(value instanceof String) )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if( key.equals( "Checkbox.Background.Default" ) ||
|
|
||||||
key.equals( "Checkbox.Foreground.Selected" ) )
|
|
||||||
{
|
|
||||||
// This two keys do not work correctly in IDEA because they
|
|
||||||
// map SVG color "#ffffff" to another color, but checkBox.svg and
|
|
||||||
// radio.svg (in package com.intellij.ide.ui.laf.icons.intellij)
|
|
||||||
// use "#fff". So use white to get same appearance as in IDEA.
|
|
||||||
value = "#ffffff";
|
|
||||||
}
|
|
||||||
|
|
||||||
String key2 = checkboxDuplicateColors.get( key );
|
|
||||||
|
|
||||||
if( dark )
|
if( dark )
|
||||||
key = StringUtils.removeTrailing( key, ".Dark" );
|
key = StringUtils.removeTrailing( key, ".Dark" );
|
||||||
|
|
||||||
@@ -502,10 +605,11 @@ public class IntelliJTheme
|
|||||||
if( !dark && newKey.startsWith( checkBoxIconPrefix ) )
|
if( !dark && newKey.startsWith( checkBoxIconPrefix ) )
|
||||||
newKey = "CheckBox.icon[filled].".concat( newKey.substring( checkBoxIconPrefix.length() ) );
|
newKey = "CheckBox.icon[filled].".concat( newKey.substring( checkBoxIconPrefix.length() ) );
|
||||||
|
|
||||||
ColorUIResource color = toColor( (String) value );
|
String color = toColor( (String) value );
|
||||||
if( color != null ) {
|
if( color != null ) {
|
||||||
defaults.put( newKey, color );
|
put( properties, newKey, color );
|
||||||
|
|
||||||
|
String key2 = checkboxDuplicateColors.get( key + ".Dark");
|
||||||
if( key2 != null ) {
|
if( key2 != null ) {
|
||||||
// When IDEA replaces colors in SVGs it uses color values and not the keys
|
// When IDEA replaces colors in SVGs it uses color values and not the keys
|
||||||
// from com.intellij.ide.ui.UITheme.colorPalette, but there are some keys that
|
// from com.intellij.ide.ui.UITheme.colorPalette, but there are some keys that
|
||||||
@@ -524,7 +628,7 @@ public class IntelliJTheme
|
|||||||
|
|
||||||
String newKey2 = checkboxKeyMapping.get( key2 );
|
String newKey2 = checkboxKeyMapping.get( key2 );
|
||||||
if( newKey2 != null )
|
if( newKey2 != null )
|
||||||
defaults.put( newKey2, color );
|
put( properties, newKey2, color );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -535,13 +639,13 @@ public class IntelliJTheme
|
|||||||
// update hover, pressed and focused colors
|
// update hover, pressed and focused colors
|
||||||
if( checkboxModified ) {
|
if( checkboxModified ) {
|
||||||
// for non-filled checkbox/radiobutton used in dark themes
|
// for non-filled checkbox/radiobutton used in dark themes
|
||||||
defaults.remove( "CheckBox.icon.focusWidth" );
|
properties.remove( "CheckBox.icon.focusWidth" );
|
||||||
defaults.put( "CheckBox.icon.hoverBorderColor", defaults.get( "CheckBox.icon.focusedBorderColor" ) );
|
put( properties, "CheckBox.icon.hoverBorderColor", properties.get( "CheckBox.icon.focusedBorderColor" ) );
|
||||||
|
|
||||||
// for filled checkbox/radiobutton used in light themes
|
// for filled checkbox/radiobutton used in light themes
|
||||||
defaults.remove( "CheckBox.icon[filled].focusWidth" );
|
properties.remove( "CheckBox.icon[filled].focusWidth" );
|
||||||
defaults.put( "CheckBox.icon[filled].hoverBorderColor", defaults.get( "CheckBox.icon[filled].focusedBorderColor" ) );
|
put( properties, "CheckBox.icon[filled].hoverBorderColor", properties.get( "CheckBox.icon[filled].focusedBorderColor" ) );
|
||||||
defaults.put( "CheckBox.icon[filled].focusedSelectedBackground", defaults.get( "CheckBox.icon[filled].selectedBackground" ) );
|
put( properties, "CheckBox.icon[filled].focusedSelectedBackground", properties.get( "CheckBox.icon[filled].selectedBackground" ) );
|
||||||
|
|
||||||
if( dark ) {
|
if( dark ) {
|
||||||
// IDEA Darcula checkBoxFocused.svg, checkBoxSelectedFocused.svg,
|
// IDEA Darcula checkBoxFocused.svg, checkBoxSelectedFocused.svg,
|
||||||
@@ -555,45 +659,98 @@ public class IntelliJTheme
|
|||||||
"CheckBox.icon[filled].focusedSelectedBorderColor",
|
"CheckBox.icon[filled].focusedSelectedBorderColor",
|
||||||
};
|
};
|
||||||
for( String key : focusedBorderColorKeys ) {
|
for( String key : focusedBorderColorKeys ) {
|
||||||
Color color = defaults.getColor( key );
|
Object color = properties.get( key );
|
||||||
if( color != null ) {
|
if( color != null )
|
||||||
defaults.put( key, new ColorUIResource( new Color(
|
put( properties, key, "fade(" + color + ", 65%)" );
|
||||||
(color.getRGB() & 0xffffff) | 0xa6000000, true ) ) );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copyIfNotSet( UIDefaults defaults, String destKey, String srcKey, Set<String> uiKeys ) {
|
private static final Set<String> uiKeyExcludesStartsWith;
|
||||||
if( !uiKeys.contains( destKey ) )
|
private static final String[] uiKeyExcludesContains;
|
||||||
defaults.put( destKey, defaults.get( srcKey ) );
|
private static final Set<String> uiKeyDoNotOverride;
|
||||||
}
|
|
||||||
|
|
||||||
/** Rename UI default keys (key --> value). */
|
/** Rename UI default keys (key --> value). */
|
||||||
private static final 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 final Map<String, String> uiKeyCopying = new HashMap<>();
|
private static final Map<String, String> uiKeyCopying = new LinkedHashMap<>();
|
||||||
private static final Map<String, String> uiKeyInverseMapping = new HashMap<>();
|
private static final Map<String, String> uiKeyInverseMapping = new HashMap<>();
|
||||||
private static final Map<String, String> checkboxKeyMapping = new HashMap<>();
|
private static final Map<String, String> checkboxKeyMapping = new HashMap<>();
|
||||||
private static final Map<String, String> checkboxDuplicateColors = new HashMap<>();
|
private static final Map<String, String> checkboxDuplicateColors = new HashMap<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
// IntelliJ UI properties that are not used in FlatLaf
|
||||||
|
uiKeyExcludesStartsWith = new HashSet<>( Arrays.asList(
|
||||||
|
"ActionButton.", "ActionToolbar.", "ActionsList.", "AppInspector.", "AssignedMnemonic.", "Autocomplete.",
|
||||||
|
"AvailableMnemonic.",
|
||||||
|
"Badge.", "Banner.", "BigSpinner.", "Bookmark.", "BookmarkIcon.", "BookmarkMnemonicAssigned.", "BookmarkMnemonicAvailable.",
|
||||||
|
"BookmarkMnemonicCurrent.", "BookmarkMnemonicIcon.", "Borders.", "Breakpoint.",
|
||||||
|
"Canvas.", "CellEditor.", "Code.", "CodeWithMe.", "ColumnControlButton.", "CombinedDiff.", "ComboBoxButton.",
|
||||||
|
"CompilationCharts.", "CompletionPopup.", "ComplexPopup.", "Content.", "ContextHelp.", "CurrentMnemonic.", "Counter.",
|
||||||
|
"Debugger.", "DebuggerPopup.", "DebuggerTabs.", "DefaultTabs.", "Dialog.", "DialogWrapper.",
|
||||||
|
"DisclosureButton.", "DragAndDrop.",
|
||||||
|
"Editor.", "EditorGroupsTabs.", "EditorTabs.",
|
||||||
|
"FileColor.", "FindPopup.", "FlameGraph.", "Focus.",
|
||||||
|
"Git.", "Github.", "GotItTooltip.", "Group.", "Gutter.", "GutterTooltip.",
|
||||||
|
"HeaderColor.", "HelpTooltip.", "Hg.",
|
||||||
|
"IconBadge.", "InformationHint.", "InlineBanner.", "InplaceRefactoringPopup.",
|
||||||
|
"Lesson.", "LineProfiler.", "Link.", "LiveIndicator.",
|
||||||
|
"MainMenu.", "MainToolbar.", "MainWindow.", "MemoryIndicator.", "MlModelBinding.", "MnemonicIcon.",
|
||||||
|
"NavBar.", "NewClass.", "NewPSD.", "Notification.", "Notifications.", "NotificationsToolwindow.",
|
||||||
|
"OnePixelDivider.", "OptionButton.", "Outline.",
|
||||||
|
"ParameterInfo.", "PresentationAssistant.", "Plugins.", "Profiler.", "ProgressIcon.", "PsiViewer.",
|
||||||
|
"Resizable.", "Review.", "ReviewList.", "RunToolbar.", "RunWidget.",
|
||||||
|
"ScreenView.", "SearchEverywhere.", "SearchFieldWithExtension.", "SearchMatch.", "SearchOption.",
|
||||||
|
"SearchResults.", "SegmentedButton.", "Settings.", "SidePanel.", "Space.", "SpeedSearch.", "StateWidget.",
|
||||||
|
"StatusBar.", "StripeToolbar.",
|
||||||
|
"Tag.", "TipOfTheDay.", "ToolbarComboWidget.", "ToolWindow.", "TrialWidget.",
|
||||||
|
"UIDesigner.", "UnattendedHostStatus.",
|
||||||
|
"ValidationTooltip.", "VersionControl.",
|
||||||
|
"WelcomeScreen.",
|
||||||
|
|
||||||
|
// lower case
|
||||||
|
"darcula.", "dropArea.", "icons.", "intellijlaf.", "macOSWindow.", "material.", "tooltips.",
|
||||||
|
|
||||||
|
// possible typos in .theme.json files
|
||||||
|
"Checkbox.", "Toolbar.", "Tooltip.", "UiDesigner.", "link."
|
||||||
|
) );
|
||||||
|
uiKeyExcludesContains = new String[] {
|
||||||
|
".darcula."
|
||||||
|
};
|
||||||
|
|
||||||
|
uiKeyDoNotOverride = new HashSet<>( Arrays.asList(
|
||||||
|
"TabbedPane.selectedForeground"
|
||||||
|
) );
|
||||||
|
|
||||||
|
// "*."
|
||||||
|
uiKeyMapping.put( "*.fontFace", "" ); // ignore (used in OnePauintxi themes)
|
||||||
|
uiKeyMapping.put( "*.fontSize", "" ); // ignore (used in OnePauintxi themes)
|
||||||
|
|
||||||
|
// Button
|
||||||
|
uiKeyMapping.put( "Button.minimumSize", "" ); // ignore (used in Material Theme UI Lite)
|
||||||
|
|
||||||
|
// CheckBox.iconSize
|
||||||
|
uiKeyMapping.put( "CheckBox.iconSize", "" ); // ignore (used in Rider themes)
|
||||||
|
|
||||||
// ComboBox
|
// ComboBox
|
||||||
uiKeyMapping.put( "ComboBox.background", "" ); // ignore
|
uiKeyMapping.put( "ComboBox.background", "" ); // ignore
|
||||||
|
uiKeyMapping.put( "ComboBox.buttonBackground", "" ); // ignore
|
||||||
uiKeyMapping.put( "ComboBox.nonEditableBackground", "ComboBox.background" );
|
uiKeyMapping.put( "ComboBox.nonEditableBackground", "ComboBox.background" );
|
||||||
uiKeyMapping.put( "ComboBox.ArrowButton.background", "ComboBox.buttonEditableBackground" );
|
uiKeyMapping.put( "ComboBox.ArrowButton.background", "ComboBox.buttonEditableBackground" );
|
||||||
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" );
|
||||||
uiKeyMapping.put( "Component.errorFocusColor", "Component.error.focusedBorderColor" );
|
uiKeyMapping.put( "Component.errorFocusColor", "Component.error.focusedBorderColor" );
|
||||||
uiKeyMapping.put( "Component.inactiveWarningFocusColor", "Component.warning.borderColor" );
|
uiKeyMapping.put( "Component.inactiveWarningFocusColor", "Component.warning.borderColor" );
|
||||||
uiKeyMapping.put( "Component.warningFocusColor", "Component.warning.focusedBorderColor" );
|
uiKeyMapping.put( "Component.warningFocusColor", "Component.warning.focusedBorderColor" );
|
||||||
|
uiKeyMapping.put( "Component.inactiveSuccessFocusColor", "Component.success.borderColor" );
|
||||||
|
uiKeyMapping.put( "Component.successFocusColor", "Component.success.focusedBorderColor" );
|
||||||
|
|
||||||
|
// Label
|
||||||
|
uiKeyMapping.put( "Label.disabledForegroundColor", "" ); // ignore (used in Material Theme UI Lite)
|
||||||
|
|
||||||
// Link
|
// Link
|
||||||
uiKeyMapping.put( "Link.activeForeground", "Component.linkColor" );
|
uiKeyMapping.put( "Link.activeForeground", "Component.linkColor" );
|
||||||
@@ -601,10 +758,7 @@ public class IntelliJTheme
|
|||||||
// Menu
|
// Menu
|
||||||
uiKeyMapping.put( "Menu.border", "Menu.margin" );
|
uiKeyMapping.put( "Menu.border", "Menu.margin" );
|
||||||
uiKeyMapping.put( "MenuItem.border", "MenuItem.margin" );
|
uiKeyMapping.put( "MenuItem.border", "MenuItem.margin" );
|
||||||
uiKeyCopying.put( "CheckBoxMenuItem.margin", "MenuItem.margin" );
|
|
||||||
uiKeyCopying.put( "RadioButtonMenuItem.margin", "MenuItem.margin" );
|
|
||||||
uiKeyMapping.put( "PopupMenu.border", "PopupMenu.borderInsets" );
|
uiKeyMapping.put( "PopupMenu.border", "PopupMenu.borderInsets" );
|
||||||
uiKeyCopying.put( "MenuItem.underlineSelectionColor", "TabbedPane.underlineColor" );
|
|
||||||
|
|
||||||
// IDEA uses List.selectionBackground also for menu selection
|
// IDEA uses List.selectionBackground also for menu selection
|
||||||
uiKeyCopying.put( "Menu.selectionBackground", "List.selectionBackground" );
|
uiKeyCopying.put( "Menu.selectionBackground", "List.selectionBackground" );
|
||||||
@@ -612,13 +766,14 @@ public class IntelliJTheme
|
|||||||
uiKeyCopying.put( "CheckBoxMenuItem.selectionBackground", "List.selectionBackground" );
|
uiKeyCopying.put( "CheckBoxMenuItem.selectionBackground", "List.selectionBackground" );
|
||||||
uiKeyCopying.put( "RadioButtonMenuItem.selectionBackground", "List.selectionBackground" );
|
uiKeyCopying.put( "RadioButtonMenuItem.selectionBackground", "List.selectionBackground" );
|
||||||
|
|
||||||
// ProgressBar
|
// ProgressBar: IDEA uses ProgressBar.trackColor and ProgressBar.progressColor
|
||||||
uiKeyMapping.put( "ProgressBar.background", "" ); // ignore
|
uiKeyMapping.put( "ProgressBar.background", "" ); // ignore
|
||||||
uiKeyMapping.put( "ProgressBar.foreground", "" ); // ignore
|
uiKeyMapping.put( "ProgressBar.foreground", "" ); // ignore
|
||||||
uiKeyMapping.put( "ProgressBar.trackColor", "ProgressBar.background" );
|
uiKeyMapping.put( "ProgressBar.trackColor", "ProgressBar.background" );
|
||||||
uiKeyMapping.put( "ProgressBar.progressColor", "ProgressBar.foreground" );
|
uiKeyMapping.put( "ProgressBar.progressColor", "ProgressBar.foreground" );
|
||||||
uiKeyCopying.put( "ProgressBar.selectionForeground", "ProgressBar.background" );
|
|
||||||
uiKeyCopying.put( "ProgressBar.selectionBackground", "ProgressBar.foreground" );
|
// RadioButton
|
||||||
|
uiKeyMapping.put( "RadioButton.iconSize", "" ); // ignore (used in Rider themes)
|
||||||
|
|
||||||
// ScrollBar
|
// ScrollBar
|
||||||
uiKeyMapping.put( "ScrollBar.trackColor", "ScrollBar.track" );
|
uiKeyMapping.put( "ScrollBar.trackColor", "ScrollBar.track" );
|
||||||
@@ -628,34 +783,30 @@ public class IntelliJTheme
|
|||||||
uiKeyMapping.put( "Separator.separatorColor", "Separator.foreground" );
|
uiKeyMapping.put( "Separator.separatorColor", "Separator.foreground" );
|
||||||
|
|
||||||
// Slider
|
// Slider
|
||||||
|
uiKeyMapping.put( "Slider.buttonColor", "Slider.thumbColor" );
|
||||||
|
uiKeyMapping.put( "Slider.buttonBorderColor", "" ); // ignore
|
||||||
|
uiKeyMapping.put( "Slider.thumb", "" ); // ignore (used in Material Theme UI Lite)
|
||||||
|
uiKeyMapping.put( "Slider.track", "" ); // ignore (used in Material Theme UI Lite)
|
||||||
|
uiKeyMapping.put( "Slider.trackDisabled", "" ); // ignore (used in Material Theme UI Lite)
|
||||||
uiKeyMapping.put( "Slider.trackWidth", "" ); // ignore (used in Material Theme UI Lite)
|
uiKeyMapping.put( "Slider.trackWidth", "" ); // ignore (used in Material Theme UI Lite)
|
||||||
uiKeyCopying.put( "Slider.trackValueColor", "ProgressBar.foreground" );
|
|
||||||
uiKeyCopying.put( "Slider.thumbColor", "ProgressBar.foreground" );
|
|
||||||
uiKeyCopying.put( "Slider.trackColor", "ProgressBar.background" );
|
|
||||||
|
|
||||||
// Spinner
|
|
||||||
uiKeyCopying.put( "Spinner.buttonSeparatorColor", "Component.borderColor" );
|
|
||||||
uiKeyCopying.put( "Spinner.buttonDisabledSeparatorColor", "Component.disabledBorderColor" );
|
|
||||||
|
|
||||||
// TabbedPane
|
// TabbedPane
|
||||||
uiKeyCopying.put( "TabbedPane.selectedBackground", "DefaultTabs.underlinedTabBackground" );
|
uiKeyMapping.put( "DefaultTabs.underlinedTabBackground", "TabbedPane.selectedBackground" );
|
||||||
uiKeyCopying.put( "TabbedPane.selectedForeground", "DefaultTabs.underlinedTabForeground" );
|
uiKeyMapping.put( "DefaultTabs.underlinedTabForeground", "TabbedPane.selectedForeground" );
|
||||||
uiKeyCopying.put( "TabbedPane.inactiveUnderlineColor", "DefaultTabs.inactiveUnderlineColor" );
|
uiKeyMapping.put( "DefaultTabs.inactiveUnderlineColor", "TabbedPane.inactiveUnderlineColor" );
|
||||||
|
uiKeyMapping.put( "TabbedPane.tabAreaInsets", "" ); // ignore (used in Material Theme UI Lite)
|
||||||
|
|
||||||
|
// TableHeader
|
||||||
|
uiKeyMapping.put( "TableHeader.cellBorder", "" ); // ignore (used in Material Theme UI Lite)
|
||||||
|
uiKeyMapping.put( "TableHeader.height", "" ); // ignore (used in Material Theme UI Lite)
|
||||||
|
|
||||||
// TitlePane
|
// TitlePane
|
||||||
uiKeyCopying.put( "TitlePane.inactiveBackground", "TitlePane.background" );
|
|
||||||
uiKeyMapping.put( "TitlePane.infoForeground", "TitlePane.foreground" );
|
uiKeyMapping.put( "TitlePane.infoForeground", "TitlePane.foreground" );
|
||||||
uiKeyMapping.put( "TitlePane.inactiveInfoForeground", "TitlePane.inactiveForeground" );
|
uiKeyMapping.put( "TitlePane.inactiveInfoForeground", "TitlePane.inactiveForeground" );
|
||||||
|
|
||||||
for( Map.Entry<String, String> e : uiKeyMapping.entrySet() )
|
for( Map.Entry<String, String> e : uiKeyMapping.entrySet() )
|
||||||
uiKeyInverseMapping.put( e.getValue(), e.getKey() );
|
uiKeyInverseMapping.put( e.getValue(), e.getKey() );
|
||||||
|
|
||||||
uiKeyCopying.put( "ToggleButton.tab.underlineColor", "TabbedPane.underlineColor" );
|
|
||||||
uiKeyCopying.put( "ToggleButton.tab.disabledUnderlineColor", "TabbedPane.disabledUnderlineColor" );
|
|
||||||
uiKeyCopying.put( "ToggleButton.tab.selectedBackground", "TabbedPane.selectedBackground" );
|
|
||||||
uiKeyCopying.put( "ToggleButton.tab.hoverBackground", "TabbedPane.hoverColor" );
|
|
||||||
uiKeyCopying.put( "ToggleButton.tab.focusBackground", "TabbedPane.focusColor" );
|
|
||||||
|
|
||||||
checkboxKeyMapping.put( "Checkbox.Background.Default", "CheckBox.icon.background" );
|
checkboxKeyMapping.put( "Checkbox.Background.Default", "CheckBox.icon.background" );
|
||||||
checkboxKeyMapping.put( "Checkbox.Background.Disabled", "CheckBox.icon.disabledBackground" );
|
checkboxKeyMapping.put( "Checkbox.Background.Disabled", "CheckBox.icon.disabledBackground" );
|
||||||
checkboxKeyMapping.put( "Checkbox.Border.Default", "CheckBox.icon.borderColor" );
|
checkboxKeyMapping.put( "Checkbox.Border.Default", "CheckBox.icon.borderColor" );
|
||||||
@@ -708,17 +859,15 @@ public class IntelliJTheme
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void applyAdditionalDefaults( UIDefaults defaults ) {
|
void applyAdditionalProperties( Properties properties ) {
|
||||||
theme.applyProperties( defaults );
|
theme.applyProperties( properties );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ArrayList<Class<?>> getLafClassesForDefaultsLoading() {
|
protected ArrayList<Class<?>> getLafClassesForDefaultsLoading() {
|
||||||
ArrayList<Class<?>> lafClasses = new ArrayList<>();
|
ArrayList<Class<?>> lafClasses = UIDefaultsLoader.getLafClassesForDefaultsLoading( getClass() );
|
||||||
lafClasses.add( FlatLaf.class );
|
lafClasses.add( 1, theme.dark ? FlatDarkLaf.class : FlatLightLaf.class );
|
||||||
lafClasses.add( theme.dark ? FlatDarkLaf.class : FlatLightLaf.class );
|
lafClasses.add( 2, theme.dark ? FlatDarculaLaf.class : FlatIntelliJLaf.class );
|
||||||
lafClasses.add( theme.dark ? FlatDarculaLaf.class : FlatIntelliJLaf.class );
|
|
||||||
lafClasses.add( ThemeLaf.class );
|
|
||||||
return lafClasses;
|
return lafClasses;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,11 +23,14 @@ import java.awt.GraphicsEnvironment;
|
|||||||
import java.awt.Toolkit;
|
import java.awt.Toolkit;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileReader;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
import javax.swing.text.StyleContext;
|
import javax.swing.text.StyleContext;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
@@ -68,7 +71,7 @@ class LinuxFontPolicy
|
|||||||
if( word.endsWith( "," ) )
|
if( word.endsWith( "," ) )
|
||||||
word = word.substring( 0, word.length() - 1 ).trim();
|
word = word.substring( 0, word.length() - 1 ).trim();
|
||||||
|
|
||||||
String lword = word.toLowerCase();
|
String lword = word.toLowerCase( Locale.ENGLISH );
|
||||||
if( lword.equals( "italic" ) || lword.equals( "oblique" ) )
|
if( lword.equals( "italic" ) || lword.equals( "oblique" ) )
|
||||||
style |= Font.ITALIC;
|
style |= Font.ITALIC;
|
||||||
else if( lword.equals( "bold" ) )
|
else if( lword.equals( "bold" ) )
|
||||||
@@ -104,11 +107,11 @@ class LinuxFontPolicy
|
|||||||
size = 1;
|
size = 1;
|
||||||
|
|
||||||
// handle logical font names
|
// handle logical font names
|
||||||
String logicalFamily = mapFcName( family.toLowerCase() );
|
String logicalFamily = mapFcName( family.toLowerCase( Locale.ENGLISH ) );
|
||||||
if( logicalFamily != null )
|
if( logicalFamily != null )
|
||||||
family = logicalFamily;
|
family = logicalFamily;
|
||||||
|
|
||||||
return createFontEx( family, style, size, dsize );
|
return createFontEx( family, style, size );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -118,9 +121,9 @@ class LinuxFontPolicy
|
|||||||
* E.g. family 'URW Bookman Light' is not found, but 'URW Bookman' is found.
|
* E.g. family 'URW Bookman Light' is not found, but 'URW Bookman' is found.
|
||||||
* If still not found, then font of family 'Dialog' is returned.
|
* If still not found, then font of family 'Dialog' is returned.
|
||||||
*/
|
*/
|
||||||
private static Font createFontEx( String family, int style, int size, double dsize ) {
|
private static Font createFontEx( String family, int style, int size ) {
|
||||||
for(;;) {
|
for(;;) {
|
||||||
Font font = createFont( family, style, size, dsize );
|
Font font = FlatLaf.createCompositeFont( family, style, size );
|
||||||
|
|
||||||
if( Font.DIALOG.equals( family ) )
|
if( Font.DIALOG.equals( family ) )
|
||||||
return font;
|
return font;
|
||||||
@@ -132,7 +135,7 @@ class LinuxFontPolicy
|
|||||||
// - character width is zero (e.g. font Cantarell; Fedora; Oracle Java 8)
|
// - character width is zero (e.g. font Cantarell; Fedora; Oracle Java 8)
|
||||||
FontMetrics fm = StyleContext.getDefaultStyleContext().getFontMetrics( font );
|
FontMetrics fm = StyleContext.getDefaultStyleContext().getFontMetrics( font );
|
||||||
if( fm.getHeight() > size * 2 || fm.stringWidth( "a" ) == 0 )
|
if( fm.getHeight() > size * 2 || fm.stringWidth( "a" ) == 0 )
|
||||||
return createFont( Font.DIALOG, style, size, dsize );
|
return FlatLaf.createCompositeFont( Font.DIALOG, style, size );
|
||||||
|
|
||||||
return font;
|
return font;
|
||||||
}
|
}
|
||||||
@@ -140,10 +143,10 @@ class LinuxFontPolicy
|
|||||||
// 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( Font.DIALOG, style, size, dsize );
|
return FlatLaf.createCompositeFont( Font.DIALOG, style, size );
|
||||||
|
|
||||||
// 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( Locale.ENGLISH );
|
||||||
if( lastWord.contains( "bold" ) || lastWord.contains( "heavy" ) || lastWord.contains( "black" ) )
|
if( lastWord.contains( "bold" ) || lastWord.contains( "heavy" ) || lastWord.contains( "black" ) )
|
||||||
style |= Font.BOLD;
|
style |= Font.BOLD;
|
||||||
|
|
||||||
@@ -152,15 +155,6 @@ class LinuxFontPolicy
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Font createFont( String family, int style, int size, double dsize ) {
|
|
||||||
Font font = FlatLaf.createCompositeFont( family, style, size );
|
|
||||||
|
|
||||||
// set font size in floating points
|
|
||||||
font = font.deriveFont( style, (float) dsize );
|
|
||||||
|
|
||||||
return font;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double getGnomeFontScale() {
|
private static double getGnomeFontScale() {
|
||||||
// do not scale font here if JRE scales
|
// do not scale font here if JRE scales
|
||||||
if( isSystemScaling() )
|
if( isSystemScaling() )
|
||||||
@@ -200,7 +194,7 @@ class LinuxFontPolicy
|
|||||||
* Gets the default font for KDE from 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). An application restart is necessary.
|
||||||
* This is the same behavior as in native KDE applications.
|
* This is the same behavior as in native KDE applications.
|
||||||
*
|
*
|
||||||
* The "display scale factor" (kdeglobals: [KScreen] > ScaleFactor) is not used
|
* The "display scale factor" (kdeglobals: [KScreen] > ScaleFactor) is not used
|
||||||
@@ -254,9 +248,10 @@ class LinuxFontPolicy
|
|||||||
if( size < 1 )
|
if( size < 1 )
|
||||||
size = 1;
|
size = 1;
|
||||||
|
|
||||||
return createFont( family, style, size, dsize );
|
return FlatLaf.createCompositeFont( family, style, size );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings( "MixedMutabilityReturnType" ) // Error Prone
|
||||||
private static List<String> readConfig( String filename ) {
|
private static List<String> readConfig( String filename ) {
|
||||||
File userHome = new File( System.getProperty( "user.home" ) );
|
File userHome = new File( System.getProperty( "user.home" ) );
|
||||||
|
|
||||||
@@ -277,7 +272,9 @@ 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 InputStreamReader(
|
||||||
|
new FileInputStream( file ), StandardCharsets.US_ASCII ) ) )
|
||||||
|
{
|
||||||
String line;
|
String line;
|
||||||
while( (line = reader.readLine()) != null )
|
while( (line = reader.readLine()) != null )
|
||||||
lines.add( line );
|
lines.add( line );
|
||||||
|
|||||||
@@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 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.Component;
|
||||||
|
import java.awt.Window;
|
||||||
|
import java.awt.event.ComponentEvent;
|
||||||
|
import java.awt.event.ComponentListener;
|
||||||
|
import java.awt.event.WindowAdapter;
|
||||||
|
import java.awt.event.WindowEvent;
|
||||||
|
import javax.swing.JPopupMenu;
|
||||||
|
import javax.swing.MenuElement;
|
||||||
|
import javax.swing.MenuSelectionManager;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels (hides) popup menus on Linux.
|
||||||
|
* <p>
|
||||||
|
* On Linux, popups are not hidden under following conditions, which results in
|
||||||
|
* misplaced popups:
|
||||||
|
* <ul>
|
||||||
|
* <li>window moved or resized
|
||||||
|
* <li>window maximized or restored
|
||||||
|
* <li>window iconified
|
||||||
|
* <li>window deactivated (e.g. activated other application)
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* On Windows and macOS, popups are automatically hidden.
|
||||||
|
* <p>
|
||||||
|
* The implementation is similar to what's done in
|
||||||
|
* {@code javax.swing.plaf.basic.BasicPopupMenuUI.MouseGrabber},
|
||||||
|
* but only hides popup in some conditions.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
class LinuxPopupMenuCanceler
|
||||||
|
extends WindowAdapter
|
||||||
|
implements ChangeListener, ComponentListener
|
||||||
|
{
|
||||||
|
private MenuElement[] lastPathSelectedPath;
|
||||||
|
private Window window;
|
||||||
|
|
||||||
|
LinuxPopupMenuCanceler() {
|
||||||
|
MenuSelectionManager msm = MenuSelectionManager.defaultManager();
|
||||||
|
msm.addChangeListener( this );
|
||||||
|
|
||||||
|
lastPathSelectedPath = msm.getSelectedPath();
|
||||||
|
if( lastPathSelectedPath.length > 0 )
|
||||||
|
addWindowListeners( lastPathSelectedPath[0] );
|
||||||
|
}
|
||||||
|
|
||||||
|
void uninstall() {
|
||||||
|
MenuSelectionManager.defaultManager().removeChangeListener( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addWindowListeners( MenuElement selected ) {
|
||||||
|
removeWindowListeners();
|
||||||
|
|
||||||
|
// see BasicPopupMenuUI.MouseGrabber.grabWindow()
|
||||||
|
Component invoker = selected.getComponent();
|
||||||
|
if( invoker instanceof JPopupMenu )
|
||||||
|
invoker = ((JPopupMenu)invoker).getInvoker();
|
||||||
|
window = (invoker instanceof Window)
|
||||||
|
? (Window) invoker
|
||||||
|
: SwingUtilities.windowForComponent( invoker );
|
||||||
|
|
||||||
|
if( window != null ) {
|
||||||
|
window.addWindowListener( this );
|
||||||
|
window.addComponentListener( this );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeWindowListeners() {
|
||||||
|
if( window != null ) {
|
||||||
|
window.removeWindowListener( this );
|
||||||
|
window.removeComponentListener( this );
|
||||||
|
window = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cancelPopupMenu() {
|
||||||
|
try {
|
||||||
|
MenuSelectionManager msm = MenuSelectionManager.defaultManager();
|
||||||
|
MenuElement[] selectedPath = msm.getSelectedPath();
|
||||||
|
for( MenuElement e : selectedPath ) {
|
||||||
|
if( e instanceof JPopupMenu )
|
||||||
|
((JPopupMenu)e).putClientProperty( "JPopupMenu.firePopupMenuCanceled", true );
|
||||||
|
}
|
||||||
|
msm.clearSelectedPath();
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
removeWindowListeners();
|
||||||
|
throw ex;
|
||||||
|
} catch( Error ex ) {
|
||||||
|
removeWindowListeners();
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- ChangeListener ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stateChanged( ChangeEvent e ) {
|
||||||
|
MenuElement[] selectedPath = MenuSelectionManager.defaultManager().getSelectedPath();
|
||||||
|
|
||||||
|
if( selectedPath.length == 0 )
|
||||||
|
removeWindowListeners();
|
||||||
|
else if( lastPathSelectedPath.length == 0 )
|
||||||
|
addWindowListeners( selectedPath[0] );
|
||||||
|
|
||||||
|
lastPathSelectedPath = selectedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- WindowListener ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void windowIconified( WindowEvent e ) {
|
||||||
|
cancelPopupMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void windowDeactivated( WindowEvent e ) {
|
||||||
|
cancelPopupMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void windowClosing( WindowEvent e ) {
|
||||||
|
cancelPopupMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- ComponentListener ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void componentResized( ComponentEvent e ) {
|
||||||
|
cancelPopupMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void componentMoved( ComponentEvent e ) {
|
||||||
|
cancelPopupMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void componentShown( ComponentEvent e ) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void componentHidden( ComponentEvent e ) {
|
||||||
|
cancelPopupMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,6 +45,7 @@ import javax.swing.UIManager;
|
|||||||
import javax.swing.event.ChangeEvent;
|
import javax.swing.event.ChangeEvent;
|
||||||
import javax.swing.event.ChangeListener;
|
import javax.swing.event.ChangeListener;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Improves usability of submenus by using a
|
* Improves usability of submenus by using a
|
||||||
@@ -64,6 +65,7 @@ class SubMenuUsabilityHelper
|
|||||||
// https://github.com/apache/netbeans/issues/4231#issuecomment-1179616607
|
// https://github.com/apache/netbeans/issues/4231#issuecomment-1179616607
|
||||||
private static SubMenuUsabilityHelper instance;
|
private static SubMenuUsabilityHelper instance;
|
||||||
|
|
||||||
|
private boolean eventQueuePushNotSupported;
|
||||||
private SubMenuEventQueue subMenuEventQueue;
|
private SubMenuEventQueue subMenuEventQueue;
|
||||||
private SafeTrianglePainter safeTrianglePainter;
|
private SafeTrianglePainter safeTrianglePainter;
|
||||||
private boolean changePending;
|
private boolean changePending;
|
||||||
@@ -83,6 +85,9 @@ class SubMenuUsabilityHelper
|
|||||||
if( instance != null )
|
if( instance != null )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if( !FlatSystemProperties.getBoolean( FlatSystemProperties.USE_SUB_MENU_SAFE_TRIANGLE, true ) )
|
||||||
|
return false;
|
||||||
|
|
||||||
instance = new SubMenuUsabilityHelper();
|
instance = new SubMenuUsabilityHelper();
|
||||||
MenuSelectionManager.defaultManager().addChangeListener( instance );
|
MenuSelectionManager.defaultManager().addChangeListener( instance );
|
||||||
return true;
|
return true;
|
||||||
@@ -99,7 +104,7 @@ class SubMenuUsabilityHelper
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stateChanged( ChangeEvent e ) {
|
public void stateChanged( ChangeEvent e ) {
|
||||||
if( !FlatUIUtils.getUIBoolean( KEY_USE_SAFE_TRIANGLE, true ))
|
if( eventQueuePushNotSupported || !FlatUIUtils.getUIBoolean( KEY_USE_SAFE_TRIANGLE, true ))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// handle menu selection change later, but only once in case of temporary changes
|
// handle menu selection change later, but only once in case of temporary changes
|
||||||
@@ -153,7 +158,7 @@ debug*/
|
|||||||
|
|
||||||
// get invoker screen bounds
|
// get invoker screen bounds
|
||||||
Component invoker = popup.getInvoker();
|
Component invoker = popup.getInvoker();
|
||||||
invokerBounds = (invoker != null)
|
invokerBounds = (invoker != null && invoker.isShowing())
|
||||||
? new Rectangle( invoker.getLocationOnScreen(), invoker.getSize() )
|
? new Rectangle( invoker.getLocationOnScreen(), invoker.getSize() )
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
@@ -172,9 +177,30 @@ debug*/
|
|||||||
targetTopY = popupLocation.y;
|
targetTopY = popupLocation.y;
|
||||||
targetBottomY = popupLocation.y + popupSize.height;
|
targetBottomY = popupLocation.y + popupSize.height;
|
||||||
|
|
||||||
// install own event queue to supress mouse events when mouse is moved within safe triangle
|
// install own event queue to suppress mouse events when mouse is moved within safe triangle
|
||||||
if( subMenuEventQueue == null )
|
if( subMenuEventQueue == null ) {
|
||||||
subMenuEventQueue = new SubMenuEventQueue();
|
SubMenuEventQueue queue = new SubMenuEventQueue();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
||||||
|
toolkit.getSystemEventQueue().push( queue );
|
||||||
|
|
||||||
|
// check whether push() worked
|
||||||
|
// (e.g. SWTSwing uses own event queue that does not support push())
|
||||||
|
if( toolkit.getSystemEventQueue() != queue ) {
|
||||||
|
eventQueuePushNotSupported = true;
|
||||||
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to push submenu event queue. Disabling submenu safe triangle.", null );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
subMenuEventQueue = queue;
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
// catch runtime exception from EventQueue.push()
|
||||||
|
eventQueuePushNotSupported = true;
|
||||||
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to push submenu event queue. Disabling submenu safe triangle.", ex );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// create safe triangle painter
|
// create safe triangle painter
|
||||||
if( safeTrianglePainter == null && UIManager.getBoolean( KEY_SHOW_SAFE_TRIANGLE ) )
|
if( safeTrianglePainter == null && UIManager.getBoolean( KEY_SHOW_SAFE_TRIANGLE ) )
|
||||||
@@ -247,8 +273,6 @@ debug*/
|
|||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
timeoutTimer.setRepeats( false );
|
timeoutTimer.setRepeats( false );
|
||||||
|
|
||||||
Toolkit.getDefaultToolkit().getSystemEventQueue().push( this );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void uninstall() {
|
void uninstall() {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.icons;
|
package com.formdev.flatlaf.icons;
|
||||||
|
|
||||||
import static com.formdev.flatlaf.util.UIScale.*;
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
@@ -29,7 +28,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
/**
|
/**
|
||||||
* Base class for icons that scales width and height, creates and initializes
|
* Base class for icons that scales width and height, creates and initializes
|
||||||
* a scaled graphics context for icon painting.
|
* a scaled graphics context for icon painting.
|
||||||
*
|
* <p>
|
||||||
* Subclasses do not need to scale icon painting.
|
* Subclasses do not need to scale icon painting.
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
@@ -37,10 +36,15 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
public abstract class FlatAbstractIcon
|
public abstract class FlatAbstractIcon
|
||||||
implements Icon, UIResource
|
implements Icon, UIResource
|
||||||
{
|
{
|
||||||
|
/** Unscaled icon width. */
|
||||||
protected final int width;
|
protected final int width;
|
||||||
|
/** Unscaled icon height. */
|
||||||
protected final int height;
|
protected final int height;
|
||||||
protected Color color;
|
protected Color color;
|
||||||
|
|
||||||
|
/** Additional icon scale factor. */
|
||||||
|
private float scale = 1;
|
||||||
|
|
||||||
public FlatAbstractIcon( int width, int height, Color color ) {
|
public FlatAbstractIcon( int width, int height, Color color ) {
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
@@ -57,8 +61,13 @@ public abstract class FlatAbstractIcon
|
|||||||
// g2.setColor( Color.blue );
|
// g2.setColor( Color.blue );
|
||||||
// g2.drawRect( x, y, getIconWidth() - 1, getIconHeight() - 1 );
|
// g2.drawRect( x, y, getIconWidth() - 1, getIconHeight() - 1 );
|
||||||
|
|
||||||
|
paintBackground( c, g2, x, y );
|
||||||
|
|
||||||
g2.translate( x, y );
|
g2.translate( x, y );
|
||||||
UIScale.scaleGraphics( g2 );
|
UIScale.scaleGraphics( g2 );
|
||||||
|
float scale = getScale();
|
||||||
|
if( scale != 1 )
|
||||||
|
g2.scale( scale, scale );
|
||||||
|
|
||||||
if( color != null )
|
if( color != null )
|
||||||
g2.setColor( color );
|
g2.setColor( color );
|
||||||
@@ -69,15 +78,71 @@ public abstract class FlatAbstractIcon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void paintIcon( Component c, Graphics2D g2 );
|
/**
|
||||||
|
* Paints icon background. Default implementation does nothing.
|
||||||
|
* Can be overridden to paint specific icon background.
|
||||||
|
* <p>
|
||||||
|
* The bounds of the area to be filled are:
|
||||||
|
* x, y, {@link #getIconWidth()}, {@link #getIconHeight()}.
|
||||||
|
* <p>
|
||||||
|
* In contrast to {@link #paintIcon(Component, Graphics2D)},
|
||||||
|
* the graphics context {@code g} is not translated and not scaled.
|
||||||
|
*
|
||||||
|
* @since 3.5.2
|
||||||
|
*/
|
||||||
|
protected void paintBackground( Component c, Graphics2D g, int x, int y ) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paints icon.
|
||||||
|
* <p>
|
||||||
|
* The graphics context is translated and scaled.
|
||||||
|
* This means that icon x,y coordinates are {@code 0,0}
|
||||||
|
* and it is not necessary to scale coordinates within this method.
|
||||||
|
* <p>
|
||||||
|
* The bounds to be used for icon painting are:
|
||||||
|
* 0, 0, {@link #width}, {@link #height}.
|
||||||
|
*/
|
||||||
|
protected abstract void paintIcon( Component c, Graphics2D g );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the scaled icon width.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int getIconWidth() {
|
public int getIconWidth() {
|
||||||
return scale( width );
|
return scale( UIScale.scale( width ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the scaled icon height.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int getIconHeight() {
|
public int getIconHeight() {
|
||||||
return scale( height );
|
return scale( UIScale.scale( height ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 3.7 */
|
||||||
|
public float getScale() {
|
||||||
|
return scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 3.7 */
|
||||||
|
public void setScale( float scale ) {
|
||||||
|
this.scale = scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiplies the given value by the icon scale factor {@link #getScale()} and rounds the result.
|
||||||
|
* <p>
|
||||||
|
* If you want scale a {@code float} or {@code double} value,
|
||||||
|
* simply use: {@code myFloatValue * }{@link #getScale()}.
|
||||||
|
* <p>
|
||||||
|
* Do not use this method when painting icon in {@link #paintIcon(Component, Graphics2D)}.
|
||||||
|
*
|
||||||
|
* @since 3.7
|
||||||
|
*/
|
||||||
|
protected int scale( int size ) {
|
||||||
|
float scale = getScale();
|
||||||
|
return (scale == 1) ? size : Math.round( size * scale );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
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.RenderingHints;
|
import java.awt.RenderingHints;
|
||||||
@@ -26,7 +25,8 @@ 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.FlatStylingSupport.StyleableField;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableObject;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,8 +36,11 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
@StyleableField( cls=FlatAbstractIcon.class, key="capsLockIconScale", fieldName="scale" )
|
||||||
|
@StyleableField( cls=FlatAbstractIcon.class, key="capsLockIconColor", fieldName="color" )
|
||||||
public class FlatCapsLockIcon
|
public class FlatCapsLockIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
|
implements StyleableObject
|
||||||
{
|
{
|
||||||
private Path2D path;
|
private Path2D path;
|
||||||
|
|
||||||
@@ -45,23 +48,6 @@ 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 ) {
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -17,19 +17,20 @@
|
|||||||
package com.formdev.flatlaf.icons;
|
package com.formdev.flatlaf.icons;
|
||||||
|
|
||||||
import static com.formdev.flatlaf.FlatClientProperties.*;
|
import static com.formdev.flatlaf.FlatClientProperties.*;
|
||||||
|
import static com.formdev.flatlaf.ui.FlatUIUtils.stateColor;
|
||||||
import java.awt.BasicStroke;
|
import java.awt.BasicStroke;
|
||||||
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.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.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableObject;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,6 +49,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
* @uiDefault CheckBox.icon.borderWidth int or float optional; defaults to Component.borderWidth
|
* @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.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.icon.disabledSelectedBorderWidth int or float optional; defaults to CheckBox.icon.selectedBorderWidth
|
||||||
|
* @uiDefault CheckBox.icon.indeterminateBorderWidth int or float optional; defaults to CheckBox.icon.selectedBorderWidth
|
||||||
|
* @uiDefault CheckBox.icon.disabledIndeterminateBorderWidth int or float optional; defaults to CheckBox.icon.disabledSelectedBorderWidth
|
||||||
* @uiDefault CheckBox.arc int
|
* @uiDefault CheckBox.arc int
|
||||||
*
|
*
|
||||||
* @uiDefault CheckBox.icon.focusColor Color optional; defaults to Component.focusColor
|
* @uiDefault CheckBox.icon.focusColor Color optional; defaults to Component.focusColor
|
||||||
@@ -56,35 +59,52 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
* @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.indeterminateBorderColor Color optional; defaults to CheckBox.icon.selectedBorderColor
|
||||||
|
* @uiDefault CheckBox.icon.indeterminateBackground Color optional; defaults to CheckBox.icon.selectedBackground
|
||||||
|
* @uiDefault CheckBox.icon.indeterminateCheckmarkColor Color optional; defaults to CheckBox.icon.checkmarkColor
|
||||||
*
|
*
|
||||||
* @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.disabledSelectedBorderColor Color optional; defaults to CheckBox.icon.disabledBorderColor
|
||||||
* @uiDefault CheckBox.icon.disabledSelectedBackground Color optional; CheckBox.icon.disabledBackground is used if not specified
|
* @uiDefault CheckBox.icon.disabledSelectedBackground Color optional; defaults to CheckBox.icon.disabledBackground
|
||||||
* @uiDefault CheckBox.icon.disabledCheckmarkColor Color
|
* @uiDefault CheckBox.icon.disabledCheckmarkColor Color
|
||||||
|
* @uiDefault CheckBox.icon.disabledIndeterminateBorderColor Color optional; defaults to CheckBox.icon.disabledSelectedBorderColor
|
||||||
|
* @uiDefault CheckBox.icon.disabledIndeterminateBackground Color optional; defaults to CheckBox.icon.disabledSelectedBackground
|
||||||
|
* @uiDefault CheckBox.icon.disabledIndeterminateCheckmarkColor Color optional; defaults to CheckBox.icon.disabledCheckmarkColor
|
||||||
*
|
*
|
||||||
* @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.focusedSelectedBorderColor Color optional; CheckBox.icon.focusedBorderColor is used if not specified
|
* @uiDefault CheckBox.icon.focusedSelectedBorderColor Color optional; defaults to CheckBox.icon.focusedBorderColor
|
||||||
* @uiDefault CheckBox.icon.focusedSelectedBackground Color optional; CheckBox.icon.focusedBackground is used if not specified
|
* @uiDefault CheckBox.icon.focusedSelectedBackground Color optional; defaults to CheckBox.icon.focusedBackground
|
||||||
* @uiDefault CheckBox.icon.focusedCheckmarkColor Color optional; CheckBox.icon.checkmarkColor is used if not specified
|
* @uiDefault CheckBox.icon.focusedCheckmarkColor Color optional; defaults to CheckBox.icon.checkmarkColor
|
||||||
|
* @uiDefault CheckBox.icon.focusedIndeterminateBorderColor Color optional; defaults to CheckBox.icon.focusedSelectedBorderColor
|
||||||
|
* @uiDefault CheckBox.icon.focusedIndeterminateBackground Color optional; defaults to CheckBox.icon.focusedSelectedBackground
|
||||||
|
* @uiDefault CheckBox.icon.focusedIndeterminateCheckmarkColor Color optional; defaults to CheckBox.icon.focusedCheckmarkColor
|
||||||
*
|
*
|
||||||
* @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.hoverSelectedBorderColor Color optional; CheckBox.icon.hoverBorderColor is used if not specified
|
* @uiDefault CheckBox.icon.hoverSelectedBorderColor Color optional; defaults to CheckBox.icon.hoverBorderColor
|
||||||
* @uiDefault CheckBox.icon.hoverSelectedBackground Color optional; CheckBox.icon.hoverBackground is used if not specified
|
* @uiDefault CheckBox.icon.hoverSelectedBackground Color optional; defaults to CheckBox.icon.hoverBackground
|
||||||
* @uiDefault CheckBox.icon.hoverCheckmarkColor Color optional; CheckBox.icon.checkmarkColor is used if not specified
|
* @uiDefault CheckBox.icon.hoverCheckmarkColor Color optional; defaults to CheckBox.icon.checkmarkColor
|
||||||
|
* @uiDefault CheckBox.icon.hoverIndeterminateBorderColor Color optional; defaults to CheckBox.icon.hoverSelectedBorderColor
|
||||||
|
* @uiDefault CheckBox.icon.hoverIndeterminateBackground Color optional; defaults to CheckBox.icon.hoverSelectedBackground
|
||||||
|
* @uiDefault CheckBox.icon.hoverIndeterminateCheckmarkColor Color optional; defaults to CheckBox.icon.hoverCheckmarkColor
|
||||||
*
|
*
|
||||||
* @uiDefault CheckBox.icon.pressedBorderColor Color optional
|
* @uiDefault CheckBox.icon.pressedBorderColor Color optional
|
||||||
* @uiDefault CheckBox.icon.pressedBackground Color optional
|
* @uiDefault CheckBox.icon.pressedBackground Color optional
|
||||||
* @uiDefault CheckBox.icon.pressedSelectedBorderColor Color optional; CheckBox.icon.pressedBorderColor is used if not specified
|
* @uiDefault CheckBox.icon.pressedSelectedBorderColor Color optional; defaults to CheckBox.icon.pressedBorderColor
|
||||||
* @uiDefault CheckBox.icon.pressedSelectedBackground Color optional; CheckBox.icon.pressedBackground is used if not specified
|
* @uiDefault CheckBox.icon.pressedSelectedBackground Color optional; defaults to CheckBox.icon.pressedBackground
|
||||||
* @uiDefault CheckBox.icon.pressedCheckmarkColor Color optional; CheckBox.icon.checkmarkColor is used if not specified
|
* @uiDefault CheckBox.icon.pressedCheckmarkColor Color optional; defaults to CheckBox.icon.checkmarkColor
|
||||||
|
* @uiDefault CheckBox.icon.pressedIndeterminateBorderColor Color optional; defaults to CheckBox.icon.pressedSelectedBorderColor
|
||||||
|
* @uiDefault CheckBox.icon.pressedIndeterminateBackground Color optional; defaults to CheckBox.icon.pressedSelectedBackground
|
||||||
|
* @uiDefault CheckBox.icon.pressedIndeterminateCheckmarkColor Color optional; defaults to CheckBox.icon.pressedCheckmarkColor
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
@StyleableField( cls=FlatAbstractIcon.class, key="scale" )
|
||||||
public class FlatCheckBoxIcon
|
public class FlatCheckBoxIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
|
implements StyleableObject
|
||||||
{
|
{
|
||||||
protected final String style = UIManager.getString( getPropertyPrefix() + "icon.style" );
|
protected final String style = UIManager.getString( getPropertyPrefix() + "icon.style" );
|
||||||
@Styleable protected float focusWidth = getUIFloat( "CheckBox.icon.focusWidth", UIManager.getInt( "Component.focusWidth" ), style );
|
@Styleable protected float focusWidth = getUIFloat( "CheckBox.icon.focusWidth", UIManager.getInt( "Component.focusWidth" ), style );
|
||||||
@@ -92,6 +112,8 @@ public class FlatCheckBoxIcon
|
|||||||
/** @since 2 */ @Styleable protected float borderWidth = getUIFloat( "CheckBox.icon.borderWidth", FlatUIUtils.getUIFloat( "Component.borderWidth", 1 ), style );
|
/** @since 2 */ @Styleable protected float borderWidth = getUIFloat( "CheckBox.icon.borderWidth", FlatUIUtils.getUIFloat( "Component.borderWidth", 1 ), style );
|
||||||
/** @since 2 */ @Styleable protected float selectedBorderWidth = getUIFloat( "CheckBox.icon.selectedBorderWidth", Float.MIN_VALUE, style );
|
/** @since 2 */ @Styleable protected float selectedBorderWidth = getUIFloat( "CheckBox.icon.selectedBorderWidth", Float.MIN_VALUE, style );
|
||||||
/** @since 2 */ @Styleable protected float disabledSelectedBorderWidth = getUIFloat( "CheckBox.icon.disabledSelectedBorderWidth", Float.MIN_VALUE, style );
|
/** @since 2 */ @Styleable protected float disabledSelectedBorderWidth = getUIFloat( "CheckBox.icon.disabledSelectedBorderWidth", Float.MIN_VALUE, style );
|
||||||
|
/** @since 3.6 */ @Styleable protected float indeterminateBorderWidth = getUIFloat( "CheckBox.icon.indeterminateBorderWidth", Float.MIN_VALUE, style );
|
||||||
|
/** @since 3.6 */ @Styleable protected float disabledIndeterminateBorderWidth = getUIFloat( "CheckBox.icon.disabledIndeterminateBorderWidth", Float.MIN_VALUE, style );
|
||||||
@Styleable protected int arc = FlatUIUtils.getUIInt( "CheckBox.arc", 2 );
|
@Styleable protected int arc = FlatUIUtils.getUIInt( "CheckBox.arc", 2 );
|
||||||
|
|
||||||
// enabled
|
// enabled
|
||||||
@@ -100,6 +122,9 @@ public class FlatCheckBoxIcon
|
|||||||
@Styleable protected Color selectedBorderColor = getUIColor( "CheckBox.icon.selectedBorderColor", style );
|
@Styleable protected Color selectedBorderColor = getUIColor( "CheckBox.icon.selectedBorderColor", style );
|
||||||
@Styleable protected Color selectedBackground = getUIColor( "CheckBox.icon.selectedBackground", style );
|
@Styleable protected Color selectedBackground = getUIColor( "CheckBox.icon.selectedBackground", style );
|
||||||
@Styleable protected Color checkmarkColor = getUIColor( "CheckBox.icon.checkmarkColor", style );
|
@Styleable protected Color checkmarkColor = getUIColor( "CheckBox.icon.checkmarkColor", style );
|
||||||
|
/** @since 3.6 */ @Styleable protected Color indeterminateBorderColor = getUIColor( "CheckBox.icon.indeterminateBorderColor", style );
|
||||||
|
/** @since 3.6 */ @Styleable protected Color indeterminateBackground = getUIColor( "CheckBox.icon.indeterminateBackground", style );
|
||||||
|
/** @since 3.6 */ @Styleable protected Color indeterminateCheckmarkColor = getUIColor( "CheckBox.icon.indeterminateCheckmarkColor", style );
|
||||||
|
|
||||||
// disabled
|
// disabled
|
||||||
@Styleable protected Color disabledBorderColor = getUIColor( "CheckBox.icon.disabledBorderColor", style );
|
@Styleable protected Color disabledBorderColor = getUIColor( "CheckBox.icon.disabledBorderColor", style );
|
||||||
@@ -107,6 +132,9 @@ public class FlatCheckBoxIcon
|
|||||||
/** @since 2 */ @Styleable protected Color disabledSelectedBorderColor = getUIColor( "CheckBox.icon.disabledSelectedBorderColor", style );
|
/** @since 2 */ @Styleable protected Color disabledSelectedBorderColor = getUIColor( "CheckBox.icon.disabledSelectedBorderColor", style );
|
||||||
/** @since 2 */ @Styleable protected Color disabledSelectedBackground = getUIColor( "CheckBox.icon.disabledSelectedBackground", style );
|
/** @since 2 */ @Styleable protected Color disabledSelectedBackground = getUIColor( "CheckBox.icon.disabledSelectedBackground", style );
|
||||||
@Styleable protected Color disabledCheckmarkColor = getUIColor( "CheckBox.icon.disabledCheckmarkColor", style );
|
@Styleable protected Color disabledCheckmarkColor = getUIColor( "CheckBox.icon.disabledCheckmarkColor", style );
|
||||||
|
/** @since 3.6 */ @Styleable protected Color disabledIndeterminateBorderColor = getUIColor( "CheckBox.icon.disabledIndeterminateBorderColor", style );
|
||||||
|
/** @since 3.6 */ @Styleable protected Color disabledIndeterminateBackground = getUIColor( "CheckBox.icon.disabledIndeterminateBackground", style );
|
||||||
|
/** @since 3.6 */ @Styleable protected Color disabledIndeterminateCheckmarkColor = getUIColor( "CheckBox.icon.disabledIndeterminateCheckmarkColor", style );
|
||||||
|
|
||||||
// focused
|
// focused
|
||||||
@Styleable protected Color focusedBorderColor = getUIColor( "CheckBox.icon.focusedBorderColor", style );
|
@Styleable protected Color focusedBorderColor = getUIColor( "CheckBox.icon.focusedBorderColor", style );
|
||||||
@@ -114,6 +142,9 @@ public class FlatCheckBoxIcon
|
|||||||
/** @since 2 */ @Styleable protected Color focusedSelectedBorderColor = getUIColor( "CheckBox.icon.focusedSelectedBorderColor", style );
|
/** @since 2 */ @Styleable protected Color focusedSelectedBorderColor = getUIColor( "CheckBox.icon.focusedSelectedBorderColor", style );
|
||||||
/** @since 2 */ @Styleable protected Color focusedSelectedBackground = getUIColor( "CheckBox.icon.focusedSelectedBackground", style );
|
/** @since 2 */ @Styleable protected Color focusedSelectedBackground = getUIColor( "CheckBox.icon.focusedSelectedBackground", style );
|
||||||
/** @since 2 */ @Styleable protected Color focusedCheckmarkColor = getUIColor( "CheckBox.icon.focusedCheckmarkColor", style );
|
/** @since 2 */ @Styleable protected Color focusedCheckmarkColor = getUIColor( "CheckBox.icon.focusedCheckmarkColor", style );
|
||||||
|
/** @since 3.6 */ @Styleable protected Color focusedIndeterminateBorderColor = getUIColor( "CheckBox.icon.focusedIndeterminateBorderColor", style );
|
||||||
|
/** @since 3.6 */ @Styleable protected Color focusedIndeterminateBackground = getUIColor( "CheckBox.icon.focusedIndeterminateBackground", style );
|
||||||
|
/** @since 3.6 */ @Styleable protected Color focusedIndeterminateCheckmarkColor = getUIColor( "CheckBox.icon.focusedIndeterminateCheckmarkColor", style );
|
||||||
|
|
||||||
// hover
|
// hover
|
||||||
@Styleable protected Color hoverBorderColor = getUIColor( "CheckBox.icon.hoverBorderColor", style );
|
@Styleable protected Color hoverBorderColor = getUIColor( "CheckBox.icon.hoverBorderColor", style );
|
||||||
@@ -121,6 +152,9 @@ public class FlatCheckBoxIcon
|
|||||||
/** @since 2 */ @Styleable protected Color hoverSelectedBorderColor = getUIColor( "CheckBox.icon.hoverSelectedBorderColor", 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 hoverSelectedBackground = getUIColor( "CheckBox.icon.hoverSelectedBackground", style );
|
||||||
/** @since 2 */ @Styleable protected Color hoverCheckmarkColor = getUIColor( "CheckBox.icon.hoverCheckmarkColor", style );
|
/** @since 2 */ @Styleable protected Color hoverCheckmarkColor = getUIColor( "CheckBox.icon.hoverCheckmarkColor", style );
|
||||||
|
/** @since 3.6 */ @Styleable protected Color hoverIndeterminateBorderColor = getUIColor( "CheckBox.icon.hoverIndeterminateBorderColor", style );
|
||||||
|
/** @since 3.6 */ @Styleable protected Color hoverIndeterminateBackground = getUIColor( "CheckBox.icon.hoverIndeterminateBackground", style );
|
||||||
|
/** @since 3.6 */ @Styleable protected Color hoverIndeterminateCheckmarkColor = getUIColor( "CheckBox.icon.hoverIndeterminateCheckmarkColor", style );
|
||||||
|
|
||||||
// pressed
|
// pressed
|
||||||
/** @since 2 */ @Styleable protected Color pressedBorderColor = getUIColor( "CheckBox.icon.pressedBorderColor", style );
|
/** @since 2 */ @Styleable protected Color pressedBorderColor = getUIColor( "CheckBox.icon.pressedBorderColor", style );
|
||||||
@@ -128,6 +162,9 @@ public class FlatCheckBoxIcon
|
|||||||
/** @since 2 */ @Styleable protected Color pressedSelectedBorderColor = getUIColor( "CheckBox.icon.pressedSelectedBorderColor", 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 pressedSelectedBackground = getUIColor( "CheckBox.icon.pressedSelectedBackground", style );
|
||||||
/** @since 2 */ @Styleable protected Color pressedCheckmarkColor = getUIColor( "CheckBox.icon.pressedCheckmarkColor", style );
|
/** @since 2 */ @Styleable protected Color pressedCheckmarkColor = getUIColor( "CheckBox.icon.pressedCheckmarkColor", style );
|
||||||
|
/** @since 3.6 */ @Styleable protected Color pressedIndeterminateBorderColor = getUIColor( "CheckBox.icon.pressedIndeterminateBorderColor", style );
|
||||||
|
/** @since 3.6 */ @Styleable protected Color pressedIndeterminateBackground = getUIColor( "CheckBox.icon.pressedIndeterminateBackground", style );
|
||||||
|
/** @since 3.6 */ @Styleable protected Color pressedIndeterminateCheckmarkColor = getUIColor( "CheckBox.icon.pressedIndeterminateCheckmarkColor", style );
|
||||||
|
|
||||||
protected String getPropertyPrefix() {
|
protected String getPropertyPrefix() {
|
||||||
return "CheckBox.";
|
return "CheckBox.";
|
||||||
@@ -162,31 +199,22 @@ 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
|
float bw = Float.MIN_VALUE;
|
||||||
? (disabledSelectedBorderWidth != Float.MIN_VALUE && !c.isEnabled()
|
if( !c.isEnabled() ) {
|
||||||
? disabledSelectedBorderWidth
|
bw = (indeterminate && disabledIndeterminateBorderWidth != Float.MIN_VALUE)
|
||||||
: (selectedBorderWidth != Float.MIN_VALUE ? selectedBorderWidth : borderWidth))
|
? disabledIndeterminateBorderWidth
|
||||||
: borderWidth;
|
: (selected ? disabledSelectedBorderWidth : selectedBorderWidth);
|
||||||
|
}
|
||||||
|
if( bw == Float.MIN_VALUE ) {
|
||||||
|
bw = (indeterminate && indeterminateBorderWidth != Float.MIN_VALUE)
|
||||||
|
? indeterminateBorderWidth
|
||||||
|
: ((selected && selectedBorderWidth != Float.MIN_VALUE) ? selectedBorderWidth : borderWidth);
|
||||||
|
}
|
||||||
|
|
||||||
// paint focused border
|
// paint focused border
|
||||||
if( isFocused && focusWidth > 0 && FlatButtonUI.isFocusPainted( c ) ) {
|
if( isFocused && focusWidth > 0 && FlatButtonUI.isFocusPainted( c ) ) {
|
||||||
@@ -195,15 +223,15 @@ public class FlatCheckBoxIcon
|
|||||||
}
|
}
|
||||||
|
|
||||||
// paint border
|
// paint border
|
||||||
g.setColor( getBorderColor( c, selected ) );
|
g.setColor( getBorderColor( c, selected, indeterminate ) );
|
||||||
paintBorder( c, g, bw );
|
paintBorder( c, g, bw );
|
||||||
|
|
||||||
// paint background
|
// paint background
|
||||||
Color bg = FlatUIUtils.deriveColor( getBackground( c, selected ),
|
Color baseBg = stateColor( indeterminate, indeterminateBackground, selected, selectedBackground, background );
|
||||||
selected ? selectedBackground : background );
|
Color bg = FlatUIUtils.deriveColor( getBackground( c, selected, indeterminate ), baseBg );
|
||||||
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( baseBg );
|
||||||
paintBackground( c, g, bw );
|
paintBackground( c, g, bw );
|
||||||
}
|
}
|
||||||
g.setColor( bg );
|
g.setColor( bg );
|
||||||
@@ -211,7 +239,7 @@ public class FlatCheckBoxIcon
|
|||||||
|
|
||||||
// paint checkmark
|
// paint checkmark
|
||||||
if( selected ) {
|
if( selected ) {
|
||||||
g.setColor( getCheckmarkColor( c ) );
|
g.setColor( getCheckmarkColor( c, indeterminate ) );
|
||||||
if( indeterminate )
|
if( indeterminate )
|
||||||
paintIndeterminate( c, g );
|
paintIndeterminate( c, g );
|
||||||
else
|
else
|
||||||
@@ -265,37 +293,40 @@ public class FlatCheckBoxIcon
|
|||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
public float getFocusWidth() {
|
public float getFocusWidth() {
|
||||||
return focusWidth;
|
return focusWidth * getScale();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Color getFocusColor( Component c ) {
|
protected Color getFocusColor( Component c ) {
|
||||||
return focusColor;
|
return focusColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Color getBorderColor( Component c, boolean selected ) {
|
/** @since 3.6 */
|
||||||
|
protected Color getBorderColor( Component c, boolean selected, boolean indeterminate ) {
|
||||||
return FlatButtonUI.buttonStateColor( c,
|
return FlatButtonUI.buttonStateColor( c,
|
||||||
selected ? selectedBorderColor : borderColor,
|
stateColor( indeterminate, indeterminateBorderColor, selected, selectedBorderColor, borderColor ),
|
||||||
(selected && disabledSelectedBorderColor != null) ? disabledSelectedBorderColor : disabledBorderColor,
|
stateColor( indeterminate, disabledIndeterminateBorderColor, selected, disabledSelectedBorderColor, disabledBorderColor ),
|
||||||
(selected && focusedSelectedBorderColor != null) ? focusedSelectedBorderColor : focusedBorderColor,
|
stateColor( indeterminate, focusedIndeterminateBorderColor, selected, focusedSelectedBorderColor, focusedBorderColor ),
|
||||||
(selected && hoverSelectedBorderColor != null) ? hoverSelectedBorderColor : hoverBorderColor,
|
stateColor( indeterminate, hoverIndeterminateBorderColor, selected, hoverSelectedBorderColor, hoverBorderColor ),
|
||||||
(selected && pressedSelectedBorderColor != null) ? pressedSelectedBorderColor : pressedBorderColor );
|
stateColor( indeterminate, pressedIndeterminateBorderColor, selected, pressedSelectedBorderColor, pressedBorderColor ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Color getBackground( Component c, boolean selected ) {
|
/** @since 3.6 */
|
||||||
|
protected Color getBackground( Component c, boolean selected, boolean indeterminate ) {
|
||||||
return FlatButtonUI.buttonStateColor( c,
|
return FlatButtonUI.buttonStateColor( c,
|
||||||
selected ? selectedBackground : background,
|
stateColor( indeterminate, indeterminateBackground, selected, selectedBackground, background ),
|
||||||
(selected && disabledSelectedBackground != null) ? disabledSelectedBackground : disabledBackground,
|
stateColor( indeterminate, disabledIndeterminateBackground, selected, disabledSelectedBackground, disabledBackground ),
|
||||||
(selected && focusedSelectedBackground != null) ? focusedSelectedBackground : focusedBackground,
|
stateColor( indeterminate, focusedIndeterminateBackground, selected, focusedSelectedBackground, focusedBackground ),
|
||||||
(selected && hoverSelectedBackground != null) ? hoverSelectedBackground : hoverBackground,
|
stateColor( indeterminate, hoverIndeterminateBackground, selected, hoverSelectedBackground, hoverBackground ),
|
||||||
(selected && pressedSelectedBackground != null) ? pressedSelectedBackground : pressedBackground );
|
stateColor( indeterminate, pressedIndeterminateBackground, selected, pressedSelectedBackground, pressedBackground ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Color getCheckmarkColor( Component c ) {
|
/** @since 3.6 */
|
||||||
|
protected Color getCheckmarkColor( Component c, boolean indeterminate ) {
|
||||||
return FlatButtonUI.buttonStateColor( c,
|
return FlatButtonUI.buttonStateColor( c,
|
||||||
checkmarkColor,
|
stateColor( indeterminate, indeterminateCheckmarkColor, checkmarkColor ),
|
||||||
disabledCheckmarkColor,
|
stateColor( indeterminate, disabledIndeterminateCheckmarkColor, disabledCheckmarkColor ),
|
||||||
focusedCheckmarkColor,
|
stateColor( indeterminate, focusedIndeterminateCheckmarkColor, focusedCheckmarkColor ),
|
||||||
hoverCheckmarkColor,
|
stateColor( indeterminate, hoverIndeterminateCheckmarkColor, hoverCheckmarkColor ),
|
||||||
pressedCheckmarkColor );
|
stateColor( indeterminate, pressedIndeterminateCheckmarkColor, pressedCheckmarkColor ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +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.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;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Icon for {@link javax.swing.JCheckBoxMenuItem}.
|
* Icon for {@link javax.swing.JCheckBoxMenuItem}.
|
||||||
@@ -38,8 +38,10 @@ import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
|||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
@StyleableField( cls=FlatAbstractIcon.class, key="scale" )
|
||||||
public class FlatCheckBoxMenuItemIcon
|
public class FlatCheckBoxMenuItemIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
|
implements StyleableObject
|
||||||
{
|
{
|
||||||
@Styleable protected Color checkmarkColor = UIManager.getColor( "CheckBoxMenuItem.icon.checkmarkColor" );
|
@Styleable protected Color checkmarkColor = UIManager.getColor( "CheckBoxMenuItem.icon.checkmarkColor" );
|
||||||
@Styleable protected Color disabledCheckmarkColor = UIManager.getColor( "CheckBoxMenuItem.icon.disabledCheckmarkColor" );
|
@Styleable protected Color disabledCheckmarkColor = UIManager.getColor( "CheckBoxMenuItem.icon.disabledCheckmarkColor" );
|
||||||
@@ -49,21 +51,6 @@ public class 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();
|
||||||
|
|||||||
@@ -21,12 +21,12 @@ 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.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
import javax.swing.ButtonModel;
|
import javax.swing.ButtonModel;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport;
|
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableObject;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,8 +39,10 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
* @since 1.5
|
* @since 1.5
|
||||||
*/
|
*/
|
||||||
|
@StyleableField( cls=FlatAbstractIcon.class, key="clearIconScale", fieldName="scale" )
|
||||||
public class FlatClearIcon
|
public class FlatClearIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
|
implements StyleableObject
|
||||||
{
|
{
|
||||||
@Styleable protected Color clearIconColor = UIManager.getColor( "SearchField.clearIconColor" );
|
@Styleable protected Color clearIconColor = UIManager.getColor( "SearchField.clearIconColor" );
|
||||||
@Styleable protected Color clearIconHoverColor = UIManager.getColor( "SearchField.clearIconHoverColor" );
|
@Styleable protected Color clearIconHoverColor = UIManager.getColor( "SearchField.clearIconHoverColor" );
|
||||||
@@ -58,21 +60,6 @@ public class FlatClearIcon
|
|||||||
this.ignoreButtonState = ignoreButtonState;
|
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
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
if( !ignoreButtonState && c instanceof AbstractButton ) {
|
if( !ignoreButtonState && c instanceof AbstractButton ) {
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.icons;
|
package com.formdev.flatlaf.icons;
|
||||||
|
|
||||||
import static com.formdev.flatlaf.util.UIScale.*;
|
|
||||||
import java.awt.BasicStroke;
|
import java.awt.BasicStroke;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
@@ -24,11 +23,12 @@ import java.awt.Graphics2D;
|
|||||||
import java.awt.RenderingHints;
|
import java.awt.RenderingHints;
|
||||||
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.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableObject;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -52,8 +52,10 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
@StyleableField( cls=FlatAbstractIcon.class, key="scale" )
|
||||||
public class FlatHelpButtonIcon
|
public class FlatHelpButtonIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
|
implements StyleableObject
|
||||||
{
|
{
|
||||||
@Styleable protected int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
@Styleable protected int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
||||||
@Styleable protected Color focusColor = UIManager.getColor( "Component.focusColor" );
|
@Styleable protected Color focusColor = UIManager.getColor( "Component.focusColor" );
|
||||||
@@ -76,21 +78,6 @@ public class 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 ) {
|
||||||
/*
|
/*
|
||||||
@@ -167,12 +154,12 @@ public class FlatHelpButtonIcon
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getIconWidth() {
|
public int getIconWidth() {
|
||||||
return scale( iconSize() );
|
return scale( UIScale.scale( iconSize() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getIconHeight() {
|
public int getIconHeight() {
|
||||||
return scale( iconSize() );
|
return scale( UIScale.scale( iconSize() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
private int iconSize() {
|
private int iconSize() {
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ public class FlatInternalFrameCloseIcon
|
|||||||
|
|
||||||
g.setColor( FlatButtonUI.buttonStateColor( c, c.getForeground(), null, null, hoverForeground, pressedForeground ) );
|
g.setColor( FlatButtonUI.buttonStateColor( c, c.getForeground(), null, null, hoverForeground, pressedForeground ) );
|
||||||
|
|
||||||
float mx = width / 2;
|
float mx = width / 2f;
|
||||||
float my = height / 2;
|
float my = height / 2f;
|
||||||
float r = 3.25f;
|
float r = 3.25f;
|
||||||
|
|
||||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD, 4 );
|
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD, 4 );
|
||||||
|
|||||||
@@ -21,12 +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;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "arrow" icon for {@link javax.swing.JMenu}.
|
* "arrow" icon for {@link javax.swing.JMenu}.
|
||||||
@@ -39,8 +39,10 @@ import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
|||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
@StyleableField( cls=FlatAbstractIcon.class, key="scale" )
|
||||||
public class FlatMenuArrowIcon
|
public class FlatMenuArrowIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
|
implements StyleableObject
|
||||||
{
|
{
|
||||||
@Styleable protected String arrowType = UIManager.getString( "Component.arrowType" );
|
@Styleable protected String arrowType = UIManager.getString( "Component.arrowType" );
|
||||||
@Styleable protected Color arrowColor = UIManager.getColor( "Menu.icon.arrowColor" );
|
@Styleable protected Color arrowColor = UIManager.getColor( "Menu.icon.arrowColor" );
|
||||||
@@ -51,21 +53,6 @@ public class 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 != null && !c.getComponentOrientation().isLeftToRight() )
|
if( c != null && !c.getComponentOrientation().isLeftToRight() )
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ import java.awt.Component;
|
|||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Area;
|
import java.awt.geom.Area;
|
||||||
import java.awt.geom.Ellipse2D;
|
import java.awt.geom.Ellipse2D;
|
||||||
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.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableObject;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,8 +38,10 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
* @since 1.5
|
* @since 1.5
|
||||||
*/
|
*/
|
||||||
|
@StyleableField( cls=FlatAbstractIcon.class, key="searchIconScale", fieldName="scale" )
|
||||||
public class FlatSearchIcon
|
public class FlatSearchIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
|
implements StyleableObject
|
||||||
{
|
{
|
||||||
@Styleable protected Color searchIconColor = UIManager.getColor( "SearchField.searchIconColor" );
|
@Styleable protected Color searchIconColor = UIManager.getColor( "SearchField.searchIconColor" );
|
||||||
@Styleable protected Color searchIconHoverColor = UIManager.getColor( "SearchField.searchIconHoverColor" );
|
@Styleable protected Color searchIconHoverColor = UIManager.getColor( "SearchField.searchIconHoverColor" );
|
||||||
@@ -58,21 +60,6 @@ public class FlatSearchIcon
|
|||||||
this.ignoreButtonState = ignoreButtonState;
|
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
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ import java.awt.Component;
|
|||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
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.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.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableObject;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,8 +46,10 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
@StyleableField( cls=FlatAbstractIcon.class, key="closeScale", fieldName="scale" )
|
||||||
public class FlatTabbedPaneCloseIcon
|
public class FlatTabbedPaneCloseIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
|
implements StyleableObject
|
||||||
{
|
{
|
||||||
@Styleable protected Dimension closeSize = UIManager.getDimension( "TabbedPane.closeSize" );
|
@Styleable protected Dimension closeSize = UIManager.getDimension( "TabbedPane.closeSize" );
|
||||||
@Styleable protected int closeArc = UIManager.getInt( "TabbedPane.closeArc" );
|
@Styleable protected int closeArc = UIManager.getInt( "TabbedPane.closeArc" );
|
||||||
@@ -65,21 +67,6 @@ public class 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
|
||||||
@@ -90,12 +77,12 @@ public class FlatTabbedPaneCloseIcon
|
|||||||
closeSize.width, closeSize.height, closeArc, closeArc );
|
closeSize.width, closeSize.height, closeArc, closeArc );
|
||||||
}
|
}
|
||||||
|
|
||||||
// set cross color
|
// set color of cross
|
||||||
Color fg = FlatButtonUI.buttonStateColor( c, closeForeground, null, null, closeHoverForeground, closePressedForeground );
|
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 / 2f;
|
||||||
float my = height / 2;
|
float my = height / 2f;
|
||||||
float r = ((bg != null) ? closeCrossFilledSize : closeCrossPlainSize) / 2;
|
float r = ((bg != null) ? closeCrossFilledSize : closeCrossPlainSize) / 2;
|
||||||
|
|
||||||
// paint cross
|
// paint cross
|
||||||
|
|||||||
@@ -57,11 +57,11 @@ public class FlatTreeOpenIcon
|
|||||||
double arc = 1.5;
|
double arc = 1.5;
|
||||||
double arc2 = 0.5;
|
double arc2 = 0.5;
|
||||||
path = FlatUIUtils.createPath( false,
|
path = FlatUIUtils.createPath( false,
|
||||||
// bottom-left of opend part
|
// bottom-left of opened part
|
||||||
2,13.5,
|
2,13.5,
|
||||||
// top-left of opend part
|
// top-left of opened part
|
||||||
FlatUIUtils.ROUNDED, 4.5,7.5, arc,
|
FlatUIUtils.ROUNDED, 4.5,7.5, arc,
|
||||||
// top-right of opend part
|
// top-right of opened part
|
||||||
FlatUIUtils.ROUNDED, 15.5,7.5, arc2,
|
FlatUIUtils.ROUNDED, 15.5,7.5, arc2,
|
||||||
|
|
||||||
// bottom-right
|
// bottom-right
|
||||||
|
|||||||
@@ -18,66 +18,141 @@ package com.formdev.flatlaf.icons;
|
|||||||
|
|
||||||
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.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.RenderingHints;
|
import java.awt.Insets;
|
||||||
import javax.swing.UIManager;
|
import java.awt.Window;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import com.formdev.flatlaf.ui.FlatButtonUI;
|
import com.formdev.flatlaf.ui.FlatButtonUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatTitlePane;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
import com.formdev.flatlaf.util.DerivedColor;
|
||||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for window icons.
|
* Base class for window icons.
|
||||||
*
|
*
|
||||||
* @uiDefault TitlePane.buttonSize Dimension
|
* @uiDefault TitlePane.buttonSize Dimension
|
||||||
* @uiDefault TitlePane.buttonHoverBackground Color
|
* @uiDefault TitlePane.buttonInsets Insets optional
|
||||||
* @uiDefault TitlePane.buttonPressedBackground Color
|
* @uiDefault TitlePane.buttonArc int optional
|
||||||
|
* @uiDefault TitlePane.buttonSymbolHeight int
|
||||||
|
* @uiDefault TitlePane.buttonBackground Color optional
|
||||||
|
* @uiDefault TitlePane.buttonForeground Color optional
|
||||||
|
* @uiDefault TitlePane.buttonInactiveBackground Color optional
|
||||||
|
* @uiDefault TitlePane.buttonInactiveForeground Color optional
|
||||||
|
* @uiDefault TitlePane.buttonHoverBackground Color optional
|
||||||
|
* @uiDefault TitlePane.buttonHoverForeground Color optional
|
||||||
|
* @uiDefault TitlePane.buttonPressedBackground Color optional
|
||||||
|
* @uiDefault TitlePane.buttonPressedForeground Color optional
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public abstract class FlatWindowAbstractIcon
|
public abstract class FlatWindowAbstractIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
{
|
{
|
||||||
private final Color hoverBackground;
|
/** @since 3.6 */ protected final Insets insets;
|
||||||
private final Color pressedBackground;
|
/** @since 3.6 */ protected final int arc;
|
||||||
|
/** @since 3.6 */ protected final int symbolHeight;
|
||||||
|
|
||||||
public FlatWindowAbstractIcon() {
|
/** @since 3.6 */ protected final Color background;
|
||||||
this( UIManager.getDimension( "TitlePane.buttonSize" ),
|
/** @since 3.6 */ protected final Color foreground;
|
||||||
UIManager.getColor( "TitlePane.buttonHoverBackground" ),
|
/** @since 3.6 */ protected final Color inactiveBackground;
|
||||||
UIManager.getColor( "TitlePane.buttonPressedBackground" ) );
|
/** @since 3.6 */ protected final Color inactiveForeground;
|
||||||
|
protected final Color hoverBackground;
|
||||||
|
/** @since 3.6 */ protected final Color hoverForeground;
|
||||||
|
protected final Color pressedBackground;
|
||||||
|
/** @since 3.6 */ protected final Color pressedForeground;
|
||||||
|
|
||||||
|
/** @since 3.2 */
|
||||||
|
protected FlatWindowAbstractIcon( String windowStyle ) {
|
||||||
|
this( windowStyle, null, null, null, null, null, null, null, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
public FlatWindowAbstractIcon( Dimension size, Color hoverBackground, Color pressedBackground ) {
|
/** @since 3.6 */
|
||||||
|
protected FlatWindowAbstractIcon( String windowStyle,
|
||||||
|
Color background, Color foreground, Color inactiveBackground, Color inactiveForeground,
|
||||||
|
Color hoverBackground, Color hoverForeground, Color pressedBackground, Color pressedForeground )
|
||||||
|
{
|
||||||
|
this( FlatUIUtils.getSubUIDimension( "TitlePane.buttonSize", windowStyle ),
|
||||||
|
FlatUIUtils.getSubUIInsets( "TitlePane.buttonInsets", windowStyle ),
|
||||||
|
FlatUIUtils.getSubUIInt( "TitlePane.buttonArc", windowStyle, 0 ),
|
||||||
|
FlatUIUtils.getSubUIInt( "TitlePane.buttonSymbolHeight", windowStyle, 10 ),
|
||||||
|
(background != null) ? background : FlatUIUtils.getSubUIColor( "TitlePane.buttonBackground", windowStyle ),
|
||||||
|
(foreground != null) ? foreground : FlatUIUtils.getSubUIColor( "TitlePane.buttonForeground", windowStyle ),
|
||||||
|
(inactiveBackground != null) ? inactiveBackground : FlatUIUtils.getSubUIColor( "TitlePane.buttonInactiveBackground", windowStyle ),
|
||||||
|
(inactiveForeground != null) ? inactiveForeground : FlatUIUtils.getSubUIColor( "TitlePane.buttonInactiveForeground", windowStyle ),
|
||||||
|
(hoverBackground != null) ? hoverBackground : FlatUIUtils.getSubUIColor( "TitlePane.buttonHoverBackground", windowStyle ),
|
||||||
|
(hoverForeground != null) ? hoverForeground : FlatUIUtils.getSubUIColor( "TitlePane.buttonHoverForeground", windowStyle ),
|
||||||
|
(pressedBackground != null) ? pressedBackground : FlatUIUtils.getSubUIColor( "TitlePane.buttonPressedBackground", windowStyle ),
|
||||||
|
(pressedForeground != null) ? pressedForeground : FlatUIUtils.getSubUIColor( "TitlePane.buttonPressedForeground", windowStyle ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 3.6 */
|
||||||
|
protected FlatWindowAbstractIcon( Dimension size, Insets insets, int arc, int symbolHeight,
|
||||||
|
Color background, Color foreground, Color inactiveBackground, Color inactiveForeground,
|
||||||
|
Color hoverBackground, Color hoverForeground, Color pressedBackground, Color pressedForeground )
|
||||||
|
{
|
||||||
super( size.width, size.height, null );
|
super( size.width, size.height, null );
|
||||||
|
this.insets = (insets != null) ? insets : new Insets( 0, 0, 0, 0 );
|
||||||
|
this.arc = arc;
|
||||||
|
this.symbolHeight = symbolHeight;
|
||||||
|
|
||||||
|
this.background = background;
|
||||||
|
this.foreground = foreground;
|
||||||
|
this.inactiveBackground = inactiveBackground;
|
||||||
|
this.inactiveForeground = inactiveForeground;
|
||||||
this.hoverBackground = hoverBackground;
|
this.hoverBackground = hoverBackground;
|
||||||
|
this.hoverForeground = hoverForeground;
|
||||||
this.pressedBackground = pressedBackground;
|
this.pressedBackground = pressedBackground;
|
||||||
|
this.pressedForeground = pressedForeground;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
paintBackground( c, g );
|
|
||||||
|
|
||||||
g.setColor( getForeground( c ) );
|
g.setColor( getForeground( c ) );
|
||||||
HiDPIUtils.paintAtScale1x( g, 0, 0, width, height, this::paintIconAt1x );
|
HiDPIUtils.paintAtScale1x( g, 0, 0, width, height, this::paintIconAt1x );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor );
|
protected abstract void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor );
|
||||||
|
|
||||||
protected void paintBackground( Component c, Graphics2D g ) {
|
/** @since 3.5.2 */
|
||||||
Color background = FlatButtonUI.buttonStateColor( c, null, null, null, hoverBackground, pressedBackground );
|
@Override
|
||||||
|
protected void paintBackground( Component c, Graphics2D g, int x, int y ) {
|
||||||
|
Color bg = null;
|
||||||
|
if( background != null || inactiveBackground != null ) {
|
||||||
|
Window window = SwingUtilities.windowForComponent( c );
|
||||||
|
bg = (window == null || window.isActive()) ? background : inactiveBackground;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color background = FlatButtonUI.buttonStateColor( c, bg, 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%)
|
Insets insets = UIScale.scale( this.insets );
|
||||||
Object oldHint = g.getRenderingHint( RenderingHints.KEY_ANTIALIASING );
|
float arc = UIScale.scale( (float) this.arc );
|
||||||
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
|
|
||||||
|
|
||||||
g.setColor( FlatUIUtils.deriveColor( background, c.getBackground() ) );
|
// derive color from title pane background
|
||||||
g.fillRect( 0, 0, width, height );
|
if( background instanceof DerivedColor ) {
|
||||||
|
Container titlePane = SwingUtilities.getAncestorOfClass( FlatTitlePane.class, c );
|
||||||
|
Component baseComp = (titlePane != null) ? titlePane : c;
|
||||||
|
background = FlatUIUtils.deriveColor( background, baseComp.getBackground() );
|
||||||
|
}
|
||||||
|
|
||||||
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, oldHint );
|
g.setColor( background );
|
||||||
|
FlatUIUtils.paintComponentBackground( g, insets.left, insets.top,
|
||||||
|
c.getWidth() - insets.left - insets.right,
|
||||||
|
c.getHeight() - insets.top - insets.bottom,
|
||||||
|
0, arc );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Color getForeground( Component c ) {
|
protected Color getForeground( Component c ) {
|
||||||
return c.getForeground();
|
Color fg = null;
|
||||||
|
if( foreground != null || inactiveForeground != null ) {
|
||||||
|
Window window = SwingUtilities.windowForComponent( c );
|
||||||
|
fg = (window == null || window.isActive()) ? foreground : inactiveForeground;
|
||||||
|
}
|
||||||
|
return FlatButtonUI.buttonStateColor( c, (fg != null) ? fg : c.getForeground(),
|
||||||
|
null, null, hoverForeground, pressedForeground );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,44 +17,54 @@
|
|||||||
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.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.FlatButtonUI;
|
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "close" icon for windows (frames and dialogs).
|
* "close" icon for windows (frames and dialogs).
|
||||||
*
|
*
|
||||||
* @uiDefault TitlePane.closeHoverBackground Color
|
* @uiDefault TitlePane.closeBackground Color optional
|
||||||
* @uiDefault TitlePane.closePressedBackground Color
|
* @uiDefault TitlePane.closeForeground Color optional
|
||||||
* @uiDefault TitlePane.closeHoverForeground Color
|
* @uiDefault TitlePane.closeInactiveBackground Color optional
|
||||||
* @uiDefault TitlePane.closePressedForeground Color
|
* @uiDefault TitlePane.closeInactiveForeground Color optional
|
||||||
|
* @uiDefault TitlePane.closeHoverBackground Color optional
|
||||||
|
* @uiDefault TitlePane.closeHoverForeground Color optional
|
||||||
|
* @uiDefault TitlePane.closePressedBackground Color optional
|
||||||
|
* @uiDefault TitlePane.closePressedForeground Color optional
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatWindowCloseIcon
|
public class FlatWindowCloseIcon
|
||||||
extends FlatWindowAbstractIcon
|
extends FlatWindowAbstractIcon
|
||||||
{
|
{
|
||||||
private final Color hoverForeground = UIManager.getColor( "TitlePane.closeHoverForeground" );
|
|
||||||
private final Color pressedForeground = UIManager.getColor( "TitlePane.closePressedForeground" );
|
|
||||||
|
|
||||||
public FlatWindowCloseIcon() {
|
public FlatWindowCloseIcon() {
|
||||||
super( UIManager.getDimension( "TitlePane.buttonSize" ),
|
this( null );
|
||||||
UIManager.getColor( "TitlePane.closeHoverBackground" ),
|
}
|
||||||
UIManager.getColor( "TitlePane.closePressedBackground" ) );
|
|
||||||
|
/** @since 3.2 */
|
||||||
|
public FlatWindowCloseIcon( String windowStyle ) {
|
||||||
|
super( windowStyle,
|
||||||
|
FlatUIUtils.getSubUIColor( "TitlePane.closeBackground", windowStyle ),
|
||||||
|
FlatUIUtils.getSubUIColor( "TitlePane.closeForeground", windowStyle ),
|
||||||
|
FlatUIUtils.getSubUIColor( "TitlePane.closeInactiveBackground", windowStyle ),
|
||||||
|
FlatUIUtils.getSubUIColor( "TitlePane.closeInactiveForeground", windowStyle ),
|
||||||
|
FlatUIUtils.getSubUIColor( "TitlePane.closeHoverBackground", windowStyle ),
|
||||||
|
FlatUIUtils.getSubUIColor( "TitlePane.closeHoverForeground", windowStyle ),
|
||||||
|
FlatUIUtils.getSubUIColor( "TitlePane.closePressedBackground", windowStyle ),
|
||||||
|
FlatUIUtils.getSubUIColor( "TitlePane.closePressedForeground", windowStyle ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
|
protected void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
|
||||||
int iwh = (int) (10 * scaleFactor);
|
int iwh = (int) (symbolHeight * 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 ix2 = ix + iwh - 1;
|
int ix2 = ix + iwh - 1;
|
||||||
int iy2 = iy + iwh - 1;
|
int iy2 = iy + iwh - 1;
|
||||||
float thickness = SystemInfo.isWindows_11_orLater ? (float) scaleFactor : (int) scaleFactor;
|
boolean isWindows10 = SystemInfo.isWindows_10_orLater && !SystemInfo.isWindows_11_orLater;
|
||||||
|
float thickness = Math.max( isWindows10 ? (int) scaleFactor : (float) scaleFactor, 1 );
|
||||||
|
|
||||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD, 4 );
|
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD, 4 );
|
||||||
path.moveTo( ix, iy );
|
path.moveTo( ix, iy );
|
||||||
@@ -64,9 +74,4 @@ public class FlatWindowCloseIcon
|
|||||||
g.setStroke( new BasicStroke( thickness ) );
|
g.setStroke( new BasicStroke( thickness ) );
|
||||||
g.draw( path );
|
g.draw( path );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Color getForeground( Component c ) {
|
|
||||||
return FlatButtonUI.buttonStateColor( c, c.getForeground(), null, null, hoverForeground, pressedForeground );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,12 +27,18 @@ public class FlatWindowIconifyIcon
|
|||||||
extends FlatWindowAbstractIcon
|
extends FlatWindowAbstractIcon
|
||||||
{
|
{
|
||||||
public FlatWindowIconifyIcon() {
|
public FlatWindowIconifyIcon() {
|
||||||
|
this( null );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 3.2 */
|
||||||
|
public FlatWindowIconifyIcon( String windowStyle ) {
|
||||||
|
super( windowStyle );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
|
protected void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
|
||||||
int iw = (int) (10 * scaleFactor);
|
int iw = (int) (symbolHeight * scaleFactor);
|
||||||
int ih = (int) scaleFactor;
|
int ih = Math.max( (int) scaleFactor, 1 );
|
||||||
int ix = x + ((width - iw) / 2);
|
int ix = x + ((width - iw) / 2);
|
||||||
int iy = y + ((height - ih) / 2);
|
int iy = y + ((height - ih) / 2);
|
||||||
|
|
||||||
|
|||||||
@@ -29,14 +29,21 @@ public class FlatWindowMaximizeIcon
|
|||||||
extends FlatWindowAbstractIcon
|
extends FlatWindowAbstractIcon
|
||||||
{
|
{
|
||||||
public FlatWindowMaximizeIcon() {
|
public FlatWindowMaximizeIcon() {
|
||||||
|
this( null );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 3.2 */
|
||||||
|
public FlatWindowMaximizeIcon( String windowStyle ) {
|
||||||
|
super( windowStyle );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
|
protected void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
|
||||||
int iwh = (int) (10 * scaleFactor);
|
int iwh = (int) (symbolHeight * 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);
|
||||||
float thickness = SystemInfo.isWindows_11_orLater ? (float) scaleFactor : (int) scaleFactor;
|
boolean isWindows10 = SystemInfo.isWindows_10_orLater && !SystemInfo.isWindows_11_orLater;
|
||||||
|
float thickness = Math.max( isWindows10 ? (int) scaleFactor : (float) scaleFactor, 1 );
|
||||||
int arc = Math.max( (int) (1.5 * scaleFactor), 2 );
|
int arc = Math.max( (int) (1.5 * scaleFactor), 2 );
|
||||||
|
|
||||||
g.fill( SystemInfo.isWindows_11_orLater
|
g.fill( SystemInfo.isWindows_11_orLater
|
||||||
|
|||||||
@@ -32,18 +32,25 @@ public class FlatWindowRestoreIcon
|
|||||||
extends FlatWindowAbstractIcon
|
extends FlatWindowAbstractIcon
|
||||||
{
|
{
|
||||||
public FlatWindowRestoreIcon() {
|
public FlatWindowRestoreIcon() {
|
||||||
|
this( null );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 3.2 */
|
||||||
|
public FlatWindowRestoreIcon( String windowStyle ) {
|
||||||
|
super( windowStyle );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
|
protected void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
|
||||||
int iwh = (int) (10 * scaleFactor);
|
int iwh = (int) (symbolHeight * 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);
|
||||||
float thickness = SystemInfo.isWindows_11_orLater ? (float) scaleFactor : (int) scaleFactor;
|
boolean isWindows10 = SystemInfo.isWindows_10_orLater && !SystemInfo.isWindows_11_orLater;
|
||||||
|
float thickness = Math.max( isWindows10 ? (int) scaleFactor : (float) scaleFactor, 1 );
|
||||||
int arc = Math.max( (int) (1.5 * scaleFactor), 2 );
|
int arc = Math.max( (int) (1.5 * scaleFactor), 2 );
|
||||||
int arcOuter = (int) (arc + (1.5 * scaleFactor));
|
int arcOuter = (int) (arc + (1.5 * scaleFactor));
|
||||||
|
|
||||||
int rwh = (int) (8 * scaleFactor);
|
int rwh = (int) ((symbolHeight - 2) * scaleFactor);
|
||||||
int ro2 = iwh - rwh;
|
int ro2 = iwh - rwh;
|
||||||
|
|
||||||
// upper-right rectangle
|
// upper-right rectangle
|
||||||
|
|||||||
@@ -502,9 +502,9 @@ class JsonParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isHexDigit() {
|
private boolean isHexDigit() {
|
||||||
return current >= '0' && current <= '9'
|
return (current >= '0' && current <= '9')
|
||||||
|| current >= 'a' && current <= 'f'
|
|| (current >= 'a' && current <= 'f')
|
||||||
|| current >= 'A' && current <= 'F';
|
|| (current >= 'A' && current <= 'F');
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isEndOfText() {
|
private boolean isEndOfText() {
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ public class Location {
|
|||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (getClass() != obj.getClass()) {
|
if (!(obj instanceof Location)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Location other = (Location)obj;
|
Location other = (Location)obj;
|
||||||
|
|||||||
@@ -23,17 +23,15 @@ 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;
|
||||||
import javax.swing.JSpinner;
|
import javax.swing.JSpinner;
|
||||||
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.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableBorder;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableObject;
|
||||||
import com.formdev.flatlaf.util.DerivedColor;
|
import com.formdev.flatlaf.util.DerivedColor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -60,13 +58,15 @@ import com.formdev.flatlaf.util.DerivedColor;
|
|||||||
* @uiDefault Component.error.focusedBorderColor Color
|
* @uiDefault Component.error.focusedBorderColor Color
|
||||||
* @uiDefault Component.warning.borderColor Color
|
* @uiDefault Component.warning.borderColor Color
|
||||||
* @uiDefault Component.warning.focusedBorderColor Color
|
* @uiDefault Component.warning.focusedBorderColor Color
|
||||||
|
* @uiDefault Component.success.borderColor Color
|
||||||
|
* @uiDefault Component.success.focusedBorderColor Color
|
||||||
* @uiDefault Component.custom.borderColor Color
|
* @uiDefault Component.custom.borderColor Color
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatBorder
|
public class FlatBorder
|
||||||
extends BasicBorders.MarginBorder
|
extends BasicBorders.MarginBorder
|
||||||
implements StyleableBorder
|
implements StyleableObject
|
||||||
{
|
{
|
||||||
@Styleable protected int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
@Styleable protected int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
||||||
@Styleable protected float innerFocusWidth = FlatUIUtils.getUIFloat( "Component.innerFocusWidth", 0 );
|
@Styleable protected float innerFocusWidth = FlatUIUtils.getUIFloat( "Component.innerFocusWidth", 0 );
|
||||||
@@ -82,6 +82,8 @@ public class FlatBorder
|
|||||||
@Styleable(dot=true) protected Color errorFocusedBorderColor = UIManager.getColor( "Component.error.focusedBorderColor" );
|
@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 warningBorderColor = UIManager.getColor( "Component.warning.borderColor" );
|
||||||
@Styleable(dot=true) protected Color warningFocusedBorderColor = UIManager.getColor( "Component.warning.focusedBorderColor" );
|
@Styleable(dot=true) protected Color warningFocusedBorderColor = UIManager.getColor( "Component.warning.focusedBorderColor" );
|
||||||
|
/** @since 3.6 */ @Styleable(dot=true) protected Color successBorderColor = UIManager.getColor( "Component.success.borderColor" );
|
||||||
|
/** @since 3.6 */ @Styleable(dot=true) protected Color successFocusedBorderColor = UIManager.getColor( "Component.success.focusedBorderColor" );
|
||||||
@Styleable(dot=true) protected Color customBorderColor = UIManager.getColor( "Component.custom.borderColor" );
|
@Styleable(dot=true) protected Color customBorderColor = UIManager.getColor( "Component.custom.borderColor" );
|
||||||
|
|
||||||
// only used via styling (not in UI defaults, but has likewise client properties)
|
// only used via styling (not in UI defaults, but has likewise client properties)
|
||||||
@@ -89,24 +91,6 @@ public class FlatBorder
|
|||||||
/** @since 2 */ @Styleable protected Color outlineColor;
|
/** @since 2 */ @Styleable protected Color outlineColor;
|
||||||
/** @since 2 */ @Styleable protected Color outlineFocusedColor;
|
/** @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 ) {
|
||||||
Graphics2D g2 = (Graphics2D) g.create();
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
@@ -136,7 +120,7 @@ public class FlatBorder
|
|||||||
Paint borderColor = (outlineColor != null) ? outlineColor : getBorderColor( c );
|
Paint borderColor = (outlineColor != null) ? outlineColor : getBorderColor( c );
|
||||||
FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height,
|
FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height,
|
||||||
focusWidth, 1, focusInnerWidth, borderWidth, arc,
|
focusWidth, 1, focusInnerWidth, borderWidth, arc,
|
||||||
focusColor, borderColor, null );
|
focusColor, borderColor, null, c instanceof JScrollPane );
|
||||||
} finally {
|
} finally {
|
||||||
g2.dispose();
|
g2.dispose();
|
||||||
}
|
}
|
||||||
@@ -169,6 +153,9 @@ public class FlatBorder
|
|||||||
|
|
||||||
case FlatClientProperties.OUTLINE_WARNING:
|
case FlatClientProperties.OUTLINE_WARNING:
|
||||||
return isFocused( c ) ? warningFocusedBorderColor : warningBorderColor;
|
return isFocused( c ) ? warningFocusedBorderColor : warningBorderColor;
|
||||||
|
|
||||||
|
case FlatClientProperties.OUTLINE_SUCCESS:
|
||||||
|
return isFocused( c ) ? successFocusedBorderColor : successBorderColor;
|
||||||
}
|
}
|
||||||
} else if( outline instanceof Color ) {
|
} else if( outline instanceof Color ) {
|
||||||
Color color = (Color) outline;
|
Color color = (Color) outline;
|
||||||
@@ -195,8 +182,7 @@ public class FlatBorder
|
|||||||
protected boolean isEnabled( Component c ) {
|
protected boolean isEnabled( Component c ) {
|
||||||
if( c instanceof JScrollPane ) {
|
if( c instanceof JScrollPane ) {
|
||||||
// check whether view component is disabled
|
// check whether view component is disabled
|
||||||
JViewport viewport = ((JScrollPane)c).getViewport();
|
Component view = FlatScrollPaneUI.getView( (JScrollPane) c );
|
||||||
Component view = (viewport != null) ? viewport.getView() : null;
|
|
||||||
if( view != null && !isEnabled( view ) )
|
if( view != null && !isEnabled( view ) )
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -279,7 +265,7 @@ public class FlatBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the (unscaled) arc diameter of the border.
|
* Returns the (unscaled) arc diameter of the border corners.
|
||||||
*/
|
*/
|
||||||
protected int getArc( Component c ) {
|
protected int getArc( Component c ) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -42,6 +42,13 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @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.pressedBorderColor Color optional
|
||||||
|
*
|
||||||
|
* @uiDefault Button.selectedBorderColor Color optional
|
||||||
|
* @uiDefault Button.disabledSelectedBorderColor Color optional
|
||||||
|
* @uiDefault Button.focusedSelectedBorderColor Color optional
|
||||||
|
* @uiDefault Button.hoverSelectedBorderColor Color optional
|
||||||
|
* @uiDefault Button.pressedSelectedBorderColor Color optional
|
||||||
*
|
*
|
||||||
* @uiDefault Button.default.borderWidth int or float
|
* @uiDefault Button.default.borderWidth int or float
|
||||||
* @uiDefault Button.default.borderColor Color
|
* @uiDefault Button.default.borderColor Color
|
||||||
@@ -49,6 +56,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @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.focusedBorderColor Color
|
* @uiDefault Button.default.focusedBorderColor Color
|
||||||
* @uiDefault Button.default.focusColor Color
|
* @uiDefault Button.default.focusColor Color
|
||||||
|
* @uiDefault Button.default.pressedBorderColor Color optional
|
||||||
* @uiDefault Button.default.hoverBorderColor Color optional
|
* @uiDefault Button.default.hoverBorderColor Color optional
|
||||||
*
|
*
|
||||||
* @uiDefault Button.toolbar.focusWidth int or float optional; default is 1.5
|
* @uiDefault Button.toolbar.focusWidth int or float optional; default is 1.5
|
||||||
@@ -65,6 +73,13 @@ public class FlatButtonBorder
|
|||||||
|
|
||||||
protected Color endBorderColor = UIManager.getColor( "Button.endBorderColor" );
|
protected Color endBorderColor = UIManager.getColor( "Button.endBorderColor" );
|
||||||
@Styleable protected Color hoverBorderColor = UIManager.getColor( "Button.hoverBorderColor" );
|
@Styleable protected Color hoverBorderColor = UIManager.getColor( "Button.hoverBorderColor" );
|
||||||
|
/** @since 3.5 */ @Styleable protected Color pressedBorderColor = UIManager.getColor( "Button.pressedBorderColor" );
|
||||||
|
|
||||||
|
/** @since 3.5 */ @Styleable protected Color selectedBorderColor = UIManager.getColor( "Button.selectedBorderColor" );
|
||||||
|
/** @since 3.5 */ @Styleable protected Color disabledSelectedBorderColor = UIManager.getColor( "Button.disabledSelectedBorderColor" );
|
||||||
|
/** @since 3.5 */ @Styleable protected Color focusedSelectedBorderColor = UIManager.getColor( "Button.focusedSelectedBorderColor" );
|
||||||
|
/** @since 3.5 */ @Styleable protected Color hoverSelectedBorderColor = UIManager.getColor( "Button.hoverSelectedBorderColor" );
|
||||||
|
/** @since 3.5 */ @Styleable protected Color pressedSelectedBorderColor = UIManager.getColor( "Button.pressedSelectedBorderColor" );
|
||||||
|
|
||||||
@Styleable(dot=true) protected float defaultBorderWidth = FlatUIUtils.getUIFloat( "Button.default.borderWidth", 1 );
|
@Styleable(dot=true) protected float defaultBorderWidth = FlatUIUtils.getUIFloat( "Button.default.borderWidth", 1 );
|
||||||
@Styleable(dot=true) protected Color defaultBorderColor = FlatUIUtils.getUIColor( "Button.default.startBorderColor", "Button.default.borderColor" );
|
@Styleable(dot=true) protected Color defaultBorderColor = FlatUIUtils.getUIColor( "Button.default.startBorderColor", "Button.default.borderColor" );
|
||||||
@@ -72,6 +87,7 @@ public class FlatButtonBorder
|
|||||||
@Styleable(dot=true) protected Color defaultFocusedBorderColor = UIManager.getColor( "Button.default.focusedBorderColor" );
|
@Styleable(dot=true) protected Color defaultFocusedBorderColor = UIManager.getColor( "Button.default.focusedBorderColor" );
|
||||||
@Styleable(dot=true) protected Color defaultFocusColor = UIManager.getColor( "Button.default.focusColor" );
|
@Styleable(dot=true) protected Color defaultFocusColor = UIManager.getColor( "Button.default.focusColor" );
|
||||||
@Styleable(dot=true) protected Color defaultHoverBorderColor = UIManager.getColor( "Button.default.hoverBorderColor" );
|
@Styleable(dot=true) protected Color defaultHoverBorderColor = UIManager.getColor( "Button.default.hoverBorderColor" );
|
||||||
|
/** @since 3.5 */ @Styleable(dot=true) protected Color defaultPressedBorderColor = UIManager.getColor( "Button.default.pressedBorderColor" );
|
||||||
|
|
||||||
/** @since 1.4 */ @Styleable(dot=true) protected float toolbarFocusWidth = FlatUIUtils.getUIFloat( "Button.toolbar.focusWidth", 1.5f );
|
/** @since 1.4 */ @Styleable(dot=true) protected float toolbarFocusWidth = FlatUIUtils.getUIFloat( "Button.toolbar.focusWidth", 1.5f );
|
||||||
/** @since 1.4 */ @Styleable(dot=true) protected Color toolbarFocusColor = UIManager.getColor( "Button.toolbar.focusColor" );
|
/** @since 1.4 */ @Styleable(dot=true) protected Color toolbarFocusColor = UIManager.getColor( "Button.toolbar.focusColor" );
|
||||||
@@ -139,12 +155,13 @@ public class FlatButtonBorder
|
|||||||
@Override
|
@Override
|
||||||
protected Paint getBorderColor( Component c ) {
|
protected Paint getBorderColor( Component c ) {
|
||||||
boolean def = FlatButtonUI.isDefaultButton( c );
|
boolean def = FlatButtonUI.isDefaultButton( c );
|
||||||
|
boolean selected = (c instanceof AbstractButton && ((AbstractButton)c).isSelected());
|
||||||
Paint color = FlatButtonUI.buttonStateColor( c,
|
Paint color = FlatButtonUI.buttonStateColor( c,
|
||||||
def ? defaultBorderColor : borderColor,
|
def ? defaultBorderColor : ((selected && selectedBorderColor != null) ? selectedBorderColor : borderColor),
|
||||||
disabledBorderColor,
|
(selected && disabledSelectedBorderColor != null) ? disabledSelectedBorderColor : disabledBorderColor,
|
||||||
def ? defaultFocusedBorderColor : focusedBorderColor,
|
def ? defaultFocusedBorderColor : ((selected && focusedSelectedBorderColor != null) ? focusedSelectedBorderColor : focusedBorderColor),
|
||||||
def ? defaultHoverBorderColor : hoverBorderColor,
|
def ? defaultHoverBorderColor : ((selected && hoverSelectedBorderColor != null) ? hoverSelectedBorderColor : hoverBorderColor),
|
||||||
null );
|
def ? defaultPressedBorderColor : ((selected && pressedSelectedBorderColor != null) ? pressedSelectedBorderColor : pressedBorderColor) );
|
||||||
|
|
||||||
// change to gradient paint if start/end colors are specified
|
// change to gradient paint if start/end colors are specified
|
||||||
Color startBg = def ? defaultBorderColor : borderColor;
|
Color startBg = def ? defaultBorderColor : borderColor;
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ 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.Rectangle;
|
||||||
|
import java.awt.event.FocusEvent;
|
||||||
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.Map;
|
||||||
@@ -53,12 +54,15 @@ import javax.swing.plaf.ToolBarUI;
|
|||||||
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 javax.swing.plaf.basic.BasicHTML;
|
||||||
|
import javax.swing.text.View;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
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.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableObject;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -276,8 +280,6 @@ public class FlatButtonUI
|
|||||||
|
|
||||||
LookAndFeel.installProperty( b, "opaque", false );
|
LookAndFeel.installProperty( b, "opaque", false );
|
||||||
LookAndFeel.installProperty( b, "iconTextGap", scale( iconTextGap ) );
|
LookAndFeel.installProperty( b, "iconTextGap", scale( iconTextGap ) );
|
||||||
|
|
||||||
MigLayoutVisualPadding.install( b );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -287,10 +289,23 @@ public class FlatButtonUI
|
|||||||
oldStyleValues = null;
|
oldStyleValues = null;
|
||||||
borderShared = null;
|
borderShared = null;
|
||||||
|
|
||||||
MigLayoutVisualPadding.uninstall( b );
|
|
||||||
defaults_initialized = false;
|
defaults_initialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installListeners( AbstractButton b ) {
|
||||||
|
super.installListeners( b );
|
||||||
|
|
||||||
|
MigLayoutVisualPadding.install( b );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallListeners( AbstractButton b ) {
|
||||||
|
super.uninstallListeners( b );
|
||||||
|
|
||||||
|
MigLayoutVisualPadding.uninstall( b );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected BasicButtonListener createButtonListener( AbstractButton b ) {
|
protected BasicButtonListener createButtonListener( AbstractButton b ) {
|
||||||
return new FlatButtonListener( b );
|
return new FlatButtonListener( b );
|
||||||
@@ -298,6 +313,10 @@ public class FlatButtonUI
|
|||||||
|
|
||||||
protected void propertyChange( AbstractButton b, PropertyChangeEvent e ) {
|
protected void propertyChange( AbstractButton b, PropertyChangeEvent e ) {
|
||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
|
case BasicHTML.propertyKey:
|
||||||
|
FlatHTML.updateRendererCSSFontBaseSize( b );
|
||||||
|
break;
|
||||||
|
|
||||||
case SQUARE_SIZE:
|
case SQUARE_SIZE:
|
||||||
case MINIMUM_WIDTH:
|
case MINIMUM_WIDTH:
|
||||||
case MINIMUM_HEIGHT:
|
case MINIMUM_HEIGHT:
|
||||||
@@ -306,11 +325,11 @@ public class FlatButtonUI
|
|||||||
|
|
||||||
case BUTTON_TYPE:
|
case BUTTON_TYPE:
|
||||||
b.revalidate();
|
b.revalidate();
|
||||||
b.repaint();
|
HiDPIUtils.repaint( b );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OUTLINE:
|
case OUTLINE:
|
||||||
b.repaint();
|
HiDPIUtils.repaint( b );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STYLE:
|
case STYLE:
|
||||||
@@ -322,7 +341,7 @@ public class FlatButtonUI
|
|||||||
} else
|
} else
|
||||||
installStyle( b );
|
installStyle( b );
|
||||||
b.revalidate();
|
b.revalidate();
|
||||||
b.repaint();
|
HiDPIUtils.repaint( b );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -350,8 +369,8 @@ public class FlatButtonUI
|
|||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
protected Object applyStyleProperty( AbstractButton b, String key, Object value ) {
|
protected Object applyStyleProperty( AbstractButton b, String key, Object value ) {
|
||||||
if( key.startsWith( "help." ) ) {
|
if( key.startsWith( "help." ) ) {
|
||||||
if( !(helpButtonIcon instanceof FlatHelpButtonIcon) )
|
if( !(helpButtonIcon instanceof StyleableObject) )
|
||||||
return new UnknownStyleException( key );
|
throw new UnknownStyleException( key );
|
||||||
|
|
||||||
if( helpButtonIconShared ) {
|
if( helpButtonIconShared ) {
|
||||||
helpButtonIcon = FlatStylingSupport.cloneIcon( helpButtonIcon );
|
helpButtonIcon = FlatStylingSupport.cloneIcon( helpButtonIcon );
|
||||||
@@ -359,9 +378,18 @@ public class FlatButtonUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
key = key.substring( "help.".length() );
|
key = key.substring( "help.".length() );
|
||||||
return ((FlatHelpButtonIcon)helpButtonIcon).applyStyleProperty( key, value );
|
return ((StyleableObject)helpButtonIcon).applyStyleProperty( key, value );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update internal values; otherwise isCustomBackground() and isCustomForeground() would return wrong results
|
||||||
|
switch( key ) {
|
||||||
|
case "background": background = (Color) value; break;
|
||||||
|
case "foreground": foreground = (Color) value; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( "iconTextGap".equals( key ) && value instanceof Integer )
|
||||||
|
value = UIScale.scale( (Integer) value );
|
||||||
|
|
||||||
if( borderShared == null )
|
if( borderShared == null )
|
||||||
borderShared = new AtomicBoolean( true );
|
borderShared = new AtomicBoolean( true );
|
||||||
return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, b, borderShared );
|
return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, b, borderShared );
|
||||||
@@ -371,8 +399,8 @@ public class FlatButtonUI
|
|||||||
@Override
|
@Override
|
||||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
Map<String, Class<?>> infos = FlatStylingSupport.getAnnotatedStyleableInfos( this, c.getBorder() );
|
Map<String, Class<?>> infos = FlatStylingSupport.getAnnotatedStyleableInfos( this, c.getBorder() );
|
||||||
if( helpButtonIcon instanceof FlatHelpButtonIcon )
|
if( helpButtonIcon instanceof StyleableObject )
|
||||||
FlatStylingSupport.putAllPrefixKey( infos, "help.", ((FlatHelpButtonIcon)helpButtonIcon).getStyleableInfos() );
|
FlatStylingSupport.putAllPrefixKey( infos, "help.", ((StyleableObject)helpButtonIcon).getStyleableInfos() );
|
||||||
return infos;
|
return infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,8 +408,8 @@ public class FlatButtonUI
|
|||||||
@Override
|
@Override
|
||||||
public Object getStyleableValue( JComponent c, String key ) {
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
if( key.startsWith( "help." ) ) {
|
if( key.startsWith( "help." ) ) {
|
||||||
return (helpButtonIcon instanceof FlatHelpButtonIcon)
|
return (helpButtonIcon instanceof StyleableObject)
|
||||||
? ((FlatHelpButtonIcon)helpButtonIcon).getStyleableValue( key.substring( "help.".length() ) )
|
? ((StyleableObject)helpButtonIcon).getStyleableValue( key.substring( "help.".length() ) )
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -548,9 +576,52 @@ public class FlatButtonUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to BasicButtonUI.paint(), but does not use zero insets for HTML text,
|
||||||
|
* which is done in BasicButtonUI.layout() since Java 19.
|
||||||
|
* See https://github.com/openjdk/jdk/pull/8407
|
||||||
|
* and https://github.com/openjdk/jdk/pull/8407#issuecomment-1761583430
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void paint( Graphics g, JComponent c ) {
|
public void paint( Graphics g, JComponent c ) {
|
||||||
super.paint( FlatLabelUI.createGraphicsHTMLTextYCorrection( g, c ), c );
|
g = FlatLabelUI.createGraphicsHTMLTextYCorrection( g, c );
|
||||||
|
|
||||||
|
AbstractButton b = (AbstractButton) c;
|
||||||
|
|
||||||
|
// layout
|
||||||
|
String clippedText = layout( b, b.getFontMetrics( b.getFont() ), b.getWidth(), b.getHeight() );
|
||||||
|
|
||||||
|
// not used in FlatLaf, but invoked for compatibility with BasicButtonUI.paint()
|
||||||
|
clearTextShiftOffset();
|
||||||
|
|
||||||
|
// not used in FlatLaf, but invoked for compatibility with BasicButtonUI.paint()
|
||||||
|
ButtonModel model = b.getModel();
|
||||||
|
if( model.isArmed() && model.isPressed() )
|
||||||
|
paintButtonPressed( g, b );
|
||||||
|
|
||||||
|
// paint icon
|
||||||
|
if( b.getIcon() != null )
|
||||||
|
paintIcon( g, b, iconR );
|
||||||
|
|
||||||
|
// paint text
|
||||||
|
if( clippedText != null && !clippedText.isEmpty() ) {
|
||||||
|
View view = (View) b.getClientProperty( BasicHTML.propertyKey );
|
||||||
|
if( view != null ) {
|
||||||
|
// update foreground color in HTML view, which is necessary
|
||||||
|
// for selected and pressed states
|
||||||
|
// (only for enabled buttons, because UIManager.getColor("textInactiveText")
|
||||||
|
// is used for disabled components; see: javax.swing.text.GlyphView.paint())
|
||||||
|
if( b.isEnabled() )
|
||||||
|
FlatHTML.updateRendererCSSForeground( view, getForeground( b ) );
|
||||||
|
|
||||||
|
view.paint( g, textR ); // HTML text
|
||||||
|
} else
|
||||||
|
paintText( g, b, textR, clippedText );
|
||||||
|
}
|
||||||
|
|
||||||
|
// not used in FlatLaf, but invoked for compatibility with BasicButtonUI.paint()
|
||||||
|
if( b.isFocusPainted() && b.hasFocus() )
|
||||||
|
paintFocus( g, b, viewR, textR, iconR );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -590,8 +661,6 @@ 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;
|
||||||
|
|
||||||
@@ -601,7 +670,8 @@ public class FlatButtonUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Color getBackground( JComponent c ) {
|
protected Color getBackground( JComponent c ) {
|
||||||
boolean toolBarButton = isToolBarButton( c ) || isBorderlessButton( c );
|
boolean def = isDefaultButton( c );
|
||||||
|
boolean toolBarButton = !def && (isToolBarButton( c ) || isBorderlessButton( c ));
|
||||||
|
|
||||||
// selected state
|
// selected state
|
||||||
if( ((AbstractButton)c).isSelected() ) {
|
if( ((AbstractButton)c).isSelected() ) {
|
||||||
@@ -629,7 +699,6 @@ public class FlatButtonUI
|
|||||||
toolbarPressedBackground );
|
toolbarPressedBackground );
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean def = isDefaultButton( c );
|
|
||||||
return buttonStateColor( c,
|
return buttonStateColor( c,
|
||||||
getBackgroundBase( c, def ),
|
getBackgroundBase( c, def ),
|
||||||
disabledBackground,
|
disabledBackground,
|
||||||
@@ -680,14 +749,16 @@ public class FlatButtonUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Color getForeground( JComponent c ) {
|
protected Color getForeground( JComponent c ) {
|
||||||
boolean toolBarButton = isToolBarButton( c ) || isBorderlessButton( c );
|
Color fg = c.getForeground();
|
||||||
|
boolean def = isDefaultButton( c );
|
||||||
|
boolean toolBarButton = !def && (isToolBarButton( c ) || isBorderlessButton( c ));
|
||||||
|
|
||||||
// selected state
|
// selected state
|
||||||
if( ((AbstractButton)c).isSelected() ) {
|
if( ((AbstractButton)c).isSelected() ) {
|
||||||
return buttonStateColor( c,
|
return buttonStateColor( c,
|
||||||
toolBarButton
|
toolBarButton
|
||||||
? (toolbarSelectedForeground != null ? toolbarSelectedForeground : c.getForeground())
|
? (toolbarSelectedForeground != null ? toolbarSelectedForeground : fg)
|
||||||
: selectedForeground,
|
: (isCustomForeground( fg ) ? fg : selectedForeground),
|
||||||
toolBarButton
|
toolBarButton
|
||||||
? (toolbarDisabledSelectedForeground != null ? toolbarDisabledSelectedForeground : disabledText)
|
? (toolbarDisabledSelectedForeground != null ? toolbarDisabledSelectedForeground : disabledText)
|
||||||
: (disabledSelectedForeground != null ? disabledSelectedForeground : disabledText),
|
: (disabledSelectedForeground != null ? disabledSelectedForeground : disabledText),
|
||||||
@@ -699,18 +770,17 @@ public class FlatButtonUI
|
|||||||
// toolbar button
|
// toolbar button
|
||||||
if( toolBarButton ) {
|
if( toolBarButton ) {
|
||||||
return buttonStateColor( c,
|
return buttonStateColor( c,
|
||||||
c.getForeground(),
|
fg,
|
||||||
disabledText,
|
disabledText,
|
||||||
null,
|
null,
|
||||||
toolbarHoverForeground,
|
toolbarHoverForeground,
|
||||||
toolbarPressedForeground );
|
toolbarPressedForeground );
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean def = isDefaultButton( c );
|
|
||||||
return buttonStateColor( c,
|
return buttonStateColor( c,
|
||||||
getForegroundBase( c, def ),
|
getForegroundBase( c, def ),
|
||||||
disabledText,
|
disabledText,
|
||||||
isCustomForeground( c.getForeground() ) ? null : (def ? defaultFocusedForeground : focusedForeground),
|
isCustomForeground( fg ) ? null : (def ? defaultFocusedForeground : focusedForeground),
|
||||||
def ? defaultHoverForeground : hoverForeground,
|
def ? defaultHoverForeground : hoverForeground,
|
||||||
def ? defaultPressedForeground : pressedForeground );
|
def ? defaultPressedForeground : pressedForeground );
|
||||||
}
|
}
|
||||||
@@ -783,6 +853,67 @@ public class FlatButtonUI
|
|||||||
return margin instanceof UIResource && Objects.equals( margin, defaultMargin );
|
return margin instanceof UIResource && Objects.equals( margin, defaultMargin );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBaseline( JComponent c, int width, int height ) {
|
||||||
|
return getBaselineImpl( c, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to BasicButtonUI.getBaseline(), but does not use zero insets for HTML text,
|
||||||
|
* which is done in BasicButtonUI.layout() since Java 19.
|
||||||
|
* See https://github.com/openjdk/jdk/pull/8407
|
||||||
|
* and https://github.com/openjdk/jdk/pull/8407#issuecomment-1761583430
|
||||||
|
*/
|
||||||
|
static int getBaselineImpl( JComponent c, int width, int height ) {
|
||||||
|
if( width < 0 || height < 0 )
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
|
||||||
|
AbstractButton b = (AbstractButton) c;
|
||||||
|
String text = b.getText();
|
||||||
|
if( text == null || text.isEmpty() )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
FontMetrics fm = b.getFontMetrics( b.getFont() );
|
||||||
|
layout( b, fm, width, height );
|
||||||
|
|
||||||
|
View view = (View) b.getClientProperty( BasicHTML.propertyKey );
|
||||||
|
if( view != null ) {
|
||||||
|
// HTML text
|
||||||
|
int baseline = BasicHTML.getHTMLBaseline( view, textR.width, textR.height );
|
||||||
|
return (baseline >= 0) ? textR.y + baseline : baseline;
|
||||||
|
} else
|
||||||
|
return textR.y + fm.getAscent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to BasicButtonUI.layout(), but does not use zero insets for HTML text,
|
||||||
|
* which is done in BasicButtonUI.layout() since Java 19.
|
||||||
|
* See https://github.com/openjdk/jdk/pull/8407
|
||||||
|
* and https://github.com/openjdk/jdk/pull/8407#issuecomment-1761583430
|
||||||
|
*/
|
||||||
|
private static String layout( AbstractButton b, FontMetrics fm, int width, int height ) {
|
||||||
|
// compute view rectangle
|
||||||
|
Insets insets = b.getInsets();
|
||||||
|
viewR.setBounds( insets.left, insets.top,
|
||||||
|
width - insets.left - insets.right,
|
||||||
|
height - insets.top - insets.bottom );
|
||||||
|
|
||||||
|
// reset rectangles
|
||||||
|
textR.setBounds( 0, 0, 0, 0 );
|
||||||
|
iconR.setBounds( 0, 0, 0, 0 );
|
||||||
|
|
||||||
|
String text = b.getText();
|
||||||
|
return SwingUtilities.layoutCompoundLabel( b, fm, text, b.getIcon(),
|
||||||
|
b.getVerticalAlignment(), b.getHorizontalAlignment(),
|
||||||
|
b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
|
||||||
|
viewR, iconR, textR,
|
||||||
|
(text != null) ? b.getIconTextGap() : 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Rectangle viewR = new Rectangle();
|
||||||
|
private static Rectangle textR = new Rectangle();
|
||||||
|
private static Rectangle iconR = new Rectangle();
|
||||||
|
|
||||||
//---- class FlatButtonListener -------------------------------------------
|
//---- class FlatButtonListener -------------------------------------------
|
||||||
|
|
||||||
protected class FlatButtonListener
|
protected class FlatButtonListener
|
||||||
@@ -803,7 +934,7 @@ public class FlatButtonUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stateChanged( ChangeEvent e ) {
|
public void stateChanged( ChangeEvent e ) {
|
||||||
super.stateChanged( e );
|
HiDPIUtils.repaint( b );
|
||||||
|
|
||||||
// if button is in toolbar, repaint button groups
|
// if button is in toolbar, repaint button groups
|
||||||
AbstractButton b = (AbstractButton) e.getSource();
|
AbstractButton b = (AbstractButton) e.getSource();
|
||||||
@@ -815,5 +946,17 @@ public class FlatButtonUI
|
|||||||
((FlatToolBarUI)ui).repaintButtonGroup( b );
|
((FlatToolBarUI)ui).repaintButtonGroup( b );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void focusGained( FocusEvent e ) {
|
||||||
|
super.focusGained( e );
|
||||||
|
HiDPIUtils.repaint( b );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void focusLost( FocusEvent e ) {
|
||||||
|
super.focusLost( e );
|
||||||
|
HiDPIUtils.repaint( b );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import static com.formdev.flatlaf.FlatClientProperties.*;
|
import static com.formdev.flatlaf.FlatClientProperties.*;
|
||||||
|
import java.awt.Container;
|
||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
@@ -24,7 +25,9 @@ import java.awt.event.FocusEvent;
|
|||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
import javax.swing.ActionMap;
|
import javax.swing.ActionMap;
|
||||||
|
import javax.swing.JComboBox;
|
||||||
import javax.swing.JFormattedTextField;
|
import javax.swing.JFormattedTextField;
|
||||||
|
import javax.swing.JSpinner;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
@@ -33,6 +36,7 @@ import javax.swing.text.DefaultCaret;
|
|||||||
import javax.swing.text.DefaultEditorKit;
|
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.Position;
|
||||||
import javax.swing.text.Utilities;
|
import javax.swing.text.Utilities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,12 +52,15 @@ public class FlatCaret
|
|||||||
{
|
{
|
||||||
private static final String KEY_CARET_INFO = "FlatLaf.internal.caretInfo";
|
private static final String KEY_CARET_INFO = "FlatLaf.internal.caretInfo";
|
||||||
|
|
||||||
|
// selectAllOnFocusPolicy
|
||||||
|
private static final int NEVER = 0, ONCE = 1, ALWAYS = 2;
|
||||||
|
|
||||||
private final String selectAllOnFocusPolicy;
|
private final String selectAllOnFocusPolicy;
|
||||||
private final boolean selectAllOnMouseClick;
|
private final boolean selectAllOnMouseClick;
|
||||||
|
|
||||||
private boolean inInstall;
|
private boolean inInstall;
|
||||||
private boolean wasFocused;
|
private boolean wasFocused;
|
||||||
private boolean wasTemporaryLost;
|
private boolean wasFocusTemporaryLost;
|
||||||
private boolean isMousePressed;
|
private boolean isMousePressed;
|
||||||
private boolean isWordSelection;
|
private boolean isWordSelection;
|
||||||
private boolean isLineSelection;
|
private boolean isLineSelection;
|
||||||
@@ -94,6 +101,9 @@ public class FlatCaret
|
|||||||
// restore selection
|
// restore selection
|
||||||
select( (int) ci[1], (int) ci[0] );
|
select( (int) ci[1], (int) ci[0] );
|
||||||
|
|
||||||
|
if( ci[4] != 0 )
|
||||||
|
wasFocused = true;
|
||||||
|
|
||||||
// if text component is focused, then caret and selection are visible,
|
// if text component is focused, then caret and selection are visible,
|
||||||
// but when switching theme, the component does not yet have
|
// but when switching theme, the component does not yet have
|
||||||
// a highlighter and the selection is not painted
|
// a highlighter and the selection is not painted
|
||||||
@@ -121,6 +131,7 @@ public class FlatCaret
|
|||||||
getMark(),
|
getMark(),
|
||||||
getBlinkRate(),
|
getBlinkRate(),
|
||||||
System.currentTimeMillis(),
|
System.currentTimeMillis(),
|
||||||
|
wasFocused ? 1 : 0,
|
||||||
} );
|
} );
|
||||||
|
|
||||||
super.deinstall( c );
|
super.deinstall( c );
|
||||||
@@ -140,11 +151,36 @@ public class FlatCaret
|
|||||||
super.adjustVisibility( nloc );
|
super.adjustVisibility( nloc );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDot( int dot ) {
|
||||||
|
super.setDot( dot );
|
||||||
|
|
||||||
|
// mark as focused if invoked from JTextComponent.setCaretPosition()
|
||||||
|
// to disable SELECT_ALL_ON_FOCUS_POLICY_ONCE if application explicitly changes selection
|
||||||
|
if( !wasFocused &&
|
||||||
|
getSelectAllOnFocusPolicy() == ONCE &&
|
||||||
|
StackUtils.wasInvokedFrom( JTextComponent.class.getName(), "setCaretPosition", 6 ) )
|
||||||
|
wasFocused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void moveDot( int dot ) {
|
||||||
|
super.moveDot( dot );
|
||||||
|
|
||||||
|
// mark as focused if invoked from JTextComponent.moveCaretPosition()
|
||||||
|
// to disable SELECT_ALL_ON_FOCUS_POLICY_ONCE if application explicitly changes selection
|
||||||
|
if( !wasFocused &&
|
||||||
|
getSelectAllOnFocusPolicy() == ONCE &&
|
||||||
|
StackUtils.wasInvokedFrom( JTextComponent.class.getName(), "moveCaretPosition", 6 ) )
|
||||||
|
wasFocused = true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void focusGained( FocusEvent e ) {
|
public void focusGained( FocusEvent e ) {
|
||||||
if( !inInstall && !wasTemporaryLost && (!isMousePressed || selectAllOnMouseClick) )
|
if( !inInstall && !wasFocusTemporaryLost && (!isMousePressed || isSelectAllOnMouseClick()) )
|
||||||
selectAllOnFocusGained();
|
selectAllOnFocusGained();
|
||||||
wasTemporaryLost = false;
|
|
||||||
|
wasFocusTemporaryLost = false;
|
||||||
wasFocused = true;
|
wasFocused = true;
|
||||||
|
|
||||||
super.focusGained( e );
|
super.focusGained( e );
|
||||||
@@ -152,7 +188,7 @@ public class FlatCaret
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void focusLost( FocusEvent e ) {
|
public void focusLost( FocusEvent e ) {
|
||||||
wasTemporaryLost = e.isTemporary();
|
wasFocusTemporaryLost = e.isTemporary();
|
||||||
super.focusLost( e );
|
super.focusLost( e );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,45 +268,73 @@ public class FlatCaret
|
|||||||
if( doc == null || !c.isEnabled() || !c.isEditable() || FlatUIUtils.isCellEditor( c ) )
|
if( doc == null || !c.isEnabled() || !c.isEditable() || FlatUIUtils.isCellEditor( c ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Object selectAllOnFocusPolicy = c.getClientProperty( SELECT_ALL_ON_FOCUS_POLICY );
|
int selectAllOnFocusPolicy = getSelectAllOnFocusPolicy();
|
||||||
if( selectAllOnFocusPolicy == null )
|
if( selectAllOnFocusPolicy == NEVER )
|
||||||
selectAllOnFocusPolicy = this.selectAllOnFocusPolicy;
|
|
||||||
|
|
||||||
if( selectAllOnFocusPolicy == null || SELECT_ALL_ON_FOCUS_POLICY_NEVER.equals( selectAllOnFocusPolicy ) )
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if( !SELECT_ALL_ON_FOCUS_POLICY_ALWAYS.equals( selectAllOnFocusPolicy ) ) {
|
if( selectAllOnFocusPolicy == ONCE && !isMousePressed ) {
|
||||||
// policy is "once" (or null or unknown)
|
|
||||||
|
|
||||||
// was already focused?
|
// was already focused?
|
||||||
if( wasFocused )
|
if( wasFocused && !(c instanceof JFormattedTextField) )
|
||||||
return;
|
|
||||||
|
|
||||||
// check whether selection was modified before gaining focus
|
|
||||||
int dot = getDot();
|
|
||||||
int mark = getMark();
|
|
||||||
if( dot != mark || dot != doc.getLength() )
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// select all
|
// select all
|
||||||
if( c instanceof JFormattedTextField ) {
|
if( c instanceof JFormattedTextField ) {
|
||||||
EventQueue.invokeLater( () -> {
|
EventQueue.invokeLater( () -> {
|
||||||
if( getComponent() == null )
|
// Warning: do not use variables from outside of this runnable
|
||||||
|
// because they may be out-of-date when this runnable is executed
|
||||||
|
|
||||||
|
JTextComponent c2 = getComponent();
|
||||||
|
if( c2 == null )
|
||||||
return; // was deinstalled
|
return; // was deinstalled
|
||||||
|
|
||||||
select( 0, doc.getLength() );
|
select( 0, c2.getDocument().getLength() );
|
||||||
} );
|
} );
|
||||||
} else {
|
} else
|
||||||
select( 0, doc.getLength() );
|
select( 0, doc.getLength() );
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void select( int mark, int dot ) {
|
private void select( int mark, int dot ) {
|
||||||
if( mark != getMark() )
|
if( mark != getMark() )
|
||||||
setDot( mark );
|
setDot( mark, Position.Bias.Forward );
|
||||||
if( dot != getDot() )
|
if( dot != getDot() )
|
||||||
moveDot( dot );
|
moveDot( dot, Position.Bias.Forward );
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getSelectAllOnFocusPolicy() {
|
||||||
|
Object value = getClientProperty( SELECT_ALL_ON_FOCUS_POLICY );
|
||||||
|
// Note: using String.valueOf() because selectAllOnFocusPolicy may be null
|
||||||
|
switch( String.valueOf( value instanceof String ? value : selectAllOnFocusPolicy ) ) {
|
||||||
|
default:
|
||||||
|
case SELECT_ALL_ON_FOCUS_POLICY_NEVER: return NEVER;
|
||||||
|
case SELECT_ALL_ON_FOCUS_POLICY_ONCE: return ONCE;
|
||||||
|
case SELECT_ALL_ON_FOCUS_POLICY_ALWAYS: return ALWAYS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSelectAllOnMouseClick() {
|
||||||
|
Object value = getClientProperty( SELECT_ALL_ON_MOUSE_CLICK );
|
||||||
|
return (value instanceof Boolean) ? (boolean) value : selectAllOnMouseClick;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getClientProperty( String key ) {
|
||||||
|
JTextComponent c = getComponent();
|
||||||
|
if( c == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Object value = c.getClientProperty( key );
|
||||||
|
if( value != null )
|
||||||
|
return value;
|
||||||
|
|
||||||
|
Container parent = c.getParent();
|
||||||
|
if( parent instanceof JComboBox )
|
||||||
|
return ((JComboBox<?>)parent).getClientProperty( key );
|
||||||
|
if( parent instanceof JSpinner.DefaultEditor ) {
|
||||||
|
parent = parent.getParent();
|
||||||
|
if( parent instanceof JSpinner )
|
||||||
|
return ((JSpinner)parent).getClientProperty( key );
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 1.4 */
|
/** @since 1.4 */
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import java.lang.invoke.MethodHandles;
|
|||||||
import java.util.Map;
|
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.BasicCheckBoxMenuItemUI;
|
import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
|
||||||
@@ -102,13 +103,23 @@ public class FlatCheckBoxMenuItemUI
|
|||||||
oldStyleValues = null;
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installComponents( JMenuItem menuItem ) {
|
||||||
|
super.installComponents( menuItem );
|
||||||
|
|
||||||
|
// update HTML renderer if necessary
|
||||||
|
FlatHTML.updateRendererCSSFontBaseSize( menuItem );
|
||||||
|
}
|
||||||
|
|
||||||
protected FlatMenuItemRenderer createRenderer() {
|
protected FlatMenuItemRenderer createRenderer() {
|
||||||
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
||||||
return FlatStylingSupport.createPropertyChangeListener( c, this::installStyle, super.createPropertyChangeListener( c ) );
|
return FlatHTML.createPropertyChangeListener(
|
||||||
|
FlatStylingSupport.createPropertyChangeListener( c, this::installStyle,
|
||||||
|
super.createPropertyChangeListener( c ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ 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.EventQueue;
|
||||||
import java.awt.FontMetrics;
|
import java.awt.FontMetrics;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
@@ -48,10 +49,12 @@ 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.ComboBoxModel;
|
||||||
import javax.swing.DefaultListCellRenderer;
|
import javax.swing.DefaultListCellRenderer;
|
||||||
import javax.swing.InputMap;
|
import javax.swing.InputMap;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComboBox;
|
import javax.swing.JComboBox;
|
||||||
|
import javax.swing.JComboBox.KeySelectionManager;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JList;
|
import javax.swing.JList;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
@@ -75,6 +78,7 @@ import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
|||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
@@ -102,7 +106,6 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
* @uiDefault ComboBox.maximumRowCount int
|
* @uiDefault ComboBox.maximumRowCount int
|
||||||
* @uiDefault ComboBox.buttonStyle String auto (default), button, mac or none
|
* @uiDefault ComboBox.buttonStyle String auto (default), button, mac or none
|
||||||
* @uiDefault Component.arrowType String chevron (default) or triangle
|
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
|
||||||
* @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
|
||||||
@@ -110,6 +113,7 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
* @uiDefault ComboBox.buttonBackground Color optional
|
* @uiDefault ComboBox.buttonBackground Color optional
|
||||||
* @uiDefault ComboBox.buttonEditableBackground Color optional
|
* @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.buttonFocusedEditableBackground Color optional; defaults to ComboBox.buttonEditableBackground
|
||||||
* @uiDefault ComboBox.buttonSeparatorWidth int or float optional; defaults to Component.borderWidth
|
* @uiDefault ComboBox.buttonSeparatorWidth int or float optional; defaults to Component.borderWidth
|
||||||
* @uiDefault ComboBox.buttonSeparatorColor Color optional
|
* @uiDefault ComboBox.buttonSeparatorColor Color optional
|
||||||
* @uiDefault ComboBox.buttonDisabledSeparatorColor Color optional
|
* @uiDefault ComboBox.buttonDisabledSeparatorColor Color optional
|
||||||
@@ -134,7 +138,6 @@ public class FlatComboBoxUI
|
|||||||
@Styleable protected int editorColumns;
|
@Styleable protected int editorColumns;
|
||||||
@Styleable protected String buttonStyle;
|
@Styleable protected String buttonStyle;
|
||||||
@Styleable protected String arrowType;
|
@Styleable protected String arrowType;
|
||||||
protected boolean isIntelliJTheme;
|
|
||||||
|
|
||||||
private Color background;
|
private Color background;
|
||||||
@Styleable protected Color editableBackground;
|
@Styleable protected Color editableBackground;
|
||||||
@@ -145,6 +148,7 @@ public class FlatComboBoxUI
|
|||||||
@Styleable protected Color buttonBackground;
|
@Styleable protected Color buttonBackground;
|
||||||
@Styleable protected Color buttonEditableBackground;
|
@Styleable protected Color buttonEditableBackground;
|
||||||
@Styleable protected Color buttonFocusedBackground;
|
@Styleable protected Color buttonFocusedBackground;
|
||||||
|
/** @since 3.7.1 */ @Styleable protected Color buttonFocusedEditableBackground;
|
||||||
/** @since 2 */ @Styleable protected float buttonSeparatorWidth;
|
/** @since 2 */ @Styleable protected float buttonSeparatorWidth;
|
||||||
/** @since 2 */ @Styleable protected Color buttonSeparatorColor;
|
/** @since 2 */ @Styleable protected Color buttonSeparatorColor;
|
||||||
/** @since 2 */ @Styleable protected Color buttonDisabledSeparatorColor;
|
/** @since 2 */ @Styleable protected Color buttonDisabledSeparatorColor;
|
||||||
@@ -182,6 +186,9 @@ public class FlatComboBoxUI
|
|||||||
private void installUIImpl( JComponent c ) {
|
private void installUIImpl( JComponent c ) {
|
||||||
super.installUI( c );
|
super.installUI( c );
|
||||||
|
|
||||||
|
// install key selection manager that shows popup when Space key is pressed
|
||||||
|
comboBox.setKeySelectionManager( new FlatKeySelectionManager( comboBox.getKeySelectionManager() ) );
|
||||||
|
|
||||||
installStyle();
|
installStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,10 +223,12 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
private void repaintArrowButton() {
|
private void repaintArrowButton() {
|
||||||
if( arrowButton != null && !comboBox.isEditable() )
|
if( arrowButton != null && !comboBox.isEditable() )
|
||||||
arrowButton.repaint();
|
HiDPIUtils.repaint( arrowButton );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
comboBox.addMouseListener( hoverListener );
|
comboBox.addMouseListener( hoverListener );
|
||||||
|
|
||||||
|
MigLayoutVisualPadding.install( comboBox );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -228,6 +237,8 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
comboBox.removeMouseListener( hoverListener );
|
comboBox.removeMouseListener( hoverListener );
|
||||||
hoverListener = null;
|
hoverListener = null;
|
||||||
|
|
||||||
|
MigLayoutVisualPadding.uninstall( comboBox );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -240,7 +251,6 @@ public class FlatComboBoxUI
|
|||||||
editorColumns = UIManager.getInt( "ComboBox.editorColumns" );
|
editorColumns = UIManager.getInt( "ComboBox.editorColumns" );
|
||||||
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" );
|
|
||||||
|
|
||||||
background = UIManager.getColor( "ComboBox.background" );
|
background = UIManager.getColor( "ComboBox.background" );
|
||||||
editableBackground = UIManager.getColor( "ComboBox.editableBackground" );
|
editableBackground = UIManager.getColor( "ComboBox.editableBackground" );
|
||||||
@@ -250,6 +260,7 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
buttonBackground = UIManager.getColor( "ComboBox.buttonBackground" );
|
buttonBackground = UIManager.getColor( "ComboBox.buttonBackground" );
|
||||||
buttonFocusedBackground = UIManager.getColor( "ComboBox.buttonFocusedBackground" );
|
buttonFocusedBackground = UIManager.getColor( "ComboBox.buttonFocusedBackground" );
|
||||||
|
buttonFocusedEditableBackground = UIManager.getColor( "ComboBox.buttonFocusedEditableBackground" );
|
||||||
buttonEditableBackground = UIManager.getColor( "ComboBox.buttonEditableBackground" );
|
buttonEditableBackground = UIManager.getColor( "ComboBox.buttonEditableBackground" );
|
||||||
buttonSeparatorWidth = FlatUIUtils.getUIFloat( "ComboBox.buttonSeparatorWidth", FlatUIUtils.getUIFloat( "Component.borderWidth", 1 ) );
|
buttonSeparatorWidth = FlatUIUtils.getUIFloat( "ComboBox.buttonSeparatorWidth", FlatUIUtils.getUIFloat( "Component.borderWidth", 1 ) );
|
||||||
buttonSeparatorColor = UIManager.getColor( "ComboBox.buttonSeparatorColor" );
|
buttonSeparatorColor = UIManager.getColor( "ComboBox.buttonSeparatorColor" );
|
||||||
@@ -270,8 +281,6 @@ public class FlatComboBoxUI
|
|||||||
comboBox.setMaximumRowCount( maximumRowCount );
|
comboBox.setMaximumRowCount( maximumRowCount );
|
||||||
|
|
||||||
paddingBorder = new CellPaddingBorder( padding );
|
paddingBorder = new CellPaddingBorder( padding );
|
||||||
|
|
||||||
MigLayoutVisualPadding.install( comboBox );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -287,6 +296,7 @@ public class FlatComboBoxUI
|
|||||||
buttonBackground = null;
|
buttonBackground = null;
|
||||||
buttonEditableBackground = null;
|
buttonEditableBackground = null;
|
||||||
buttonFocusedBackground = null;
|
buttonFocusedBackground = null;
|
||||||
|
buttonFocusedEditableBackground = null;
|
||||||
buttonSeparatorColor = null;
|
buttonSeparatorColor = null;
|
||||||
buttonDisabledSeparatorColor = null;
|
buttonDisabledSeparatorColor = null;
|
||||||
buttonArrowColor = null;
|
buttonArrowColor = null;
|
||||||
@@ -300,8 +310,6 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
oldStyleValues = null;
|
oldStyleValues = null;
|
||||||
borderShared = null;
|
borderShared = null;
|
||||||
|
|
||||||
MigLayoutVisualPadding.uninstall( comboBox );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -348,15 +356,15 @@ public class FlatComboBoxUI
|
|||||||
@Override
|
@Override
|
||||||
public void focusGained( FocusEvent e ) {
|
public void focusGained( FocusEvent e ) {
|
||||||
super.focusGained( e );
|
super.focusGained( e );
|
||||||
if( comboBox != null && comboBox.isEditable() )
|
if( comboBox != null )
|
||||||
comboBox.repaint();
|
HiDPIUtils.repaint( comboBox );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void focusLost( FocusEvent e ) {
|
public void focusLost( FocusEvent e ) {
|
||||||
super.focusLost( e );
|
super.focusLost( e );
|
||||||
if( comboBox != null && comboBox.isEditable() )
|
if( comboBox != null )
|
||||||
comboBox.repaint();
|
HiDPIUtils.repaint( comboBox );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -383,12 +391,12 @@ public class FlatComboBoxUI
|
|||||||
switch( propertyName ) {
|
switch( propertyName ) {
|
||||||
case PLACEHOLDER_TEXT:
|
case PLACEHOLDER_TEXT:
|
||||||
if( editor != null )
|
if( editor != null )
|
||||||
editor.repaint();
|
HiDPIUtils.repaint( editor );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COMPONENT_ROUND_RECT:
|
case COMPONENT_ROUND_RECT:
|
||||||
case OUTLINE:
|
case OUTLINE:
|
||||||
comboBox.repaint();
|
HiDPIUtils.repaint( comboBox );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MINIMUM_WIDTH:
|
case MINIMUM_WIDTH:
|
||||||
@@ -399,7 +407,7 @@ public class FlatComboBoxUI
|
|||||||
case STYLE_CLASS:
|
case STYLE_CLASS:
|
||||||
installStyle();
|
installStyle();
|
||||||
comboBox.revalidate();
|
comboBox.revalidate();
|
||||||
comboBox.repaint();
|
HiDPIUtils.repaint( comboBox );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -581,9 +589,9 @@ public class FlatComboBoxUI
|
|||||||
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
|
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
|
||||||
|
|
||||||
// paint arrow button background
|
// paint arrow button background
|
||||||
if( enabled && !isCellRenderer ) {
|
if( enabled && !isCellRenderer && arrowButton.isVisible() ) {
|
||||||
Color buttonColor = paintButton
|
Color buttonColor = paintButton
|
||||||
? buttonEditableBackground
|
? (buttonFocusedEditableBackground != null && isPermanentFocusOwner( comboBox ) ? buttonFocusedEditableBackground : buttonEditableBackground)
|
||||||
: (buttonFocusedBackground != null || focusedBackground != null) && isPermanentFocusOwner( comboBox )
|
: (buttonFocusedBackground != null || focusedBackground != null) && isPermanentFocusOwner( comboBox )
|
||||||
? (buttonFocusedBackground != null ? buttonFocusedBackground : focusedBackground)
|
? (buttonFocusedBackground != null ? buttonFocusedBackground : focusedBackground)
|
||||||
: buttonBackground;
|
: buttonBackground;
|
||||||
@@ -608,7 +616,7 @@ public class FlatComboBoxUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// paint vertical line between value and arrow button
|
// paint vertical line between value and arrow button
|
||||||
if( paintButton ) {
|
if( paintButton && arrowButton.isVisible() ) {
|
||||||
Color separatorColor = enabled ? buttonSeparatorColor : buttonDisabledSeparatorColor;
|
Color separatorColor = enabled ? buttonSeparatorColor : buttonDisabledSeparatorColor;
|
||||||
if( separatorColor != null && buttonSeparatorWidth > 0 ) {
|
if( separatorColor != null && buttonSeparatorWidth > 0 ) {
|
||||||
g2.setColor( separatorColor );
|
g2.setColor( separatorColor );
|
||||||
@@ -679,7 +687,7 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
return (editableBackground != null && comboBox.isEditable()) ? editableBackground : background;
|
return (editableBackground != null && comboBox.isEditable()) ? editableBackground : background;
|
||||||
} else
|
} else
|
||||||
return isIntelliJTheme ? FlatUIUtils.getParentBackground( comboBox ) : disabledBackground;
|
return disabledBackground;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Color getForeground( boolean enabled ) {
|
protected Color getForeground( boolean enabled ) {
|
||||||
@@ -878,7 +886,7 @@ public class FlatComboBoxUI
|
|||||||
GraphicsConfiguration gc = comboBox.getGraphicsConfiguration();
|
GraphicsConfiguration gc = comboBox.getGraphicsConfiguration();
|
||||||
if( gc != null ) {
|
if( gc != null ) {
|
||||||
Rectangle screenBounds = gc.getBounds();
|
Rectangle screenBounds = gc.getBounds();
|
||||||
Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets( gc );
|
Insets screenInsets = FlatUIUtils.getScreenInsets( gc );
|
||||||
displayWidth = Math.min( displayWidth, screenBounds.width - screenInsets.left - screenInsets.right );
|
displayWidth = Math.min( displayWidth, screenBounds.width - screenInsets.left - screenInsets.right );
|
||||||
} else {
|
} else {
|
||||||
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
|
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
|
||||||
@@ -923,7 +931,7 @@ 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)
|
// make opaque to avoid that background shines through border (e.g. at 150% scaling)
|
||||||
setOpaque( true );
|
setOpaque( true );
|
||||||
|
|
||||||
// set popup border
|
// set popup border
|
||||||
@@ -941,7 +949,7 @@ public class FlatComboBoxUI
|
|||||||
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%)
|
// set popup background because it may shine through when scaled (e.g. at 150%)
|
||||||
// use non-UIResource to avoid that it is overwritten when making
|
// use non-UIResource to avoid that it is overwritten when making
|
||||||
// popup visible (see JPopupMenu.setInvoker()) in theme editor preview
|
// popup visible (see JPopupMenu.setInvoker()) in theme editor preview
|
||||||
setBackground( FlatUIUtils.nonUIResource( list.getBackground() ) );
|
setBackground( FlatUIUtils.nonUIResource( list.getBackground() ) );
|
||||||
@@ -986,6 +994,29 @@ public class FlatComboBoxUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// improve location of selected item in popup if list is large and scrollable
|
||||||
|
if( list.getHeight() == 0 ) {
|
||||||
|
// If popup is shown for the first time (or after a laf switch) and is scrollable,
|
||||||
|
// then BasicComboPopup scrolls the selected item to the top of the visible area.
|
||||||
|
// But for usability it would be better to have selected item somewhere
|
||||||
|
// in the middle of the visible area so that the user can see items above
|
||||||
|
// the selected item, which are usually more "important".
|
||||||
|
|
||||||
|
int selectedIndex = list.getSelectedIndex();
|
||||||
|
if( selectedIndex >= 1 ) {
|
||||||
|
int maximumRowCount = comboBox.getMaximumRowCount();
|
||||||
|
if( selectedIndex < maximumRowCount ) {
|
||||||
|
// selected item is in the first visible items --> scroll to top
|
||||||
|
list.scrollRectToVisible( new Rectangle() );
|
||||||
|
} else {
|
||||||
|
// scroll the selected item to the middle of the visible area
|
||||||
|
int firstVisibleIndex = Math.max( selectedIndex - (maximumRowCount / 2), 0 );
|
||||||
|
if( firstVisibleIndex > 0 )
|
||||||
|
list.ensureIndexIsVisible( firstVisibleIndex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
super.show( invoker, x, y );
|
super.show( invoker, x, y );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1064,7 +1095,7 @@ public class FlatComboBoxUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// using synchronized to avoid problems with code that modifies combo box
|
// using synchronized to avoid problems with code that modifies combo box
|
||||||
// (model, selection, etc) not on AWT thread (which should be not done)
|
// (model, selection, etc.) not on AWT thread (which should be not done)
|
||||||
synchronized void install( Component c, int focusWidth ) {
|
synchronized void install( Component c, int focusWidth ) {
|
||||||
if( !(c instanceof JComponent) )
|
if( !(c instanceof JComponent) )
|
||||||
return;
|
return;
|
||||||
@@ -1209,4 +1240,46 @@ public class FlatComboBoxUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- class FlatKeySelectionManager --------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key selection manager that delegates to the default manager.
|
||||||
|
* Shows the popup if Space key is pressed and "typed characters" buffer is empty.
|
||||||
|
* If items contain spaces (e.g. "a b") it is still possible to select them
|
||||||
|
* by pressing keys 'a', 'Space' and 'b'.
|
||||||
|
*/
|
||||||
|
private class FlatKeySelectionManager
|
||||||
|
implements JComboBox.KeySelectionManager, UIResource
|
||||||
|
{
|
||||||
|
private final KeySelectionManager delegate;
|
||||||
|
private final long timeFactor;
|
||||||
|
private long lastTime;
|
||||||
|
|
||||||
|
FlatKeySelectionManager( JComboBox.KeySelectionManager delegate ) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
|
||||||
|
Long value = (Long) UIManager.get( "ComboBox.timeFactor" );
|
||||||
|
timeFactor = (value != null) ? value : 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings( "rawtypes" )
|
||||||
|
@Override
|
||||||
|
public int selectionForKey( char aKey, ComboBoxModel aModel ) {
|
||||||
|
long time = EventQueue.getMostRecentEventTime();
|
||||||
|
long oldLastTime = lastTime;
|
||||||
|
lastTime = time;
|
||||||
|
|
||||||
|
// SPACE key shows popup if not yet visible
|
||||||
|
if( aKey == ' ' &&
|
||||||
|
time - oldLastTime >= timeFactor &&
|
||||||
|
!comboBox.isPopupVisible() )
|
||||||
|
{
|
||||||
|
comboBox.setPopupVisible( true );
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return delegate.selectionForKey( aKey, aModel );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,9 +24,8 @@ 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.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableBorder;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableObject;
|
||||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -43,7 +42,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*/
|
*/
|
||||||
public class FlatDropShadowBorder
|
public class FlatDropShadowBorder
|
||||||
extends FlatEmptyBorder
|
extends FlatEmptyBorder
|
||||||
implements StyleableBorder
|
implements StyleableObject
|
||||||
{
|
{
|
||||||
@Styleable protected Color shadowColor;
|
@Styleable protected Color shadowColor;
|
||||||
@Styleable protected Insets shadowInsets;
|
@Styleable protected Insets shadowInsets;
|
||||||
@@ -74,7 +73,7 @@ public class FlatDropShadowBorder
|
|||||||
|
|
||||||
this.shadowColor = shadowColor;
|
this.shadowColor = shadowColor;
|
||||||
this.shadowInsets = shadowInsets;
|
this.shadowInsets = shadowInsets;
|
||||||
this.shadowOpacity = shadowOpacity;
|
this.shadowOpacity = Math.min( Math.max( shadowOpacity, 0f ), 1f );
|
||||||
|
|
||||||
shadowSize = maxInset( shadowInsets );
|
shadowSize = maxInset( shadowInsets );
|
||||||
}
|
}
|
||||||
@@ -93,7 +92,7 @@ public class FlatDropShadowBorder
|
|||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
@Override
|
@Override
|
||||||
public Object applyStyleProperty( String key, Object value ) {
|
public Object applyStyleProperty( String key, Object value ) {
|
||||||
Object oldValue = FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
Object oldValue = StyleableObject.super.applyStyleProperty( key, value );
|
||||||
if( key.equals( "shadowInsets" ) ) {
|
if( key.equals( "shadowInsets" ) ) {
|
||||||
applyStyleProperty( nonNegativeInsets( shadowInsets ) );
|
applyStyleProperty( nonNegativeInsets( shadowInsets ) );
|
||||||
shadowSize = maxInset( shadowInsets );
|
shadowSize = maxInset( shadowInsets );
|
||||||
@@ -101,18 +100,6 @@ public class FlatDropShadowBorder
|
|||||||
return oldValue;
|
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 )
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ import com.formdev.flatlaf.util.LoggingFacade;
|
|||||||
* <!-- FlatEditorPaneUI -->
|
* <!-- FlatEditorPaneUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault Component.minimumWidth int
|
* @uiDefault Component.minimumWidth int
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
|
||||||
* @uiDefault EditorPane.focusedBackground Color optional
|
* @uiDefault EditorPane.focusedBackground Color optional
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
@@ -69,7 +68,6 @@ public class FlatEditorPaneUI
|
|||||||
implements StyleableUI
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
@Styleable protected int minimumWidth;
|
@Styleable protected int minimumWidth;
|
||||||
protected boolean isIntelliJTheme;
|
|
||||||
private Color background;
|
private Color background;
|
||||||
@Styleable protected Color disabledBackground;
|
@Styleable protected Color disabledBackground;
|
||||||
@Styleable protected Color inactiveBackground;
|
@Styleable protected Color inactiveBackground;
|
||||||
@@ -101,7 +99,6 @@ public class FlatEditorPaneUI
|
|||||||
|
|
||||||
String prefix = getPropertyPrefix();
|
String prefix = getPropertyPrefix();
|
||||||
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
||||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
|
||||||
background = UIManager.getColor( prefix + ".background" );
|
background = UIManager.getColor( prefix + ".background" );
|
||||||
disabledBackground = UIManager.getColor( prefix + ".disabledBackground" );
|
disabledBackground = UIManager.getColor( prefix + ".disabledBackground" );
|
||||||
inactiveBackground = UIManager.getColor( prefix + ".inactiveBackground" );
|
inactiveBackground = UIManager.getColor( prefix + ".inactiveBackground" );
|
||||||
@@ -174,7 +171,7 @@ public class FlatEditorPaneUI
|
|||||||
case FlatClientProperties.STYLE_CLASS:
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
installStyle.run();
|
installStyle.run();
|
||||||
c.revalidate();
|
c.revalidate();
|
||||||
c.repaint();
|
HiDPIUtils.repaint( c );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -252,11 +249,11 @@ public class FlatEditorPaneUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintBackground( Graphics g ) {
|
protected void paintBackground( Graphics g ) {
|
||||||
paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground );
|
paintBackground( g, getComponent(), focusedBackground );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void paintBackground( Graphics g, JTextComponent c, boolean isIntelliJTheme, Color focusedBackground ) {
|
static void paintBackground( Graphics g, JTextComponent c, Color focusedBackground ) {
|
||||||
g.setColor( FlatTextFieldUI.getBackground( c, isIntelliJTheme, focusedBackground ) );
|
g.setColor( FlatTextFieldUI.getBackground( c, focusedBackground ) );
|
||||||
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
|
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,13 +24,16 @@ import java.awt.Graphics2D;
|
|||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.LayoutManager;
|
import java.awt.LayoutManager;
|
||||||
|
import java.awt.Rectangle;
|
||||||
import java.awt.RenderingHints;
|
import java.awt.RenderingHints;
|
||||||
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.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
|
import javax.swing.BorderFactory;
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.ButtonGroup;
|
import javax.swing.ButtonGroup;
|
||||||
@@ -45,6 +48,7 @@ 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.JToolBar;
|
||||||
|
import javax.swing.Scrollable;
|
||||||
import javax.swing.SwingConstants;
|
import javax.swing.SwingConstants;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.filechooser.FileSystemView;
|
import javax.swing.filechooser.FileSystemView;
|
||||||
@@ -53,6 +57,7 @@ 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.icons.FlatFileViewDirectoryIcon;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
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;
|
||||||
@@ -162,6 +167,7 @@ public class FlatFileChooserUI
|
|||||||
{
|
{
|
||||||
private final FlatFileView fileView = new FlatFileView();
|
private final FlatFileView fileView = new FlatFileView();
|
||||||
private FlatShortcutsPanel shortcutsPanel;
|
private FlatShortcutsPanel shortcutsPanel;
|
||||||
|
private JScrollPane shortcutsScrollPane;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatFileChooserUI( (JFileChooser) c );
|
return new FlatFileChooserUI( (JFileChooser) c );
|
||||||
@@ -181,7 +187,10 @@ public class FlatFileChooserUI
|
|||||||
FlatShortcutsPanel panel = createShortcutsPanel( fc );
|
FlatShortcutsPanel panel = createShortcutsPanel( fc );
|
||||||
if( panel.getComponentCount() > 0 ) {
|
if( panel.getComponentCount() > 0 ) {
|
||||||
shortcutsPanel = panel;
|
shortcutsPanel = panel;
|
||||||
fc.add( shortcutsPanel, BorderLayout.LINE_START );
|
shortcutsScrollPane = new JScrollPane( shortcutsPanel,
|
||||||
|
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER );
|
||||||
|
shortcutsScrollPane.setBorder( BorderFactory.createEmptyBorder() );
|
||||||
|
fc.add( shortcutsScrollPane, BorderLayout.LINE_START );
|
||||||
fc.addPropertyChangeListener( shortcutsPanel );
|
fc.addPropertyChangeListener( shortcutsPanel );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,6 +203,7 @@ public class FlatFileChooserUI
|
|||||||
if( shortcutsPanel != null ) {
|
if( shortcutsPanel != null ) {
|
||||||
fc.removePropertyChangeListener( shortcutsPanel );
|
fc.removePropertyChangeListener( shortcutsPanel );
|
||||||
shortcutsPanel = null;
|
shortcutsPanel = null;
|
||||||
|
shortcutsScrollPane = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,7 +332,7 @@ public class FlatFileChooserUI
|
|||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
Dimension prefSize = super.getPreferredSize( c );
|
Dimension prefSize = super.getPreferredSize( c );
|
||||||
Dimension minSize = getMinimumSize( c );
|
Dimension minSize = getMinimumSize( c );
|
||||||
int shortcutsPanelWidth = (shortcutsPanel != null) ? shortcutsPanel.getPreferredSize().width : 0;
|
int shortcutsPanelWidth = (shortcutsScrollPane != null) ? shortcutsScrollPane.getPreferredSize().width : 0;
|
||||||
return new Dimension(
|
return new Dimension(
|
||||||
Math.max( prefSize.width, minSize.width + shortcutsPanelWidth ),
|
Math.max( prefSize.width, minSize.width + shortcutsPanelWidth ),
|
||||||
Math.max( prefSize.height, minSize.height ) );
|
Math.max( prefSize.height, minSize.height ) );
|
||||||
@@ -346,12 +356,12 @@ public class FlatFileChooserUI
|
|||||||
fileView.clearIconCache();
|
fileView.clearIconCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean doNotUseSystemIcons() {
|
private static boolean doNotUseSystemIcons() {
|
||||||
// Java 17 32bit craches on Windows when using system icons
|
// Java 17 32bit craches on Windows when using system icons
|
||||||
// fixed in Java 18+ (see https://bugs.openjdk.java.net/browse/JDK-8277299)
|
// fixed in Java 18+, fix backported in Java 17.0.3+ (see https://bugs.openjdk.java.net/browse/JDK-8277299)
|
||||||
return SystemInfo.isWindows &&
|
return SystemInfo.isWindows &&
|
||||||
SystemInfo.isX86 &&
|
SystemInfo.isX86 &&
|
||||||
(SystemInfo.isJava_17_orLater && !SystemInfo.isJava_18_orLater);
|
(SystemInfo.isJava_17_orLater && SystemInfo.javaVersion < SystemInfo.toVersion( 17, 0, 3, 0 ));
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class FlatFileView -------------------------------------------------
|
//---- class FlatFileView -------------------------------------------------
|
||||||
@@ -366,27 +376,68 @@ public class FlatFileChooserUI
|
|||||||
if( icon != null )
|
if( icon != null )
|
||||||
return icon;
|
return icon;
|
||||||
|
|
||||||
// get system icon
|
// new proxy icon
|
||||||
if( f != null ) {
|
//
|
||||||
icon = getFileChooser().getFileSystemView().getSystemIcon( f );
|
// Note: Since this is a super light weight icon object, we do not add it
|
||||||
|
// to the icon cache here. This keeps cache small in case of large directories
|
||||||
|
// with thousands of files when icons of all files are only needed to compute
|
||||||
|
// the layout of list/table, but never painted because located outside of visible area.
|
||||||
|
// When an icon needs to be painted, the proxy adds it to the icon cache
|
||||||
|
// and loads the real icon.
|
||||||
|
return new FlatFileViewIcon( f );
|
||||||
|
}
|
||||||
|
|
||||||
if( icon != null ) {
|
//---- class FlatFileViewIcon -----------------------------------------
|
||||||
if( icon instanceof ImageIcon )
|
|
||||||
icon = new ScaledImageIcon( (ImageIcon) icon );
|
/**
|
||||||
cacheIcon( f, icon );
|
* A proxy icon that has a fixed (scaled) width/height (16x16) and
|
||||||
return icon;
|
* gets/loads the real (system) icon only for painting.
|
||||||
|
* Avoids unnecessary getting/loading system icons.
|
||||||
|
*/
|
||||||
|
private class FlatFileViewIcon
|
||||||
|
implements Icon
|
||||||
|
{
|
||||||
|
private final File f;
|
||||||
|
private Icon realIcon;
|
||||||
|
|
||||||
|
FlatFileViewIcon( File f ) {
|
||||||
|
this.f = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getIconWidth() {
|
||||||
|
return UIScale.scale( 16 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getIconHeight() {
|
||||||
|
return UIScale.scale( 16 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paintIcon( Component c, Graphics g, int x, int y ) {
|
||||||
|
// get icon on demand
|
||||||
|
if( realIcon == null ) {
|
||||||
|
// get system icon
|
||||||
|
try {
|
||||||
|
if( f != null )
|
||||||
|
realIcon = getFileChooser().getFileSystemView().getSystemIcon( f );
|
||||||
|
} catch( NullPointerException ex ) {
|
||||||
|
// Java 21 may throw a NPE for exe files that use default Windows exe icon
|
||||||
|
}
|
||||||
|
|
||||||
|
// get default icon
|
||||||
|
if( realIcon == null )
|
||||||
|
realIcon = FlatFileView.super.getIcon( f );
|
||||||
|
|
||||||
|
if( realIcon instanceof ImageIcon )
|
||||||
|
realIcon = new ScaledImageIcon( (ImageIcon) realIcon );
|
||||||
|
|
||||||
|
cacheIcon( f, this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
realIcon.paintIcon( c, g, x, y );
|
||||||
}
|
}
|
||||||
|
|
||||||
// get default icon
|
|
||||||
icon = super.getIcon( f );
|
|
||||||
|
|
||||||
if( icon instanceof ImageIcon ) {
|
|
||||||
icon = new ScaledImageIcon( (ImageIcon) icon );
|
|
||||||
cacheIcon( f, icon );
|
|
||||||
}
|
|
||||||
|
|
||||||
return icon;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,7 +446,7 @@ public class FlatFileChooserUI
|
|||||||
/** @since 2.3 */
|
/** @since 2.3 */
|
||||||
public static class FlatShortcutsPanel
|
public static class FlatShortcutsPanel
|
||||||
extends JToolBar
|
extends JToolBar
|
||||||
implements PropertyChangeListener
|
implements PropertyChangeListener, Scrollable
|
||||||
{
|
{
|
||||||
private final JFileChooser fc;
|
private final JFileChooser fc;
|
||||||
|
|
||||||
@@ -407,13 +458,14 @@ public class FlatFileChooserUI
|
|||||||
|
|
||||||
protected final File[] files;
|
protected final File[] files;
|
||||||
protected final JToggleButton[] buttons;
|
protected final JToggleButton[] buttons;
|
||||||
protected final ButtonGroup buttonGroup;
|
protected final ButtonGroup buttonGroup = new ButtonGroup();
|
||||||
|
|
||||||
@SuppressWarnings( "unchecked" )
|
@SuppressWarnings( "unchecked" )
|
||||||
public FlatShortcutsPanel( JFileChooser fc ) {
|
public FlatShortcutsPanel( JFileChooser fc ) {
|
||||||
super( JToolBar.VERTICAL );
|
super( JToolBar.VERTICAL );
|
||||||
this.fc = fc;
|
this.fc = fc;
|
||||||
setFloatable( false );
|
setFloatable( false );
|
||||||
|
putClientProperty( FlatClientProperties.STYLE, "hoverButtonGroupBackground: null" );
|
||||||
|
|
||||||
buttonSize = UIScale.scale( getUIDimension( "FileChooser.shortcuts.buttonSize", 84, 64 ) );
|
buttonSize = UIScale.scale( getUIDimension( "FileChooser.shortcuts.buttonSize", 84, 64 ) );
|
||||||
iconSize = getUIDimension( "FileChooser.shortcuts.iconSize", 32, 32 );
|
iconSize = getUIDimension( "FileChooser.shortcuts.iconSize", 32, 32 );
|
||||||
@@ -423,22 +475,25 @@ public class FlatFileChooserUI
|
|||||||
iconFunction = (Function<File, Icon>) UIManager.get( "FileChooser.shortcuts.iconFunction" );
|
iconFunction = (Function<File, Icon>) UIManager.get( "FileChooser.shortcuts.iconFunction" );
|
||||||
|
|
||||||
FileSystemView fsv = fc.getFileSystemView();
|
FileSystemView fsv = fc.getFileSystemView();
|
||||||
File[] files = getChooserShortcutPanelFiles( fsv );
|
File[] files = JavaCompatibility2.getChooserShortcutPanelFiles( fsv );
|
||||||
if( filesFunction != null )
|
if( filesFunction != null )
|
||||||
files = filesFunction.apply( files );
|
files = filesFunction.apply( files );
|
||||||
this.files = files;
|
|
||||||
|
|
||||||
// create toolbar buttons
|
// create toolbar buttons
|
||||||
buttons = new JToggleButton[files.length];
|
ArrayList<File> filesList = new ArrayList<>();
|
||||||
buttonGroup = new ButtonGroup();
|
ArrayList<JToggleButton> buttonsList = new ArrayList<>();
|
||||||
for( int i = 0; i < files.length; i++ ) {
|
for( File file : files ) {
|
||||||
// wrap drive path
|
if( file == null )
|
||||||
if( fsv.isFileSystemRoot( files[i] ) )
|
continue;
|
||||||
files[i] = fsv.createFileObject( files[i].getAbsolutePath() );
|
|
||||||
|
// wrap drive path
|
||||||
|
if( fsv.isFileSystemRoot( file ) )
|
||||||
|
file = fsv.createFileObject( file.getAbsolutePath() );
|
||||||
|
|
||||||
File file = files[i];
|
|
||||||
String name = getDisplayName( fsv, file );
|
String name = getDisplayName( fsv, file );
|
||||||
Icon icon = getIcon( fsv, file );
|
Icon icon = getIcon( fsv, file );
|
||||||
|
if( name == null )
|
||||||
|
continue;
|
||||||
|
|
||||||
// remove path from name
|
// remove path from name
|
||||||
int lastSepIndex = name.lastIndexOf( File.separatorChar );
|
int lastSepIndex = name.lastIndexOf( File.separatorChar );
|
||||||
@@ -452,16 +507,22 @@ public class FlatFileChooserUI
|
|||||||
icon = new ShortcutIcon( icon, iconSize.width, iconSize.height );
|
icon = new ShortcutIcon( icon, iconSize.width, iconSize.height );
|
||||||
|
|
||||||
// create button
|
// create button
|
||||||
JToggleButton button = createButton( name, icon );
|
JToggleButton button = createButton( name, icon, file.toString() );
|
||||||
|
File f = file;
|
||||||
button.addActionListener( e -> {
|
button.addActionListener( e -> {
|
||||||
fc.setCurrentDirectory( file );
|
fc.setCurrentDirectory( f );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
add( button );
|
add( button );
|
||||||
buttonGroup.add( button );
|
buttonGroup.add( button );
|
||||||
buttons[i] = button;
|
|
||||||
|
filesList.add( file );
|
||||||
|
buttonsList.add( button );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.files = filesList.toArray( new File[filesList.size()] );
|
||||||
|
this.buttons = buttonsList.toArray( new JToggleButton[buttonsList.size()] );
|
||||||
|
|
||||||
directoryChanged( fc.getCurrentDirectory() );
|
directoryChanged( fc.getCurrentDirectory() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -472,8 +533,10 @@ public class FlatFileChooserUI
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected JToggleButton createButton( String name, Icon icon ) {
|
/** @since 3.5 */
|
||||||
|
protected JToggleButton createButton( String name, Icon icon, String toolTip ) {
|
||||||
JToggleButton button = new JToggleButton( name, icon );
|
JToggleButton button = new JToggleButton( name, icon );
|
||||||
|
button.setToolTipText( toolTip );
|
||||||
button.setVerticalTextPosition( SwingConstants.BOTTOM );
|
button.setVerticalTextPosition( SwingConstants.BOTTOM );
|
||||||
button.setHorizontalTextPosition( SwingConstants.CENTER );
|
button.setHorizontalTextPosition( SwingConstants.CENTER );
|
||||||
button.setAlignmentX( Component.CENTER_ALIGNMENT );
|
button.setAlignmentX( Component.CENTER_ALIGNMENT );
|
||||||
@@ -483,32 +546,6 @@ public class FlatFileChooserUI
|
|||||||
return button;
|
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 ) {
|
protected String getDisplayName( FileSystemView fsv, File file ) {
|
||||||
if( displayNameFunction != null ) {
|
if( displayNameFunction != null ) {
|
||||||
String name = displayNameFunction.apply( file );
|
String name = displayNameFunction.apply( file );
|
||||||
@@ -526,29 +563,39 @@ public class FlatFileChooserUI
|
|||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Java 17+ supports getting larger system icons
|
if( doNotUseSystemIcons() )
|
||||||
try {
|
return new FlatFileViewDirectoryIcon();
|
||||||
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
|
try {
|
||||||
return fsv.getSystemIcon( file );
|
// 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( Exception ex ) {
|
||||||
|
// do not log InaccessibleObjectException because access
|
||||||
|
// may be denied via VM option '--illegal-access=deny' (default in Java 16)
|
||||||
|
// (not catching InaccessibleObjectException here because it is new in Java 9, but FlatLaf also runs on Java 8)
|
||||||
|
if( !"java.lang.reflect.InaccessibleObjectException".equals( ex.getClass().getName() ) )
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
|
||||||
|
// get system icon in default size 16x16
|
||||||
|
return fsv.getSystemIcon( file );
|
||||||
|
} catch( NullPointerException ex ) {
|
||||||
|
// Java 21 may throw a NPE for exe files that use default Windows exe icon
|
||||||
|
return new FlatFileViewDirectoryIcon();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void directoryChanged( File file ) {
|
protected void directoryChanged( File file ) {
|
||||||
@@ -567,6 +614,8 @@ public class FlatFileChooserUI
|
|||||||
buttonGroup.clearSelection();
|
buttonGroup.clearSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- interface PropertyChangeListener ----
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void propertyChange( PropertyChangeEvent e ) {
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
@@ -575,6 +624,41 @@ public class FlatFileChooserUI
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- interface Scrollable ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension getPreferredScrollableViewportSize() {
|
||||||
|
if( getComponentCount() > 0 ) {
|
||||||
|
Insets insets = getInsets();
|
||||||
|
int height = (getComponent( 0 ).getPreferredSize().height * 5) + insets.top + insets.bottom;
|
||||||
|
return new Dimension( getPreferredSize().width, height );
|
||||||
|
}
|
||||||
|
return getPreferredSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getScrollableUnitIncrement( Rectangle visibleRect, int orientation, int direction ) {
|
||||||
|
if( orientation == SwingConstants.VERTICAL && getComponentCount() > 0 )
|
||||||
|
return getComponent( 0 ).getPreferredSize().height;
|
||||||
|
|
||||||
|
return getScrollableBlockIncrement( visibleRect, orientation, direction ) / 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getScrollableBlockIncrement( Rectangle visibleRect, int orientation, int direction ) {
|
||||||
|
return (orientation == SwingConstants.VERTICAL) ? visibleRect.height : visibleRect.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getScrollableTracksViewportWidth() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getScrollableTracksViewportHeight() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class ShortcutIcon -------------------------------------------------
|
//---- class ShortcutIcon -------------------------------------------------
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ import javax.swing.plaf.ComponentUI;
|
|||||||
* <!-- FlatTextFieldUI -->
|
* <!-- FlatTextFieldUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault Component.minimumWidth int
|
* @uiDefault Component.minimumWidth int
|
||||||
* @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 FormattedTextField.iconTextGap int optional, default is 4
|
||||||
|
|||||||
290
flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatHTML.java
Normal file
290
flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatHTML.java
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Font;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import javax.swing.AbstractButton;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JToolTip;
|
||||||
|
import javax.swing.plaf.basic.BasicHTML;
|
||||||
|
import javax.swing.text.AttributeSet;
|
||||||
|
import javax.swing.text.Document;
|
||||||
|
import javax.swing.text.LabelView;
|
||||||
|
import javax.swing.text.Style;
|
||||||
|
import javax.swing.text.StyleConstants;
|
||||||
|
import javax.swing.text.View;
|
||||||
|
import javax.swing.text.html.CSS;
|
||||||
|
import javax.swing.text.html.HTMLDocument;
|
||||||
|
import javax.swing.text.html.StyleSheet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karl Tauber
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public class FlatHTML
|
||||||
|
{
|
||||||
|
private FlatHTML() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds CSS rule BASE_SIZE to the style sheet of the HTML view,
|
||||||
|
* which re-calculates font sizes based on current component font size.
|
||||||
|
* This is necessary for "absolute-size" keywords (e.g. "x-large")
|
||||||
|
* for "font-size" attributes in default style sheet (see javax/swing/text/html/default.css).
|
||||||
|
* See also <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/font-size#values">CSS font-size</a>.
|
||||||
|
* <p>
|
||||||
|
* This method should be invoked after {@link BasicHTML#updateRenderer(JComponent, String)}.
|
||||||
|
*/
|
||||||
|
public static void updateRendererCSSFontBaseSize( JComponent c ) {
|
||||||
|
View view = (View) c.getClientProperty( BasicHTML.propertyKey );
|
||||||
|
if( view == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// dumpViews( view, 0 );
|
||||||
|
|
||||||
|
Document doc = view.getDocument();
|
||||||
|
if( !(doc instanceof HTMLDocument) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// add BASE_SIZE rule if necessary
|
||||||
|
// - if point size at index 7 is not 36, then probably HTML text contains BASE_SIZE rule
|
||||||
|
// - if point size at index 4 is equal to given font size, then it is not necessary to add BASE_SIZE rule
|
||||||
|
StyleSheet styleSheet = ((HTMLDocument)doc).getStyleSheet();
|
||||||
|
/*debug
|
||||||
|
for( int i = 1; i <= 7; i++ )
|
||||||
|
System.out.println( i+": "+ styleSheet.getPointSize( i ) );
|
||||||
|
debug*/
|
||||||
|
Font font = c.getFont();
|
||||||
|
if( styleSheet.getPointSize( 7 ) != 36f ||
|
||||||
|
font == null || styleSheet.getPointSize( 4 ) == font.getSize() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// check whether view uses "absolute-size" keywords (e.g. "x-large") for font-size
|
||||||
|
if( !usesAbsoluteSizeKeywordForFontSize( view ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// get HTML text from component
|
||||||
|
String text;
|
||||||
|
if( c instanceof JLabel )
|
||||||
|
text = ((JLabel)c).getText();
|
||||||
|
else if( c instanceof AbstractButton )
|
||||||
|
text = ((AbstractButton)c).getText();
|
||||||
|
else if( c instanceof JToolTip )
|
||||||
|
text = ((JToolTip)c).getTipText();
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
if( text == null || !BasicHTML.isHTMLString( text ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// BASE_SIZE rule is parsed in javax.swing.text.html.StyleSheet.addRule()
|
||||||
|
String style = "<style>BASE_SIZE " + font.getSize() + "</style>";
|
||||||
|
String openTag = "";
|
||||||
|
String closeTag = "";
|
||||||
|
|
||||||
|
int headIndex;
|
||||||
|
int styleIndex;
|
||||||
|
|
||||||
|
int insertIndex;
|
||||||
|
if( (headIndex = indexOfTag( text, "head", true )) >= 0 ) {
|
||||||
|
// there is a <head> tag --> insert after <head> tag
|
||||||
|
insertIndex = headIndex;
|
||||||
|
} else if( (styleIndex = indexOfTag( text, "style", false )) >= 0 ) {
|
||||||
|
// there is a <style> tag --> insert before <style> tag
|
||||||
|
insertIndex = styleIndex;
|
||||||
|
} else {
|
||||||
|
// no <head> or <style> tag --> insert <head> tag after <html> tag
|
||||||
|
insertIndex = "<html>".length();
|
||||||
|
openTag = "<head>";
|
||||||
|
closeTag = "</head>";
|
||||||
|
}
|
||||||
|
|
||||||
|
String newText = text.substring( 0, insertIndex )
|
||||||
|
+ openTag + style + closeTag
|
||||||
|
+ text.substring( insertIndex );
|
||||||
|
|
||||||
|
BasicHTML.updateRenderer( c, newText );
|
||||||
|
|
||||||
|
// for unit tests
|
||||||
|
if( testUpdateRenderer != null )
|
||||||
|
testUpdateRenderer.accept( c, newText );
|
||||||
|
}
|
||||||
|
|
||||||
|
// for unit tests
|
||||||
|
static BiConsumer<JComponent, String> testUpdateRenderer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns start or end index of a HTML tag.
|
||||||
|
* Checks only for leading '<' character and (case-ignore) tag name.
|
||||||
|
*/
|
||||||
|
private static int indexOfTag( String html, String tag, boolean endIndex ) {
|
||||||
|
int tagLength = tag.length();
|
||||||
|
int maxLength = html.length() - tagLength - 2;
|
||||||
|
char lastTagChar = tag.charAt( tagLength - 1 );
|
||||||
|
|
||||||
|
for( int i = "<html>".length(); i < maxLength; i++ ) {
|
||||||
|
// check for leading '<' and last tag name character
|
||||||
|
if( html.charAt( i ) == '<' && Character.toLowerCase( html.charAt( i + tagLength ) ) == lastTagChar ) {
|
||||||
|
// compare tag characters from last to first
|
||||||
|
for( int j = tagLength - 2; j >= 0; j-- ) {
|
||||||
|
if( Character.toLowerCase( html.charAt( i + 1 + j ) ) != tag.charAt( j ) )
|
||||||
|
break; // not equal
|
||||||
|
|
||||||
|
if( j == 0 ) {
|
||||||
|
// tag found
|
||||||
|
return endIndex ? html.indexOf( '>', i + tagLength ) + 1 : i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Set<String> absoluteSizeKeywordsSet = new HashSet<>( Arrays.asList(
|
||||||
|
"xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large" ) );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether view uses "absolute-size" keywords (e.g. "x-large") for font-size
|
||||||
|
* (see javax/swing/text/html/default.css).
|
||||||
|
*/
|
||||||
|
private static boolean usesAbsoluteSizeKeywordForFontSize( View view ) {
|
||||||
|
AttributeSet attributes = view.getAttributes();
|
||||||
|
if( attributes != null ) {
|
||||||
|
Object fontSize = attributes.getAttribute( CSS.Attribute.FONT_SIZE );
|
||||||
|
if( fontSize != null ) {
|
||||||
|
if( absoluteSizeKeywordsSet.contains( fontSize.toString() ) )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int viewCount = view.getViewCount();
|
||||||
|
for( int i = 0; i < viewCount; i++ ) {
|
||||||
|
if( usesAbsoluteSizeKeywordForFontSize( view.getView( i ) ) )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates foreground in style sheet of the HTML view.
|
||||||
|
* Adds "body { color: #<foreground-hex>; }"
|
||||||
|
*/
|
||||||
|
public static void updateRendererCSSForeground( View view, Color foreground ) {
|
||||||
|
Document doc = view.getDocument();
|
||||||
|
if( !(doc instanceof HTMLDocument) || foreground == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// add foreground rule if necessary
|
||||||
|
// - use tag 'body' because BasicHTML.createHTMLView() also uses this tag
|
||||||
|
// to set font and color styles to component font/color
|
||||||
|
// see: SwingUtilities2.displayPropertiesToCSS()
|
||||||
|
// - this color is not used if component is disabled;
|
||||||
|
// JTextComponent.getDisabledTextColor() is used for disabled text components;
|
||||||
|
// UIManager.getColor("textInactiveText") is used for other disabled components
|
||||||
|
// see: javax.swing.text.GlyphView.paint()
|
||||||
|
Style bodyStyle = ((HTMLDocument)doc).getStyle( "body" );
|
||||||
|
if( bodyStyle == null ) {
|
||||||
|
StyleSheet styleSheet = ((HTMLDocument)doc).getStyleSheet();
|
||||||
|
styleSheet.addRule( String.format( "body { color: #%06x; }", foreground.getRGB() & 0xffffff ) );
|
||||||
|
clearViewCaches( view );
|
||||||
|
} else if( !foreground.equals( bodyStyle.getAttribute( StyleConstants.Foreground ) ) ) {
|
||||||
|
bodyStyle.addAttribute( StyleConstants.Foreground, foreground );
|
||||||
|
clearViewCaches( view );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears cached values in view so that CSS changes take effect.
|
||||||
|
*/
|
||||||
|
private static void clearViewCaches( View view ) {
|
||||||
|
if( view instanceof LabelView )
|
||||||
|
((LabelView)view).changedUpdate( null, null, null );
|
||||||
|
|
||||||
|
int viewCount = view.getViewCount();
|
||||||
|
for( int i = 0; i < viewCount; i++ )
|
||||||
|
clearViewCaches( view.getView( i ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PropertyChangeListener createPropertyChangeListener( PropertyChangeListener superListener ) {
|
||||||
|
return e -> {
|
||||||
|
if( superListener != null )
|
||||||
|
superListener.propertyChange( e );
|
||||||
|
propertyChange( e );
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes {@link #updateRendererCSSFontBaseSize(JComponent)}
|
||||||
|
* for {@link BasicHTML#propertyKey} property change events,
|
||||||
|
* which are fired when {@link BasicHTML#updateRenderer(JComponent, String)}
|
||||||
|
* updates the HTML view.
|
||||||
|
*/
|
||||||
|
public static void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
if( BasicHTML.propertyKey.equals( e.getPropertyName() ) && e.getNewValue() instanceof View )
|
||||||
|
updateRendererCSSFontBaseSize( (JComponent) e.getSource() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*debug
|
||||||
|
public static void dumpView( JComponent c ) {
|
||||||
|
View view = (View) c.getClientProperty( BasicHTML.propertyKey );
|
||||||
|
if( view != null )
|
||||||
|
dumpViews( view, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void dumpViews( View view, int indent ) {
|
||||||
|
for( int i = 0; i < indent; i++ )
|
||||||
|
System.out.print( " " );
|
||||||
|
|
||||||
|
System.out.printf( "%s @%-8x %3d,%2d",
|
||||||
|
view.getClass().isAnonymousClass() ? view.getClass().getName() : view.getClass().getSimpleName(),
|
||||||
|
System.identityHashCode( view ),
|
||||||
|
(int) view.getPreferredSpan( View.X_AXIS ),
|
||||||
|
(int) view.getPreferredSpan( View.Y_AXIS ) );
|
||||||
|
|
||||||
|
AttributeSet attrs = view.getAttributes();
|
||||||
|
if( attrs != null ) {
|
||||||
|
Object fontSize = attrs.getAttribute( CSS.Attribute.FONT_SIZE );
|
||||||
|
System.out.printf( " %-8s", fontSize );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( view instanceof javax.swing.text.GlyphView ) {
|
||||||
|
javax.swing.text.GlyphView gview = ((javax.swing.text.GlyphView)view);
|
||||||
|
java.awt.Font font = gview.getFont();
|
||||||
|
System.out.printf( " %3d-%-3d %s %2d (@%x) #%06x '%s'",
|
||||||
|
gview.getStartOffset(), gview.getEndOffset() - 1,
|
||||||
|
font.getName(), font.getSize(), System.identityHashCode( font ),
|
||||||
|
gview.getForeground().getRGB() & 0xffffff,
|
||||||
|
gview.getText( gview.getStartOffset(), gview.getEndOffset() ) );
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
|
||||||
|
int viewCount = view.getViewCount();
|
||||||
|
for( int i = 0; i < viewCount; i++ ) {
|
||||||
|
View child = view.getView( i );
|
||||||
|
dumpViews( child, indent + 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug*/
|
||||||
|
}
|
||||||
@@ -35,7 +35,7 @@ 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.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableBorder;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableObject;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
@@ -209,7 +209,7 @@ public class FlatInternalFrameUI
|
|||||||
|
|
||||||
public static class FlatInternalFrameBorder
|
public static class FlatInternalFrameBorder
|
||||||
extends FlatEmptyBorder
|
extends FlatEmptyBorder
|
||||||
implements StyleableBorder
|
implements StyleableObject
|
||||||
{
|
{
|
||||||
@Styleable protected Color activeBorderColor = UIManager.getColor( "InternalFrame.activeBorderColor" );
|
@Styleable protected Color activeBorderColor = UIManager.getColor( "InternalFrame.activeBorderColor" );
|
||||||
@Styleable protected Color inactiveBorderColor = UIManager.getColor( "InternalFrame.inactiveBorderColor" );
|
@Styleable protected Color inactiveBorderColor = UIManager.getColor( "InternalFrame.inactiveBorderColor" );
|
||||||
|
|||||||
@@ -22,10 +22,7 @@ import java.awt.Graphics;
|
|||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
@@ -63,6 +60,9 @@ public class FlatLabelUI
|
|||||||
{
|
{
|
||||||
@Styleable protected Color disabledForeground;
|
@Styleable protected Color disabledForeground;
|
||||||
|
|
||||||
|
// only used via styling (not in UI defaults)
|
||||||
|
/** @since 3.5 */ @Styleable protected int arc = -1;
|
||||||
|
|
||||||
private final boolean shared;
|
private final boolean shared;
|
||||||
private boolean defaults_initialized = false;
|
private boolean defaults_initialized = false;
|
||||||
private Map<String, Object> oldStyleValues;
|
private Map<String, Object> oldStyleValues;
|
||||||
@@ -109,16 +109,13 @@ public class FlatLabelUI
|
|||||||
super.installComponents( c );
|
super.installComponents( c );
|
||||||
|
|
||||||
// update HTML renderer if necessary
|
// update HTML renderer if necessary
|
||||||
updateHTMLRenderer( c, c.getText(), false );
|
FlatHTML.updateRendererCSSFontBaseSize( c );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void propertyChange( PropertyChangeEvent e ) {
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
String name = e.getPropertyName();
|
String name = e.getPropertyName();
|
||||||
if( name == "text" || name == "font" || name == "foreground" ) {
|
if( name.equals( FlatClientProperties.STYLE ) || name.equals( FlatClientProperties.STYLE_CLASS ) ) {
|
||||||
JLabel label = (JLabel) e.getSource();
|
|
||||||
updateHTMLRenderer( label, label.getText(), true );
|
|
||||||
} else if( name.equals( FlatClientProperties.STYLE ) || name.equals( FlatClientProperties.STYLE_CLASS ) ) {
|
|
||||||
JLabel label = (JLabel) e.getSource();
|
JLabel label = (JLabel) e.getSource();
|
||||||
if( shared && FlatStylingSupport.hasStyleProperty( label ) ) {
|
if( shared && FlatStylingSupport.hasStyleProperty( label ) ) {
|
||||||
// unshare component UI if necessary
|
// unshare component UI if necessary
|
||||||
@@ -127,9 +124,11 @@ public class FlatLabelUI
|
|||||||
} else
|
} else
|
||||||
installStyle( label );
|
installStyle( label );
|
||||||
label.revalidate();
|
label.revalidate();
|
||||||
label.repaint();
|
HiDPIUtils.repaint( label );
|
||||||
} else
|
}
|
||||||
super.propertyChange( e );
|
|
||||||
|
super.propertyChange( e );
|
||||||
|
FlatHTML.propertyChange( e );
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
@@ -164,83 +163,10 @@ public class FlatLabelUI
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Checks whether text contains HTML tags that use "absolute-size" keywords
|
public void update( Graphics g, JComponent c ) {
|
||||||
* (e.g. "x-large") for font-size in default style sheet
|
FlatPanelUI.fillRoundedBackground( g, c, arc );
|
||||||
* (see javax/swing/text/html/default.css).
|
paint( g, c );
|
||||||
* If yes, adds a special CSS rule (BASE_SIZE) to the HTML text, which
|
|
||||||
* re-calculates font sizes based on current component font size.
|
|
||||||
*/
|
|
||||||
static void updateHTMLRenderer( JComponent c, String text, boolean always ) {
|
|
||||||
if( BasicHTML.isHTMLString( text ) &&
|
|
||||||
c.getClientProperty( "html.disable" ) != Boolean.TRUE &&
|
|
||||||
needsFontBaseSize( text ) )
|
|
||||||
{
|
|
||||||
// BASE_SIZE rule is parsed in javax.swing.text.html.StyleSheet.addRule()
|
|
||||||
String style = "<style>BASE_SIZE " + c.getFont().getSize() + "</style>";
|
|
||||||
|
|
||||||
String lowerText = text.toLowerCase();
|
|
||||||
int headIndex;
|
|
||||||
int styleIndex;
|
|
||||||
|
|
||||||
int insertIndex;
|
|
||||||
if( (headIndex = lowerText.indexOf( "<head>" )) >= 0 ) {
|
|
||||||
// there is a <head> tag --> insert after <head> tag
|
|
||||||
insertIndex = headIndex + "<head>".length();
|
|
||||||
} else if( (styleIndex = lowerText.indexOf( "<style>" )) >= 0 ) {
|
|
||||||
// there is a <style> tag --> insert before <style> tag
|
|
||||||
insertIndex = styleIndex;
|
|
||||||
} else {
|
|
||||||
// no <head> or <style> tag --> insert <head> tag after <html> tag
|
|
||||||
style = "<head>" + style + "</head>";
|
|
||||||
insertIndex = "<html>".length();
|
|
||||||
}
|
|
||||||
|
|
||||||
text = text.substring( 0, insertIndex )
|
|
||||||
+ style
|
|
||||||
+ text.substring( insertIndex );
|
|
||||||
} else if( !always )
|
|
||||||
return; // not necessary to invoke BasicHTML.updateRenderer()
|
|
||||||
|
|
||||||
BasicHTML.updateRenderer( c, text );
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Set<String> tagsUseFontSizeSet;
|
|
||||||
|
|
||||||
private static boolean needsFontBaseSize( String text ) {
|
|
||||||
if( tagsUseFontSizeSet == null ) {
|
|
||||||
// tags that use font-size in javax/swing/text/html/default.css
|
|
||||||
tagsUseFontSizeSet = new HashSet<>( Arrays.asList(
|
|
||||||
"h1", "h2", "h3", "h4", "h5", "h6", "code", "kbd", "big", "small", "samp" ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// search for tags in HTML text
|
|
||||||
int textLength = text.length();
|
|
||||||
for( int i = 6; i < textLength - 1; i++ ) {
|
|
||||||
if( text.charAt( i ) == '<' ) {
|
|
||||||
switch( text.charAt( i + 1 ) ) {
|
|
||||||
// first letters of tags in tagsUseFontSizeSet
|
|
||||||
case 'b': case 'B':
|
|
||||||
case 'c': case 'C':
|
|
||||||
case 'h': case 'H':
|
|
||||||
case 'k': case 'K':
|
|
||||||
case 's': case 'S':
|
|
||||||
int tagBegin = i + 1;
|
|
||||||
for( i += 2; i < textLength; i++ ) {
|
|
||||||
if( !Character.isLetterOrDigit( text.charAt( i ) ) ) {
|
|
||||||
String tag = text.substring( tagBegin, i ).toLowerCase();
|
|
||||||
if( tagsUseFontSizeSet.contains( tag ) )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Graphics createGraphicsHTMLTextYCorrection( Graphics g, JComponent c ) {
|
static Graphics createGraphicsHTMLTextYCorrection( Graphics g, JComponent c ) {
|
||||||
|
|||||||
@@ -22,13 +22,21 @@ 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 javax.swing.JComponent;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Line border for various components.
|
* Line border for various components.
|
||||||
*
|
* <p>
|
||||||
* Paints a scaled (usually 1px thick) line around the component.
|
* Paints a scaled (usually 1px thick) line around the component.
|
||||||
* The line thickness is not added to the border insets.
|
* The line thickness is not added to the border insets.
|
||||||
* The insets should be at least have line thickness (usually 1,1,1,1).
|
* The insets should be at least have line thickness (usually 1,1,1,1).
|
||||||
|
* <p>
|
||||||
|
* For {@link javax.swing.JPanel} and {@link javax.swing.JLabel}, this border
|
||||||
|
* can be used paint rounded background (if line color is {@code null}) or
|
||||||
|
* paint rounded line border with rounded background.
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
@@ -40,7 +48,7 @@ public class FlatLineBorder
|
|||||||
/** @since 2 */ private final int arc;
|
/** @since 2 */ private final int arc;
|
||||||
|
|
||||||
public FlatLineBorder( Insets insets, Color lineColor ) {
|
public FlatLineBorder( Insets insets, Color lineColor ) {
|
||||||
this( insets, lineColor, 1f, 0 );
|
this( insets, lineColor, 1f, -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
@@ -51,26 +59,62 @@ public class FlatLineBorder
|
|||||||
this.arc = arc;
|
this.arc = arc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 3.5 */
|
||||||
|
public FlatLineBorder( Insets insets, int arc ) {
|
||||||
|
this( insets, null, 0, arc );
|
||||||
|
}
|
||||||
|
|
||||||
public Color getLineColor() {
|
public Color getLineColor() {
|
||||||
return lineColor;
|
return lineColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the (unscaled) line thickness used to paint the border.
|
||||||
|
* The line thickness does not affect the border insets.
|
||||||
|
*/
|
||||||
public float getLineThickness() {
|
public float getLineThickness() {
|
||||||
return lineThickness;
|
return lineThickness;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/**
|
||||||
|
* Returns the (unscaled) arc diameter of the border corners.
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
public int getArc() {
|
public int getArc() {
|
||||||
return arc;
|
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 ) {
|
||||||
|
if( c instanceof JComponent && ((JComponent)c).getClientProperty( FlatPopupFactory.KEY_POPUP_USES_NATIVE_BORDER ) != null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Color lineColor = getLineColor();
|
||||||
|
float lineThickness = getLineThickness();
|
||||||
|
if( lineColor == null || lineThickness <= 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
int arc = getArc();
|
||||||
|
if( arc < 0 ) {
|
||||||
|
// get arc from label or panel
|
||||||
|
ComponentUI ui = (c instanceof JLabel)
|
||||||
|
? ((JLabel)c).getUI()
|
||||||
|
: (c instanceof JPanel ? ((JPanel)c).getUI() : null);
|
||||||
|
if( ui instanceof FlatLabelUI )
|
||||||
|
arc = ((FlatLabelUI)ui).arc;
|
||||||
|
else if( ui instanceof FlatPanelUI )
|
||||||
|
arc = ((FlatPanelUI)ui).arc;
|
||||||
|
|
||||||
|
if( arc < 0 )
|
||||||
|
arc = 0;
|
||||||
|
}
|
||||||
|
|
||||||
Graphics2D g2 = (Graphics2D) g.create();
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
try {
|
try {
|
||||||
FlatUIUtils.setRenderingHints( g2 );
|
FlatUIUtils.setRenderingHints( g2 );
|
||||||
FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height,
|
FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height,
|
||||||
0, 0, 0, scale( getLineThickness() ), scale( getArc() ), null, getLineColor(), null );
|
0, 0, 0, scale( lineThickness ), scale( arc ), null, lineColor, null );
|
||||||
} finally {
|
} finally {
|
||||||
g2.dispose();
|
g2.dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import com.formdev.flatlaf.FlatClientProperties;
|
|||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.util.Graphics2DProxy;
|
import com.formdev.flatlaf.util.Graphics2DProxy;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -56,6 +57,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault List.foreground Color
|
* @uiDefault List.foreground Color
|
||||||
* @uiDefault List.selectionBackground Color
|
* @uiDefault List.selectionBackground Color
|
||||||
* @uiDefault List.selectionForeground Color
|
* @uiDefault List.selectionForeground Color
|
||||||
|
* @uiDefault List.alternateRowColor Color
|
||||||
* @uiDefault List.dropLineColor Color
|
* @uiDefault List.dropLineColor Color
|
||||||
* @uiDefault List.border Border
|
* @uiDefault List.border Border
|
||||||
* @uiDefault List.cellRenderer ListCellRenderer
|
* @uiDefault List.cellRenderer ListCellRenderer
|
||||||
@@ -92,6 +94,7 @@ public class FlatListUI
|
|||||||
@Styleable protected Color selectionForeground;
|
@Styleable protected Color selectionForeground;
|
||||||
@Styleable protected Color selectionInactiveBackground;
|
@Styleable protected Color selectionInactiveBackground;
|
||||||
@Styleable protected Color selectionInactiveForeground;
|
@Styleable protected Color selectionInactiveForeground;
|
||||||
|
/** @since 3.6 */ @Styleable protected Color alternateRowColor;
|
||||||
/** @since 3 */ @Styleable protected Insets selectionInsets;
|
/** @since 3 */ @Styleable protected Insets selectionInsets;
|
||||||
/** @since 3 */ @Styleable protected int selectionArc;
|
/** @since 3 */ @Styleable protected int selectionArc;
|
||||||
|
|
||||||
@@ -128,6 +131,7 @@ public class FlatListUI
|
|||||||
selectionForeground = UIManager.getColor( "List.selectionForeground" );
|
selectionForeground = UIManager.getColor( "List.selectionForeground" );
|
||||||
selectionInactiveBackground = UIManager.getColor( "List.selectionInactiveBackground" );
|
selectionInactiveBackground = UIManager.getColor( "List.selectionInactiveBackground" );
|
||||||
selectionInactiveForeground = UIManager.getColor( "List.selectionInactiveForeground" );
|
selectionInactiveForeground = UIManager.getColor( "List.selectionInactiveForeground" );
|
||||||
|
alternateRowColor = UIManager.getColor( "List.alternateRowColor" );
|
||||||
selectionInsets = UIManager.getInsets( "List.selectionInsets" );
|
selectionInsets = UIManager.getInsets( "List.selectionInsets" );
|
||||||
selectionArc = UIManager.getInt( "List.selectionArc" );
|
selectionArc = UIManager.getInt( "List.selectionArc" );
|
||||||
|
|
||||||
@@ -142,6 +146,7 @@ public class FlatListUI
|
|||||||
selectionForeground = null;
|
selectionForeground = null;
|
||||||
selectionInactiveBackground = null;
|
selectionInactiveBackground = null;
|
||||||
selectionInactiveForeground = null;
|
selectionInactiveForeground = null;
|
||||||
|
alternateRowColor = null;
|
||||||
|
|
||||||
oldStyleValues = null;
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
@@ -182,7 +187,7 @@ public class FlatListUI
|
|||||||
case FlatClientProperties.STYLE_CLASS:
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
installStyle();
|
installStyle();
|
||||||
list.revalidate();
|
list.revalidate();
|
||||||
list.repaint();
|
HiDPIUtils.repaint( list );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -205,7 +210,7 @@ public class FlatListUI
|
|||||||
Rectangle r = getCellBounds( list, firstIndex, lastIndex );
|
Rectangle r = getCellBounds( list, firstIndex, lastIndex );
|
||||||
if( r != null ) {
|
if( r != null ) {
|
||||||
int arc = (int) Math.ceil( UIScale.scale( selectionArc / 2f ) );
|
int arc = (int) Math.ceil( UIScale.scale( selectionArc / 2f ) );
|
||||||
list.repaint( r.x - arc, r.y - arc, r.width + (arc * 2), r.height + (arc * 2) );
|
HiDPIUtils.repaint( list, r.x - arc, r.y - arc, r.width + (arc * 2), r.height + (arc * 2) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -297,13 +302,26 @@ public class FlatListUI
|
|||||||
ListModel dataModel, ListSelectionModel selModel, int leadIndex )
|
ListModel dataModel, ListSelectionModel selModel, int leadIndex )
|
||||||
{
|
{
|
||||||
boolean isSelected = selModel.isSelectedIndex( row );
|
boolean isSelected = selModel.isSelectedIndex( row );
|
||||||
|
boolean isDropRow = isDropRow( row );
|
||||||
|
|
||||||
|
// paint alternating rows
|
||||||
|
if( alternateRowColor != null && row % 2 != 0 &&
|
||||||
|
!"ComboBox.list".equals( list.getName() ) ) // combobox does not support alternate row color
|
||||||
|
{
|
||||||
|
g.setColor( alternateRowColor );
|
||||||
|
|
||||||
|
float arc = UIScale.scale( selectionArc / 2f );
|
||||||
|
FlatUIUtils.paintSelection( (Graphics2D) g, rowBounds.x, rowBounds.y, rowBounds.width, rowBounds.height,
|
||||||
|
UIScale.scale( selectionInsets ), arc, arc, arc, arc, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
// get renderer component
|
// get renderer component
|
||||||
@SuppressWarnings( "unchecked" )
|
@SuppressWarnings( "unchecked" )
|
||||||
Component rendererComponent = cellRenderer.getListCellRendererComponent( list,
|
Component rendererComponent = cellRenderer.getListCellRendererComponent( list,
|
||||||
dataModel.getElementAt( row ), row, isSelected, list.hasFocus() && (row == leadIndex) );
|
dataModel.getElementAt( row ), row, isSelected,
|
||||||
|
FlatUIUtils.isPermanentFocusOwner( list ) && (row == leadIndex) );
|
||||||
|
|
||||||
//
|
// use smaller cell width if list is used in JFileChooser
|
||||||
boolean isFileList = Boolean.TRUE.equals( list.getClientProperty( "List.isFileList" ) );
|
boolean isFileList = Boolean.TRUE.equals( list.getClientProperty( "List.isFileList" ) );
|
||||||
int cx, cw;
|
int cx, cw;
|
||||||
if( isFileList ) {
|
if( isFileList ) {
|
||||||
@@ -318,13 +336,12 @@ public class FlatListUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// rounded selection or selection insets
|
// rounded selection or selection insets
|
||||||
if( isSelected &&
|
if( (isSelected || isDropRow) &&
|
||||||
!isFileList && // rounded selection is not supported for file list
|
!isFileList && // rounded selection is not supported for file list
|
||||||
(rendererComponent instanceof DefaultListCellRenderer ||
|
(rendererComponent instanceof DefaultListCellRenderer ||
|
||||||
rendererComponent instanceof BasicComboBoxRenderer) &&
|
rendererComponent instanceof BasicComboBoxRenderer) &&
|
||||||
(selectionArc > 0 ||
|
(selectionArc > 0 ||
|
||||||
(selectionInsets != null &&
|
(selectionInsets != null && !FlatUIUtils.isInsetsEmpty( selectionInsets ))) )
|
||||||
(selectionInsets.top != 0 || selectionInsets.left != 0 || selectionInsets.bottom != 0 || selectionInsets.right != 0))) )
|
|
||||||
{
|
{
|
||||||
// Because selection painting is done in the cell renderer, it would be
|
// Because selection painting is done in the cell renderer, it would be
|
||||||
// necessary to require a FlatLaf specific renderer to implement rounded selection.
|
// necessary to require a FlatLaf specific renderer to implement rounded selection.
|
||||||
@@ -360,7 +377,22 @@ public class FlatListUI
|
|||||||
this.getColor() == rendererComponent.getBackground() )
|
this.getColor() == rendererComponent.getBackground() )
|
||||||
{
|
{
|
||||||
inPaintSelection = true;
|
inPaintSelection = true;
|
||||||
paintCellSelection( this, row, x, y, width, height );
|
if( isDropRow ) {
|
||||||
|
// for rounded drop background, it is necessary to first
|
||||||
|
// paint selection background because may be not rounded on some corners
|
||||||
|
if( isSelected ) {
|
||||||
|
Color oldColor = getColor();
|
||||||
|
setColor( list.getSelectionBackground() );
|
||||||
|
paintCellSelection( this, row, x, y, width, height );
|
||||||
|
setColor( oldColor );
|
||||||
|
}
|
||||||
|
|
||||||
|
// paint drop background
|
||||||
|
float arc = UIScale.scale( selectionArc / 2f );
|
||||||
|
FlatUIUtils.paintSelection( this, x, y, width, height,
|
||||||
|
UIScale.scale( selectionInsets ), arc, arc, arc, arc, 0 );
|
||||||
|
} else
|
||||||
|
paintCellSelection( this, row, x, y, width, height );
|
||||||
inPaintSelection = false;
|
inPaintSelection = false;
|
||||||
} else
|
} else
|
||||||
super.fillRect( x, y, width, height );
|
super.fillRect( x, y, width, height );
|
||||||
@@ -373,7 +405,15 @@ public class FlatListUI
|
|||||||
rendererPane.paintComponent( g, rendererComponent, list, cx, rowBounds.y, cw, rowBounds.height, true );
|
rendererPane.paintComponent( g, rendererComponent, list, cx, rowBounds.y, cw, rowBounds.height, true );
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 3 */
|
/**
|
||||||
|
* Paints (rounded) cell selection.
|
||||||
|
* Supports {@link #selectionArc} and {@link #selectionInsets}.
|
||||||
|
* <p>
|
||||||
|
* <b>Note:</b> This method is only invoked if either selection arc
|
||||||
|
* is greater than zero or if selection insets are not empty.
|
||||||
|
*
|
||||||
|
* @since 3
|
||||||
|
*/
|
||||||
protected void paintCellSelection( Graphics g, int row, int x, int y, int width, int height ) {
|
protected void paintCellSelection( Graphics g, int row, int x, int y, int width, int height ) {
|
||||||
float arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight;
|
float arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight;
|
||||||
arcTopLeft = arcTopRight = arcBottomLeft = arcBottomRight = UIScale.scale( selectionArc / 2f );
|
arcTopLeft = arcTopRight = arcBottomLeft = arcBottomRight = UIScale.scale( selectionArc / 2f );
|
||||||
@@ -410,7 +450,7 @@ public class FlatListUI
|
|||||||
int leftIndex = locationToIndex( list, new Point( r.x - 1, r.y ) );
|
int leftIndex = locationToIndex( list, new Point( r.x - 1, r.y ) );
|
||||||
int rightIndex = locationToIndex( list, new Point( r.x + r.width, r.y ) );
|
int rightIndex = locationToIndex( list, new Point( r.x + r.width, r.y ) );
|
||||||
|
|
||||||
// special handling for the case that last column contains less cells than the other columns
|
// special handling for the case that last column contains fewer cells than the other columns
|
||||||
boolean ltr = list.getComponentOrientation().isLeftToRight();
|
boolean ltr = list.getComponentOrientation().isLeftToRight();
|
||||||
if( !ltr && leftIndex >= 0 && leftIndex != row && leftIndex == locationToIndex( list, new Point( r.x - 1, r.y - 1 ) ) )
|
if( !ltr && leftIndex >= 0 && leftIndex != row && leftIndex == locationToIndex( list, new Point( r.x - 1, r.y - 1 ) ) )
|
||||||
leftIndex = -1;
|
leftIndex = -1;
|
||||||
@@ -439,7 +479,8 @@ public class FlatListUI
|
|||||||
* Paints a cell selection at the given coordinates.
|
* Paints a cell selection at the given coordinates.
|
||||||
* The selection color must be set on the graphics context.
|
* The selection color must be set on the graphics context.
|
||||||
* <p>
|
* <p>
|
||||||
* This method is intended for use in custom cell renderers.
|
* This method is intended for use in custom cell renderers
|
||||||
|
* to support {@link #selectionArc} and {@link #selectionInsets}.
|
||||||
*
|
*
|
||||||
* @since 3
|
* @since 3
|
||||||
*/
|
*/
|
||||||
@@ -450,4 +491,15 @@ public class FlatListUI
|
|||||||
FlatListUI ui = (FlatListUI) list.getUI();
|
FlatListUI ui = (FlatListUI) list.getUI();
|
||||||
ui.paintCellSelection( g, row, x, y, width, height );
|
ui.paintCellSelection( g, row, x, y, width, height );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether dropping on a row.
|
||||||
|
* See DefaultListCellRenderer.getListCellRendererComponent().
|
||||||
|
*/
|
||||||
|
private boolean isDropRow( int row ) {
|
||||||
|
JList.DropLocation dropLocation = list.getDropLocation();
|
||||||
|
return dropLocation != null &&
|
||||||
|
!dropLocation.isInsert() &&
|
||||||
|
dropLocation.getIndex() == row;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,11 +21,10 @@ 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.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableBorder;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Border for {@link javax.swing.JMenuBar}.
|
* Border for {@link javax.swing.JMenuBar}.
|
||||||
@@ -36,27 +35,10 @@ import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableBorder;
|
|||||||
*/
|
*/
|
||||||
public class FlatMenuBarBorder
|
public class FlatMenuBarBorder
|
||||||
extends FlatMarginBorder
|
extends FlatMarginBorder
|
||||||
implements StyleableBorder
|
implements StyleableObject
|
||||||
{
|
{
|
||||||
@Styleable protected 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 ) )
|
if( !showBottomSeparator( c ) )
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import java.awt.event.ActionEvent;
|
|||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import javax.swing.AbstractAction;
|
|
||||||
import javax.swing.ActionMap;
|
import javax.swing.ActionMap;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
@@ -39,7 +38,6 @@ import javax.swing.MenuElement;
|
|||||||
import javax.swing.MenuSelectionManager;
|
import javax.swing.MenuSelectionManager;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
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;
|
||||||
@@ -144,12 +142,10 @@ public class FlatMenuBarUI
|
|||||||
protected void installKeyboardActions() {
|
protected void installKeyboardActions() {
|
||||||
super.installKeyboardActions();
|
super.installKeyboardActions();
|
||||||
|
|
||||||
|
// get shared action map, used for all menu bars
|
||||||
ActionMap map = SwingUtilities.getUIActionMap( menuBar );
|
ActionMap map = SwingUtilities.getUIActionMap( menuBar );
|
||||||
if( map == null ) {
|
if( map != null && !(map.get( "takeFocus" ) instanceof TakeFocusAction) )
|
||||||
map = new ActionMapUIResource();
|
map.put( "takeFocus", new TakeFocusAction( "takeFocus" ) );
|
||||||
SwingUtilities.replaceUIActionMap( menuBar, map );
|
|
||||||
}
|
|
||||||
map.put( "takeFocus", new TakeFocus() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
@@ -365,16 +361,20 @@ public class FlatMenuBarUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class TakeFocus ----------------------------------------------------
|
//---- class TakeFocusAction ----------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activates the menu bar and shows mnemonics.
|
* Activates the menu bar and shows mnemonics.
|
||||||
* On Windows, the popup of the first menu is not shown.
|
* On Windows, the popup of the first menu is not shown.
|
||||||
* On other platforms, the popup of the first menu is shown.
|
* On other platforms, the popup of the first menu is shown.
|
||||||
*/
|
*/
|
||||||
private static class TakeFocus
|
private static class TakeFocusAction
|
||||||
extends AbstractAction
|
extends FlatUIAction
|
||||||
{
|
{
|
||||||
|
TakeFocusAction( String name ) {
|
||||||
|
super( name );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed( ActionEvent e ) {
|
public void actionPerformed( ActionEvent e ) {
|
||||||
JMenuBar menuBar = (JMenuBar) e.getSource();
|
JMenuBar menuBar = (JMenuBar) e.getSource();
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ 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.GridLayout;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
|
import java.awt.LayoutManager;
|
||||||
import java.awt.Paint;
|
import java.awt.Paint;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.event.InputEvent;
|
import java.awt.event.InputEvent;
|
||||||
@@ -35,6 +37,7 @@ import java.util.Map;
|
|||||||
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.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
@@ -222,7 +225,7 @@ public class FlatMenuItemRenderer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// arrow size
|
// arrow size
|
||||||
if( !isTopLevelMenu && arrowIcon != null ) {
|
if( arrowIcon != null && (!isTopLevelMenu || isInVerticalMenuBar( menuItem )) ) {
|
||||||
// gap between text and arrow
|
// gap between text and arrow
|
||||||
if( accelText == null )
|
if( accelText == null )
|
||||||
width += scale( textNoAcceleratorGap );
|
width += scale( textNoAcceleratorGap );
|
||||||
@@ -254,7 +257,8 @@ public class FlatMenuItemRenderer
|
|||||||
boolean isTopLevelMenu = isTopLevelMenu( menuItem );
|
boolean isTopLevelMenu = isTopLevelMenu( menuItem );
|
||||||
|
|
||||||
// layout arrow
|
// layout arrow
|
||||||
if( !isTopLevelMenu && arrowIcon != null ) {
|
boolean showArrowIcon = (arrowIcon != null && (!isTopLevelMenu || isInVerticalMenuBar( menuItem )));
|
||||||
|
if( showArrowIcon ) {
|
||||||
arrowRect.width = arrowIcon.getIconWidth();
|
arrowRect.width = arrowIcon.getIconWidth();
|
||||||
arrowRect.height = arrowIcon.getIconHeight();
|
arrowRect.height = arrowIcon.getIconHeight();
|
||||||
} else
|
} else
|
||||||
@@ -288,7 +292,7 @@ public class FlatMenuItemRenderer
|
|||||||
int accelArrowWidth = accelRect.width + arrowRect.width;
|
int accelArrowWidth = accelRect.width + arrowRect.width;
|
||||||
if( accelText != null )
|
if( accelText != null )
|
||||||
accelArrowWidth += scale( !isTopLevelMenu ? textAcceleratorGap : menuItem.getIconTextGap() );
|
accelArrowWidth += scale( !isTopLevelMenu ? textAcceleratorGap : menuItem.getIconTextGap() );
|
||||||
if( !isTopLevelMenu && arrowIcon != null ) {
|
if( showArrowIcon ) {
|
||||||
if( accelText == null )
|
if( accelText == null )
|
||||||
accelArrowWidth += scale( textNoAcceleratorGap );
|
accelArrowWidth += scale( textNoAcceleratorGap );
|
||||||
accelArrowWidth += scale( acceleratorArrowGap );
|
accelArrowWidth += scale( acceleratorArrowGap );
|
||||||
@@ -355,7 +359,7 @@ debug*/
|
|||||||
paintIcon( g, iconRect, getIconForPainting(), underlineSelection ? underlineSelectionCheckBackground : checkBackground, selectionBackground );
|
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( arrowIcon != null && (!isTopLevelMenu( menuItem ) || isInVerticalMenuBar( menuItem )) )
|
||||||
paintArrowIcon( g, arrowRect, arrowIcon );
|
paintArrowIcon( g, arrowRect, arrowIcon );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,10 +460,11 @@ 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, iconRect.x, y );
|
icon.paintIcon( menuItem, g, x, y );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void paintText( Graphics g, JMenuItem menuItem,
|
protected static void paintText( Graphics g, JMenuItem menuItem,
|
||||||
@@ -519,6 +524,15 @@ debug*/
|
|||||||
return menuItem instanceof JMenu && ((JMenu)menuItem).isTopLevelMenu();
|
return menuItem instanceof JMenu && ((JMenu)menuItem).isTopLevelMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 3.5 */
|
||||||
|
public static boolean isInVerticalMenuBar( JMenuItem menuItem ) {
|
||||||
|
if( !(menuItem instanceof JMenu) || !(menuItem.getParent() instanceof JMenuBar) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
LayoutManager layout = menuItem.getParent().getLayout();
|
||||||
|
return layout instanceof GridLayout && ((GridLayout)layout).getRows() != 1;
|
||||||
|
}
|
||||||
|
|
||||||
protected boolean isUnderlineSelection() {
|
protected boolean isUnderlineSelection() {
|
||||||
return "underline".equals( UIManager.getString( "MenuItem.selectionType" ) );
|
return "underline".equals( UIManager.getString( "MenuItem.selectionType" ) );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,13 +103,23 @@ public class FlatMenuItemUI
|
|||||||
oldStyleValues = null;
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installComponents( JMenuItem menuItem ) {
|
||||||
|
super.installComponents( menuItem );
|
||||||
|
|
||||||
|
// update HTML renderer if necessary
|
||||||
|
FlatHTML.updateRendererCSSFontBaseSize( menuItem );
|
||||||
|
}
|
||||||
|
|
||||||
protected FlatMenuItemRenderer createRenderer() {
|
protected FlatMenuItemRenderer createRenderer() {
|
||||||
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
||||||
return FlatStylingSupport.createPropertyChangeListener( c, this::installStyle, super.createPropertyChangeListener( c ) );
|
return FlatHTML.createPropertyChangeListener(
|
||||||
|
FlatStylingSupport.createPropertyChangeListener( c, this::installStyle,
|
||||||
|
super.createPropertyChangeListener( c ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
|
|||||||
@@ -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.Font;
|
import java.awt.Font;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
@@ -46,6 +47,7 @@ import javax.swing.plaf.basic.BasicMenuUI;
|
|||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -135,6 +137,14 @@ public class FlatMenuUI
|
|||||||
oldStyleValues = null;
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installComponents( JMenuItem menuItem ) {
|
||||||
|
super.installComponents( menuItem );
|
||||||
|
|
||||||
|
// update HTML renderer if necessary
|
||||||
|
FlatHTML.updateRendererCSSFontBaseSize( menuItem );
|
||||||
|
}
|
||||||
|
|
||||||
protected FlatMenuItemRenderer createRenderer() {
|
protected FlatMenuItemRenderer createRenderer() {
|
||||||
return new FlatMenuRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
return new FlatMenuRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
||||||
}
|
}
|
||||||
@@ -158,7 +168,7 @@ public class FlatMenuUI
|
|||||||
JMenu menu = (JMenu) e.getSource();
|
JMenu menu = (JMenu) e.getSource();
|
||||||
if( menu.isTopLevelMenu() && menu.isRolloverEnabled() ) {
|
if( menu.isTopLevelMenu() && menu.isRolloverEnabled() ) {
|
||||||
menu.getModel().setRollover( rollover );
|
menu.getModel().setRollover( rollover );
|
||||||
menu.repaint();
|
HiDPIUtils.repaint( menu );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -166,7 +176,9 @@ public class FlatMenuUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
||||||
return FlatStylingSupport.createPropertyChangeListener( c, this::installStyle, super.createPropertyChangeListener( c ) );
|
return FlatHTML.createPropertyChangeListener(
|
||||||
|
FlatStylingSupport.createPropertyChangeListener( c, this::installStyle,
|
||||||
|
super.createPropertyChangeListener( c ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
@@ -271,7 +283,7 @@ public class FlatMenuUI
|
|||||||
if( !isHover() )
|
if( !isHover() )
|
||||||
selectionBackground = getStyleFromMenuBarUI( ui -> ui.selectionBackground, menuBarSelectionBackground, selectionBackground );
|
selectionBackground = getStyleFromMenuBarUI( ui -> ui.selectionBackground, menuBarSelectionBackground, selectionBackground );
|
||||||
|
|
||||||
JMenuBar menuBar = (JMenuBar) menuItem.getParent();
|
Container menuBar = menuItem.getParent();
|
||||||
JRootPane rootPane = SwingUtilities.getRootPane( menuBar );
|
JRootPane rootPane = SwingUtilities.getRootPane( menuBar );
|
||||||
if( rootPane != null && rootPane.getParent() instanceof Window &&
|
if( rootPane != null && rootPane.getParent() instanceof Window &&
|
||||||
rootPane.getJMenuBar() == menuBar &&
|
rootPane.getJMenuBar() == menuBar &&
|
||||||
@@ -321,12 +333,17 @@ public class FlatMenuUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
private <T> T getStyleFromMenuBarUI( Function<FlatMenuBarUI, T> f, T defaultValue ) {
|
private <T> T getStyleFromMenuBarUI( Function<FlatMenuBarUI, T> f, T defaultValue ) {
|
||||||
MenuBarUI ui = ((JMenuBar)menuItem.getParent()).getUI();
|
Container menuItemParent = menuItem.getParent();
|
||||||
if( !(ui instanceof FlatMenuBarUI) )
|
if( menuItemParent instanceof JMenuBar ) {
|
||||||
return defaultValue;
|
MenuBarUI ui = ((JMenuBar) menuItemParent).getUI();
|
||||||
|
if( ui instanceof FlatMenuBarUI ) {
|
||||||
T value = f.apply( (FlatMenuBarUI) ui );
|
T value = f.apply( (FlatMenuBarUI) ui );
|
||||||
return (value != null) ? value : defaultValue;
|
if( value != null ) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,9 +17,12 @@
|
|||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.security.CodeSource;
|
||||||
import com.formdev.flatlaf.FlatSystemProperties;
|
import com.formdev.flatlaf.FlatSystemProperties;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.NativeLibrary;
|
import com.formdev.flatlaf.util.NativeLibrary;
|
||||||
|
import com.formdev.flatlaf.util.StringUtils;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -31,40 +34,71 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
*/
|
*/
|
||||||
class FlatNativeLibrary
|
class FlatNativeLibrary
|
||||||
{
|
{
|
||||||
|
private static boolean initialized;
|
||||||
private static NativeLibrary nativeLibrary;
|
private static NativeLibrary nativeLibrary;
|
||||||
|
|
||||||
|
private native static int getApiVersion();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads native library (if available) and returns whether loaded successfully.
|
* Loads native library (if available) and returns whether loaded successfully.
|
||||||
* Returns {@code false} if no native library is available.
|
* Returns {@code false} if no native library is available.
|
||||||
*/
|
*/
|
||||||
static synchronized boolean isLoaded() {
|
static synchronized boolean isLoaded( int apiVersion ) {
|
||||||
initialize();
|
initialize( apiVersion );
|
||||||
return (nativeLibrary != null) ? nativeLibrary.isLoaded() : false;
|
return (nativeLibrary != null) ? nativeLibrary.isLoaded() : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void initialize() {
|
private static void initialize( int apiVersion ) {
|
||||||
if( nativeLibrary != null )
|
if( initialized )
|
||||||
|
return;
|
||||||
|
initialized = true;
|
||||||
|
|
||||||
|
if( !FlatSystemProperties.getBoolean( FlatSystemProperties.USE_NATIVE_LIBRARY, true ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
String libraryName;
|
String classifier;
|
||||||
if( SystemInfo.isWindows_10_orLater && (SystemInfo.isX86 || SystemInfo.isX86_64) ) {
|
String ext;
|
||||||
// Windows: requires Windows 10/11 (x86 or x86_64)
|
boolean unknownArch = false;
|
||||||
|
if( SystemInfo.isWindows_10_orLater && (SystemInfo.isX86 || SystemInfo.isX86_64 || SystemInfo.isAARCH64) ) {
|
||||||
|
// Windows: requires Windows 10/11 (x86, x86_64 or aarch64)
|
||||||
|
|
||||||
libraryName = "flatlaf-windows-x86";
|
if( SystemInfo.isAARCH64 )
|
||||||
if( SystemInfo.isX86_64 )
|
classifier = "windows-arm64";
|
||||||
libraryName += "_64";
|
else if( SystemInfo.isX86_64 )
|
||||||
|
classifier = "windows-x86_64";
|
||||||
|
else
|
||||||
|
classifier = "windows-x86";
|
||||||
|
|
||||||
// In Java 8, load jawt.dll (part of JRE) explicitly because it
|
ext = "dll";
|
||||||
// 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,
|
|
||||||
// but load jawt.dll anyway to be on the safe side.
|
|
||||||
loadJAWT();
|
|
||||||
} else if( SystemInfo.isLinux && SystemInfo.isX86_64 ) {
|
|
||||||
// Linux: requires x86_64
|
|
||||||
|
|
||||||
libraryName = "flatlaf-linux-x86_64";
|
// Do not load jawt.dll (part of JRE) here explicitly because
|
||||||
|
// the FlatLaf native library flatlaf.dll may be loaded very early on Windows
|
||||||
|
// (e.g. from class com.formdev.flatlaf.util.SystemInfo) and before AWT is
|
||||||
|
// initialized (and awt.dll is loaded). Loading jawt.dll also loads awt.dll.
|
||||||
|
// In Java 8, loading jawt.dll before AWT is initialized may load
|
||||||
|
// a wrong version of awt.dll if a newer Java version (e.g. 19)
|
||||||
|
// is in PATH environment variable. Then Java 19 awt.dll and Java 8 awt.dll
|
||||||
|
// are loaded at same time and calling JAWT_GetAWT() crashes the application.
|
||||||
|
//
|
||||||
|
// To avoid this, flatlaf.dll is not linked to jawt.dll,
|
||||||
|
// which avoids loading jawt.dll when flatlaf.dll is loaded.
|
||||||
|
// Instead, flatlaf.dll dynamically loads jawt.dll when first used,
|
||||||
|
// which is guaranteed after AWT initialization.
|
||||||
|
|
||||||
|
} else if( SystemInfo.isMacOS_10_14_Mojave_orLater && (SystemInfo.isAARCH64 || SystemInfo.isX86_64) ) {
|
||||||
|
// macOS: requires macOS 10.14 or later (arm64 or x86_64)
|
||||||
|
|
||||||
|
classifier = SystemInfo.isAARCH64 ? "macos-arm64" : "macos-x86_64";
|
||||||
|
ext = "dylib";
|
||||||
|
|
||||||
|
} else if( SystemInfo.isLinux ) {
|
||||||
|
// Linux: x86_64 or aarch64 (but also supports unknown architectures)
|
||||||
|
|
||||||
|
classifier = SystemInfo.isAARCH64 ? "linux-arm64"
|
||||||
|
: (SystemInfo.isX86_64 ? "linux-x86_64"
|
||||||
|
: "linux-" + sanitize( System.getProperty( "os.arch" ) ));
|
||||||
|
ext = "so";
|
||||||
|
unknownArch = !SystemInfo.isX86_64 && !SystemInfo.isAARCH64;
|
||||||
|
|
||||||
// Load libjawt.so (part of JRE) explicitly because it is not found
|
// Load libjawt.so (part of JRE) explicitly because it is not found
|
||||||
// in all Java versions/distributions.
|
// in all Java versions/distributions.
|
||||||
@@ -76,10 +110,32 @@ class FlatNativeLibrary
|
|||||||
return; // no native library available for current OS or CPU architecture
|
return; // no native library available for current OS or CPU architecture
|
||||||
|
|
||||||
// load native library
|
// load native library
|
||||||
nativeLibrary = createNativeLibrary( libraryName );
|
NativeLibrary nativeLibrary = createNativeLibrary( classifier, ext, unknownArch );
|
||||||
|
if( !nativeLibrary.isLoaded() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// check API version (and check whether library works)
|
||||||
|
try {
|
||||||
|
int actualApiVersion = getApiVersion();
|
||||||
|
if( actualApiVersion != apiVersion ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Wrong API version in native library (expected "
|
||||||
|
+ apiVersion + ", actual " + actualApiVersion + "). Ignoring native library.", null );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch( Throwable ex ) {
|
||||||
|
// could be a UnsatisfiedLinkError in case that loading native library
|
||||||
|
// from temp directory was blocked by some OS security mechanism
|
||||||
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to get API version of native library. Ignoring native library.", ex );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatNativeLibrary.nativeLibrary = nativeLibrary;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static NativeLibrary createNativeLibrary( String libraryName ) {
|
private static NativeLibrary createNativeLibrary( String classifier, String ext, boolean unknownArch ) {
|
||||||
|
String libraryName = "flatlaf-" + classifier;
|
||||||
|
|
||||||
|
// load from "java.library.path" or from path specified in system property "flatlaf.nativeLibraryPath"
|
||||||
String libraryPath = System.getProperty( FlatSystemProperties.NATIVE_LIBRARY_PATH );
|
String libraryPath = System.getProperty( FlatSystemProperties.NATIVE_LIBRARY_PATH );
|
||||||
if( libraryPath != null ) {
|
if( libraryPath != null ) {
|
||||||
if( "system".equals( libraryPath ) ) {
|
if( "system".equals( libraryPath ) ) {
|
||||||
@@ -87,17 +143,149 @@ class FlatNativeLibrary
|
|||||||
if( library.isLoaded() )
|
if( library.isLoaded() )
|
||||||
return library;
|
return library;
|
||||||
|
|
||||||
LoggingFacade.INSTANCE.logSevere( "Did not find library " + libraryName + " in java.library.path, using extracted library instead", null );
|
if( !unknownArch ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Native library '" + System.mapLibraryName( libraryName )
|
||||||
|
+ "' not found in java.library.path '" + System.getProperty( "java.library.path" )
|
||||||
|
+ "'. Using extracted native library instead.", null );
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// try standard library naming scheme
|
||||||
|
// (same as in flatlaf.jar in package 'com/formdev/flatlaf/natives')
|
||||||
File libraryFile = new File( libraryPath, System.mapLibraryName( libraryName ) );
|
File libraryFile = new File( libraryPath, System.mapLibraryName( libraryName ) );
|
||||||
if( libraryFile.exists() )
|
if( libraryFile.exists() )
|
||||||
return new NativeLibrary( libraryFile, true );
|
return new NativeLibrary( libraryFile, true );
|
||||||
|
|
||||||
LoggingFacade.INSTANCE.logSevere( "Did not find external library " + libraryFile + ", using extracted library instead", null );
|
// try Maven naming scheme
|
||||||
|
// (see https://www.formdev.com/flatlaf/native-libraries/)
|
||||||
|
String libraryName2 = null;
|
||||||
|
File jarFile = getJarFile();
|
||||||
|
if( jarFile != null ) {
|
||||||
|
libraryName2 = buildLibraryName( jarFile, classifier, ext );
|
||||||
|
File libraryFile2 = new File( libraryPath, libraryName2 );
|
||||||
|
if( libraryFile2.exists() )
|
||||||
|
return new NativeLibrary( libraryFile2, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !unknownArch ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Native library '"
|
||||||
|
+ libraryFile.getName()
|
||||||
|
+ (libraryName2 != null ? ("' or '" + libraryName2) : "")
|
||||||
|
+ "' not found in '" + libraryFile.getParentFile().getAbsolutePath()
|
||||||
|
+ "'. Using extracted native library instead.", null );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new NativeLibrary( "com/formdev/flatlaf/natives/" + libraryName, null, true );
|
// load from beside the FlatLaf jar
|
||||||
|
// e.g. for flatlaf-3.1.jar, load flatlaf-3.1-windows-x86_64.dll (from same directory)
|
||||||
|
File libraryFile = findLibraryBesideJar( classifier, ext );
|
||||||
|
if( libraryFile != null )
|
||||||
|
return new NativeLibrary( libraryFile, true );
|
||||||
|
|
||||||
|
// load from FlatLaf jar (extract native library to temp folder)
|
||||||
|
return new NativeLibrary( "com/formdev/flatlaf/natives/" + libraryName, null, !unknownArch );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for a native library beside the jar that contains this class
|
||||||
|
* (usually the FlatLaf jar).
|
||||||
|
* The native library must be in the same directory (or in "../bin" if jar is in "lib")
|
||||||
|
* as the jar and have the same basename as the jar.
|
||||||
|
* If FlatLaf jar is repackaged into fat/uber application jar, "-flatlaf" is appended to jar basename.
|
||||||
|
* The classifier and the extension are appended to the jar basename.
|
||||||
|
*
|
||||||
|
* E.g.
|
||||||
|
* flatlaf-3.1.jar
|
||||||
|
* flatlaf-3.1-windows-x86_64.dll
|
||||||
|
* flatlaf-3.1-linux-x86_64.so
|
||||||
|
*/
|
||||||
|
private static File findLibraryBesideJar( String classifier, String ext ) {
|
||||||
|
// get location of FlatLaf jar (or fat/uber application jar)
|
||||||
|
File jarFile = getJarFile();
|
||||||
|
if( jarFile == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// build library file
|
||||||
|
String libraryName = buildLibraryName( jarFile, classifier, ext );
|
||||||
|
File jarDir = jarFile.getParentFile();
|
||||||
|
|
||||||
|
// check whether native library exists in same directory as jar
|
||||||
|
File libraryFile = new File( jarDir, libraryName );
|
||||||
|
if( libraryFile.isFile() )
|
||||||
|
return libraryFile;
|
||||||
|
|
||||||
|
// if jar is in "lib" directory, then also check whether native library exists
|
||||||
|
// in "../bin" directory
|
||||||
|
if( jarDir.getName().equalsIgnoreCase( "lib" ) ) {
|
||||||
|
libraryFile = new File( jarDir.getParentFile(), "bin/" + libraryName );
|
||||||
|
if( libraryFile.isFile() )
|
||||||
|
return libraryFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
// special case: support Gradle cache when running in development environment
|
||||||
|
// <user-home>/.gradle/caches/modules-2/files-2.1/com.formdev/flatlaf/<version>/<hash-1>/flatlaf-<version>.jar
|
||||||
|
// <user-home>/.gradle/caches/modules-2/files-2.1/com.formdev/flatlaf/<version>/<hash-2>/flatlaf-<version>-windows-x86_64.dll
|
||||||
|
String path = jarDir.getAbsolutePath().replace( '\\', '/' );
|
||||||
|
if( path.contains( "/.gradle/caches/" ) ) {
|
||||||
|
File versionDir = jarDir.getParentFile();
|
||||||
|
if( libraryName.contains( versionDir.getName() ) ) {
|
||||||
|
File[] dirs = versionDir.listFiles();
|
||||||
|
if( dirs != null ) {
|
||||||
|
for( File dir : dirs ) {
|
||||||
|
libraryFile = new File( dir, libraryName );
|
||||||
|
if( libraryFile.isFile() )
|
||||||
|
return libraryFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// native library not found
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static File getJarFile() {
|
||||||
|
try {
|
||||||
|
// get location of FlatLaf jar
|
||||||
|
CodeSource codeSource = FlatNativeLibrary.class.getProtectionDomain().getCodeSource();
|
||||||
|
URL jarUrl = (codeSource != null) ? codeSource.getLocation() : null;
|
||||||
|
if( jarUrl == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// if url is not a file, then we're running in a special environment (e.g. WebStart)
|
||||||
|
if( !"file".equals( jarUrl.getProtocol() ) )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
File jarFile = new File( jarUrl.toURI() );
|
||||||
|
|
||||||
|
// if jarFile is a directory, then we're in a development environment
|
||||||
|
if( !jarFile.isFile() )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return jarFile;
|
||||||
|
} catch( Exception ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( ex.getMessage(), ex );
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildLibraryName( File jarFile, String classifier, String ext ) {
|
||||||
|
String jarName = jarFile.getName();
|
||||||
|
String jarBasename = jarName.substring( 0, jarName.lastIndexOf( '.' ) );
|
||||||
|
|
||||||
|
// remove classifier "no-natives" (if used)
|
||||||
|
jarBasename = StringUtils.removeTrailing( jarBasename, "-no-natives" );
|
||||||
|
|
||||||
|
return jarBasename
|
||||||
|
+ (jarBasename.contains( "flatlaf" ) ? "" : "-flatlaf")
|
||||||
|
+ '-' + classifier + '.' + ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow only 'a'-'z', 'A'-'Z', '0'-'9', '_' and '-' in filenames.
|
||||||
|
*/
|
||||||
|
private static String sanitize( String s ) {
|
||||||
|
return s.replaceAll( "[^a-zA-Z0-9_-]", "_" );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void loadJAWT() {
|
private static void loadJAWT() {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.GraphicsConfiguration;
|
||||||
import java.awt.Point;
|
import java.awt.Point;
|
||||||
import java.awt.Toolkit;
|
import java.awt.Toolkit;
|
||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
@@ -23,6 +24,8 @@ import java.awt.event.MouseEvent;
|
|||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
import javax.swing.JFrame;
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Native methods for Linux.
|
* Native methods for Linux.
|
||||||
@@ -32,15 +35,35 @@ import javax.swing.JFrame;
|
|||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
* @since 2.5
|
* @since 2.5
|
||||||
*/
|
*/
|
||||||
class FlatNativeLinuxLibrary
|
public class FlatNativeLinuxLibrary
|
||||||
{
|
{
|
||||||
static boolean isLoaded() {
|
private static int API_VERSION_LINUX = 3003;
|
||||||
return FlatNativeLibrary.isLoaded();
|
|
||||||
|
/**
|
||||||
|
* Checks whether native library is loaded/available.
|
||||||
|
* <p>
|
||||||
|
* <b>Note</b>: It is required to invoke this method before invoking any other
|
||||||
|
* method of this class. Otherwise, the native library may not be loaded.
|
||||||
|
*/
|
||||||
|
public static boolean isLoaded() {
|
||||||
|
return SystemInfo.isLinux && FlatNativeLibrary.isLoaded( API_VERSION_LINUX );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---- X Window System ----------------------------------------------------
|
||||||
|
|
||||||
// direction for _NET_WM_MOVERESIZE message
|
// direction for _NET_WM_MOVERESIZE message
|
||||||
// see https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html
|
// see https://specifications.freedesktop.org/wm-spec/latest/ar01s04.html
|
||||||
static final int MOVE = 8;
|
static final int
|
||||||
|
SIZE_TOPLEFT = 0,
|
||||||
|
SIZE_TOP = 1,
|
||||||
|
SIZE_TOPRIGHT = 2,
|
||||||
|
SIZE_RIGHT = 3,
|
||||||
|
SIZE_BOTTOMRIGHT = 4,
|
||||||
|
SIZE_BOTTOM = 5,
|
||||||
|
SIZE_BOTTOMLEFT = 6,
|
||||||
|
SIZE_LEFT = 7,
|
||||||
|
MOVE = 8;
|
||||||
|
|
||||||
private static Boolean isXWindowSystem;
|
private static Boolean isXWindowSystem;
|
||||||
|
|
||||||
@@ -87,7 +110,11 @@ class FlatNativeLinuxLibrary
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Point scale( Window window, Point pt ) {
|
private static Point scale( Window window, Point pt ) {
|
||||||
AffineTransform transform = window.getGraphicsConfiguration().getDefaultTransform();
|
GraphicsConfiguration gc = window.getGraphicsConfiguration();
|
||||||
|
if( gc == null )
|
||||||
|
return pt;
|
||||||
|
|
||||||
|
AffineTransform transform = gc.getDefaultTransform();
|
||||||
int x = (int) Math.round( pt.x * transform.getScaleX() );
|
int x = (int) Math.round( pt.x * transform.getScaleX() );
|
||||||
int y = (int) Math.round( pt.y * transform.getScaleY() );
|
int y = (int) Math.round( pt.y * transform.getScaleY() );
|
||||||
return new Point( x, y );
|
return new Point( x, y );
|
||||||
@@ -101,4 +128,110 @@ class FlatNativeLinuxLibrary
|
|||||||
return (window instanceof JFrame && JFrame.isDefaultLookAndFeelDecorated() && ((JFrame)window).isUndecorated()) ||
|
return (window instanceof JFrame && JFrame.isDefaultLookAndFeelDecorated() && ((JFrame)window).isUndecorated()) ||
|
||||||
(window instanceof JDialog && JDialog.isDefaultLookAndFeelDecorated() && ((JDialog)window).isUndecorated());
|
(window instanceof JDialog && JDialog.isDefaultLookAndFeelDecorated() && ((JDialog)window).isUndecorated());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---- GTK ----------------------------------------------------------------
|
||||||
|
|
||||||
|
private static Boolean isGtk3Available;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether GTK 3 is available.
|
||||||
|
* Use this before invoking any native method that uses GTK.
|
||||||
|
* Otherwise the app may terminate immediately if GTK is not installed.
|
||||||
|
* <p>
|
||||||
|
* This works because Java uses {@code dlopen(RTLD_LAZY)} to load JNI libraries,
|
||||||
|
* which only resolves symbols as the code that references them is executed.
|
||||||
|
*
|
||||||
|
* @since 3.7
|
||||||
|
*/
|
||||||
|
public static boolean isGtk3Available() {
|
||||||
|
if( isGtk3Available == null )
|
||||||
|
isGtk3Available = isLibAvailable( "libgtk-3.so.0" ) || isLibAvailable( "libgtk-3.so" );
|
||||||
|
return isGtk3Available;
|
||||||
|
}
|
||||||
|
|
||||||
|
private native static boolean isLibAvailable( String libname );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://docs.gtk.org/gtk3/iface.FileChooser.html#properties
|
||||||
|
*
|
||||||
|
* @since 3.7
|
||||||
|
*/
|
||||||
|
public static final int
|
||||||
|
FC_select_folder = 1 << 0,
|
||||||
|
FC_select_multiple = 1 << 1,
|
||||||
|
FC_show_hidden = 1 << 2,
|
||||||
|
FC_local_only = 1 << 3, // default
|
||||||
|
FC_do_overwrite_confirmation = 1 << 4, // GTK 3 only; removed and always-on in GTK 4
|
||||||
|
FC_create_folders = 1 << 5; // default for Save
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the Linux/GTK system file dialog
|
||||||
|
* <a href="https://docs.gtk.org/gtk3/class.FileChooserDialog.html">GtkFileChooserDialog</a>.
|
||||||
|
* <p>
|
||||||
|
* Uses {@code GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER} if {@link #FC_select_folder} is set in parameter {@code optionsSet}.
|
||||||
|
* Otherwise uses {@code GTK_FILE_CHOOSER_ACTION_OPEN} if parameter {@code open} is {@code true},
|
||||||
|
* or {@code GTK_FILE_CHOOSER_ACTION_SAVE} if {@code false}.
|
||||||
|
* <p>
|
||||||
|
* <b>Note:</b> This method blocks the current thread until the user closes
|
||||||
|
* the file dialog. It is highly recommended to invoke it from a new thread
|
||||||
|
* to avoid blocking the AWT event dispatching thread.
|
||||||
|
*
|
||||||
|
* @param owner the owner of the file dialog; or {@code null}
|
||||||
|
* @param dark preferred appearance of the file dialog: {@code 1} = prefer dark, {@code 0} = prefer light, {@code -1} = default
|
||||||
|
* @param open if {@code true}, shows the open dialog; if {@code false}, shows the save dialog
|
||||||
|
* @param title text displayed in dialog title; or {@code null}
|
||||||
|
* @param okButtonLabel text displayed in default button; or {@code null}.
|
||||||
|
* Use '_' for mnemonics (e.g. "_Choose")
|
||||||
|
* Use '__' for '_' character (e.g. "Choose__and__Quit").
|
||||||
|
* @param currentName user-editable filename currently shown in the filename field in save dialog; or {@code null}
|
||||||
|
* @param currentFolder current directory shown in the dialog; or {@code null}
|
||||||
|
* @param optionsSet options to set; see {@code FOS_*} constants
|
||||||
|
* @param optionsClear options to clear; see {@code FOS_*} constants
|
||||||
|
* @param callback approve callback; or {@code null}
|
||||||
|
* @param fileTypeIndex the file type that appears as selected (zero-based)
|
||||||
|
* @param fileTypes file types that the dialog can open or save.
|
||||||
|
* Two or more strings and {@code null} are required for each filter.
|
||||||
|
* First string is the display name of the filter shown in the combobox (e.g. "Text Files").
|
||||||
|
* Subsequent strings are the filter patterns (e.g. "*.txt" or "*").
|
||||||
|
* {@code null} is required to mark end of filter.
|
||||||
|
* @return file path(s) that the user selected; an empty array if canceled;
|
||||||
|
* or {@code null} on failures (no dialog shown)
|
||||||
|
*
|
||||||
|
* @since 3.7
|
||||||
|
*/
|
||||||
|
public native static String[] showFileChooser( Window owner, int dark, boolean open,
|
||||||
|
String title, String okButtonLabel, String currentName, String currentFolder,
|
||||||
|
int optionsSet, int optionsClear, FileChooserCallback callback,
|
||||||
|
int fileTypeIndex, String... fileTypes );
|
||||||
|
|
||||||
|
/** @since 3.7 */
|
||||||
|
public interface FileChooserCallback {
|
||||||
|
boolean approve( String[] files, long hwndFileDialog );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a GTK message box
|
||||||
|
* <a href="https://docs.gtk.org/gtk3/class.MessageDialog.html">GtkMessageDialog</a>.
|
||||||
|
* <p>
|
||||||
|
* For use in {@link FileChooserCallback} only.
|
||||||
|
*
|
||||||
|
* @param hwndParent the parent of the message box
|
||||||
|
* @param messageType type of message being displayed:
|
||||||
|
* {@link JOptionPane#ERROR_MESSAGE}, {@link JOptionPane#INFORMATION_MESSAGE},
|
||||||
|
* {@link JOptionPane#WARNING_MESSAGE}, {@link JOptionPane#QUESTION_MESSAGE} or
|
||||||
|
* {@link JOptionPane#PLAIN_MESSAGE}
|
||||||
|
* @param primaryText primary text; if the dialog has a secondary text,
|
||||||
|
* this will appear as title in a larger bold font
|
||||||
|
* @param secondaryText secondary text; shown below of primary text; or {@code null}
|
||||||
|
* @param defaultButton index of the default button, which can be pressed using ENTER key
|
||||||
|
* @param buttons texts of the buttons; if no buttons given the a default "OK" button is shown.
|
||||||
|
* Use '_' for mnemonics (e.g. "_Choose")
|
||||||
|
* Use '__' for '_' character (e.g. "Choose__and__Quit").
|
||||||
|
* @return index of pressed button; or -1 for ESC key
|
||||||
|
*
|
||||||
|
* @since 3.7
|
||||||
|
*/
|
||||||
|
public native static int showMessageDialog( long hwndParent, int messageType,
|
||||||
|
String primaryText, String secondaryText, int defaultButton, String... buttons );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 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.Rectangle;
|
||||||
|
import java.awt.Window;
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Native methods for macOS.
|
||||||
|
* <p>
|
||||||
|
* <b>Note</b>: This is private API. Do not use!
|
||||||
|
*
|
||||||
|
* <h2>Methods that use windows as parameter</h2>
|
||||||
|
*
|
||||||
|
* For all methods that accept a {@link java.awt.Window} as parameter,
|
||||||
|
* the underlying macOS window must be already created,
|
||||||
|
* otherwise the method fails. You can use following to ensure this:
|
||||||
|
* <pre>{@code
|
||||||
|
* if( !window.isDisplayable() )
|
||||||
|
* window.addNotify();
|
||||||
|
* }</pre>
|
||||||
|
* or invoke the method after packing the window. E.g.
|
||||||
|
* <pre>{@code
|
||||||
|
* window.pack();
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
public class FlatNativeMacLibrary
|
||||||
|
{
|
||||||
|
private static int API_VERSION_MACOS = 2002;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether native library is loaded/available.
|
||||||
|
* <p>
|
||||||
|
* <b>Note</b>: It is required to invoke this method before invoking any other
|
||||||
|
* method of this class. Otherwise, the native library may not be loaded.
|
||||||
|
*/
|
||||||
|
public static boolean isLoaded() {
|
||||||
|
return SystemInfo.isMacOS && FlatNativeLibrary.isLoaded( API_VERSION_MACOS );
|
||||||
|
}
|
||||||
|
|
||||||
|
public native static boolean setWindowRoundedBorder( Window window, float radius, float borderWidth, int borderColor );
|
||||||
|
|
||||||
|
/** @since 3.4 */
|
||||||
|
public static final int
|
||||||
|
BUTTONS_SPACING_DEFAULT = 0,
|
||||||
|
BUTTONS_SPACING_MEDIUM = 1,
|
||||||
|
BUTTONS_SPACING_LARGE = 2;
|
||||||
|
|
||||||
|
/** @since 3.4 */ public native static boolean setWindowButtonsSpacing( Window window, int buttonsSpacing );
|
||||||
|
/** @since 3.4 */ public native static Rectangle getWindowButtonsBounds( Window window );
|
||||||
|
/** @since 3.4 */ public native static boolean isWindowFullScreen( Window window );
|
||||||
|
/** @since 3.4 */ public native static boolean toggleWindowFullScreen( Window window );
|
||||||
|
|
||||||
|
|
||||||
|
/** @since 3.7 */
|
||||||
|
public static final int
|
||||||
|
// NSOpenPanel (extends NSSavePanel)
|
||||||
|
FC_canChooseFiles = 1 << 0, // default
|
||||||
|
FC_canChooseDirectories = 1 << 1,
|
||||||
|
FC_resolvesAliases = 1 << 2, // default
|
||||||
|
FC_allowsMultipleSelection = 1 << 3,
|
||||||
|
FC_accessoryViewDisclosed = 1 << 4,
|
||||||
|
// NSSavePanel
|
||||||
|
FC_showsTagField = 1 << 8, // default for Save
|
||||||
|
FC_canCreateDirectories = 1 << 9, // default for Save
|
||||||
|
FC_canSelectHiddenExtension = 1 << 10,
|
||||||
|
FC_showsHiddenFiles = 1 << 11,
|
||||||
|
FC_extensionHidden = 1 << 12,
|
||||||
|
FC_allowsOtherFileTypes = 1 << 13,
|
||||||
|
FC_treatsFilePackagesAsDirectories = 1 << 14,
|
||||||
|
// custom
|
||||||
|
FC_showSingleFilterField = 1 << 24;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the macOS system file dialogs
|
||||||
|
* <a href="https://developer.apple.com/documentation/appkit/nsopenpanel?language=objc">NSOpenPanel</a> or
|
||||||
|
* <a href="https://developer.apple.com/documentation/appkit/nssavepanel?language=objc">NSSavePanel</a>.
|
||||||
|
* <p>
|
||||||
|
* <b>Note:</b> This method blocks the current thread until the user closes
|
||||||
|
* the file dialog. It is highly recommended to invoke it from a new thread
|
||||||
|
* to avoid blocking the AWT event dispatching thread.
|
||||||
|
*
|
||||||
|
* @param owner the owner of the file dialog; or {@code null}
|
||||||
|
* @param dark appearance of the file dialog: {@code 1} = dark, {@code 0} = light, {@code -1} = default
|
||||||
|
* @param open if {@code true}, shows the open dialog; if {@code false}, shows the save dialog
|
||||||
|
* @param title text displayed at top of save dialog (not used in open dialog); or {@code null}
|
||||||
|
* @param prompt text displayed in default button; or {@code null}
|
||||||
|
* @param message text displayed at top of open/save dialogs; or {@code null}
|
||||||
|
* @param filterFieldLabel text displayed in front of the filter combobox; or {@code null}
|
||||||
|
* @param nameFieldLabel text displayed in front of the filename text field in save dialog (not used in open dialog); or {@code null}
|
||||||
|
* @param nameFieldStringValue user-editable filename currently shown in the name field in save dialog (not used in open dialog); or {@code null}
|
||||||
|
* @param directoryURL current directory shown in the dialog; or {@code null}
|
||||||
|
* @param optionsSet options to set; see {@code FC_*} constants
|
||||||
|
* @param optionsClear options to clear; see {@code FC_*} constants
|
||||||
|
* @param fileTypeIndex the file type that appears as selected (zero-based)
|
||||||
|
* @param fileTypes file types that the dialog can open or save.
|
||||||
|
* Two or more strings and {@code null} are required for each filter.
|
||||||
|
* First string is the display name of the filter shown in the combobox (e.g. "Text Files").
|
||||||
|
* Subsequent strings are the filter patterns (e.g. "txt" or "*").
|
||||||
|
* {@code null} is required to mark end of filter.
|
||||||
|
* @return file path(s) that the user selected; an empty array if canceled;
|
||||||
|
* or {@code null} on failures (no dialog shown)
|
||||||
|
*
|
||||||
|
* @since 3.7
|
||||||
|
*/
|
||||||
|
public native static String[] showFileChooser( Window owner, int dark, boolean open,
|
||||||
|
String title, String prompt, String message, String filterFieldLabel,
|
||||||
|
String nameFieldLabel, String nameFieldStringValue, String directoryURL,
|
||||||
|
int optionsSet, int optionsClear, FileChooserCallback callback,
|
||||||
|
int fileTypeIndex, String... fileTypes );
|
||||||
|
|
||||||
|
/** @since 3.7 */
|
||||||
|
public interface FileChooserCallback {
|
||||||
|
boolean approve( String[] files, long hwndFileDialog );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a macOS alert
|
||||||
|
* <a href="https://developer.apple.com/documentation/appkit/nsalert?language=objc">NSAlert</a>.
|
||||||
|
* <p>
|
||||||
|
* For use in {@link FileChooserCallback} only.
|
||||||
|
*
|
||||||
|
* @param hwndParent the parent of the message box
|
||||||
|
* @param alertStyle type of alert being displayed:
|
||||||
|
* {@link JOptionPane#ERROR_MESSAGE}, {@link JOptionPane#INFORMATION_MESSAGE} or
|
||||||
|
* {@link JOptionPane#WARNING_MESSAGE}
|
||||||
|
* @param messageText main message of the alert
|
||||||
|
* @param informativeText additional information about the alert; shown below of main message; or {@code null}
|
||||||
|
* @param defaultButton index of the default button, which can be pressed using ENTER key
|
||||||
|
* @param buttons texts of the buttons; if no buttons given the a default "OK" button is shown
|
||||||
|
* @return index of pressed button
|
||||||
|
*
|
||||||
|
* @since 3.7
|
||||||
|
*/
|
||||||
|
public native static int showMessageDialog( long hwndParent, int alertStyle,
|
||||||
|
String messageText, String informativeText, int defaultButton, String... buttons );
|
||||||
|
}
|
||||||
@@ -17,20 +17,27 @@
|
|||||||
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.Container;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Point;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.Toolkit;
|
||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.List;
|
import java.util.function.Predicate;
|
||||||
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 javax.swing.plaf.BorderUIResource;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
import com.formdev.flatlaf.FlatSystemProperties;
|
import com.formdev.flatlaf.FlatSystemProperties;
|
||||||
import com.formdev.flatlaf.ui.JBRCustomDecorations.JBRWindowTopBorder;
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,27 +61,15 @@ public class FlatNativeWindowBorder
|
|||||||
!SystemInfo.isWinPE &&
|
!SystemInfo.isWinPE &&
|
||||||
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_WINDOW_DECORATIONS, true );
|
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_WINDOW_DECORATIONS, true );
|
||||||
|
|
||||||
// check this field before using class JBRCustomDecorations to avoid unnecessary loading of that class
|
|
||||||
private static final boolean canUseJBRCustomDecorations =
|
|
||||||
canUseWindowDecorations &&
|
|
||||||
SystemInfo.isJetBrainsJVM_11_orLater &&
|
|
||||||
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, false );
|
|
||||||
|
|
||||||
private static Boolean supported;
|
private static Boolean supported;
|
||||||
private static Provider nativeProvider;
|
private static Provider nativeProvider;
|
||||||
|
|
||||||
public static boolean isSupported() {
|
public static boolean isSupported() {
|
||||||
if( canUseJBRCustomDecorations )
|
|
||||||
return JBRCustomDecorations.isSupported();
|
|
||||||
|
|
||||||
initialize();
|
initialize();
|
||||||
return supported;
|
return supported;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Object install( JRootPane rootPane ) {
|
static Object install( JRootPane rootPane ) {
|
||||||
if( canUseJBRCustomDecorations )
|
|
||||||
return JBRCustomDecorations.install( rootPane );
|
|
||||||
|
|
||||||
if( !isSupported() )
|
if( !isSupported() )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@@ -163,11 +158,6 @@ public class FlatNativeWindowBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void uninstall( JRootPane rootPane, Object data ) {
|
static void uninstall( JRootPane rootPane, Object data ) {
|
||||||
if( canUseJBRCustomDecorations ) {
|
|
||||||
JBRCustomDecorations.uninstall( rootPane, data );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !isSupported() )
|
if( !isSupported() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -215,9 +205,6 @@ public class FlatNativeWindowBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasCustomDecoration( Window window ) {
|
public static boolean hasCustomDecoration( Window window ) {
|
||||||
if( canUseJBRCustomDecorations )
|
|
||||||
return JBRCustomDecorations.hasCustomDecoration( window );
|
|
||||||
|
|
||||||
if( !isSupported() )
|
if( !isSupported() )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -225,11 +212,6 @@ public class FlatNativeWindowBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void setHasCustomDecoration( Window window, boolean hasCustomDecoration ) {
|
public static void setHasCustomDecoration( Window window, boolean hasCustomDecoration ) {
|
||||||
if( canUseJBRCustomDecorations ) {
|
|
||||||
JBRCustomDecorations.setHasCustomDecoration( window, hasCustomDecoration );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !isSupported() )
|
if( !isSupported() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -237,23 +219,18 @@ public class FlatNativeWindowBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight,
|
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight,
|
||||||
List<Rectangle> hitTestSpots, Rectangle appIconBounds, Rectangle minimizeButtonBounds,
|
Predicate<Point> captionHitTestCallback, Rectangle appIconBounds, Rectangle minimizeButtonBounds,
|
||||||
Rectangle maximizeButtonBounds, Rectangle closeButtonBounds )
|
Rectangle maximizeButtonBounds, Rectangle closeButtonBounds )
|
||||||
{
|
{
|
||||||
if( canUseJBRCustomDecorations ) {
|
|
||||||
JBRCustomDecorations.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !isSupported() )
|
if( !isSupported() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
nativeProvider.updateTitleBarInfo( window, titleBarHeight, hitTestSpots,
|
nativeProvider.updateTitleBarInfo( window, titleBarHeight, captionHitTestCallback,
|
||||||
appIconBounds, minimizeButtonBounds, maximizeButtonBounds, closeButtonBounds );
|
appIconBounds, minimizeButtonBounds, maximizeButtonBounds, closeButtonBounds );
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean showWindow( Window window, int cmd ) {
|
static boolean showWindow( Window window, int cmd ) {
|
||||||
if( canUseJBRCustomDecorations || !isSupported() )
|
if( !isSupported() )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return nativeProvider.showWindow( window, cmd );
|
return nativeProvider.showWindow( window, cmd );
|
||||||
@@ -294,7 +271,7 @@ public class FlatNativeWindowBorder
|
|||||||
{
|
{
|
||||||
boolean hasCustomDecoration( Window window );
|
boolean hasCustomDecoration( Window window );
|
||||||
void setHasCustomDecoration( Window window, boolean hasCustomDecoration );
|
void setHasCustomDecoration( Window window, boolean hasCustomDecoration );
|
||||||
void updateTitleBarInfo( Window window, int titleBarHeight, List<Rectangle> hitTestSpots,
|
void updateTitleBarInfo( Window window, int titleBarHeight, Predicate<Point> captionHitTestCallback,
|
||||||
Rectangle appIconBounds, Rectangle minimizeButtonBounds, Rectangle maximizeButtonBounds,
|
Rectangle appIconBounds, Rectangle minimizeButtonBounds, Rectangle maximizeButtonBounds,
|
||||||
Rectangle closeButtonBounds );
|
Rectangle closeButtonBounds );
|
||||||
|
|
||||||
@@ -320,20 +297,36 @@ public class FlatNativeWindowBorder
|
|||||||
* No longer needed since Windows 11.
|
* No longer needed since Windows 11.
|
||||||
*/
|
*/
|
||||||
static class WindowTopBorder
|
static class WindowTopBorder
|
||||||
extends JBRCustomDecorations.JBRWindowTopBorder
|
extends BorderUIResource.EmptyBorderUIResource
|
||||||
{
|
{
|
||||||
private static WindowTopBorder instance;
|
private static WindowTopBorder instance;
|
||||||
|
|
||||||
static JBRWindowTopBorder getInstance() {
|
private final Color activeLightColor = new Color( 0x707070 );
|
||||||
if( canUseJBRCustomDecorations )
|
private final Color activeDarkColor = new Color( 0x2D2E2F );
|
||||||
return JBRWindowTopBorder.getInstance();
|
private final Color inactiveLightColor = new Color( 0xaaaaaa );
|
||||||
|
private final Color inactiveDarkColor = new Color( 0x494A4B );
|
||||||
|
|
||||||
|
private boolean colorizationAffectsBorders;
|
||||||
|
private Color activeColor;
|
||||||
|
|
||||||
|
static WindowTopBorder getInstance() {
|
||||||
if( instance == null )
|
if( instance == null )
|
||||||
instance = new WindowTopBorder();
|
instance = new WindowTopBorder();
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
WindowTopBorder() {
|
||||||
|
super( 1, 0, 0, 0 );
|
||||||
|
|
||||||
|
update();
|
||||||
|
installListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void update() {
|
||||||
|
colorizationAffectsBorders = isColorizationColorAffectsBorders();
|
||||||
|
activeColor = calculateActiveBorderColor();
|
||||||
|
}
|
||||||
|
|
||||||
void installListeners() {
|
void installListeners() {
|
||||||
nativeProvider.addChangeListener( e -> {
|
nativeProvider.addChangeListener( e -> {
|
||||||
update();
|
update();
|
||||||
@@ -346,19 +339,69 @@ public class FlatNativeWindowBorder
|
|||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean isColorizationColorAffectsBorders() {
|
boolean isColorizationColorAffectsBorders() {
|
||||||
return nativeProvider.isColorizationColorAffectsBorders();
|
return nativeProvider.isColorizationColorAffectsBorders();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
Color getColorizationColor() {
|
Color getColorizationColor() {
|
||||||
return nativeProvider.getColorizationColor();
|
return nativeProvider.getColorizationColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
int getColorizationColorBalance() {
|
int getColorizationColorBalance() {
|
||||||
return nativeProvider.getColorizationColorBalance();
|
return nativeProvider.getColorizationColorBalance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Color calculateActiveBorderColor() {
|
||||||
|
if( !colorizationAffectsBorders )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Color colorizationColor = getColorizationColor();
|
||||||
|
if( colorizationColor != null ) {
|
||||||
|
int colorizationColorBalance = getColorizationColorBalance();
|
||||||
|
if( colorizationColorBalance < 0 || colorizationColorBalance > 100 )
|
||||||
|
colorizationColorBalance = 100;
|
||||||
|
|
||||||
|
if( colorizationColorBalance == 0 )
|
||||||
|
return new Color( 0xD9D9D9 );
|
||||||
|
if( colorizationColorBalance == 100 )
|
||||||
|
return colorizationColor;
|
||||||
|
|
||||||
|
float alpha = colorizationColorBalance / 100.0f;
|
||||||
|
float remainder = 1 - alpha;
|
||||||
|
int r = Math.round( colorizationColor.getRed() * alpha + 0xD9 * remainder );
|
||||||
|
int g = Math.round( colorizationColor.getGreen() * alpha + 0xD9 * remainder );
|
||||||
|
int b = Math.round( colorizationColor.getBlue() * alpha + 0xD9 * remainder );
|
||||||
|
|
||||||
|
// avoid potential IllegalArgumentException in Color constructor
|
||||||
|
r = Math.min( Math.max( r, 0 ), 255 );
|
||||||
|
g = Math.min( Math.max( g, 0 ), 255 );
|
||||||
|
b = Math.min( Math.max( b, 0 ), 255 );
|
||||||
|
|
||||||
|
return new Color( r, g, b );
|
||||||
|
}
|
||||||
|
|
||||||
|
Color activeBorderColor = (Color) Toolkit.getDefaultToolkit().getDesktopProperty( "win.frame.activeBorderColor" );
|
||||||
|
return (activeBorderColor != null) ? activeBorderColor : UIManager.getColor( "MenuBar.borderColor" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
|
Window window = SwingUtilities.windowForComponent( c );
|
||||||
|
boolean active = window != null && window.isActive();
|
||||||
|
boolean dark = FlatLaf.isLafDark();
|
||||||
|
|
||||||
|
g.setColor( active
|
||||||
|
? (activeColor != null ? activeColor : (dark ? activeDarkColor : activeLightColor))
|
||||||
|
: (dark ? inactiveDarkColor : inactiveLightColor) );
|
||||||
|
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void paintImpl( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
|
||||||
|
g.fillRect( x, y, width, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
void repaintBorder( Component c ) {
|
||||||
|
c.repaint( 0, 0, c.getWidth(), 1 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,283 @@
|
|||||||
|
/*
|
||||||
|
* 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.Color;
|
||||||
|
import java.awt.Window;
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Native methods for Windows.
|
||||||
|
* <p>
|
||||||
|
* <b>Note</b>: This is private API. Do not use!
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
* @since 3.1
|
||||||
|
*/
|
||||||
|
public class FlatNativeWindowsLibrary
|
||||||
|
{
|
||||||
|
private static int API_VERSION_WINDOWS = 1002;
|
||||||
|
|
||||||
|
private static long osBuildNumber = Long.MIN_VALUE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether native library is loaded/available.
|
||||||
|
* <p>
|
||||||
|
* <b>Note</b>: It is required to invoke this method before invoking any other
|
||||||
|
* method of this class. Otherwise, the native library may not be loaded.
|
||||||
|
*/
|
||||||
|
public static boolean isLoaded() {
|
||||||
|
return SystemInfo.isWindows && FlatNativeLibrary.isLoaded( API_VERSION_WINDOWS );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the Windows operating system build number.
|
||||||
|
* <p>
|
||||||
|
* Invokes Win32 API method {@code GetVersionEx()} and returns {@code OSVERSIONINFO.dwBuildNumber}.
|
||||||
|
* See https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getversionexa
|
||||||
|
*/
|
||||||
|
public static long getOSBuildNumber() {
|
||||||
|
if( osBuildNumber == Long.MIN_VALUE )
|
||||||
|
osBuildNumber = getOSBuildNumberImpl();
|
||||||
|
return osBuildNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes Win32 API method {@code GetVersionEx()} and returns {@code OSVERSIONINFO.dwBuildNumber}.
|
||||||
|
* See https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getversionexa
|
||||||
|
*/
|
||||||
|
private native static long getOSBuildNumberImpl();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the Windows window handle (HWND) for the given Swing window.
|
||||||
|
* <p>
|
||||||
|
* Note that the underlying Windows window must be already created,
|
||||||
|
* otherwise this method returns zero. Use following to ensure this:
|
||||||
|
* <pre>{@code
|
||||||
|
* if( !window.isDisplayable() )
|
||||||
|
* window.addNotify();
|
||||||
|
* }</pre>
|
||||||
|
* or invoke this method after packing the window. E.g.
|
||||||
|
* <pre>{@code
|
||||||
|
* window.pack();
|
||||||
|
* long hwnd = getHWND( window );
|
||||||
|
* }</pre>
|
||||||
|
*/
|
||||||
|
public native static long getHWND( Window window );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DWM_WINDOW_CORNER_PREFERENCE
|
||||||
|
* see https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_window_corner_preference
|
||||||
|
*/
|
||||||
|
public static final int
|
||||||
|
DWMWCP_DEFAULT = 0,
|
||||||
|
DWMWCP_DONOTROUND = 1,
|
||||||
|
DWMWCP_ROUND = 2,
|
||||||
|
DWMWCP_ROUNDSMALL = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the rounded corner preference for the window.
|
||||||
|
* Allowed values are {@link #DWMWCP_DEFAULT}, {@link #DWMWCP_DONOTROUND},
|
||||||
|
* {@link #DWMWCP_ROUND} and {@link #DWMWCP_ROUNDSMALL}.
|
||||||
|
* <p>
|
||||||
|
* Invokes Win32 API method {@code DwmSetWindowAttribute(DWMWA_WINDOW_CORNER_PREFERENCE)}.
|
||||||
|
* See https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute
|
||||||
|
* <p>
|
||||||
|
* Supported since Windows 11 Build 22000.
|
||||||
|
*/
|
||||||
|
public native static boolean setWindowCornerPreference( long hwnd, int cornerPreference );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DWMWINDOWATTRIBUTE
|
||||||
|
* see https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
|
||||||
|
*
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
public static final int
|
||||||
|
DWMWA_USE_IMMERSIVE_DARK_MODE = 20,
|
||||||
|
DWMWA_BORDER_COLOR = 34,
|
||||||
|
DWMWA_CAPTION_COLOR = 35,
|
||||||
|
DWMWA_TEXT_COLOR = 36;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes Win32 API method {@code DwmSetWindowAttribute()} with a {@code BOOL} attribute value.
|
||||||
|
* See https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute
|
||||||
|
*
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
public native static boolean dwmSetWindowAttributeBOOL( long hwnd, int attribute, boolean value );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes Win32 API method {@code DwmSetWindowAttribute()} with a {@code DWORD} attribute value.
|
||||||
|
* See https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute
|
||||||
|
*
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
public native static boolean dwmSetWindowAttributeDWORD( long hwnd, int attribute, int value );
|
||||||
|
|
||||||
|
/** @since 3.3 */
|
||||||
|
public static final int
|
||||||
|
// use this constant to reset any window part colors to the system default behavior
|
||||||
|
DWMWA_COLOR_DEFAULT = 0xFFFFFFFF,
|
||||||
|
// use this constant to specify that a window part should not be rendered
|
||||||
|
DWMWA_COLOR_NONE = 0xFFFFFFFE;
|
||||||
|
|
||||||
|
/** @since 3.3 */
|
||||||
|
public static final Color COLOR_NONE = new Color( 0, true );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes Win32 API method {@code DwmSetWindowAttribute()} with a {@code COLORREF} attribute value.
|
||||||
|
* See https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute
|
||||||
|
* <p>
|
||||||
|
* Supported since Windows 11 Build 22000.
|
||||||
|
*
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
public static boolean dwmSetWindowAttributeCOLORREF( long hwnd, int attribute, Color color ) {
|
||||||
|
// convert color to Windows RGB value
|
||||||
|
int rgb = (color == COLOR_NONE)
|
||||||
|
? DWMWA_COLOR_NONE
|
||||||
|
: (color != null
|
||||||
|
? (color.getRed() | (color.getGreen() << 8) | (color.getBlue() << 16))
|
||||||
|
: DWMWA_COLOR_DEFAULT);
|
||||||
|
|
||||||
|
// DwmSetWindowAttribute() expects COLORREF as attribute value, which is defined as DWORD
|
||||||
|
return dwmSetWindowAttributeDWORD( hwnd, attribute, rgb );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FILEOPENDIALOGOPTIONS
|
||||||
|
* see https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/ne-shobjidl_core-_fileopendialogoptions
|
||||||
|
*
|
||||||
|
* @since 3.7
|
||||||
|
*/
|
||||||
|
public static final int
|
||||||
|
FOS_OVERWRITEPROMPT = 0x2, // default for Save
|
||||||
|
FOS_STRICTFILETYPES = 0x4,
|
||||||
|
FOS_NOCHANGEDIR = 0x8, // default
|
||||||
|
FOS_PICKFOLDERS = 0x20,
|
||||||
|
FOS_FORCEFILESYSTEM = 0x40,
|
||||||
|
FOS_ALLNONSTORAGEITEMS = 0x80,
|
||||||
|
FOS_NOVALIDATE = 0x100,
|
||||||
|
FOS_ALLOWMULTISELECT = 0x200,
|
||||||
|
FOS_PATHMUSTEXIST = 0x800, // default
|
||||||
|
FOS_FILEMUSTEXIST = 0x1000, // default for Open
|
||||||
|
FOS_CREATEPROMPT = 0x2000,
|
||||||
|
FOS_SHAREAWARE = 0x4000,
|
||||||
|
FOS_NOREADONLYRETURN = 0x8000, // default for Save
|
||||||
|
FOS_NOTESTFILECREATE = 0x10000,
|
||||||
|
FOS_HIDEMRUPLACES = 0x20000,
|
||||||
|
FOS_HIDEPINNEDPLACES = 0x40000,
|
||||||
|
FOS_NODEREFERENCELINKS = 0x100000,
|
||||||
|
FOS_OKBUTTONNEEDSINTERACTION = 0x200000,
|
||||||
|
FOS_DONTADDTORECENT = 0x2000000,
|
||||||
|
FOS_FORCESHOWHIDDEN = 0x10000000,
|
||||||
|
FOS_DEFAULTNOMINIMODE = 0x20000000,
|
||||||
|
FOS_FORCEPREVIEWPANEON = 0x40000000,
|
||||||
|
FOS_SUPPORTSTREAMABLEITEMS = 0x80000000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the Windows system
|
||||||
|
* <a href="https://learn.microsoft.com/en-us/windows/win32/shell/common-file-dialog">file dialogs</a>
|
||||||
|
* <a href="https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ifileopendialog">IFileOpenDialog</a> or
|
||||||
|
* <a href="https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ifilesavedialog">IFileSaveDialog</a>.
|
||||||
|
* <p>
|
||||||
|
* <b>Note:</b> This method blocks the current thread until the user closes
|
||||||
|
* the file dialog. It is highly recommended to invoke it from a new thread
|
||||||
|
* to avoid blocking the AWT event dispatching thread.
|
||||||
|
*
|
||||||
|
* @param owner the owner of the file dialog; or {@code null}
|
||||||
|
* @param open if {@code true}, shows the open dialog; if {@code false}, shows the save dialog
|
||||||
|
* @param title text displayed in dialog title; or {@code null}
|
||||||
|
* @param okButtonLabel text displayed in default button; or {@code null}.
|
||||||
|
* Use '&' for mnemonics (e.g. "&Choose").
|
||||||
|
* Use '&&' for '&' character (e.g. "Choose && Quit").
|
||||||
|
* @param fileNameLabel text displayed in front of the filename text field; or {@code null}
|
||||||
|
* @param fileName user-editable filename currently shown in the filename field; or {@code null}
|
||||||
|
* @param folder current directory shown in the dialog; or {@code null}
|
||||||
|
* @param saveAsItem file to be used as the initial entry in a Save As dialog; or {@code null}.
|
||||||
|
* File name is shown in filename text field, folder is selected in view.
|
||||||
|
* To be used for saving files that already exist. For new files use {@code fileName}.
|
||||||
|
* @param defaultFolder folder used as a default if there is not a recently used folder value available; or {@code null}.
|
||||||
|
* Windows somewhere stores default folder on a per-app basis.
|
||||||
|
* So this is probably used only once when the app opens a file dialog for first time.
|
||||||
|
* @param defaultExtension default extension to be added to file name in save dialog; or {@code null}
|
||||||
|
* @param optionsSet options to set; see {@code FOS_*} constants
|
||||||
|
* @param optionsClear options to clear; see {@code FOS_*} constants
|
||||||
|
* @param callback approve callback; or {@code null}
|
||||||
|
* @param fileTypeIndex the file type that appears as selected (zero-based)
|
||||||
|
* @param fileTypes file types that the dialog can open or save.
|
||||||
|
* Pairs of strings are required for each filter.
|
||||||
|
* First string is the display name of the filter shown in the combobox (e.g. "Text Files").
|
||||||
|
* Second string is the filter pattern (e.g. "*.txt", "*.exe;*.dll" or "*.*").
|
||||||
|
* @return file path(s) that the user selected; an empty array if canceled;
|
||||||
|
* or {@code null} on failures (no dialog shown)
|
||||||
|
*
|
||||||
|
* @since 3.7
|
||||||
|
*/
|
||||||
|
public native static String[] showFileChooser( Window owner, boolean open,
|
||||||
|
String title, String okButtonLabel, String fileNameLabel, String fileName,
|
||||||
|
String folder, String saveAsItem, String defaultFolder, String defaultExtension,
|
||||||
|
int optionsSet, int optionsClear, FileChooserCallback callback,
|
||||||
|
int fileTypeIndex, String... fileTypes );
|
||||||
|
|
||||||
|
/** @since 3.7 */
|
||||||
|
public interface FileChooserCallback {
|
||||||
|
boolean approve( String[] files, long hwndFileDialog );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a modal Windows message dialog.
|
||||||
|
* <p>
|
||||||
|
* For use in {@link FileChooserCallback} only.
|
||||||
|
*
|
||||||
|
* @param hwndParent the parent of the message box
|
||||||
|
* @param messageType type of message being displayed:
|
||||||
|
* {@link JOptionPane#ERROR_MESSAGE}, {@link JOptionPane#INFORMATION_MESSAGE},
|
||||||
|
* {@link JOptionPane#WARNING_MESSAGE}, {@link JOptionPane#QUESTION_MESSAGE} or
|
||||||
|
* {@link JOptionPane#PLAIN_MESSAGE}
|
||||||
|
* @param title dialog box title; or {@code null} to use title from parent window
|
||||||
|
* @param text message to be displayed
|
||||||
|
* @param defaultButton index of the default button, which can be pressed using ENTER key
|
||||||
|
* @param buttons texts of the buttons.
|
||||||
|
* Use '&' for mnemonics (e.g. "&Choose").
|
||||||
|
* Use '&&' for '&' character (e.g. "Choose && Quit").
|
||||||
|
* @return index of pressed button; or -1 for ESC key
|
||||||
|
*
|
||||||
|
* @since 3.7
|
||||||
|
*/
|
||||||
|
public native static int showMessageDialog( long hwndParent, int messageType,
|
||||||
|
String title, String text, int defaultButton, String... buttons );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a Windows message box
|
||||||
|
* <a href="https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebox">MessageBox</a>.
|
||||||
|
* <p>
|
||||||
|
* For use in {@link FileChooserCallback} only.
|
||||||
|
*
|
||||||
|
* @param hwndParent the parent of the message box
|
||||||
|
* @param text message to be displayed
|
||||||
|
* @param caption dialog box title
|
||||||
|
* @param type see <a href="https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebox#parameters">MessageBox parameter uType</a>
|
||||||
|
* @return see <a href="https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebox#return-value">MessageBox Return value</a>
|
||||||
|
*
|
||||||
|
* @since 3.7
|
||||||
|
*/
|
||||||
|
public native static int showMessageBox( long hwndParent, String text, String caption, int type );
|
||||||
|
}
|
||||||
@@ -30,9 +30,7 @@ import javax.swing.JPanel;
|
|||||||
import javax.swing.JRootPane;
|
import javax.swing.JRootPane;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.border.Border;
|
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
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.FlatClientProperties;
|
||||||
@@ -115,13 +113,6 @@ public class FlatOptionPaneUI
|
|||||||
sameSizeButtons = FlatUIUtils.getUIBoolean( "OptionPane.sameSizeButtons", true );
|
sameSizeButtons = FlatUIUtils.getUIBoolean( "OptionPane.sameSizeButtons", true );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void installComponents() {
|
|
||||||
super.installComponents();
|
|
||||||
|
|
||||||
updateChildPanels( optionPane );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PropertyChangeListener createPropertyChangeListener() {
|
protected PropertyChangeListener createPropertyChangeListener() {
|
||||||
PropertyChangeListener superListener = super.createPropertyChangeListener();
|
PropertyChangeListener superListener = super.createPropertyChangeListener();
|
||||||
@@ -155,6 +146,13 @@ public class FlatOptionPaneUI
|
|||||||
protected Container createMessageArea() {
|
protected Container createMessageArea() {
|
||||||
Container messageArea = super.createMessageArea();
|
Container messageArea = super.createMessageArea();
|
||||||
|
|
||||||
|
// use non-UIResource OptionPane.messageAreaBorder to avoid that it is replaced when switching LaF
|
||||||
|
// and make panel non-opaque for OptionPane.background
|
||||||
|
updateAreaPanel( messageArea );
|
||||||
|
|
||||||
|
// make known sub-panels non-opaque for OptionPane.background
|
||||||
|
updateKnownChildPanels( messageArea );
|
||||||
|
|
||||||
// set icon-message gap
|
// set icon-message gap
|
||||||
if( iconMessageGap > 0 ) {
|
if( iconMessageGap > 0 ) {
|
||||||
Component iconMessageSeparator = SwingUtils.getComponentByName( messageArea, "OptionPane.separator" );
|
Component iconMessageSeparator = SwingUtils.getComponentByName( messageArea, "OptionPane.separator" );
|
||||||
@@ -169,6 +167,10 @@ public class FlatOptionPaneUI
|
|||||||
protected Container createButtonArea() {
|
protected Container createButtonArea() {
|
||||||
Container buttonArea = super.createButtonArea();
|
Container buttonArea = super.createButtonArea();
|
||||||
|
|
||||||
|
// use non-UIResource OptionPane.buttonAreaBorder to avoid that it is replaced when switching LaF
|
||||||
|
// and make panel non-opaque for OptionPane.background
|
||||||
|
updateAreaPanel( buttonArea );
|
||||||
|
|
||||||
// scale button padding and subtract focusWidth
|
// scale button padding and subtract focusWidth
|
||||||
if( buttonArea.getLayout() instanceof ButtonAreaLayout ) {
|
if( buttonArea.getLayout() instanceof ButtonAreaLayout ) {
|
||||||
ButtonAreaLayout layout = (ButtonAreaLayout) buttonArea.getLayout();
|
ButtonAreaLayout layout = (ButtonAreaLayout) buttonArea.getLayout();
|
||||||
@@ -218,22 +220,33 @@ public class FlatOptionPaneUI
|
|||||||
super.addMessageComponents( container, cons, msg, maxll, internallyCreated );
|
super.addMessageComponents( container, cons, msg, maxll, internallyCreated );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateChildPanels( Container c ) {
|
private void updateAreaPanel( Container area ) {
|
||||||
|
if( !(area instanceof JPanel) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// use non-UIResource border to avoid that it is replaced when switching LaF
|
||||||
|
// and make panel non-opaque for OptionPane.background
|
||||||
|
JPanel panel = (JPanel) area;
|
||||||
|
panel.setBorder( FlatUIUtils.nonUIResource( panel.getBorder() ) );
|
||||||
|
panel.setOpaque( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateKnownChildPanels( Container c ) {
|
||||||
for( Component child : c.getComponents() ) {
|
for( Component child : c.getComponents() ) {
|
||||||
if( child.getClass() == JPanel.class ) {
|
if( child instanceof JPanel && child.getName() != null ) {
|
||||||
JPanel panel = (JPanel)child;
|
switch( child.getName() ) {
|
||||||
|
case "OptionPane.realBody":
|
||||||
// make sub-panel non-opaque for OptionPane.background
|
case "OptionPane.body":
|
||||||
panel.setOpaque( false );
|
case "OptionPane.separator":
|
||||||
|
case "OptionPane.break":
|
||||||
// use non-UIResource borders to avoid that they are replaced when switching LaF
|
// make known sub-panels non-opaque for OptionPane.background
|
||||||
Border border = panel.getBorder();
|
((JPanel)child).setOpaque( false );
|
||||||
if( border instanceof UIResource )
|
break;
|
||||||
panel.setBorder( FlatUIUtils.nonUIResource( border ) );
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( child instanceof Container )
|
if( child instanceof Container )
|
||||||
updateChildPanels( (Container) child );
|
updateKnownChildPanels( (Container) child );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
@@ -23,11 +24,14 @@ import java.beans.PropertyChangeListener;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.LookAndFeel;
|
||||||
|
import javax.swing.border.Border;
|
||||||
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.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -69,6 +73,8 @@ public class FlatPanelUI
|
|||||||
super.installUI( c );
|
super.installUI( c );
|
||||||
|
|
||||||
c.addPropertyChangeListener( this );
|
c.addPropertyChangeListener( this );
|
||||||
|
if( c.getClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER ) != null )
|
||||||
|
FullWindowContentSupport.registerPlaceholder( c );
|
||||||
|
|
||||||
installStyle( (JPanel) c );
|
installStyle( (JPanel) c );
|
||||||
}
|
}
|
||||||
@@ -78,10 +84,20 @@ public class FlatPanelUI
|
|||||||
super.uninstallUI( c );
|
super.uninstallUI( c );
|
||||||
|
|
||||||
c.removePropertyChangeListener( this );
|
c.removePropertyChangeListener( this );
|
||||||
|
if( c.getClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER ) != null )
|
||||||
|
FullWindowContentSupport.unregisterPlaceholder( c );
|
||||||
|
|
||||||
oldStyleValues = null;
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installDefaults( JPanel p ) {
|
||||||
|
super.installDefaults( p );
|
||||||
|
|
||||||
|
if( p.getClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER ) != null )
|
||||||
|
LookAndFeel.installProperty( p, "opaque", false );
|
||||||
|
}
|
||||||
|
|
||||||
/** @since 2.0.1 */
|
/** @since 2.0.1 */
|
||||||
@Override
|
@Override
|
||||||
public void propertyChange( PropertyChangeEvent e ) {
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
@@ -96,7 +112,18 @@ public class FlatPanelUI
|
|||||||
} else
|
} else
|
||||||
installStyle( c );
|
installStyle( c );
|
||||||
c.revalidate();
|
c.revalidate();
|
||||||
c.repaint();
|
HiDPIUtils.repaint( c );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER:
|
||||||
|
JPanel p = (JPanel) e.getSource();
|
||||||
|
if( e.getOldValue() != null )
|
||||||
|
FullWindowContentSupport.unregisterPlaceholder( p );
|
||||||
|
if( e.getNewValue() != null )
|
||||||
|
FullWindowContentSupport.registerPlaceholder( p );
|
||||||
|
|
||||||
|
// make panel non-opaque for placeholders
|
||||||
|
LookAndFeel.installProperty( p, "opaque", e.getNewValue() == null );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,31 +162,52 @@ public class FlatPanelUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update( Graphics g, JComponent c ) {
|
public void update( Graphics g, JComponent c ) {
|
||||||
// fill background
|
fillRoundedBackground( g, c, arc );
|
||||||
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 );
|
paint( g, c );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 3.5 */
|
||||||
|
public static void fillRoundedBackground( Graphics g, JComponent c, int arc ) {
|
||||||
|
if( arc < 0 ) {
|
||||||
|
Border border = c.getBorder();
|
||||||
|
arc = ((border instanceof FlatLineBorder)
|
||||||
|
? ((FlatLineBorder)border).getArc()
|
||||||
|
: 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill background
|
||||||
|
if( c.isOpaque() ) {
|
||||||
|
if( arc > 0 ) {
|
||||||
|
// fill background with parent color to avoid garbage in rounded corners
|
||||||
|
FlatUIUtils.paintParentBackground( g, c );
|
||||||
|
} else {
|
||||||
|
g.setColor( c.getBackground() );
|
||||||
|
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill rounded rectangle if having rounded corners
|
||||||
|
if( arc > 0 ) {
|
||||||
|
g.setColor( c.getBackground() );
|
||||||
|
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
||||||
|
FlatUIUtils.paintComponentBackground( (Graphics2D) g, 0, 0, c.getWidth(), c.getHeight(),
|
||||||
|
0, UIScale.scale( arc ) );
|
||||||
|
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
|
Object value = c.getClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER );
|
||||||
|
if( value != null )
|
||||||
|
return FullWindowContentSupport.getPlaceholderPreferredSize( c, (String) value );
|
||||||
|
|
||||||
|
return super.getPreferredSize( c );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paint( Graphics g, JComponent c ) {
|
||||||
|
if( c.getClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER ) != null )
|
||||||
|
FullWindowContentSupport.debugPaint( g, c );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.Toolkit;
|
import java.awt.Toolkit;
|
||||||
@@ -41,8 +40,9 @@ import javax.swing.text.JTextComponent;
|
|||||||
import javax.swing.text.PasswordView;
|
import javax.swing.text.PasswordView;
|
||||||
import javax.swing.text.View;
|
import javax.swing.text.View;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.icons.FlatCapsLockIcon;
|
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableObject;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,7 +66,6 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* <!-- FlatTextFieldUI -->
|
* <!-- FlatTextFieldUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault Component.minimumWidth int
|
* @uiDefault Component.minimumWidth int
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
|
||||||
* @uiDefault PasswordField.placeholderForeground Color
|
* @uiDefault PasswordField.placeholderForeground Color
|
||||||
* @uiDefault PasswordField.focusedBackground Color optional
|
* @uiDefault PasswordField.focusedBackground Color optional
|
||||||
* @uiDefault PasswordField.iconTextGap int optional, default is 4
|
* @uiDefault PasswordField.iconTextGap int optional, default is 4
|
||||||
@@ -164,7 +163,7 @@ public class FlatPasswordFieldUI
|
|||||||
}
|
}
|
||||||
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();
|
HiDPIUtils.repaint( e.getComponent() );
|
||||||
scrollCaretToVisible();
|
scrollCaretToVisible();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -215,12 +214,12 @@ public class FlatPasswordFieldUI
|
|||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
@Override
|
@Override
|
||||||
protected Object applyStyleProperty( String key, Object value ) {
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
if( key.equals( "capsLockIconColor" ) && capsLockIcon instanceof FlatCapsLockIcon ) {
|
if( key.startsWith( "capsLockIcon" ) && capsLockIcon instanceof StyleableObject ) {
|
||||||
if( capsLockIconShared ) {
|
if( capsLockIconShared ) {
|
||||||
capsLockIcon = FlatStylingSupport.cloneIcon( capsLockIcon );
|
capsLockIcon = FlatStylingSupport.cloneIcon( capsLockIcon );
|
||||||
capsLockIconShared = false;
|
capsLockIconShared = false;
|
||||||
}
|
}
|
||||||
return ((FlatCapsLockIcon)capsLockIcon).applyStyleProperty( key, value );
|
return ((StyleableObject)capsLockIcon).applyStyleProperty( key, value );
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.applyStyleProperty( key, value );
|
return super.applyStyleProperty( key, value );
|
||||||
@@ -230,14 +229,15 @@ public class FlatPasswordFieldUI
|
|||||||
@Override
|
@Override
|
||||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
Map<String, Class<?>> infos = super.getStyleableInfos( c );
|
Map<String, Class<?>> infos = super.getStyleableInfos( c );
|
||||||
infos.put( "capsLockIconColor", Color.class );
|
if( capsLockIcon instanceof StyleableObject )
|
||||||
|
infos.putAll( ((StyleableObject)capsLockIcon).getStyleableInfos() );
|
||||||
return infos;
|
return infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getStyleableValue( JComponent c, String key ) {
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
if( key.equals( "capsLockIconColor" ) && capsLockIcon instanceof FlatCapsLockIcon )
|
if( key.startsWith( "capsLockIcon" ) && capsLockIcon instanceof StyleableObject )
|
||||||
return ((FlatCapsLockIcon)capsLockIcon).getStyleableValue( key );
|
return ((StyleableObject)capsLockIcon).getStyleableValue( key );
|
||||||
|
|
||||||
return super.getStyleableValue( c, key );
|
return super.getStyleableValue( c, key );
|
||||||
}
|
}
|
||||||
@@ -327,7 +327,7 @@ public class FlatPasswordFieldUI
|
|||||||
if( visible != revealButton.isVisible() ) {
|
if( visible != revealButton.isVisible() ) {
|
||||||
revealButton.setVisible( visible );
|
revealButton.setVisible( visible );
|
||||||
c.revalidate();
|
c.revalidate();
|
||||||
c.repaint();
|
HiDPIUtils.repaint( c );
|
||||||
|
|
||||||
if( !visible ) {
|
if( !visible ) {
|
||||||
revealButton.setSelected( false );
|
revealButton.setSelected( false );
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import java.awt.AWTEvent;
|
import java.awt.AWTEvent;
|
||||||
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
@@ -31,18 +32,23 @@ import java.awt.Panel;
|
|||||||
import java.awt.Point;
|
import java.awt.Point;
|
||||||
import java.awt.PointerInfo;
|
import java.awt.PointerInfo;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
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.awt.event.HierarchyEvent;
|
||||||
|
import java.awt.event.HierarchyListener;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.event.WindowFocusListener;
|
||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.invoke.MethodHandle;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.invoke.MethodType;
|
import java.lang.invoke.MethodType;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JLayeredPane;
|
import javax.swing.JLayeredPane;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JPopupMenu;
|
||||||
import javax.swing.JToolTip;
|
import javax.swing.JToolTip;
|
||||||
import javax.swing.JWindow;
|
import javax.swing.JWindow;
|
||||||
import javax.swing.Popup;
|
import javax.swing.Popup;
|
||||||
@@ -52,7 +58,11 @@ import javax.swing.SwingUtilities;
|
|||||||
import javax.swing.ToolTipManager;
|
import javax.swing.ToolTipManager;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
|
import javax.swing.border.EmptyBorder;
|
||||||
|
import javax.swing.border.LineBorder;
|
||||||
|
import javax.swing.plaf.basic.BasicComboPopup;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.FlatSystemProperties;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -66,9 +76,13 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
public class FlatPopupFactory
|
public class FlatPopupFactory
|
||||||
extends PopupFactory
|
extends PopupFactory
|
||||||
{
|
{
|
||||||
|
static final String KEY_POPUP_USES_NATIVE_BORDER = "FlatLaf.internal.FlatPopupFactory.popupUsesNativeBorder";
|
||||||
|
|
||||||
private MethodHandle java8getPopupMethod;
|
private MethodHandle java8getPopupMethod;
|
||||||
private MethodHandle java9getPopupMethod;
|
private MethodHandle java9getPopupMethod;
|
||||||
|
|
||||||
|
private final ArrayList<NonFlashingPopup> stillShownHeavyWeightPopups = new ArrayList<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Popup getPopup( Component owner, Component contents, int x, int y )
|
public Popup getPopup( Component owner, Component contents, int x, int y )
|
||||||
throws IllegalArgumentException
|
throws IllegalArgumentException
|
||||||
@@ -79,17 +93,48 @@ public class FlatPopupFactory
|
|||||||
y = pt.y;
|
y = pt.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean forceHeavyWeight = isOptionEnabled( owner, contents, FlatClientProperties.POPUP_FORCE_HEAVY_WEIGHT, "Popup.forceHeavyWeight" );
|
fixLinuxWaylandJava21focusIssue( owner );
|
||||||
|
|
||||||
|
// reuse a heavy weight popup window, which is still shown on screen,
|
||||||
|
// to avoid flicker when popup (e.g. tooltip) is moving while mouse is moved
|
||||||
|
for( NonFlashingPopup popup : stillShownHeavyWeightPopups ) {
|
||||||
|
if( popup.delegate != null &&
|
||||||
|
popup.owner == owner &&
|
||||||
|
(popup.contents == contents ||
|
||||||
|
(popup.contents instanceof JToolTip && contents instanceof JToolTip)) )
|
||||||
|
{
|
||||||
|
stillShownHeavyWeightPopups.remove( popup );
|
||||||
|
return reuseStillShownHeavyWeightPopups( popup, contents, x, y );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if( !isOptionEnabled( owner, contents, FlatClientProperties.POPUP_DROP_SHADOW_PAINTED, "Popup.dropShadowPainted" ) || SystemInfo.isProjector || SystemInfo.isWebswing )
|
if( !isOptionEnabled( owner, contents, FlatClientProperties.POPUP_DROP_SHADOW_PAINTED, "Popup.dropShadowPainted" ) || SystemInfo.isProjector || SystemInfo.isWebswing )
|
||||||
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), contents );
|
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, isForceHeavyWeight( owner, contents, x, y ) ), owner, contents );
|
||||||
|
|
||||||
// macOS and Linux adds drop shadow to heavy weight popups
|
// macOS and Linux adds drop shadow to heavy weight popups
|
||||||
if( SystemInfo.isMacOS || SystemInfo.isLinux )
|
if( SystemInfo.isMacOS || SystemInfo.isLinux ) {
|
||||||
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), contents );
|
NonFlashingPopup popup = new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), owner, contents );
|
||||||
|
if( popup.popupWindow != null && isMacOSBorderSupported() )
|
||||||
|
setupRoundedBorder( popup.popupWindow, owner, contents );
|
||||||
|
return popup;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Windows 11 with FlatLaf native library can use rounded corners and shows drop shadow for heavy weight popups
|
||||||
|
if( isWindows11BorderSupported() &&
|
||||||
|
getBorderCornerRadius( owner, contents ) > 0 )
|
||||||
|
{
|
||||||
|
NonFlashingPopup popup = new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), owner, contents );
|
||||||
|
if( popup.popupWindow != null )
|
||||||
|
setupRoundedBorder( popup.popupWindow, owner, contents );
|
||||||
|
return popup;
|
||||||
|
}
|
||||||
|
|
||||||
// create drop shadow popup
|
// create drop shadow popup
|
||||||
return new DropShadowPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), owner, contents );
|
Popup popupForScreenOfOwner = getPopupForScreenOfOwner( owner, contents, x, y, isForceHeavyWeight( owner, contents, x, y ) );
|
||||||
|
GraphicsConfiguration gc = (owner != null) ? owner.getGraphicsConfiguration() : null;
|
||||||
|
return (gc != null && gc.isTranslucencyCapable())
|
||||||
|
? new DropShadowPopup( popupForScreenOfOwner, owner, contents )
|
||||||
|
: new NonFlashingPopup( popupForScreenOfOwner, owner, contents );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -140,47 +185,6 @@ public class FlatPopupFactory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows the given popup and, if necessary, fixes the location of a heavy weight popup window.
|
|
||||||
* <p>
|
|
||||||
* 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.
|
|
||||||
* E.g. when opening a dialog on the secondary screen and making combobox popup visible.
|
|
||||||
* <p>
|
|
||||||
* This is a workaround for https://bugs.openjdk.java.net/browse/JDK-8224608
|
|
||||||
*/
|
|
||||||
private static void showPopupAndFixLocation( Popup popup, Window popupWindow ) {
|
|
||||||
if( popupWindow != null ) {
|
|
||||||
// remember location of heavy weight popup window
|
|
||||||
int x = popupWindow.getX();
|
|
||||||
int y = popupWindow.getY();
|
|
||||||
|
|
||||||
popup.show();
|
|
||||||
|
|
||||||
// restore popup window location if it has changed
|
|
||||||
// (probably scaled when screens use different scale factors)
|
|
||||||
if( popupWindow.getX() != x || popupWindow.getY() != y )
|
|
||||||
popupWindow.setLocation( x, y );
|
|
||||||
} else
|
|
||||||
popup.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isOptionEnabled( Component owner, Component contents, String clientKey, String uiKey ) {
|
|
||||||
if( owner instanceof JComponent ) {
|
|
||||||
Boolean b = FlatClientProperties.clientPropertyBooleanStrict( (JComponent) owner, clientKey, null );
|
|
||||||
if( b != null )
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( contents instanceof JComponent ) {
|
|
||||||
Boolean b = FlatClientProperties.clientPropertyBooleanStrict( (JComponent) contents, clientKey, null );
|
|
||||||
if( b != null )
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
return UIManager.getBoolean( uiKey );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* There is no API in Java 8 to force creation of heavy weight popups,
|
* There is no API in Java 8 to force creation of heavy weight popups,
|
||||||
* but it is possible with reflection. Java 9 provides a new method.
|
* but it is possible with reflection. Java 9 provides a new method.
|
||||||
@@ -216,6 +220,56 @@ public class FlatPopupFactory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isForceHeavyWeight( Component owner, Component contents, int x, int y ) {
|
||||||
|
boolean forceHeavyWeight = isOptionEnabled( owner, contents, FlatClientProperties.POPUP_FORCE_HEAVY_WEIGHT, "Popup.forceHeavyWeight" );
|
||||||
|
return forceHeavyWeight || hasVisibleGlassPane( owner ) || overlapsHeavyWeightComponent( owner, contents, x, y );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isOptionEnabled( Component owner, Component contents, String clientKey, String uiKey ) {
|
||||||
|
Object value = getOption( owner, contents, clientKey, uiKey );
|
||||||
|
return (value instanceof Boolean) ? (Boolean) value : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get option from:
|
||||||
|
* <ol>
|
||||||
|
* <li>client property {@code clientKey} of {@code owner}
|
||||||
|
* <li>client property {@code clientKey} of {@code contents}
|
||||||
|
* <li>UI property {@code uiKey}
|
||||||
|
* </ol>
|
||||||
|
*/
|
||||||
|
private static Object getOption( Component owner, Component contents, String clientKey, String uiKey ) {
|
||||||
|
for( Component c : new Component[] { owner, contents } ) {
|
||||||
|
if( c instanceof JComponent ) {
|
||||||
|
Object value = ((JComponent)c).getClientProperty( clientKey );
|
||||||
|
if( value != null )
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UIManager.get( uiKey );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reuse a heavy weight popup window, which is still shown on screen,
|
||||||
|
* by updating window location and contents.
|
||||||
|
* This avoid flicker when popup (e.g. a tooltip) is moving while mouse is moved.
|
||||||
|
* E.g. overridden JComponent.getToolTipLocation(MouseEvent).
|
||||||
|
* See ToolTipManager.checkForTipChange(MouseEvent).
|
||||||
|
*/
|
||||||
|
private static NonFlashingPopup reuseStillShownHeavyWeightPopups(
|
||||||
|
NonFlashingPopup reusePopup, Component contents, int ownerX, int ownerY )
|
||||||
|
{
|
||||||
|
// clone popup because PopupFactory.getPopup() should not return old instance
|
||||||
|
NonFlashingPopup popup = reusePopup.cloneForReuse();
|
||||||
|
|
||||||
|
// update popup location, size and contents
|
||||||
|
popup.reset( contents, ownerX, ownerY );
|
||||||
|
return popup;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- tooltips -----------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Usually ToolTipManager places a tooltip at (mouseLocation.x, mouseLocation.y + 20).
|
* Usually ToolTipManager places a tooltip at (mouseLocation.x, mouseLocation.y + 20).
|
||||||
* In case that the tooltip would be partly outside of the screen,
|
* In case that the tooltip would be partly outside of the screen,
|
||||||
@@ -250,13 +304,13 @@ public class FlatPopupFactory
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if( gc == null )
|
if( gc == null && owner != null )
|
||||||
gc = owner.getGraphicsConfiguration();
|
gc = owner.getGraphicsConfiguration();
|
||||||
if( gc == null )
|
if( gc == null )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
Rectangle screenBounds = gc.getBounds();
|
Rectangle screenBounds = gc.getBounds();
|
||||||
Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets( gc );
|
Insets screenInsets = FlatUIUtils.getScreenInsets( gc );
|
||||||
int screenTop = screenBounds.y + screenInsets.top;
|
int screenTop = screenBounds.y + screenInsets.top;
|
||||||
|
|
||||||
// place tooltip above mouse location if there is enough space
|
// place tooltip above mouse location if there is enough space
|
||||||
@@ -300,20 +354,254 @@ public class FlatPopupFactory
|
|||||||
((JComponent)owner).getToolTipLocation( me ) != null;
|
((JComponent)owner).getToolTipLocation( me ) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- native rounded border ----------------------------------------------
|
||||||
|
|
||||||
|
private static boolean isWindows11BorderSupported() {
|
||||||
|
return SystemInfo.isWindows_11_orLater &&
|
||||||
|
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_ROUNDED_POPUP_BORDER, true ) &&
|
||||||
|
FlatNativeWindowsLibrary.isLoaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isMacOSBorderSupported() {
|
||||||
|
return SystemInfo.isMacOS &&
|
||||||
|
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_ROUNDED_POPUP_BORDER, true ) &&
|
||||||
|
FlatNativeMacLibrary.isLoaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setupRoundedBorder( Window popupWindow, Component owner, Component contents ) {
|
||||||
|
int borderCornerRadius = getBorderCornerRadius( owner, contents );
|
||||||
|
float borderWidth = getRoundedBorderWidth( owner, contents );
|
||||||
|
|
||||||
|
// get Swing border color
|
||||||
|
Color borderColor;
|
||||||
|
if( contents instanceof JComponent ) {
|
||||||
|
Border border = ((JComponent)contents).getBorder();
|
||||||
|
border = FlatUIUtils.unwrapNonUIResourceBorder( border );
|
||||||
|
|
||||||
|
// get color from border of contents (e.g. JPopupMenu or JToolTip)
|
||||||
|
if( border instanceof FlatLineBorder )
|
||||||
|
borderColor = ((FlatLineBorder)border).getLineColor();
|
||||||
|
else if( border instanceof LineBorder )
|
||||||
|
borderColor = ((LineBorder)border).getLineColor();
|
||||||
|
else if( border instanceof EmptyBorder )
|
||||||
|
borderColor = FlatNativeWindowsLibrary.COLOR_NONE; // do not paint border
|
||||||
|
else
|
||||||
|
borderColor = null; // use system default color
|
||||||
|
|
||||||
|
// avoid that FlatLineBorder paints the Swing border
|
||||||
|
((JComponent)contents).putClientProperty( KEY_POPUP_USES_NATIVE_BORDER, true );
|
||||||
|
} else
|
||||||
|
borderColor = null; // use system default color
|
||||||
|
|
||||||
|
if( popupWindow.isDisplayable() ) {
|
||||||
|
// native window already created
|
||||||
|
setupRoundedBorderImpl( popupWindow, borderCornerRadius, borderWidth, borderColor );
|
||||||
|
} else {
|
||||||
|
// native window not yet created --> add listener to set native border after window creation
|
||||||
|
AtomicReference<HierarchyListener> l = new AtomicReference<>();
|
||||||
|
l.set( e -> {
|
||||||
|
if( e.getID() == HierarchyEvent.HIERARCHY_CHANGED &&
|
||||||
|
(e.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0 )
|
||||||
|
{
|
||||||
|
setupRoundedBorderImpl( popupWindow, borderCornerRadius, borderWidth, borderColor );
|
||||||
|
popupWindow.removeHierarchyListener( l.get() );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
popupWindow.addHierarchyListener( l.get() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setupRoundedBorderImpl( Window popupWindow, int borderCornerRadius, float borderWidth, Color borderColor ) {
|
||||||
|
if( SystemInfo.isWindows ) {
|
||||||
|
// get native window handle
|
||||||
|
long hwnd = FlatNativeWindowsLibrary.getHWND( popupWindow );
|
||||||
|
|
||||||
|
// set corner preference
|
||||||
|
int cornerPreference = (borderCornerRadius <= 4)
|
||||||
|
? FlatNativeWindowsLibrary.DWMWCP_ROUNDSMALL // 4px
|
||||||
|
: FlatNativeWindowsLibrary.DWMWCP_ROUND; // 8px
|
||||||
|
FlatNativeWindowsLibrary.setWindowCornerPreference( hwnd, cornerPreference );
|
||||||
|
|
||||||
|
// set border color
|
||||||
|
FlatNativeWindowsLibrary.dwmSetWindowAttributeCOLORREF( hwnd, FlatNativeWindowsLibrary.DWMWA_BORDER_COLOR, borderColor );
|
||||||
|
} else if( SystemInfo.isMacOS ) {
|
||||||
|
if( borderColor == null || borderColor == FlatNativeWindowsLibrary.COLOR_NONE )
|
||||||
|
borderWidth = 0;
|
||||||
|
|
||||||
|
// set corner radius, border width and color
|
||||||
|
FlatNativeMacLibrary.setWindowRoundedBorder( popupWindow, borderCornerRadius,
|
||||||
|
borderWidth, (borderColor != null) ? borderColor.getRGB() : 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void resetWindows11Border( Window popupWindow ) {
|
||||||
|
// get window handle
|
||||||
|
long hwnd = FlatNativeWindowsLibrary.getHWND( popupWindow );
|
||||||
|
if( hwnd == 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// reset corner preference
|
||||||
|
FlatNativeWindowsLibrary.setWindowCornerPreference( hwnd, FlatNativeWindowsLibrary.DWMWCP_DONOTROUND );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getBorderCornerRadius( Component owner, Component contents ) {
|
||||||
|
String uiKey =
|
||||||
|
(contents instanceof BasicComboPopup) ? "ComboBox.borderCornerRadius" :
|
||||||
|
(contents instanceof JPopupMenu) ? "PopupMenu.borderCornerRadius" :
|
||||||
|
(contents instanceof JToolTip) ? "ToolTip.borderCornerRadius" :
|
||||||
|
"Popup.borderCornerRadius";
|
||||||
|
|
||||||
|
Object value = getOption( owner, contents, FlatClientProperties.POPUP_BORDER_CORNER_RADIUS, uiKey );
|
||||||
|
return (value instanceof Integer) ? (Integer) value : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float getRoundedBorderWidth( Component owner, Component contents ) {
|
||||||
|
String uiKey =
|
||||||
|
(contents instanceof BasicComboPopup) ? "ComboBox.roundedBorderWidth" :
|
||||||
|
(contents instanceof JPopupMenu) ? "PopupMenu.roundedBorderWidth" :
|
||||||
|
(contents instanceof JToolTip) ? "ToolTip.roundedBorderWidth" :
|
||||||
|
"Popup.roundedBorderWidth";
|
||||||
|
|
||||||
|
Object value = getOption( owner, contents, FlatClientProperties.POPUP_ROUNDED_BORDER_WIDTH, uiKey );
|
||||||
|
return (value instanceof Number) ? ((Number)value).floatValue() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- fixes --------------------------------------------------------------
|
||||||
|
|
||||||
|
private static boolean hasVisibleGlassPane( Component owner ) {
|
||||||
|
if( owner == null )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Window window = SwingUtilities.windowForComponent( owner );
|
||||||
|
if( !(window instanceof RootPaneContainer) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Component glassPane = ((RootPaneContainer)window).getGlassPane();
|
||||||
|
return (glassPane != null && glassPane.isVisible());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean overlapsHeavyWeightComponent( Component owner, Component contents, int x, int y ) {
|
||||||
|
if( owner == null )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Window window = SwingUtilities.getWindowAncestor( owner );
|
||||||
|
if( window == null )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Rectangle r = new Rectangle( new Point( x, y ), contents.getPreferredSize() );
|
||||||
|
return overlapsHeavyWeightComponent( window, r );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean overlapsHeavyWeightComponent( Component parent, Rectangle r ) {
|
||||||
|
if( !parent.isVisible() || !r.intersects( parent.getBounds() ) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if( !parent.isLightweight() && !(parent instanceof Window) )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if( parent instanceof Container ) {
|
||||||
|
Rectangle r2 = new Rectangle( r.x - parent.getX(), r.y - parent.getY(), r.width, r.height );
|
||||||
|
for( Component c : ((Container)parent).getComponents() ) {
|
||||||
|
if( overlapsHeavyWeightComponent( c, r2 ) )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On Linux with Wayland, since Java 21, Swing adds a window focus listener to popup owner/invoker window,
|
||||||
|
* which hides the popup as soon as the owner/invoker window looses focus.
|
||||||
|
* This works fine for light-weight popups.
|
||||||
|
* It also works for heavy-weight popups if they do not request focus.
|
||||||
|
* Because FlatLaf always uses heavy-weight popups, all popups that request focus
|
||||||
|
* are broken since Java 21.
|
||||||
|
*
|
||||||
|
* This method removes the problematic window focus listener.
|
||||||
|
*
|
||||||
|
* https://bugs.openjdk.org/browse/JDK-8280993
|
||||||
|
* https://github.com/openjdk/jdk/pull/13830
|
||||||
|
*/
|
||||||
|
private static void fixLinuxWaylandJava21focusIssue( Component owner ) {
|
||||||
|
// only necessary on Linux when running in Java 21+
|
||||||
|
if( owner == null || !SystemInfo.isLinux || SystemInfo.javaVersion < SystemInfo.toVersion( 21, 0, 0, 0 ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// get window
|
||||||
|
Window window = SwingUtilities.getWindowAncestor( owner );
|
||||||
|
if( window == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// remove window focus listener, which was added from class sun.awt.UNIXToolkit since Java 21
|
||||||
|
for( WindowFocusListener l : window.getWindowFocusListeners() ) {
|
||||||
|
if( "sun.awt.UNIXToolkit$1".equals( l.getClass().getName() ) ) {
|
||||||
|
window.removeWindowFocusListener( l );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the given popup and, if necessary, fixes the location of a heavy weight popup window.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* E.g. when opening a dialog on the secondary screen and making combobox popup visible.
|
||||||
|
* <p>
|
||||||
|
* This is a workaround for https://bugs.openjdk.java.net/browse/JDK-8224608
|
||||||
|
*/
|
||||||
|
private static void showPopupAndFixLocation( Popup popup, Window popupWindow ) {
|
||||||
|
if( popupWindow != null ) {
|
||||||
|
// remember location of heavy weight popup window
|
||||||
|
int x = popupWindow.getX();
|
||||||
|
int y = popupWindow.getY();
|
||||||
|
|
||||||
|
if( !popupWindow.isVisible() )
|
||||||
|
popup.show();
|
||||||
|
else {
|
||||||
|
// if the popup window is already visible (because it is reused),
|
||||||
|
// do not invoke Popup.show() because this would invoke Window.toFront(),
|
||||||
|
// which may have the side effect that an inactive owner window
|
||||||
|
// would be also moved to front and maybe hide previously active window
|
||||||
|
popupWindow.pack();
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore popup window location if it has changed
|
||||||
|
// (probably scaled when screens use different scale factors)
|
||||||
|
if( popupWindow.getX() != x || popupWindow.getY() != y )
|
||||||
|
popupWindow.setLocation( x, y );
|
||||||
|
} else
|
||||||
|
popup.show();
|
||||||
|
}
|
||||||
|
|
||||||
//---- class NonFlashingPopup ---------------------------------------------
|
//---- class NonFlashingPopup ---------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fixes popup background flashing effect when using dark theme on light platform theme,
|
||||||
|
* where the light popup background is shown for a fraction of a second before
|
||||||
|
* the dark popup content is shown.
|
||||||
|
* This is fixed by setting popup background to content background.
|
||||||
|
* <p>
|
||||||
|
* Defers hiding of heavy weight popup window for an event cycle,
|
||||||
|
* which allows reusing popup window to avoid flicker when "moving" popup.
|
||||||
|
*/
|
||||||
private class NonFlashingPopup
|
private class NonFlashingPopup
|
||||||
extends Popup
|
extends Popup
|
||||||
{
|
{
|
||||||
private Popup delegate;
|
private Popup delegate;
|
||||||
|
Component owner;
|
||||||
private Component contents;
|
private Component contents;
|
||||||
|
|
||||||
// heavy weight
|
// heavy weight
|
||||||
protected Window popupWindow;
|
Window popupWindow;
|
||||||
private Color oldPopupWindowBackground;
|
private Color oldPopupWindowBackground;
|
||||||
|
|
||||||
NonFlashingPopup( Popup delegate, Component contents ) {
|
private boolean disposed;
|
||||||
|
|
||||||
|
NonFlashingPopup( Popup delegate, Component owner, Component contents ) {
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
|
this.owner = owner;
|
||||||
this.contents = contents;
|
this.contents = contents;
|
||||||
|
|
||||||
popupWindow = SwingUtilities.windowForComponent( contents );
|
popupWindow = SwingUtilities.windowForComponent( contents );
|
||||||
@@ -327,10 +615,52 @@ public class FlatPopupFactory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private NonFlashingPopup( NonFlashingPopup reusePopup ) {
|
||||||
|
delegate = reusePopup.delegate;
|
||||||
|
owner = reusePopup.owner;
|
||||||
|
contents = reusePopup.contents;
|
||||||
|
popupWindow = reusePopup.popupWindow;
|
||||||
|
oldPopupWindowBackground = reusePopup.oldPopupWindowBackground;
|
||||||
|
}
|
||||||
|
|
||||||
|
NonFlashingPopup cloneForReuse() {
|
||||||
|
return new NonFlashingPopup( this );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void show() {
|
public final void show() {
|
||||||
|
if( disposed )
|
||||||
|
return;
|
||||||
|
|
||||||
|
showImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
void showImpl() {
|
||||||
if( delegate != null ) {
|
if( delegate != null ) {
|
||||||
showPopupAndFixLocation( delegate, popupWindow );
|
// On macOS and Linux, the empty popup window is shown in popup.show()
|
||||||
|
// (in peer.setVisible(true) invoked from Component.show()),
|
||||||
|
// but the popup content is painted later via repaint manager.
|
||||||
|
// This may cause some flicker, especially during JVM warm-up or
|
||||||
|
// when running JVM in interpreter mode (option -Xint).
|
||||||
|
// To reduce flicker, immediately paint popup content as soon as popup window becomes visible.
|
||||||
|
// This also fixes a problem with JetBrainsRuntime JVM, where sometimes the popups were empty.
|
||||||
|
if( (SystemInfo.isMacOS || SystemInfo.isLinux) && popupWindow instanceof JWindow ) {
|
||||||
|
HierarchyListener l = e -> {
|
||||||
|
if( e.getID() == HierarchyEvent.HIERARCHY_CHANGED &&
|
||||||
|
(e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0 )
|
||||||
|
{
|
||||||
|
((JWindow)popupWindow).getRootPane().paintImmediately(
|
||||||
|
0, 0, popupWindow.getWidth(), popupWindow.getHeight() );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
popupWindow.addHierarchyListener( l );
|
||||||
|
try {
|
||||||
|
showPopupAndFixLocation( delegate, popupWindow );
|
||||||
|
} finally {
|
||||||
|
popupWindow.removeHierarchyListener( l );
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
showPopupAndFixLocation( delegate, popupWindow );
|
||||||
|
|
||||||
// increase tooltip size if necessary because it may be too small on HiDPI screens
|
// increase tooltip size if necessary because it may be too small on HiDPI screens
|
||||||
// https://bugs.openjdk.java.net/browse/JDK-8213535
|
// https://bugs.openjdk.java.net/browse/JDK-8213535
|
||||||
@@ -352,10 +682,39 @@ public class FlatPopupFactory
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void hide() {
|
public final void hide() {
|
||||||
|
if( disposed )
|
||||||
|
return;
|
||||||
|
disposed = true;
|
||||||
|
|
||||||
|
// immediately hide non-heavy weight popups, popup menus and combobox popups
|
||||||
|
// of if system property is false
|
||||||
|
if( !(popupWindow instanceof JWindow) || contents instanceof JPopupMenu ||
|
||||||
|
!FlatSystemProperties.getBoolean( FlatSystemProperties.REUSE_VISIBLE_POPUP_WINDOW, true ) )
|
||||||
|
{
|
||||||
|
hideImpl();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// defer hiding of heavy weight popup window for an event cycle,
|
||||||
|
// which allows reusing popup window to avoid flicker when "moving" popup
|
||||||
|
((JWindow)popupWindow).getContentPane().removeAll();
|
||||||
|
stillShownHeavyWeightPopups.add( this );
|
||||||
|
EventQueue.invokeLater( () -> {
|
||||||
|
// hide popup if it was not reused
|
||||||
|
if( stillShownHeavyWeightPopups.remove( this ) )
|
||||||
|
hideImpl();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
void hideImpl() {
|
||||||
|
if( contents instanceof JComponent )
|
||||||
|
((JComponent)contents).putClientProperty( KEY_POPUP_USES_NATIVE_BORDER, null );
|
||||||
|
|
||||||
if( delegate != null ) {
|
if( delegate != null ) {
|
||||||
delegate.hide();
|
delegate.hide();
|
||||||
delegate = null;
|
delegate = null;
|
||||||
|
owner = null;
|
||||||
contents = null;
|
contents = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,18 +722,54 @@ public class FlatPopupFactory
|
|||||||
// restore background so that it can not affect other LaFs (when switching)
|
// restore background so that it can not affect other LaFs (when switching)
|
||||||
// because popup windows are cached and reused
|
// because popup windows are cached and reused
|
||||||
popupWindow.setBackground( oldPopupWindowBackground );
|
popupWindow.setBackground( oldPopupWindowBackground );
|
||||||
|
|
||||||
|
// On macOS, popupWindow.setBackground(...) invoked from constructor,
|
||||||
|
// has no affect if the popup window peer (a NSWindow) is already created,
|
||||||
|
// which is the case when reusing a cached popup window
|
||||||
|
// (see class PopupFactory.HeavyWeightPopup).
|
||||||
|
// This may result in flicker when e.g. showing a popup in a light theme,
|
||||||
|
// then switching to a dark theme and again showing a popup,
|
||||||
|
// because the underling NSWindow still has a light background,
|
||||||
|
// which may be shown shortly before the actual dark popup content is shown.
|
||||||
|
// To fix this, dispose the popup window, which disposes the NSWindow.
|
||||||
|
// The AWT popup window stays in the popup cache, but when reusing it later,
|
||||||
|
// a new peer and a new NSWindow is created and gets the correct background.
|
||||||
|
if( SystemInfo.isMacOS )
|
||||||
|
popupWindow.dispose();
|
||||||
|
|
||||||
popupWindow = null;
|
popupWindow = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reset( Component contents, int ownerX, int ownerY ) {
|
||||||
|
// update popup window location
|
||||||
|
popupWindow.setLocation( ownerX, ownerY );
|
||||||
|
|
||||||
|
// replace component in content pane
|
||||||
|
Container contentPane = ((JWindow)popupWindow).getContentPane();
|
||||||
|
contentPane.removeAll();
|
||||||
|
contentPane.add( contents, BorderLayout.CENTER );
|
||||||
|
popupWindow.pack();
|
||||||
|
|
||||||
|
// update client property on contents
|
||||||
|
if( this.contents != contents ) {
|
||||||
|
Object old = (this.contents instanceof JComponent)
|
||||||
|
? ((JComponent)this.contents).getClientProperty( KEY_POPUP_USES_NATIVE_BORDER )
|
||||||
|
: null;
|
||||||
|
if( contents instanceof JComponent )
|
||||||
|
((JComponent)contents).putClientProperty( KEY_POPUP_USES_NATIVE_BORDER, old );
|
||||||
|
|
||||||
|
this.contents = contents;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class DropShadowPopup ----------------------------------------------
|
//---- class DropShadowPopup ----------------------------------------------
|
||||||
|
|
||||||
private class DropShadowPopup
|
private class DropShadowPopup
|
||||||
extends NonFlashingPopup
|
extends NonFlashingPopup
|
||||||
|
implements ComponentListener
|
||||||
{
|
{
|
||||||
private final Component owner;
|
|
||||||
|
|
||||||
// light weight
|
// light weight
|
||||||
private JComponent lightComp;
|
private JComponent lightComp;
|
||||||
private Border oldBorder;
|
private Border oldBorder;
|
||||||
@@ -389,11 +784,11 @@ public class FlatPopupFactory
|
|||||||
// heavy weight
|
// heavy weight
|
||||||
private Popup dropShadowDelegate;
|
private Popup dropShadowDelegate;
|
||||||
private Window dropShadowWindow;
|
private Window dropShadowWindow;
|
||||||
|
private JPanel dropShadowPanel2;
|
||||||
private Color oldDropShadowWindowBackground;
|
private Color oldDropShadowWindowBackground;
|
||||||
|
|
||||||
DropShadowPopup( Popup delegate, Component owner, Component contents ) {
|
DropShadowPopup( Popup delegate, Component owner, Component contents ) {
|
||||||
super( delegate, contents );
|
super( delegate, owner, contents );
|
||||||
this.owner = owner;
|
|
||||||
|
|
||||||
Dimension size = contents.getPreferredSize();
|
Dimension size = contents.getPreferredSize();
|
||||||
if( size.width <= 0 || size.height <= 0 )
|
if( size.width <= 0 || size.height <= 0 )
|
||||||
@@ -409,28 +804,36 @@ public class FlatPopupFactory
|
|||||||
// the drop shadow and is positioned behind the popup window.
|
// the drop shadow and is positioned behind the popup window.
|
||||||
|
|
||||||
// create panel that paints the drop shadow
|
// create panel that paints the drop shadow
|
||||||
JPanel dropShadowPanel = new JPanel();
|
dropShadowPanel2 = new JPanel();
|
||||||
dropShadowPanel.setBorder( createDropShadowBorder() );
|
dropShadowPanel2.setBorder( createDropShadowBorder() );
|
||||||
dropShadowPanel.setOpaque( false );
|
dropShadowPanel2.setOpaque( false );
|
||||||
|
|
||||||
// set preferred size of drop shadow panel
|
// set preferred size of drop shadow panel
|
||||||
Dimension prefSize = popupWindow.getPreferredSize();
|
Dimension prefSize = popupWindow.getPreferredSize();
|
||||||
Insets insets = dropShadowPanel.getInsets();
|
Insets insets = dropShadowPanel2.getInsets();
|
||||||
dropShadowPanel.setPreferredSize( new Dimension(
|
dropShadowPanel2.setPreferredSize( new Dimension(
|
||||||
prefSize.width + insets.left + insets.right,
|
prefSize.width + insets.left + insets.right,
|
||||||
prefSize.height + insets.top + insets.bottom ) );
|
prefSize.height + insets.top + insets.bottom ) );
|
||||||
|
|
||||||
// create heavy weight popup for drop shadow
|
// create heavy weight popup for drop shadow
|
||||||
int x = popupWindow.getX() - insets.left;
|
int x = popupWindow.getX() - insets.left;
|
||||||
int y = popupWindow.getY() - insets.top;
|
int y = popupWindow.getY() - insets.top;
|
||||||
dropShadowDelegate = getPopupForScreenOfOwner( owner, dropShadowPanel, x, y, true );
|
dropShadowDelegate = getPopupForScreenOfOwner( owner, dropShadowPanel2, x, y, true );
|
||||||
|
|
||||||
// make drop shadow popup window translucent
|
// make drop shadow popup window translucent
|
||||||
dropShadowWindow = SwingUtilities.windowForComponent( dropShadowPanel );
|
dropShadowWindow = SwingUtilities.windowForComponent( dropShadowPanel2 );
|
||||||
if( dropShadowWindow != null ) {
|
if( dropShadowWindow != null ) {
|
||||||
oldDropShadowWindowBackground = dropShadowWindow.getBackground();
|
oldDropShadowWindowBackground = dropShadowWindow.getBackground();
|
||||||
dropShadowWindow.setBackground( new Color( 0, true ) );
|
dropShadowWindow.setBackground( new Color( 0, true ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Windows 11: reset corner preference on reused heavy weight popups
|
||||||
|
if( SystemInfo.isWindows_11_orLater && FlatNativeWindowsLibrary.isLoaded() ) {
|
||||||
|
resetWindows11Border( popupWindow );
|
||||||
|
if( dropShadowWindow != null )
|
||||||
|
resetWindows11Border( dropShadowWindow );
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
mediumWeightPanel = (Panel) SwingUtilities.getAncestorOfClass( Panel.class, contents );
|
mediumWeightPanel = (Panel) SwingUtilities.getAncestorOfClass( Panel.class, contents );
|
||||||
if( mediumWeightPanel != null ) {
|
if( mediumWeightPanel != null ) {
|
||||||
@@ -455,6 +858,23 @@ public class FlatPopupFactory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DropShadowPopup( DropShadowPopup reusePopup ) {
|
||||||
|
super( reusePopup );
|
||||||
|
|
||||||
|
// not necessary to clone fields used for light/medium weight popups
|
||||||
|
|
||||||
|
// heavy weight
|
||||||
|
dropShadowDelegate = reusePopup.dropShadowDelegate;
|
||||||
|
dropShadowWindow = reusePopup.dropShadowWindow;
|
||||||
|
dropShadowPanel2 = reusePopup.dropShadowPanel2;
|
||||||
|
oldDropShadowWindowBackground = reusePopup.oldDropShadowWindowBackground;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
NonFlashingPopup cloneForReuse() {
|
||||||
|
return new DropShadowPopup( this );
|
||||||
|
}
|
||||||
|
|
||||||
private Border createDropShadowBorder() {
|
private Border createDropShadowBorder() {
|
||||||
return new FlatDropShadowBorder(
|
return new FlatDropShadowBorder(
|
||||||
UIManager.getColor( "Popup.dropShadowColor" ),
|
UIManager.getColor( "Popup.dropShadowColor" ),
|
||||||
@@ -463,14 +883,14 @@ public class FlatPopupFactory
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void show() {
|
void showImpl() {
|
||||||
if( dropShadowDelegate != null )
|
if( dropShadowDelegate != null )
|
||||||
showPopupAndFixLocation( dropShadowDelegate, dropShadowWindow );
|
showPopupAndFixLocation( dropShadowDelegate, dropShadowWindow );
|
||||||
|
|
||||||
if( mediumWeightPanel != null )
|
if( mediumWeightPanel != null )
|
||||||
showMediumWeightDropShadow();
|
showMediumWeightDropShadow();
|
||||||
|
|
||||||
super.show();
|
super.showImpl();
|
||||||
|
|
||||||
// fix location of light weight popup in case it has left or top drop shadow
|
// fix location of light weight popup in case it has left or top drop shadow
|
||||||
if( lightComp != null ) {
|
if( lightComp != null ) {
|
||||||
@@ -478,13 +898,22 @@ public class FlatPopupFactory
|
|||||||
if( insets.left != 0 || insets.top != 0 )
|
if( insets.left != 0 || insets.top != 0 )
|
||||||
lightComp.setLocation( lightComp.getX() - insets.left, lightComp.getY() - insets.top );
|
lightComp.setLocation( lightComp.getX() - insets.left, lightComp.getY() - insets.top );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( popupWindow != null ) {
|
||||||
|
removeAllPopupWindowComponentListeners();
|
||||||
|
popupWindow.addComponentListener( this );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void hide() {
|
void hideImpl() {
|
||||||
|
if( popupWindow != null )
|
||||||
|
removeAllPopupWindowComponentListeners();
|
||||||
|
|
||||||
if( dropShadowDelegate != null ) {
|
if( dropShadowDelegate != null ) {
|
||||||
dropShadowDelegate.hide();
|
dropShadowDelegate.hide();
|
||||||
dropShadowDelegate = null;
|
dropShadowDelegate = null;
|
||||||
|
dropShadowPanel2 = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( mediumWeightPanel != null ) {
|
if( mediumWeightPanel != null ) {
|
||||||
@@ -493,7 +922,7 @@ public class FlatPopupFactory
|
|||||||
mediumWeightPanel = null;
|
mediumWeightPanel = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
super.hide();
|
super.hideImpl();
|
||||||
|
|
||||||
if( dropShadowWindow != null ) {
|
if( dropShadowWindow != null ) {
|
||||||
dropShadowWindow.setBackground( oldDropShadowWindowBackground );
|
dropShadowWindow.setBackground( oldDropShadowWindowBackground );
|
||||||
@@ -577,5 +1006,58 @@ public class FlatPopupFactory
|
|||||||
if( dropShadowPanel != null && mediumWeightPanel != null )
|
if( dropShadowPanel != null && mediumWeightPanel != null )
|
||||||
dropShadowPanel.setSize( FlatUIUtils.addInsets( mediumWeightPanel.getSize(), dropShadowPanel.getInsets() ) );
|
dropShadowPanel.setSize( FlatUIUtils.addInsets( mediumWeightPanel.getSize(), dropShadowPanel.getInsets() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void reset( Component contents, int ownerX, int ownerY ) {
|
||||||
|
if( popupWindow != null )
|
||||||
|
removeAllPopupWindowComponentListeners();
|
||||||
|
|
||||||
|
super.reset( contents, ownerX, ownerY );
|
||||||
|
|
||||||
|
updateDropShadowWindowBounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDropShadowWindowBounds() {
|
||||||
|
if( dropShadowWindow == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// calculate size of drop shadow window
|
||||||
|
Dimension size = popupWindow.getSize();
|
||||||
|
Insets insets = dropShadowPanel2.getInsets();
|
||||||
|
int w = size.width + insets.left + insets.right;
|
||||||
|
int h = size.height + insets.top + insets.bottom;
|
||||||
|
|
||||||
|
// update drop shadow popup window bounds
|
||||||
|
int x = popupWindow.getX() - insets.left;
|
||||||
|
int y = popupWindow.getY() - insets.top;
|
||||||
|
dropShadowWindow.setBounds( x, y, w, h );
|
||||||
|
dropShadowWindow.validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeAllPopupWindowComponentListeners() {
|
||||||
|
// make sure that there is no old component listener
|
||||||
|
// necessary because this class is cloned if reusing popup windows
|
||||||
|
for( ComponentListener l : popupWindow.getComponentListeners() ) {
|
||||||
|
if( l instanceof DropShadowPopup )
|
||||||
|
popupWindow.removeComponentListener( l );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- interface ComponentListener ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void componentResized( ComponentEvent e ) {
|
||||||
|
if( e.getSource() == popupWindow )
|
||||||
|
updateDropShadowWindowBounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void componentMoved( ComponentEvent e ) {
|
||||||
|
if( e.getSource() == popupWindow )
|
||||||
|
updateDropShadowWindowBounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void componentShown( ComponentEvent e ) {}
|
||||||
|
@Override public void componentHidden( ComponentEvent e ) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import java.awt.Insets;
|
|||||||
import java.util.Map;
|
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.StyleableObject;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*/
|
*/
|
||||||
public class FlatPopupMenuBorder
|
public class FlatPopupMenuBorder
|
||||||
extends FlatLineBorder
|
extends FlatLineBorder
|
||||||
implements StyleableBorder
|
implements StyleableObject
|
||||||
{
|
{
|
||||||
private Color borderColor;
|
private Color borderColor;
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ import javax.swing.plaf.basic.BasicComboPopup;
|
|||||||
import javax.swing.plaf.basic.BasicMenuItemUI;
|
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 javax.swing.plaf.basic.DefaultMenuLayout;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
@@ -192,27 +193,38 @@ public class FlatPopupMenuUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Popup getPopup( JPopupMenu popup, int x, int y ) {
|
public Popup getPopup( JPopupMenu popup, int x, int y ) {
|
||||||
|
Dimension popupSize = popup.getPreferredSize();
|
||||||
|
Rectangle screenBounds = getScreenBoundsAt( x, y );
|
||||||
|
|
||||||
|
// make sure that popup does not overlap any task/side bar
|
||||||
|
if( x + popupSize.width > screenBounds.x + screenBounds.width )
|
||||||
|
x = screenBounds.x + screenBounds.width - popupSize.width;
|
||||||
|
if( y + popupSize.height > screenBounds.y + screenBounds.height )
|
||||||
|
y = screenBounds.y + screenBounds.height - popupSize.height;
|
||||||
|
if( x < screenBounds.x )
|
||||||
|
x = screenBounds.x;
|
||||||
|
if( y < screenBounds.y )
|
||||||
|
y = screenBounds.y;
|
||||||
|
|
||||||
// do not add scroller to combobox popups or to popups that already have a scroll pane
|
// do not add scroller to combobox popups or to popups that already have a scroll pane
|
||||||
if( popup instanceof BasicComboPopup ||
|
if( popup instanceof BasicComboPopup ||
|
||||||
(popup.getComponentCount() > 0 && popup.getComponent( 0 ) instanceof JScrollPane) )
|
(popup.getComponentCount() > 0 && popup.getComponent( 0 ) instanceof JScrollPane) )
|
||||||
return super.getPopup( popup, x, y );
|
return super.getPopup( popup, x, y );
|
||||||
|
|
||||||
// do not add scroller if popup fits into screen
|
// do not add scroller if popup fits into screen
|
||||||
Dimension prefSize = popup.getPreferredSize();
|
if( popupSize.height <= screenBounds.height )
|
||||||
int screenHeight = getScreenHeightAt( x, y );
|
|
||||||
if( prefSize.height <= screenHeight )
|
|
||||||
return super.getPopup( popup, x, y );
|
return super.getPopup( popup, x, y );
|
||||||
|
|
||||||
// create scroller
|
// create scroller
|
||||||
FlatPopupScroller scroller = new FlatPopupScroller( popup );
|
FlatPopupScroller scroller = new FlatPopupScroller( popup );
|
||||||
scroller.setPreferredSize( new Dimension( prefSize.width, screenHeight ) );
|
scroller.setPreferredSize( new Dimension( popupSize.width, screenBounds.height ) );
|
||||||
|
|
||||||
// create popup
|
// create popup
|
||||||
PopupFactory popupFactory = PopupFactory.getSharedInstance();
|
PopupFactory popupFactory = PopupFactory.getSharedInstance();
|
||||||
return popupFactory.getPopup( popup.getInvoker(), scroller, x, y );
|
return popupFactory.getPopup( popup.getInvoker(), scroller, x, y );
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getScreenHeightAt( int x, int y ) {
|
private Rectangle getScreenBoundsAt( int x, int y ) {
|
||||||
// find GraphicsConfiguration at popup location (similar to JPopupMenu.getCurrentGraphicsConfiguration())
|
// find GraphicsConfiguration at popup location (similar to JPopupMenu.getCurrentGraphicsConfiguration())
|
||||||
GraphicsConfiguration gc = null;
|
GraphicsConfiguration gc = null;
|
||||||
for( GraphicsDevice device : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices() ) {
|
for( GraphicsDevice device : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices() ) {
|
||||||
@@ -227,13 +239,15 @@ public class FlatPopupMenuUI
|
|||||||
if( gc == null && popupMenu.getInvoker() != null )
|
if( gc == null && popupMenu.getInvoker() != null )
|
||||||
gc = popupMenu.getInvoker().getGraphicsConfiguration();
|
gc = popupMenu.getInvoker().getGraphicsConfiguration();
|
||||||
|
|
||||||
// compute screen height
|
if( gc == null )
|
||||||
|
return new Rectangle( Toolkit.getDefaultToolkit().getScreenSize() );
|
||||||
|
|
||||||
|
// compute screen bounds
|
||||||
// (always subtract screen insets because there is no API to detect whether
|
// (always subtract screen insets because there is no API to detect whether
|
||||||
// the popup can overlap the taskbar; see JPopupMenu.canPopupOverlapTaskBar())
|
// the popup can overlap the taskbar; see JPopupMenu.canPopupOverlapTaskBar())
|
||||||
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
Rectangle screenBounds = gc.getBounds();
|
||||||
Rectangle screenBounds = (gc != null) ? gc.getBounds() : new Rectangle( toolkit.getScreenSize() );
|
Insets screenInsets = FlatUIUtils.getScreenInsets( gc );
|
||||||
Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets( gc );
|
return FlatUIUtils.subtractInsets( screenBounds, screenInsets );
|
||||||
return screenBounds.height - screenInsets.top - screenInsets.bottom;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class FlatPopupMenuLayout ------------------------------------------
|
//---- class FlatPopupMenuLayout ------------------------------------------
|
||||||
@@ -297,6 +311,9 @@ public class FlatPopupMenuUI
|
|||||||
popup.addMenuKeyListener( this );
|
popup.addMenuKeyListener( this );
|
||||||
|
|
||||||
updateArrowButtons();
|
updateArrowButtons();
|
||||||
|
|
||||||
|
putClientProperty( FlatClientProperties.POPUP_BORDER_CORNER_RADIUS,
|
||||||
|
UIManager.getInt( "PopupMenu.borderCornerRadius" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void scroll( int unitsToScroll ) {
|
void scroll( int unitsToScroll ) {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import static com.formdev.flatlaf.FlatClientProperties.*;
|
import static com.formdev.flatlaf.FlatClientProperties.*;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
|
import java.awt.EventQueue;
|
||||||
import java.awt.FontMetrics;
|
import java.awt.FontMetrics;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
@@ -86,6 +87,17 @@ public class FlatProgressBarUI
|
|||||||
installStyle();
|
installStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uninstallUI( JComponent c ) {
|
||||||
|
if( !EventQueue.isDispatchThread() && progressBar.isIndeterminate() ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere(
|
||||||
|
"FlatLaf: Uninstalling indeterminate progress bar UI not on AWT thread may throw NPE in FlatProgressBarUI.paint(). Use SwingUtilities.invokeLater().",
|
||||||
|
new IllegalStateException() );
|
||||||
|
}
|
||||||
|
|
||||||
|
super.uninstallUI( c );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
@@ -110,17 +122,25 @@ public class FlatProgressBarUI
|
|||||||
|
|
||||||
propertyChangeListener = e -> {
|
propertyChangeListener = e -> {
|
||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
|
case "indeterminate":
|
||||||
|
if( !EventQueue.isDispatchThread() && !progressBar.isIndeterminate() ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere(
|
||||||
|
"FlatLaf: Using JProgressBar.setIndeterminate(false) not on AWT thread may throw NPE in FlatProgressBarUI.paint(). Use SwingUtilities.invokeLater().",
|
||||||
|
new IllegalStateException() );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case PROGRESS_BAR_LARGE_HEIGHT:
|
case PROGRESS_BAR_LARGE_HEIGHT:
|
||||||
case PROGRESS_BAR_SQUARE:
|
case PROGRESS_BAR_SQUARE:
|
||||||
progressBar.revalidate();
|
progressBar.revalidate();
|
||||||
progressBar.repaint();
|
HiDPIUtils.repaint( progressBar );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STYLE:
|
case STYLE:
|
||||||
case STYLE_CLASS:
|
case STYLE_CLASS:
|
||||||
installStyle();
|
installStyle();
|
||||||
progressBar.revalidate();
|
progressBar.revalidate();
|
||||||
progressBar.repaint();
|
HiDPIUtils.repaint( progressBar );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -274,6 +294,6 @@ public class FlatProgressBarUI
|
|||||||
// Only solution is to repaint whole progress bar.
|
// Only solution is to repaint whole progress bar.
|
||||||
double systemScaleFactor = UIScale.getSystemScaleFactor( progressBar.getGraphicsConfiguration() );
|
double systemScaleFactor = UIScale.getSystemScaleFactor( progressBar.getGraphicsConfiguration() );
|
||||||
if( (int) systemScaleFactor != systemScaleFactor )
|
if( (int) systemScaleFactor != systemScaleFactor )
|
||||||
progressBar.repaint();
|
HiDPIUtils.repaint( progressBar );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import java.lang.invoke.MethodHandles;
|
|||||||
import java.util.Map;
|
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;
|
||||||
@@ -102,13 +103,23 @@ public class FlatRadioButtonMenuItemUI
|
|||||||
oldStyleValues = null;
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installComponents( JMenuItem menuItem ) {
|
||||||
|
super.installComponents( menuItem );
|
||||||
|
|
||||||
|
// update HTML renderer if necessary
|
||||||
|
FlatHTML.updateRendererCSSFontBaseSize( menuItem );
|
||||||
|
}
|
||||||
|
|
||||||
protected FlatMenuItemRenderer createRenderer() {
|
protected FlatMenuItemRenderer createRenderer() {
|
||||||
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
||||||
return FlatStylingSupport.createPropertyChangeListener( c, this::installStyle, super.createPropertyChangeListener( c ) );
|
return FlatHTML.createPropertyChangeListener(
|
||||||
|
FlatStylingSupport.createPropertyChangeListener( c, this::installStyle,
|
||||||
|
super.createPropertyChangeListener( c ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
|
|||||||
@@ -35,16 +35,20 @@ import javax.swing.CellRendererPane;
|
|||||||
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.SwingConstants;
|
||||||
import javax.swing.SwingUtilities;
|
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.BasicButtonListener;
|
||||||
|
import javax.swing.plaf.basic.BasicHTML;
|
||||||
import javax.swing.plaf.basic.BasicRadioButtonUI;
|
import javax.swing.plaf.basic.BasicRadioButtonUI;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
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.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableObject;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -158,6 +162,10 @@ public class FlatRadioButtonUI
|
|||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
protected void propertyChange( AbstractButton b, PropertyChangeEvent e ) {
|
protected void propertyChange( AbstractButton b, PropertyChangeEvent e ) {
|
||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
|
case BasicHTML.propertyKey:
|
||||||
|
FlatHTML.updateRendererCSSFontBaseSize( b );
|
||||||
|
break;
|
||||||
|
|
||||||
case FlatClientProperties.STYLE:
|
case FlatClientProperties.STYLE:
|
||||||
case FlatClientProperties.STYLE_CLASS:
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
if( shared && FlatStylingSupport.hasStyleProperty( b ) ) {
|
if( shared && FlatStylingSupport.hasStyleProperty( b ) ) {
|
||||||
@@ -167,7 +175,7 @@ public class FlatRadioButtonUI
|
|||||||
} else
|
} else
|
||||||
installStyle( b );
|
installStyle( b );
|
||||||
b.revalidate();
|
b.revalidate();
|
||||||
b.repaint();
|
HiDPIUtils.repaint( b );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,18 +204,22 @@ public class FlatRadioButtonUI
|
|||||||
protected Object applyStyleProperty( AbstractButton b, String key, Object value ) {
|
protected Object applyStyleProperty( AbstractButton b, String key, Object value ) {
|
||||||
// style icon
|
// style icon
|
||||||
if( key.startsWith( "icon." ) ) {
|
if( key.startsWith( "icon." ) ) {
|
||||||
if( !(icon instanceof FlatCheckBoxIcon) )
|
Icon icon = getRealIcon( b );
|
||||||
return new UnknownStyleException( key );
|
if( !(icon instanceof StyleableObject) )
|
||||||
|
throw new UnknownStyleException( key );
|
||||||
|
|
||||||
if( iconShared ) {
|
if( icon == this.icon && iconShared ) {
|
||||||
icon = FlatStylingSupport.cloneIcon( icon );
|
this.icon = icon = FlatStylingSupport.cloneIcon( icon );
|
||||||
iconShared = false;
|
iconShared = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
key = key.substring( "icon.".length() );
|
key = key.substring( "icon.".length() );
|
||||||
return ((FlatCheckBoxIcon)icon).applyStyleProperty( key, value );
|
return ((StyleableObject)icon).applyStyleProperty( key, value );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( "iconTextGap".equals( key ) && value instanceof Integer )
|
||||||
|
value = UIScale.scale( (Integer) value );
|
||||||
|
|
||||||
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, b, key, value );
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, b, key, value );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,10 +227,9 @@ public class FlatRadioButtonUI
|
|||||||
@Override
|
@Override
|
||||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
Map<String, Class<?>> infos = FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
Map<String, Class<?>> infos = FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
if( icon instanceof FlatCheckBoxIcon ) {
|
Icon icon = getRealIcon( c );
|
||||||
for( Map.Entry<String, Class<?>> e : ((FlatCheckBoxIcon)icon).getStyleableInfos().entrySet() )
|
if( icon instanceof StyleableObject )
|
||||||
infos.put( "icon.".concat( e.getKey() ), e.getValue() );
|
FlatStylingSupport.putAllPrefixKey( infos, "icon.", ((StyleableObject)icon).getStyleableInfos() );
|
||||||
}
|
|
||||||
return infos;
|
return infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,8 +238,9 @@ public class FlatRadioButtonUI
|
|||||||
public Object getStyleableValue( JComponent c, String key ) {
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
// style icon
|
// style icon
|
||||||
if( key.startsWith( "icon." ) ) {
|
if( key.startsWith( "icon." ) ) {
|
||||||
return (icon instanceof FlatCheckBoxIcon)
|
Icon icon = getRealIcon( c );
|
||||||
? ((FlatCheckBoxIcon)icon).getStyleableValue( key.substring( "icon.".length() ) )
|
return (icon instanceof StyleableObject)
|
||||||
|
? ((StyleableObject)icon).getStyleableValue( key.substring( "icon.".length() ) )
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,7 +271,7 @@ public class FlatRadioButtonUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paint( Graphics g, JComponent c ) {
|
public void paint( Graphics g, JComponent c ) {
|
||||||
// fill background even if not opaque if
|
// fill background even if not opaque and if:
|
||||||
// - contentAreaFilled is true and
|
// - contentAreaFilled is true and
|
||||||
// - if background color is different to default background color
|
// - if background color is different to default background color
|
||||||
// (this paints selection if using the component as cell renderer)
|
// (this paints selection if using the component as cell renderer)
|
||||||
@@ -275,20 +287,27 @@ public class FlatRadioButtonUI
|
|||||||
int focusWidth = getIconFocusWidth( c );
|
int focusWidth = getIconFocusWidth( c );
|
||||||
if( focusWidth > 0 ) {
|
if( focusWidth > 0 ) {
|
||||||
boolean ltr = c.getComponentOrientation().isLeftToRight();
|
boolean ltr = c.getComponentOrientation().isLeftToRight();
|
||||||
|
int halign = ((AbstractButton)c).getHorizontalAlignment();
|
||||||
|
if( halign == SwingConstants.LEADING )
|
||||||
|
halign = ltr ? SwingConstants.LEFT : SwingConstants.RIGHT;
|
||||||
|
else if( halign == SwingConstants.TRAILING )
|
||||||
|
halign = ltr ? SwingConstants.RIGHT : SwingConstants.LEFT;
|
||||||
|
|
||||||
Insets insets = c.getInsets( tempInsets );
|
Insets insets = c.getInsets( tempInsets );
|
||||||
int leftOrRightInset = ltr ? insets.left : insets.right;
|
if( (focusWidth > insets.left || focusWidth > insets.right) &&
|
||||||
if( focusWidth > leftOrRightInset ) {
|
(halign == SwingConstants.LEFT || halign == SwingConstants.RIGHT) )
|
||||||
|
{
|
||||||
// The left (or right) inset is smaller than the focus width, which may be
|
// The left (or right) inset is smaller than the focus width, which may be
|
||||||
// the case if insets were explicitly reduced (e.g. with an EmptyBorder).
|
// the case if insets were explicitly reduced (e.g. with an EmptyBorder).
|
||||||
// In this case the width has been increased in getPreferredSize() and
|
// In this case the width has been increased in getPreferredSize() and
|
||||||
// here it is necessary to fix icon and text painting location.
|
// here it is necessary to fix icon and text painting location.
|
||||||
int offset = focusWidth - leftOrRightInset;
|
int offset = (halign == SwingConstants.LEFT)
|
||||||
if( !ltr )
|
? Math.max( focusWidth - insets.left, 0 )
|
||||||
offset = -offset;
|
: -Math.max( focusWidth - insets.right, 0 );
|
||||||
|
|
||||||
// move the graphics origin to the left (or right)
|
// move the graphics origin to the left (or right)
|
||||||
g.translate( offset, 0 );
|
g.translate( offset, 0 );
|
||||||
super.paint( g, c );
|
super.paint( FlatLabelUI.createGraphicsHTMLTextYCorrection( g, c ), c );
|
||||||
g.translate( -offset, 0 );
|
g.translate( -offset, 0 );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -315,16 +334,23 @@ public class FlatRadioButtonUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int getIconFocusWidth( JComponent c ) {
|
private int getIconFocusWidth( JComponent c ) {
|
||||||
AbstractButton b = (AbstractButton) c;
|
Icon icon = getRealIcon( c );
|
||||||
Icon icon = b.getIcon();
|
|
||||||
if( icon == null )
|
|
||||||
icon = getDefaultIcon();
|
|
||||||
|
|
||||||
return (icon instanceof FlatCheckBoxIcon)
|
return (icon instanceof FlatCheckBoxIcon)
|
||||||
? Math.round( UIScale.scale( ((FlatCheckBoxIcon)icon).getFocusWidth() ) )
|
? Math.round( UIScale.scale( ((FlatCheckBoxIcon)icon).getFocusWidth() ) )
|
||||||
: 0;
|
: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Icon getRealIcon( JComponent c ) {
|
||||||
|
AbstractButton b = (AbstractButton) c;
|
||||||
|
Icon icon = b.getIcon();
|
||||||
|
return (icon != null) ? icon : getDefaultIcon();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBaseline( JComponent c, int width, int height ) {
|
||||||
|
return FlatButtonUI.getBaselineImpl( c, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
//---- class FlatRadioButtonListener --------------------------------------
|
//---- class FlatRadioButtonListener --------------------------------------
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
|
|||||||
@@ -20,18 +20,19 @@ 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.Frame;
|
import java.awt.Frame;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.IllegalComponentStateException;
|
||||||
import java.awt.Insets;
|
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.ComponentAdapter;
|
|
||||||
import java.awt.event.ComponentEvent;
|
|
||||||
import java.awt.event.ComponentListener;
|
import java.awt.event.ComponentListener;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Objects;
|
||||||
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;
|
||||||
@@ -40,7 +41,6 @@ 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;
|
||||||
@@ -87,8 +87,8 @@ public class FlatRootPaneUI
|
|||||||
|
|
||||||
private Object nativeWindowBorderData;
|
private Object nativeWindowBorderData;
|
||||||
private LayoutManager oldLayout;
|
private LayoutManager oldLayout;
|
||||||
private PropertyChangeListener ancestorListener;
|
private ComponentListener macFullWindowContentListener;
|
||||||
private ComponentListener componentListener;
|
private PropertyChangeListener macWindowBackgroundListener;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatRootPaneUI();
|
return new FlatRootPaneUI();
|
||||||
@@ -106,6 +106,7 @@ public class FlatRootPaneUI
|
|||||||
installBorder();
|
installBorder();
|
||||||
|
|
||||||
installNativeWindowBorder();
|
installNativeWindowBorder();
|
||||||
|
macInstallFullWindowContentSupport();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void installBorder() {
|
protected void installBorder() {
|
||||||
@@ -122,6 +123,7 @@ public class FlatRootPaneUI
|
|||||||
|
|
||||||
uninstallNativeWindowBorder();
|
uninstallNativeWindowBorder();
|
||||||
uninstallClientDecorations();
|
uninstallClientDecorations();
|
||||||
|
macUninstallFullWindowContentSupport();
|
||||||
rootPane = null;
|
rootPane = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,13 +154,31 @@ public class FlatRootPaneUI
|
|||||||
Container parent = c.getParent();
|
Container parent = c.getParent();
|
||||||
if( parent instanceof JFrame || parent instanceof JDialog ) {
|
if( parent instanceof JFrame || parent instanceof JDialog ) {
|
||||||
Color background = parent.getBackground();
|
Color background = parent.getBackground();
|
||||||
if( background == null || background instanceof UIResource )
|
if( background == null || background instanceof UIResource ) {
|
||||||
parent.setBackground( UIManager.getColor( "control" ) );
|
if( SystemInfo.isMacOS ) {
|
||||||
|
// Setting window background on macOS immediately fills the whole window
|
||||||
|
// with that color, and slightly delayed, the Swing repaint manager
|
||||||
|
// repaints the actual window content. This results in some flashing
|
||||||
|
// when switching from a light to a dark theme (or vice versa).
|
||||||
|
// --> delay setting window background and immediately repaint window content
|
||||||
|
Runnable r = () -> {
|
||||||
|
parent.setBackground( UIManager.getColor( "control" ) );
|
||||||
|
c.paintImmediately( 0, 0, c.getWidth(), c.getHeight() );
|
||||||
|
};
|
||||||
|
|
||||||
|
// for class FlatAnimatedLafChange:
|
||||||
|
// if animated Laf change is in progress, set background color when
|
||||||
|
// animation has finished to avoid/reduce flashing
|
||||||
|
if( c.getClientProperty( "FlatLaf.internal.animatedLafChange" ) != null )
|
||||||
|
c.putClientProperty( "FlatLaf.internal.animatedLafChange.runWhenFinished", r );
|
||||||
|
else
|
||||||
|
EventQueue.invokeLater( r );
|
||||||
|
} else
|
||||||
|
parent.setBackground( UIManager.getColor( "control" ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// enable dark window appearance on macOS when running in JetBrains Runtime
|
macClearBackgroundForTranslucentWindow( c );
|
||||||
if( SystemInfo.isJetBrainsJVM && SystemInfo.isMacOS_10_14_Mojave_orLater )
|
|
||||||
c.putClientProperty( "jetbrains.awt.windowDarkAppearance", FlatLaf.isLafDark() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -178,55 +198,20 @@ public class FlatRootPaneUI
|
|||||||
protected void installListeners( JRootPane root ) {
|
protected void installListeners( JRootPane root ) {
|
||||||
super.installListeners( root );
|
super.installListeners( root );
|
||||||
|
|
||||||
if( SystemInfo.isJava_9_orLater ) {
|
if( SystemInfo.isMacFullWindowContentSupported )
|
||||||
// On HiDPI screens, where scaling is used, there may be white lines on the
|
macFullWindowContentListener = FullWindowContentSupport.macInstallListeners( root );
|
||||||
// bottom and on the right side of the window when it is initially shown.
|
macInstallWindowBackgroundListener( root );
|
||||||
// This is very disturbing in dark themes, but hard to notice in light themes.
|
|
||||||
// Seems to be a rounding issue when Swing adds dirty region of window
|
|
||||||
// using RepaintManager.nativeAddDirtyRegion().
|
|
||||||
//
|
|
||||||
// Note: Not using a HierarchyListener here, which would be much easier,
|
|
||||||
// because this causes problems with mouse clicks in heavy-weight popups.
|
|
||||||
// Instead, add a listener to the root pane that waits until it is added
|
|
||||||
// to a window, then add a component listener to the window.
|
|
||||||
// See: https://github.com/JFormDesigner/FlatLaf/issues/371
|
|
||||||
ancestorListener = e -> {
|
|
||||||
Object oldValue = e.getOldValue();
|
|
||||||
Object newValue = e.getNewValue();
|
|
||||||
if( newValue instanceof Window ) {
|
|
||||||
if( componentListener == null ) {
|
|
||||||
componentListener = new ComponentAdapter() {
|
|
||||||
@Override
|
|
||||||
public void componentShown( ComponentEvent e ) {
|
|
||||||
// add whole root pane to dirty regions when window is initially shown
|
|
||||||
root.getParent().repaint( root.getX(), root.getY(), root.getWidth(), root.getHeight() );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
((Window)newValue).addComponentListener( componentListener );
|
|
||||||
} else if( newValue == null && oldValue instanceof Window ) {
|
|
||||||
if( componentListener != null )
|
|
||||||
((Window)oldValue).removeComponentListener( componentListener );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
root.addPropertyChangeListener( "ancestor", ancestorListener );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void uninstallListeners( JRootPane root ) {
|
protected void uninstallListeners( JRootPane root ) {
|
||||||
super.uninstallListeners( root );
|
super.uninstallListeners( root );
|
||||||
|
|
||||||
if( SystemInfo.isJava_9_orLater ) {
|
if( SystemInfo.isMacFullWindowContentSupported ) {
|
||||||
if( componentListener != null ) {
|
FullWindowContentSupport.macUninstallListeners( root, macFullWindowContentListener );
|
||||||
Window window = SwingUtilities.windowForComponent( root );
|
macFullWindowContentListener = null;
|
||||||
if( window != null )
|
|
||||||
window.removeComponentListener( componentListener );
|
|
||||||
componentListener = null;
|
|
||||||
}
|
|
||||||
root.removePropertyChangeListener( "ancestor", ancestorListener );
|
|
||||||
ancestorListener = null;
|
|
||||||
}
|
}
|
||||||
|
macUninstallWindowBackgroundListener( root );
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 1.1.2 */
|
/** @since 1.1.2 */
|
||||||
@@ -306,19 +291,141 @@ public class FlatRootPaneUI
|
|||||||
|
|
||||||
// layer title pane under frame content layer to allow placing menu bar over title pane
|
// layer title pane under frame content layer to allow placing menu bar over title pane
|
||||||
protected final static Integer TITLE_PANE_LAYER = JLayeredPane.FRAME_CONTENT_LAYER - 1;
|
protected final static Integer TITLE_PANE_LAYER = JLayeredPane.FRAME_CONTENT_LAYER - 1;
|
||||||
|
private final static Integer TITLE_PANE_MOUSE_LAYER = JLayeredPane.FRAME_CONTENT_LAYER - 2;
|
||||||
|
private final static Integer WINDOW_TOP_BORDER_LAYER = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
// for fullWindowContent mode, layer title pane over frame content layer to allow placing title bar buttons over content
|
||||||
|
/** @since 3.4 */
|
||||||
|
protected final static Integer TITLE_PANE_FULL_WINDOW_CONTENT_LAYER = JLayeredPane.FRAME_CONTENT_LAYER + 1;
|
||||||
|
|
||||||
|
private Integer getLayerForTitlePane() {
|
||||||
|
return isFullWindowContent( rootPane ) ? TITLE_PANE_FULL_WINDOW_CONTENT_LAYER : TITLE_PANE_LAYER;
|
||||||
|
}
|
||||||
|
|
||||||
protected void setTitlePane( FlatTitlePane newTitlePane ) {
|
protected void setTitlePane( FlatTitlePane newTitlePane ) {
|
||||||
JLayeredPane layeredPane = rootPane.getLayeredPane();
|
JLayeredPane layeredPane = rootPane.getLayeredPane();
|
||||||
|
|
||||||
if( titlePane != null )
|
if( titlePane != null ) {
|
||||||
layeredPane.remove( titlePane );
|
layeredPane.remove( titlePane );
|
||||||
|
layeredPane.remove( titlePane.mouseLayer );
|
||||||
|
if( titlePane.windowTopBorderLayer != null )
|
||||||
|
layeredPane.remove( titlePane.windowTopBorderLayer );
|
||||||
|
}
|
||||||
|
|
||||||
if( newTitlePane != null )
|
if( newTitlePane != null ) {
|
||||||
layeredPane.add( newTitlePane, TITLE_PANE_LAYER );
|
layeredPane.add( newTitlePane, getLayerForTitlePane() );
|
||||||
|
layeredPane.add( newTitlePane.mouseLayer, TITLE_PANE_MOUSE_LAYER );
|
||||||
|
if( newTitlePane.windowTopBorderLayer != null && newTitlePane.isWindowTopBorderNeeded() && isFullWindowContent( rootPane ) )
|
||||||
|
layeredPane.add( newTitlePane.windowTopBorderLayer, WINDOW_TOP_BORDER_LAYER );
|
||||||
|
}
|
||||||
|
|
||||||
titlePane = newTitlePane;
|
titlePane = newTitlePane;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void macInstallFullWindowContentSupport() {
|
||||||
|
if( !SystemInfo.isMacOS )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// set window buttons spacing
|
||||||
|
if( isMacButtonsSpacingSupported() && rootPane.isDisplayable() ) {
|
||||||
|
int buttonsSpacing = FlatNativeMacLibrary.BUTTONS_SPACING_DEFAULT;
|
||||||
|
String value = (String) rootPane.getClientProperty( FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING );
|
||||||
|
if( value != null ) {
|
||||||
|
switch( value ) {
|
||||||
|
case FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING_MEDIUM:
|
||||||
|
buttonsSpacing = FlatNativeMacLibrary.BUTTONS_SPACING_MEDIUM;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING_LARGE:
|
||||||
|
buttonsSpacing = FlatNativeMacLibrary.BUTTONS_SPACING_LARGE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatNativeMacLibrary.setWindowButtonsSpacing( getParentWindow( rootPane ), buttonsSpacing );
|
||||||
|
}
|
||||||
|
|
||||||
|
// update buttons bounds client property
|
||||||
|
FullWindowContentSupport.macUpdateFullWindowContentButtonsBoundsProperty( rootPane );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void macUninstallFullWindowContentSupport() {
|
||||||
|
if( !SystemInfo.isMacOS )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// do not uninstall when switching to another FlatLaf theme
|
||||||
|
if( UIManager.getLookAndFeel() instanceof FlatLaf )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// reset window buttons spacing
|
||||||
|
if( isMacButtonsSpacingSupported() )
|
||||||
|
FlatNativeMacLibrary.setWindowButtonsSpacing( getParentWindow( rootPane ), FlatNativeMacLibrary.BUTTONS_SPACING_DEFAULT );
|
||||||
|
|
||||||
|
// remove buttons bounds client property
|
||||||
|
FullWindowContentSupport.macUninstallFullWindowContentButtonsBoundsProperty( rootPane );
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isMacButtonsSpacingSupported() {
|
||||||
|
return SystemInfo.isMacOS && SystemInfo.isJava_17_orLater && FlatNativeMacLibrary.isLoaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void macInstallWindowBackgroundListener( JRootPane c ) {
|
||||||
|
if( !SystemInfo.isMacOS )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Window window = getParentWindow( c );
|
||||||
|
if( window != null && macWindowBackgroundListener == null ) {
|
||||||
|
macWindowBackgroundListener = e -> macClearBackgroundForTranslucentWindow( c );
|
||||||
|
window.addPropertyChangeListener( "background", macWindowBackgroundListener );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void macUninstallWindowBackgroundListener( JRootPane c ) {
|
||||||
|
if( !SystemInfo.isMacOS )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Window window = getParentWindow( c );
|
||||||
|
if( window != null && macWindowBackgroundListener != null ) {
|
||||||
|
window.removePropertyChangeListener( "background", macWindowBackgroundListener );
|
||||||
|
macWindowBackgroundListener = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When setting window background to translucent color (alpha < 255),
|
||||||
|
* Swing paints that window translucent on Windows and Linux, but not on macOS.
|
||||||
|
* The reason for this is that FlatLaf sets the background color of the root pane,
|
||||||
|
* and Swing behaves a bit differently on macOS than on other platforms in that case.
|
||||||
|
* Other L&Fs do not set root pane background, which is {@code null} by default.
|
||||||
|
* <p>
|
||||||
|
* To fix this problem, set the root pane background to {@code null}
|
||||||
|
* if windows uses a translucent background.
|
||||||
|
*/
|
||||||
|
private void macClearBackgroundForTranslucentWindow( JRootPane c ) {
|
||||||
|
if( !SystemInfo.isMacOS )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Window window = getParentWindow( c );
|
||||||
|
if( window != null ) {
|
||||||
|
Color windowBackground = window.getBackground();
|
||||||
|
if( windowBackground != null &&
|
||||||
|
windowBackground.getAlpha() < 255 &&
|
||||||
|
c.getBackground() instanceof UIResource )
|
||||||
|
{
|
||||||
|
// clear root pane background
|
||||||
|
c.setBackground( null );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Window getParentWindow( JRootPane c ) {
|
||||||
|
// not using SwingUtilities.windowForComponent() or SwingUtilities.getWindowAncestor()
|
||||||
|
// here because root panes may be nested and used anywhere (e.g. in JInternalFrame)
|
||||||
|
// but we're only interested in the "root" root pane, which is a direct child of the window
|
||||||
|
Container parent = c.getParent();
|
||||||
|
return (parent instanceof Window) ? (Window) parent : null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void propertyChange( PropertyChangeEvent e ) {
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
super.propertyChange( e );
|
super.propertyChange( e );
|
||||||
@@ -362,14 +469,83 @@ public class FlatRootPaneUI
|
|||||||
if( titlePane != null )
|
if( titlePane != null )
|
||||||
titlePane.titleBarColorsChanged();
|
titlePane.titleBarColorsChanged();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.TITLE_BAR_HEIGHT:
|
||||||
|
if( titlePane != null )
|
||||||
|
titlePane.revalidate();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.FULL_WINDOW_CONTENT:
|
||||||
|
if( titlePane != null ) {
|
||||||
|
rootPane.getLayeredPane().setLayer( titlePane, getLayerForTitlePane() );
|
||||||
|
if( titlePane.windowTopBorderLayer != null ) {
|
||||||
|
JLayeredPane layeredPane = rootPane.getLayeredPane();
|
||||||
|
if( titlePane.isWindowTopBorderNeeded() && isFullWindowContent( rootPane ) )
|
||||||
|
layeredPane.add( titlePane.windowTopBorderLayer, WINDOW_TOP_BORDER_LAYER );
|
||||||
|
else
|
||||||
|
layeredPane.remove( titlePane.windowTopBorderLayer );
|
||||||
|
}
|
||||||
|
titlePane.updateIcon();
|
||||||
|
titlePane.updateVisibility();
|
||||||
|
titlePane.updateFullWindowContentButtonsBoundsProperty();
|
||||||
|
}
|
||||||
|
FullWindowContentSupport.revalidatePlaceholders( rootPane );
|
||||||
|
rootPane.revalidate();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_BOUNDS:
|
||||||
|
FullWindowContentSupport.revalidatePlaceholders( rootPane );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.GLASS_PANE_FULL_HEIGHT:
|
||||||
|
rootPane.revalidate();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.WINDOW_STYLE:
|
||||||
|
if( rootPane.isDisplayable() )
|
||||||
|
throw new IllegalComponentStateException( "The client property 'Window.style' must be set before the window becomes displayable." );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "ancestor":
|
||||||
|
if( e.getNewValue() instanceof Window ) {
|
||||||
|
if( titlePane != null && !Objects.equals( titlePane.windowStyle, FlatTitlePane.getWindowStyle( rootPane ) ) )
|
||||||
|
setTitlePane( createTitlePane() );
|
||||||
|
|
||||||
|
macClearBackgroundForTranslucentWindow( rootPane );
|
||||||
|
}
|
||||||
|
|
||||||
|
macUninstallWindowBackgroundListener( rootPane );
|
||||||
|
macInstallWindowBackgroundListener( rootPane );
|
||||||
|
|
||||||
|
// FlatNativeMacLibrary.setWindowButtonsSpacing() and
|
||||||
|
// FullWindowContentSupport.macUpdateFullWindowContentButtonsBoundsProperty()
|
||||||
|
// require a native window, but setting the client properties
|
||||||
|
// "apple.awt.fullWindowContent" or FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING
|
||||||
|
// is usually done before the native window is created
|
||||||
|
// --> try again when native window is created
|
||||||
|
if( e.getNewValue() instanceof Window )
|
||||||
|
macInstallFullWindowContentSupport();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING:
|
||||||
|
macInstallFullWindowContentSupport();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "apple.awt.fullWindowContent":
|
||||||
|
if( SystemInfo.isMacFullWindowContentSupported )
|
||||||
|
FullWindowContentSupport.macUpdateFullWindowContentButtonsBoundsProperty( rootPane );
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 3.4 */
|
||||||
|
protected static boolean isFullWindowContent( JRootPane rootPane ) {
|
||||||
|
return FlatClientProperties.clientPropertyBoolean( rootPane, FlatClientProperties.FULL_WINDOW_CONTENT, false );
|
||||||
|
}
|
||||||
|
|
||||||
protected static boolean isMenuBarEmbedded( JRootPane rootPane ) {
|
protected static boolean isMenuBarEmbedded( JRootPane rootPane ) {
|
||||||
RootPaneUI ui = rootPane.getUI();
|
FlatTitlePane titlePane = getTitlePane( rootPane );
|
||||||
return ui instanceof FlatRootPaneUI &&
|
return titlePane != null && titlePane.isMenuBarEmbedded();
|
||||||
((FlatRootPaneUI)ui).titlePane != null &&
|
|
||||||
((FlatRootPaneUI)ui).titlePane.isMenuBarEmbedded();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2.4 */
|
/** @since 2.4 */
|
||||||
@@ -405,23 +581,21 @@ public class FlatRootPaneUI
|
|||||||
private Dimension computeLayoutSize( Container parent, Function<Component, Dimension> getSizeFunc ) {
|
private Dimension computeLayoutSize( Container parent, Function<Component, Dimension> getSizeFunc ) {
|
||||||
JRootPane rootPane = (JRootPane) parent;
|
JRootPane rootPane = (JRootPane) parent;
|
||||||
|
|
||||||
Dimension titlePaneSize = (titlePane != null)
|
|
||||||
? getSizeFunc.apply( titlePane )
|
|
||||||
: new Dimension();
|
|
||||||
Dimension contentSize = (rootPane.getContentPane() != null)
|
Dimension contentSize = (rootPane.getContentPane() != null)
|
||||||
? getSizeFunc.apply( rootPane.getContentPane() )
|
? getSizeFunc.apply( rootPane.getContentPane() )
|
||||||
: rootPane.getSize();
|
: rootPane.getSize(); // same as in JRootPane.RootLayout.preferredLayoutSize()
|
||||||
|
|
||||||
int width = contentSize.width; // title pane width is not considered here
|
int width = contentSize.width; // title pane width is not considered here
|
||||||
int height = titlePaneSize.height + contentSize.height;
|
int height = contentSize.height;
|
||||||
|
if( titlePane != null && !isFullWindowContent( rootPane ) )
|
||||||
|
height += getSizeFunc.apply( titlePane ).height;
|
||||||
if( titlePane == null || !titlePane.isMenuBarEmbedded() ) {
|
if( titlePane == null || !titlePane.isMenuBarEmbedded() ) {
|
||||||
JMenuBar menuBar = rootPane.getJMenuBar();
|
JMenuBar menuBar = rootPane.getJMenuBar();
|
||||||
Dimension menuBarSize = (menuBar != null && menuBar.isVisible())
|
if( menuBar != null && menuBar.isVisible() ) {
|
||||||
? getSizeFunc.apply( menuBar )
|
Dimension menuBarSize = getSizeFunc.apply( menuBar );
|
||||||
: new Dimension();
|
width = Math.max( width, menuBarSize.width );
|
||||||
|
height += menuBarSize.height;
|
||||||
width = Math.max( width, menuBarSize.width );
|
}
|
||||||
height += menuBarSize.height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Insets insets = rootPane.getInsets();
|
Insets insets = rootPane.getInsets();
|
||||||
@@ -442,18 +616,44 @@ public class FlatRootPaneUI
|
|||||||
int width = rootPane.getWidth() - insets.left - insets.right;
|
int width = rootPane.getWidth() - insets.left - insets.right;
|
||||||
int height = rootPane.getHeight() - insets.top - insets.bottom;
|
int height = rootPane.getHeight() - insets.top - insets.bottom;
|
||||||
|
|
||||||
|
// layered pane
|
||||||
if( rootPane.getLayeredPane() != null )
|
if( rootPane.getLayeredPane() != null )
|
||||||
rootPane.getLayeredPane().setBounds( x, y, width, height );
|
rootPane.getLayeredPane().setBounds( x, y, width, height );
|
||||||
if( rootPane.getGlassPane() != null )
|
|
||||||
rootPane.getGlassPane().setBounds( x, y, width, height );
|
|
||||||
|
|
||||||
|
// title pane (is a child of layered pane)
|
||||||
int nextY = 0;
|
int nextY = 0;
|
||||||
if( titlePane != null ) {
|
if( titlePane != null ) {
|
||||||
int prefHeight = !isFullScreen ? titlePane.getPreferredSize().height : 0;
|
int prefHeight = !isFullScreen ? titlePane.getPreferredSize().height : 0;
|
||||||
titlePane.setBounds( 0, 0, width, prefHeight );
|
boolean isFullWindowContent = isFullWindowContent( rootPane );
|
||||||
nextY += prefHeight;
|
if( isFullWindowContent && !UIManager.getBoolean( FlatTitlePane.KEY_DEBUG_SHOW_RECTANGLES ) ) {
|
||||||
|
// place title bar into top-right corner
|
||||||
|
int tw = Math.min( titlePane.getPreferredSize().width, width );
|
||||||
|
int tx = titlePane.getComponentOrientation().isLeftToRight() ? width - tw : 0;
|
||||||
|
titlePane.setBounds( tx, 0, tw, prefHeight );
|
||||||
|
} else
|
||||||
|
titlePane.setBounds( 0, 0, width, prefHeight );
|
||||||
|
|
||||||
|
titlePane.mouseLayer.setBounds( 0, 0, width, prefHeight );
|
||||||
|
if( titlePane.windowTopBorderLayer != null ) {
|
||||||
|
boolean show = isFullWindowContent && !titlePane.isWindowMaximized() && !isFullScreen;
|
||||||
|
if( show )
|
||||||
|
titlePane.windowTopBorderLayer.setBounds( 0, 0, width, 1 );
|
||||||
|
titlePane.windowTopBorderLayer.setVisible( show );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !isFullWindowContent )
|
||||||
|
nextY += prefHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// glass pane
|
||||||
|
if( rootPane.getGlassPane() != null ) {
|
||||||
|
boolean fullHeight = FlatClientProperties.clientPropertyBoolean(
|
||||||
|
rootPane, FlatClientProperties.GLASS_PANE_FULL_HEIGHT, false );
|
||||||
|
int offset = fullHeight ? 0 : nextY;
|
||||||
|
rootPane.getGlassPane().setBounds( x, y + offset, width, height - offset );
|
||||||
|
}
|
||||||
|
|
||||||
|
// menu bar (is a child of layered pane)
|
||||||
JMenuBar menuBar = rootPane.getJMenuBar();
|
JMenuBar menuBar = rootPane.getJMenuBar();
|
||||||
if( menuBar != null && menuBar.isVisible() ) {
|
if( menuBar != null && menuBar.isVisible() ) {
|
||||||
boolean embedded = !isFullScreen && titlePane != null && titlePane.isMenuBarEmbedded();
|
boolean embedded = !isFullScreen && titlePane != null && titlePane.isMenuBarEmbedded();
|
||||||
@@ -461,16 +661,28 @@ public class FlatRootPaneUI
|
|||||||
titlePane.validate();
|
titlePane.validate();
|
||||||
menuBar.setBounds( titlePane.getMenuBarBounds() );
|
menuBar.setBounds( titlePane.getMenuBarBounds() );
|
||||||
} else {
|
} else {
|
||||||
|
int mx = 0;
|
||||||
|
int mw = width;
|
||||||
|
if( titlePane != null && isFullWindowContent( rootPane ) ) {
|
||||||
|
// make menu bar width smaller to avoid that it overlaps title bar buttons
|
||||||
|
int tw = Math.min( titlePane.getPreferredSize().width, width );
|
||||||
|
mw -= tw;
|
||||||
|
if( !titlePane.getComponentOrientation().isLeftToRight() )
|
||||||
|
mx = tw;
|
||||||
|
}
|
||||||
|
|
||||||
Dimension prefSize = menuBar.getPreferredSize();
|
Dimension prefSize = menuBar.getPreferredSize();
|
||||||
menuBar.setBounds( 0, nextY, width, prefSize.height );
|
menuBar.setBounds( mx, nextY, mw, prefSize.height );
|
||||||
nextY += prefSize.height;
|
nextY += prefSize.height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// content pane (is a child of layered pane)
|
||||||
Container contentPane = rootPane.getContentPane();
|
Container contentPane = rootPane.getContentPane();
|
||||||
if( contentPane != null )
|
if( contentPane != null )
|
||||||
contentPane.setBounds( 0, nextY, width, Math.max( height - nextY, 0 ) );
|
contentPane.setBounds( 0, nextY, width, Math.max( height - nextY, 0 ) );
|
||||||
|
|
||||||
|
// title pane
|
||||||
if( titlePane != null )
|
if( titlePane != null )
|
||||||
titlePane.menuBarLayouted();
|
titlePane.menuBarLayouted();
|
||||||
}
|
}
|
||||||
@@ -478,7 +690,7 @@ public class FlatRootPaneUI
|
|||||||
@Override
|
@Override
|
||||||
public void invalidateLayout( Container parent ) {
|
public void invalidateLayout( Container parent ) {
|
||||||
if( titlePane != null )
|
if( titlePane != null )
|
||||||
titlePane.menuBarChanged();
|
titlePane.menuBarInvalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -498,7 +710,7 @@ public class FlatRootPaneUI
|
|||||||
* Window border used for non-native window decorations.
|
* Window border used for non-native window decorations.
|
||||||
*/
|
*/
|
||||||
public static class FlatWindowBorder
|
public static class FlatWindowBorder
|
||||||
extends BorderUIResource.EmptyBorderUIResource
|
extends FlatEmptyBorder
|
||||||
{
|
{
|
||||||
protected final Color activeBorderColor = UIManager.getColor( "RootPane.activeBorderColor" );
|
protected final Color activeBorderColor = UIManager.getColor( "RootPane.activeBorderColor" );
|
||||||
protected final Color inactiveBorderColor = UIManager.getColor( "RootPane.inactiveBorderColor" );
|
protected final Color inactiveBorderColor = UIManager.getColor( "RootPane.inactiveBorderColor" );
|
||||||
@@ -531,12 +743,15 @@ public class FlatRootPaneUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void paintImpl( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
|
private void paintImpl( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
|
||||||
g.drawRect( x, y, width - 1, height - 1 );
|
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
||||||
|
float lineWidth = (float) (UIScale.scale( 1f ) * scaleFactor);
|
||||||
|
g.fill( FlatUIUtils.createRectangle( x, y, width, height, lineWidth ) );
|
||||||
|
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isWindowMaximized( Component c ) {
|
protected boolean isWindowMaximized( Component c ) {
|
||||||
Container parent = c.getParent();
|
Container parent = c.getParent();
|
||||||
return parent instanceof Frame && (((Frame)parent).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0;
|
return parent instanceof Frame && (((Frame)parent).getExtendedState() & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_BOTH;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
|||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
@@ -212,14 +213,14 @@ public class FlatScrollBarUI
|
|||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
case FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS:
|
case FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS:
|
||||||
scrollbar.revalidate();
|
scrollbar.revalidate();
|
||||||
scrollbar.repaint();
|
HiDPIUtils.repaint( scrollbar );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FlatClientProperties.STYLE:
|
case FlatClientProperties.STYLE:
|
||||||
case FlatClientProperties.STYLE_CLASS:
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
installStyle();
|
installStyle();
|
||||||
scrollbar.revalidate();
|
scrollbar.revalidate();
|
||||||
scrollbar.repaint();
|
HiDPIUtils.repaint( scrollbar );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "componentOrientation":
|
case "componentOrientation":
|
||||||
@@ -245,7 +246,7 @@ public class FlatScrollBarUI
|
|||||||
// because scroll bars do not receive mouse exited event.
|
// because scroll bars do not receive mouse exited event.
|
||||||
// The scroll pane, including its scroll bars, is not part
|
// The scroll pane, including its scroll bars, is not part
|
||||||
// of the component hierarchy and does not receive mouse events
|
// of the component hierarchy and does not receive mouse events
|
||||||
// directly. Instead LWComponentPeer receives mouse events
|
// directly. Instead, LWComponentPeer receives mouse events
|
||||||
// and delegates them to peers, but entered/exited events
|
// and delegates them to peers, but entered/exited events
|
||||||
// are sent only for the whole scroll pane.
|
// are sent only for the whole scroll pane.
|
||||||
// Exited event is only sent when mouse leaves scroll pane.
|
// Exited event is only sent when mouse leaves scroll pane.
|
||||||
@@ -492,7 +493,7 @@ public class FlatScrollBarUI
|
|||||||
|
|
||||||
private void repaint() {
|
private void repaint() {
|
||||||
if( scrollbar.isEnabled() )
|
if( scrollbar.isEnabled() )
|
||||||
scrollbar.repaint();
|
HiDPIUtils.repaint( scrollbar );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 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
|
||||||
|
*
|
||||||
|
* http://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.Component;
|
||||||
|
import java.awt.Insets;
|
||||||
|
import javax.swing.JList;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JTable;
|
||||||
|
import javax.swing.JTree;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.text.JTextComponent;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Border for {@link javax.swing.JScrollPane}.
|
||||||
|
*
|
||||||
|
* @uiDefault ScrollPane.arc int
|
||||||
|
* @uiDefault ScrollPane.List.arc int
|
||||||
|
* @uiDefault ScrollPane.Table.arc int
|
||||||
|
* @uiDefault ScrollPane.TextComponent.arc int
|
||||||
|
* @uiDefault ScrollPane.Tree.arc int
|
||||||
|
|
||||||
|
* @author Karl Tauber
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
public class FlatScrollPaneBorder
|
||||||
|
extends FlatBorder
|
||||||
|
{
|
||||||
|
@Styleable protected int arc = UIManager.getInt( "ScrollPane.arc" );
|
||||||
|
|
||||||
|
private boolean isArcStyled;
|
||||||
|
private final int listArc = FlatUIUtils.getUIInt( "ScrollPane.List.arc", -1 );
|
||||||
|
private final int tableArc = FlatUIUtils.getUIInt( "ScrollPane.Table.arc", -1 );
|
||||||
|
private final int textComponentArc = FlatUIUtils.getUIInt( "ScrollPane.TextComponent.arc", -1 );
|
||||||
|
private final int treeArc = FlatUIUtils.getUIInt( "ScrollPane.Tree.arc", -1 );
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object applyStyleProperty( String key, Object value ) {
|
||||||
|
Object oldValue = super.applyStyleProperty( key, value );
|
||||||
|
|
||||||
|
if( "arc".equals( key ) )
|
||||||
|
isArcStyled = true;
|
||||||
|
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
|
insets = super.getBorderInsets( c, insets );
|
||||||
|
|
||||||
|
// if view is rounded, increase left and right insets to avoid that the viewport
|
||||||
|
// is painted over the rounded border on the corners
|
||||||
|
int padding = getLeftRightPadding( c );
|
||||||
|
if( padding > 0 ) {
|
||||||
|
insets.left += padding;
|
||||||
|
insets.right += padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
return insets;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getArc( Component c ) {
|
||||||
|
if( isCellEditor( c ) )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if( isArcStyled )
|
||||||
|
return arc;
|
||||||
|
|
||||||
|
if( c instanceof JScrollPane ) {
|
||||||
|
Component view = FlatScrollPaneUI.getView( (JScrollPane) c );
|
||||||
|
if( listArc >= 0 && view instanceof JList )
|
||||||
|
return listArc;
|
||||||
|
if( tableArc >= 0 && view instanceof JTable )
|
||||||
|
return tableArc;
|
||||||
|
if( textComponentArc >= 0&& view instanceof JTextComponent )
|
||||||
|
return textComponentArc;
|
||||||
|
if( treeArc >= 0 && view instanceof JTree )
|
||||||
|
return treeArc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return arc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the scaled left/right padding used when arc is larger than zero.
|
||||||
|
* <p>
|
||||||
|
* This is the distance from the inside of the left border to the left side of the view component.
|
||||||
|
* On the right side, this is the distance between the right side of the view component and
|
||||||
|
* the vertical scrollbar. Or the inside of the right border if the scrollbar is hidden.
|
||||||
|
*/
|
||||||
|
public int getLeftRightPadding( Component c ) {
|
||||||
|
// Subtract lineWidth from radius because radius is given for the outside
|
||||||
|
// of the painted line, but insets from super already include lineWidth.
|
||||||
|
// Reduce padding by 10% to make padding slightly smaller because it is not recognizable
|
||||||
|
// when the view is minimally painted over the beginning of the border curve.
|
||||||
|
int arc = getArc( c );
|
||||||
|
return (arc > 0)
|
||||||
|
? Math.max( Math.round( UIScale.scale( ((arc / 2f) - getLineWidth( c )) * 0.9f ) ), 0 )
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,9 +17,12 @@
|
|||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
|
import java.awt.Container;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.KeyboardFocusManager;
|
import java.awt.KeyboardFocusManager;
|
||||||
|
import java.awt.LayoutManager;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.event.ContainerEvent;
|
import java.awt.event.ContainerEvent;
|
||||||
import java.awt.event.ContainerListener;
|
import java.awt.event.ContainerListener;
|
||||||
@@ -41,16 +44,20 @@ import javax.swing.JTree;
|
|||||||
import javax.swing.JViewport;
|
import javax.swing.JViewport;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.ScrollPaneConstants;
|
import javax.swing.ScrollPaneConstants;
|
||||||
|
import javax.swing.ScrollPaneLayout;
|
||||||
import javax.swing.Scrollable;
|
import javax.swing.Scrollable;
|
||||||
import javax.swing.SwingConstants;
|
import javax.swing.SwingConstants;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.border.Border;
|
||||||
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.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JScrollPane}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JScrollPane}.
|
||||||
@@ -97,7 +104,13 @@ public class FlatScrollPaneUI
|
|||||||
super.installUI( c );
|
super.installUI( c );
|
||||||
|
|
||||||
int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
||||||
LookAndFeel.installProperty( c, "opaque", focusWidth == 0 );
|
int arc = UIManager.getInt( "ScrollPane.arc" );
|
||||||
|
LookAndFeel.installProperty( c, "opaque", focusWidth == 0 && arc == 0 );
|
||||||
|
|
||||||
|
// install layout manager
|
||||||
|
LayoutManager layout = c.getLayout();
|
||||||
|
if( layout != null && layout.getClass() == ScrollPaneLayout.UIResource.class )
|
||||||
|
c.setLayout( createScrollPaneLayout() );
|
||||||
|
|
||||||
installStyle();
|
installStyle();
|
||||||
|
|
||||||
@@ -108,6 +121,10 @@ public class FlatScrollPaneUI
|
|||||||
public void uninstallUI( JComponent c ) {
|
public void uninstallUI( JComponent c ) {
|
||||||
MigLayoutVisualPadding.uninstall( scrollpane );
|
MigLayoutVisualPadding.uninstall( scrollpane );
|
||||||
|
|
||||||
|
// uninstall layout manager
|
||||||
|
if( c.getLayout() instanceof FlatScrollPaneLayout )
|
||||||
|
c.setLayout( new ScrollPaneLayout.UIResource() );
|
||||||
|
|
||||||
super.uninstallUI( c );
|
super.uninstallUI( c );
|
||||||
|
|
||||||
oldStyleValues = null;
|
oldStyleValues = null;
|
||||||
@@ -130,6 +147,13 @@ public class FlatScrollPaneUI
|
|||||||
handler = null;
|
handler = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
protected FlatScrollPaneLayout createScrollPaneLayout() {
|
||||||
|
return new FlatScrollPaneLayout();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected MouseWheelListener createMouseWheelListener() {
|
protected MouseWheelListener createMouseWheelListener() {
|
||||||
MouseWheelListener superListener = super.createMouseWheelListener();
|
MouseWheelListener superListener = super.createMouseWheelListener();
|
||||||
@@ -186,7 +210,7 @@ public class FlatScrollPaneUI
|
|||||||
|
|
||||||
// Use (0, 0) view position to obtain a constant unit increment of first item.
|
// Use (0, 0) view position to obtain a constant unit increment of first item.
|
||||||
// Unit increment may be different for each item.
|
// Unit increment may be different for each item.
|
||||||
Rectangle visibleRect = new Rectangle( viewport.getViewSize() );
|
Rectangle visibleRect = new Rectangle( viewport.getExtentSize() );
|
||||||
unitIncrement = scrollable.getScrollableUnitIncrement( visibleRect, orientation, 1 );
|
unitIncrement = scrollable.getScrollableUnitIncrement( visibleRect, orientation, 1 );
|
||||||
|
|
||||||
if( unitIncrement > 0 ) {
|
if( unitIncrement > 0 ) {
|
||||||
@@ -274,11 +298,11 @@ public class FlatScrollPaneUI
|
|||||||
JScrollBar hsb = scrollpane.getHorizontalScrollBar();
|
JScrollBar hsb = scrollpane.getHorizontalScrollBar();
|
||||||
if( vsb != null ) {
|
if( vsb != null ) {
|
||||||
vsb.revalidate();
|
vsb.revalidate();
|
||||||
vsb.repaint();
|
HiDPIUtils.repaint( vsb );
|
||||||
}
|
}
|
||||||
if( hsb != null ) {
|
if( hsb != null ) {
|
||||||
hsb.revalidate();
|
hsb.revalidate();
|
||||||
hsb.repaint();
|
HiDPIUtils.repaint( hsb );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -290,8 +314,7 @@ public class FlatScrollPaneUI
|
|||||||
Object corner = e.getNewValue();
|
Object corner = e.getNewValue();
|
||||||
if( corner instanceof JButton &&
|
if( corner instanceof JButton &&
|
||||||
((JButton)corner).getBorder() instanceof FlatButtonBorder &&
|
((JButton)corner).getBorder() instanceof FlatButtonBorder &&
|
||||||
scrollpane.getViewport() != null &&
|
getView( scrollpane ) instanceof JTable )
|
||||||
scrollpane.getViewport().getView() instanceof JTable )
|
|
||||||
{
|
{
|
||||||
((JButton)corner).setBorder( BorderFactory.createEmptyBorder() );
|
((JButton)corner).setBorder( BorderFactory.createEmptyBorder() );
|
||||||
((JButton)corner).setFocusable( false );
|
((JButton)corner).setFocusable( false );
|
||||||
@@ -299,14 +322,26 @@ public class FlatScrollPaneUI
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case FlatClientProperties.OUTLINE:
|
case FlatClientProperties.OUTLINE:
|
||||||
scrollpane.repaint();
|
HiDPIUtils.repaint( scrollpane );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FlatClientProperties.STYLE:
|
case FlatClientProperties.STYLE:
|
||||||
case FlatClientProperties.STYLE_CLASS:
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
installStyle();
|
installStyle();
|
||||||
scrollpane.revalidate();
|
scrollpane.revalidate();
|
||||||
scrollpane.repaint();
|
HiDPIUtils.repaint( scrollpane );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "border":
|
||||||
|
Object newBorder = e.getNewValue();
|
||||||
|
if( newBorder != null && newBorder == UIManager.getBorder( "Table.scrollPaneBorder" ) ) {
|
||||||
|
// JTable.configureEnclosingScrollPaneUI() replaces the scrollpane border
|
||||||
|
// with another one --> re-apply style on new border
|
||||||
|
borderShared = null;
|
||||||
|
installStyle();
|
||||||
|
scrollpane.revalidate();
|
||||||
|
HiDPIUtils.repaint( scrollpane );
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -334,9 +369,10 @@ public class FlatScrollPaneUI
|
|||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
protected Object applyStyleProperty( String key, Object value ) {
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
if( key.equals( "focusWidth" ) ) {
|
if( key.equals( "focusWidth" ) || key.equals( "arc" ) ) {
|
||||||
int focusWidth = (value instanceof Integer) ? (int) value : UIManager.getInt( "Component.focusWidth" );
|
int focusWidth = (value instanceof Integer) ? (int) value : UIManager.getInt( "Component.focusWidth" );
|
||||||
LookAndFeel.installProperty( scrollpane, "opaque", focusWidth == 0 );
|
int arc = (value instanceof Integer) ? (int) value : UIManager.getInt( "ScrollPane.arc" );
|
||||||
|
LookAndFeel.installProperty( scrollpane, "opaque", focusWidth == 0 && arc == 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( borderShared == null )
|
if( borderShared == null )
|
||||||
@@ -360,8 +396,8 @@ public class FlatScrollPaneUI
|
|||||||
protected void updateViewport( PropertyChangeEvent e ) {
|
protected void updateViewport( PropertyChangeEvent e ) {
|
||||||
super.updateViewport( e );
|
super.updateViewport( e );
|
||||||
|
|
||||||
JViewport oldViewport = (JViewport) (e.getOldValue());
|
JViewport oldViewport = (JViewport) e.getOldValue();
|
||||||
JViewport newViewport = (JViewport) (e.getNewValue());
|
JViewport newViewport = (JViewport) e.getNewValue();
|
||||||
|
|
||||||
removeViewportListeners( oldViewport );
|
removeViewportListeners( oldViewport );
|
||||||
addViewportListeners( newViewport );
|
addViewportListeners( newViewport );
|
||||||
@@ -402,13 +438,46 @@ public class FlatScrollPaneUI
|
|||||||
c.getHeight() - insets.top - insets.bottom );
|
c.getHeight() - insets.top - insets.bottom );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if view is rounded, paint rounded background with view background color
|
||||||
|
// to ensure that free areas at left and right have same color as view
|
||||||
|
Component view;
|
||||||
|
float arc = getBorderArc( scrollpane );
|
||||||
|
if( arc > 0 && (view = getView( scrollpane )) != null ) {
|
||||||
|
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
|
||||||
|
|
||||||
|
g.setColor( view.getBackground() );
|
||||||
|
|
||||||
|
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
||||||
|
FlatUIUtils.paintComponentBackground( (Graphics2D) g, 0, 0, c.getWidth(), c.getHeight(), focusWidth, arc );
|
||||||
|
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||||
|
}
|
||||||
|
|
||||||
paint( g, c );
|
paint( g, c );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paint( Graphics g, JComponent c ) {
|
||||||
|
Border viewportBorder = scrollpane.getViewportBorder();
|
||||||
|
if( viewportBorder != null ) {
|
||||||
|
Rectangle r = scrollpane.getViewportBorderBounds();
|
||||||
|
int padding = getBorderLeftRightPadding( scrollpane );
|
||||||
|
JScrollBar vsb = scrollpane.getVerticalScrollBar();
|
||||||
|
if( padding > 0 &&
|
||||||
|
vsb != null && vsb.isVisible() &&
|
||||||
|
scrollpane.getLayout() instanceof FlatScrollPaneLayout &&
|
||||||
|
((FlatScrollPaneLayout)scrollpane.getLayout()).canIncreaseViewportWidth( scrollpane ) )
|
||||||
|
{
|
||||||
|
boolean ltr = scrollpane.getComponentOrientation().isLeftToRight();
|
||||||
|
int extraWidth = Math.min( padding, vsb.getWidth() );
|
||||||
|
viewportBorder.paintBorder( scrollpane, g, r.x - (ltr ? 0 : extraWidth), r.y, r.width + extraWidth, r.height );
|
||||||
|
} else
|
||||||
|
viewportBorder.paintBorder( scrollpane, g, r.x, r.y, r.width, r.height );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @since 1.3 */
|
/** @since 1.3 */
|
||||||
public static boolean isPermanentFocusOwner( JScrollPane scrollPane ) {
|
public static boolean isPermanentFocusOwner( JScrollPane scrollPane ) {
|
||||||
JViewport viewport = scrollPane.getViewport();
|
Component view = getView( scrollPane );
|
||||||
Component view = (viewport != null) ? viewport.getView() : null;
|
|
||||||
if( view == null )
|
if( view == null )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -428,6 +497,25 @@ public class FlatScrollPaneUI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Component getView( JScrollPane scrollPane ) {
|
||||||
|
JViewport viewport = scrollPane.getViewport();
|
||||||
|
return (viewport != null) ? viewport.getView() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float getBorderArc( JScrollPane scrollPane ) {
|
||||||
|
Border border = scrollPane.getBorder();
|
||||||
|
return (border instanceof FlatScrollPaneBorder)
|
||||||
|
? UIScale.scale( (float) ((FlatScrollPaneBorder)border).getArc( scrollPane ) )
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getBorderLeftRightPadding( JScrollPane scrollPane ) {
|
||||||
|
Border border = scrollPane.getBorder();
|
||||||
|
return (border instanceof FlatScrollPaneBorder)
|
||||||
|
? ((FlatScrollPaneBorder)border).getLeftRightPadding( scrollPane )
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
|
||||||
//---- class Handler ------------------------------------------------------
|
//---- class Handler ------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -450,13 +538,71 @@ public class FlatScrollPaneUI
|
|||||||
@Override
|
@Override
|
||||||
public void focusGained( FocusEvent e ) {
|
public void focusGained( FocusEvent e ) {
|
||||||
// necessary to update focus border
|
// necessary to update focus border
|
||||||
scrollpane.repaint();
|
if( scrollpane.getBorder() instanceof FlatBorder )
|
||||||
|
HiDPIUtils.repaint( scrollpane );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void focusLost( FocusEvent e ) {
|
public void focusLost( FocusEvent e ) {
|
||||||
// necessary to update focus border
|
// necessary to update focus border
|
||||||
scrollpane.repaint();
|
if( scrollpane.getBorder() instanceof FlatBorder )
|
||||||
|
HiDPIUtils.repaint( scrollpane );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class FlatScrollPaneLayout -----------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 3.3
|
||||||
|
*/
|
||||||
|
protected static class FlatScrollPaneLayout
|
||||||
|
extends ScrollPaneLayout.UIResource
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void layoutContainer( Container parent ) {
|
||||||
|
super.layoutContainer( parent );
|
||||||
|
|
||||||
|
JScrollPane scrollPane = (JScrollPane) parent;
|
||||||
|
int padding = getBorderLeftRightPadding( scrollPane );
|
||||||
|
if( padding > 0 && vsb != null && vsb.isVisible() ) {
|
||||||
|
// move vertical scrollbar to trailing edge
|
||||||
|
Insets insets = scrollPane.getInsets();
|
||||||
|
Rectangle r = vsb.getBounds();
|
||||||
|
int y = Math.max( r.y, insets.top + padding );
|
||||||
|
int y2 = Math.min( r.y + r.height, scrollPane.getHeight() - insets.bottom - padding );
|
||||||
|
boolean ltr = scrollPane.getComponentOrientation().isLeftToRight();
|
||||||
|
|
||||||
|
vsb.setBounds( r.x + (ltr ? padding : -padding), y, r.width, y2 - y );
|
||||||
|
|
||||||
|
// increase width of viewport, column header and horizontal scrollbar
|
||||||
|
if( canIncreaseViewportWidth( scrollPane ) ) {
|
||||||
|
int extraWidth = Math.min( padding, vsb.getWidth() );
|
||||||
|
resizeViewport( viewport, extraWidth, ltr );
|
||||||
|
resizeViewport( colHead, extraWidth, ltr );
|
||||||
|
resizeViewport( hsb, extraWidth, ltr );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean canIncreaseViewportWidth( JScrollPane scrollPane ) {
|
||||||
|
return scrollPane.getComponentOrientation().isLeftToRight()
|
||||||
|
? !isCornerVisible( upperRight ) && !isCornerVisible( lowerRight )
|
||||||
|
: !isCornerVisible( upperLeft ) && !isCornerVisible( lowerLeft );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isCornerVisible( Component corner ) {
|
||||||
|
return corner != null &&
|
||||||
|
corner.getWidth() > 0 &&
|
||||||
|
corner.getHeight() > 0 &&
|
||||||
|
corner.isVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void resizeViewport( Component c, int extraWidth, boolean ltr ) {
|
||||||
|
if( c == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Rectangle vr = c.getBounds();
|
||||||
|
c.setBounds( vr.x - (ltr ? 0 : extraWidth), vr.y, vr.width + extraWidth, vr.height );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import javax.swing.plaf.basic.BasicSeparatorUI;
|
|||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,7 +135,7 @@ public class FlatSeparatorUI
|
|||||||
} else
|
} else
|
||||||
installStyle( s );
|
installStyle( s );
|
||||||
s.revalidate();
|
s.revalidate();
|
||||||
s.repaint();
|
HiDPIUtils.repaint( s );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ import java.awt.Graphics2D;
|
|||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.Shape;
|
import java.awt.Shape;
|
||||||
|
import java.awt.event.FocusEvent;
|
||||||
|
import java.awt.event.FocusListener;
|
||||||
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;
|
||||||
@@ -191,6 +193,23 @@ public class FlatSliderUI
|
|||||||
return new FlatTrackListener();
|
return new FlatTrackListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected FocusListener createFocusListener( JSlider slider ) {
|
||||||
|
return new BasicSliderUI.FocusHandler() {
|
||||||
|
@Override
|
||||||
|
public void focusGained( FocusEvent e ) {
|
||||||
|
super.focusGained( e );
|
||||||
|
HiDPIUtils.repaint( slider );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void focusLost( FocusEvent e ) {
|
||||||
|
super.focusLost( e );
|
||||||
|
HiDPIUtils.repaint( slider );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PropertyChangeListener createPropertyChangeListener( JSlider slider ) {
|
protected PropertyChangeListener createPropertyChangeListener( JSlider slider ) {
|
||||||
return FlatStylingSupport.createPropertyChangeListener( slider, this::installStyle,
|
return FlatStylingSupport.createPropertyChangeListener( slider, this::installStyle,
|
||||||
@@ -208,7 +227,13 @@ public class FlatSliderUI
|
|||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
protected void applyStyle( Object style ) {
|
protected void applyStyle( Object style ) {
|
||||||
|
Dimension oldThumbSize = thumbSize;
|
||||||
|
int oldFocusWidth = focusWidth;
|
||||||
|
|
||||||
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||||
|
|
||||||
|
if( !thumbSize.equals( oldThumbSize ) || focusWidth != oldFocusWidth )
|
||||||
|
calculateGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
@@ -422,7 +447,7 @@ debug*/
|
|||||||
Color thumbColor, Color thumbBorderColor, Color focusedColor, float thumbBorderWidth, 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 != (int) systemScaleFactor ) {
|
||||||
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
|
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
|
||||||
HiDPIUtils.paintAtScale1x( (Graphics2D) g, thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height,
|
HiDPIUtils.paintAtScale1x( (Graphics2D) g, thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height,
|
||||||
(g2d, x2, y2, width2, height2, scaleFactor) -> {
|
(g2d, x2, y2, width2, height2, scaleFactor) -> {
|
||||||
@@ -579,15 +604,15 @@ debug*/
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setThumbLocation( int x, int y ) {
|
public void setThumbLocation( int x, int y ) {
|
||||||
|
// set new thumb location and compute union of old and new thumb bounds
|
||||||
|
Rectangle r = new Rectangle( thumbRect );
|
||||||
|
thumbRect.setLocation( x, y );
|
||||||
|
SwingUtilities.computeUnion( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, r );
|
||||||
|
|
||||||
if( !isRoundThumb() ) {
|
if( !isRoundThumb() ) {
|
||||||
// the needle of the directional thumb is painted outside of thumbRect
|
// the needle of the directional thumb is painted outside of thumbRect
|
||||||
// --> must increase repaint rectangle
|
// --> must increase repaint rectangle
|
||||||
|
|
||||||
// set new thumb location and compute union of old and new thumb bounds
|
|
||||||
Rectangle r = new Rectangle( thumbRect );
|
|
||||||
thumbRect.setLocation( x, y );
|
|
||||||
SwingUtilities.computeUnion( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, r );
|
|
||||||
|
|
||||||
// increase union rectangle for repaint
|
// increase union rectangle for repaint
|
||||||
int extra = (int) Math.ceil( UIScale.scale( focusWidth ) * 0.4142f );
|
int extra = (int) Math.ceil( UIScale.scale( focusWidth ) * 0.4142f );
|
||||||
if( slider.getOrientation() == JSlider.HORIZONTAL )
|
if( slider.getOrientation() == JSlider.HORIZONTAL )
|
||||||
@@ -597,10 +622,9 @@ debug*/
|
|||||||
if( !slider.getComponentOrientation().isLeftToRight() )
|
if( !slider.getComponentOrientation().isLeftToRight() )
|
||||||
r.x -= extra;
|
r.x -= extra;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
slider.repaint( r );
|
HiDPIUtils.repaint( slider, r );
|
||||||
} else
|
|
||||||
super.setThumbLocation( x, y );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class FlatTrackListener --------------------------------------------
|
//---- class FlatTrackListener --------------------------------------------
|
||||||
@@ -688,21 +712,21 @@ debug*/
|
|||||||
!UIManager.getBoolean( "Slider.snapToTicksOnReleased" ) )
|
!UIManager.getBoolean( "Slider.snapToTicksOnReleased" ) )
|
||||||
{
|
{
|
||||||
calculateThumbLocation();
|
calculateThumbLocation();
|
||||||
slider.repaint();
|
HiDPIUtils.repaint( slider );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setThumbHover( boolean hover ) {
|
protected void setThumbHover( boolean hover ) {
|
||||||
if( hover != thumbHover ) {
|
if( hover != thumbHover ) {
|
||||||
thumbHover = hover;
|
thumbHover = hover;
|
||||||
slider.repaint( thumbRect );
|
HiDPIUtils.repaint( slider, thumbRect );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setThumbPressed( boolean pressed ) {
|
protected void setThumbPressed( boolean pressed ) {
|
||||||
if( pressed != thumbPressed ) {
|
if( pressed != thumbPressed ) {
|
||||||
thumbPressed = pressed;
|
thumbPressed = pressed;
|
||||||
slider.repaint( thumbRect );
|
HiDPIUtils.repaint( slider, thumbRect );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ 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.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,7 +68,6 @@ import com.formdev.flatlaf.util.LoggingFacade;
|
|||||||
* @uiDefault Component.minimumWidth int
|
* @uiDefault Component.minimumWidth int
|
||||||
* @uiDefault Spinner.buttonStyle String button (default), mac or none
|
* @uiDefault Spinner.buttonStyle String button (default), mac or none
|
||||||
* @uiDefault Component.arrowType String chevron (default) or triangle
|
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
|
||||||
* @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
|
||||||
@@ -92,7 +92,6 @@ public class FlatSpinnerUI
|
|||||||
@Styleable protected int minimumWidth;
|
@Styleable protected int minimumWidth;
|
||||||
@Styleable protected String buttonStyle;
|
@Styleable protected String buttonStyle;
|
||||||
@Styleable protected String arrowType;
|
@Styleable protected String arrowType;
|
||||||
protected boolean isIntelliJTheme;
|
|
||||||
@Styleable protected Color disabledBackground;
|
@Styleable protected Color disabledBackground;
|
||||||
@Styleable protected Color disabledForeground;
|
@Styleable protected Color disabledForeground;
|
||||||
@Styleable protected Color focusedBackground;
|
@Styleable protected Color focusedBackground;
|
||||||
@@ -129,7 +128,6 @@ public class FlatSpinnerUI
|
|||||||
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
||||||
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" );
|
|
||||||
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" );
|
||||||
@@ -142,8 +140,6 @@ public class FlatSpinnerUI
|
|||||||
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" );
|
||||||
|
|
||||||
MigLayoutVisualPadding.install( spinner );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -164,8 +160,6 @@ public class FlatSpinnerUI
|
|||||||
|
|
||||||
oldStyleValues = null;
|
oldStyleValues = null;
|
||||||
borderShared = null;
|
borderShared = null;
|
||||||
|
|
||||||
MigLayoutVisualPadding.uninstall( spinner );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -175,6 +169,8 @@ public class FlatSpinnerUI
|
|||||||
addEditorFocusListener( spinner.getEditor() );
|
addEditorFocusListener( spinner.getEditor() );
|
||||||
spinner.addFocusListener( getHandler() );
|
spinner.addFocusListener( getHandler() );
|
||||||
spinner.addPropertyChangeListener( getHandler() );
|
spinner.addPropertyChangeListener( getHandler() );
|
||||||
|
|
||||||
|
MigLayoutVisualPadding.install( spinner );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -186,6 +182,8 @@ public class FlatSpinnerUI
|
|||||||
spinner.removePropertyChangeListener( getHandler() );
|
spinner.removePropertyChangeListener( getHandler() );
|
||||||
|
|
||||||
handler = null;
|
handler = null;
|
||||||
|
|
||||||
|
MigLayoutVisualPadding.uninstall( spinner );
|
||||||
}
|
}
|
||||||
|
|
||||||
private Handler getHandler() {
|
private Handler getHandler() {
|
||||||
@@ -316,7 +314,7 @@ public class FlatSpinnerUI
|
|||||||
|
|
||||||
return background;
|
return background;
|
||||||
} else
|
} else
|
||||||
return isIntelliJTheme ? FlatUIUtils.getParentBackground( spinner ) : disabledBackground;
|
return disabledBackground;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Color getForeground( boolean enabled ) {
|
protected Color getForeground( boolean enabled ) {
|
||||||
@@ -589,7 +587,7 @@ public class FlatSpinnerUI
|
|||||||
@Override
|
@Override
|
||||||
public void focusGained( FocusEvent e ) {
|
public void focusGained( FocusEvent e ) {
|
||||||
// necessary to update focus border
|
// necessary to update focus border
|
||||||
spinner.repaint();
|
HiDPIUtils.repaint( spinner );
|
||||||
|
|
||||||
// if spinner gained focus, transfer it to the editor text field
|
// if spinner gained focus, transfer it to the editor text field
|
||||||
if( e.getComponent() == spinner ) {
|
if( e.getComponent() == spinner ) {
|
||||||
@@ -602,7 +600,7 @@ public class FlatSpinnerUI
|
|||||||
@Override
|
@Override
|
||||||
public void focusLost( FocusEvent e ) {
|
public void focusLost( FocusEvent e ) {
|
||||||
// necessary to update focus border
|
// necessary to update focus border
|
||||||
spinner.repaint();
|
HiDPIUtils.repaint( spinner );
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- interface PropertyChangeListener ----
|
//---- interface PropertyChangeListener ----
|
||||||
@@ -617,7 +615,7 @@ public class FlatSpinnerUI
|
|||||||
|
|
||||||
case FlatClientProperties.COMPONENT_ROUND_RECT:
|
case FlatClientProperties.COMPONENT_ROUND_RECT:
|
||||||
case FlatClientProperties.OUTLINE:
|
case FlatClientProperties.OUTLINE:
|
||||||
spinner.repaint();
|
HiDPIUtils.repaint( spinner );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FlatClientProperties.MINIMUM_WIDTH:
|
case FlatClientProperties.MINIMUM_WIDTH:
|
||||||
@@ -628,7 +626,7 @@ public class FlatSpinnerUI
|
|||||||
case FlatClientProperties.STYLE_CLASS:
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
installStyle();
|
installStyle();
|
||||||
spinner.revalidate();
|
spinner.revalidate();
|
||||||
spinner.repaint();
|
HiDPIUtils.repaint( spinner );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,9 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Canvas;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.Cursor;
|
import java.awt.Cursor;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
@@ -67,6 +69,8 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* <!-- FlatSplitPaneUI -->
|
* <!-- FlatSplitPaneUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault Component.arrowType String chevron (default) or triangle
|
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||||
|
* @uiDefault SplitPaneDivider.hoverColor Color optional
|
||||||
|
* @uiDefault SplitPaneDivider.pressedColor Color optional
|
||||||
* @uiDefault SplitPaneDivider.oneTouchArrowColor Color
|
* @uiDefault SplitPaneDivider.oneTouchArrowColor Color
|
||||||
* @uiDefault SplitPaneDivider.oneTouchHoverArrowColor Color
|
* @uiDefault SplitPaneDivider.oneTouchHoverArrowColor Color
|
||||||
* @uiDefault SplitPaneDivider.oneTouchPressedArrowColor Color
|
* @uiDefault SplitPaneDivider.oneTouchPressedArrowColor Color
|
||||||
@@ -80,14 +84,14 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*/
|
*/
|
||||||
public class FlatSplitPaneUI
|
public class FlatSplitPaneUI
|
||||||
extends BasicSplitPaneUI
|
extends BasicSplitPaneUI
|
||||||
implements StyleableUI
|
implements StyleableUI, FlatTitlePane.TitleBarCaptionHitTest
|
||||||
{
|
{
|
||||||
@Styleable protected String arrowType;
|
@Styleable protected String arrowType;
|
||||||
|
/** @since 3.3 */ @Styleable protected Color draggingColor;
|
||||||
@Styleable protected Color oneTouchArrowColor;
|
@Styleable protected Color oneTouchArrowColor;
|
||||||
@Styleable protected Color oneTouchHoverArrowColor;
|
@Styleable protected Color oneTouchHoverArrowColor;
|
||||||
@Styleable protected Color oneTouchPressedArrowColor;
|
@Styleable protected Color oneTouchPressedArrowColor;
|
||||||
|
|
||||||
private PropertyChangeListener propertyChangeListener;
|
|
||||||
private Map<String, Object> oldStyleValues;
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
@@ -105,6 +109,8 @@ public class FlatSplitPaneUI
|
|||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
arrowType = UIManager.getString( "Component.arrowType" );
|
arrowType = UIManager.getString( "Component.arrowType" );
|
||||||
|
|
||||||
|
draggingColor = UIManager.getColor( "SplitPaneDivider.draggingColor" );
|
||||||
|
|
||||||
// get one-touch colors before invoking super.installDefaults() because they are
|
// get one-touch colors before invoking super.installDefaults() because they are
|
||||||
// used in there on LaF switching
|
// used in there on LaF switching
|
||||||
oneTouchArrowColor = UIManager.getColor( "SplitPaneDivider.oneTouchArrowColor" );
|
oneTouchArrowColor = UIManager.getColor( "SplitPaneDivider.oneTouchArrowColor" );
|
||||||
@@ -118,6 +124,8 @@ public class FlatSplitPaneUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
draggingColor = null;
|
||||||
|
|
||||||
oneTouchArrowColor = null;
|
oneTouchArrowColor = null;
|
||||||
oneTouchHoverArrowColor = null;
|
oneTouchHoverArrowColor = null;
|
||||||
oneTouchPressedArrowColor = null;
|
oneTouchPressedArrowColor = null;
|
||||||
@@ -126,19 +134,9 @@ public class FlatSplitPaneUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installListeners() {
|
protected PropertyChangeListener createPropertyChangeListener() {
|
||||||
super.installListeners();
|
return FlatStylingSupport.createPropertyChangeListener( splitPane, this::installStyle,
|
||||||
|
super.createPropertyChangeListener() );
|
||||||
propertyChangeListener = FlatStylingSupport.createPropertyChangeListener( splitPane, this::installStyle, null );
|
|
||||||
splitPane.addPropertyChangeListener( propertyChangeListener );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void uninstallListeners() {
|
|
||||||
super.uninstallListeners();
|
|
||||||
|
|
||||||
splitPane.removePropertyChangeListener( propertyChangeListener );
|
|
||||||
propertyChangeListener = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -194,12 +192,58 @@ public class FlatSplitPaneUI
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Component createDefaultNonContinuousLayoutDivider() {
|
||||||
|
// only used for non-continuous layout if left or right component is heavy weight
|
||||||
|
return new Canvas() {
|
||||||
|
@Override
|
||||||
|
public void paint( Graphics g ) {
|
||||||
|
if( !isContinuousLayout() && getLastDragLocation() != -1 )
|
||||||
|
paintDragDivider( g, 0 );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finishedPaintingChildren( JSplitPane sp, Graphics g ) {
|
||||||
|
if( sp == splitPane && getLastDragLocation() != -1 && !isContinuousLayout() && !draggingHW )
|
||||||
|
paintDragDivider( g, getLastDragLocation() );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void paintDragDivider( Graphics g, int dividerLocation ) {
|
||||||
|
// divider bounds
|
||||||
|
boolean horizontal = (getOrientation() == JSplitPane.HORIZONTAL_SPLIT);
|
||||||
|
int x = horizontal ? dividerLocation : 0;
|
||||||
|
int y = !horizontal ? dividerLocation : 0;
|
||||||
|
int width = horizontal ? dividerSize : splitPane.getWidth();
|
||||||
|
int height = !horizontal ? dividerSize : splitPane.getHeight();
|
||||||
|
|
||||||
|
// paint background
|
||||||
|
g.setColor( FlatUIUtils.deriveColor( draggingColor, splitPane.getBackground() ) );
|
||||||
|
g.fillRect( x, y, width, height );
|
||||||
|
|
||||||
|
// paint divider style (e.g. grip)
|
||||||
|
if( divider instanceof FlatSplitPaneDivider )
|
||||||
|
((FlatSplitPaneDivider)divider).paintStyle( g, x, y, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- interface FlatTitlePane.TitleBarCaptionHitTest ----
|
||||||
|
|
||||||
|
/** @since 3.4 */
|
||||||
|
@Override
|
||||||
|
public Boolean isTitleBarCaptionAt( int x, int y ) {
|
||||||
|
// necessary because BasicSplitPaneDivider adds some mouse listeners for dragging divider
|
||||||
|
return null; // check children
|
||||||
|
}
|
||||||
|
|
||||||
//---- class FlatSplitPaneDivider -----------------------------------------
|
//---- class FlatSplitPaneDivider -----------------------------------------
|
||||||
|
|
||||||
protected class FlatSplitPaneDivider
|
protected class FlatSplitPaneDivider
|
||||||
extends BasicSplitPaneDivider
|
extends BasicSplitPaneDivider
|
||||||
{
|
{
|
||||||
@Styleable protected String style = UIManager.getString( "SplitPaneDivider.style" );
|
@Styleable protected String style = UIManager.getString( "SplitPaneDivider.style" );
|
||||||
|
/** @since 3.3 */ @Styleable protected Color hoverColor = UIManager.getColor( "SplitPaneDivider.hoverColor" );
|
||||||
|
/** @since 3.3 */ @Styleable protected Color pressedColor = UIManager.getColor( "SplitPaneDivider.pressedColor" );
|
||||||
@Styleable protected Color gripColor = UIManager.getColor( "SplitPaneDivider.gripColor" );
|
@Styleable protected Color gripColor = UIManager.getColor( "SplitPaneDivider.gripColor" );
|
||||||
@Styleable protected int gripDotCount = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotCount", 3 );
|
@Styleable protected int gripDotCount = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotCount", 3 );
|
||||||
@Styleable protected int gripDotSize = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotSize", 3 );
|
@Styleable protected int gripDotSize = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotSize", 3 );
|
||||||
@@ -257,20 +301,40 @@ public class FlatSplitPaneUI
|
|||||||
// necessary to show/hide one-touch buttons on expand/collapse
|
// necessary to show/hide one-touch buttons on expand/collapse
|
||||||
doLayout();
|
doLayout();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.SPLIT_PANE_EXPANDABLE_SIDE:
|
||||||
|
revalidate();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paint( Graphics g ) {
|
public void paint( Graphics g ) {
|
||||||
|
// paint hover or pressed background
|
||||||
|
Color hoverOrPressedColor = (isContinuousLayout() && dragger != null)
|
||||||
|
? pressedColor
|
||||||
|
: (isMouseOver() && dragger == null
|
||||||
|
? hoverColor
|
||||||
|
: null);
|
||||||
|
if( hoverOrPressedColor != null ) {
|
||||||
|
g.setColor( FlatUIUtils.deriveColor( hoverOrPressedColor, splitPane.getBackground() ) );
|
||||||
|
g.fillRect( 0, 0, getWidth(), getHeight() );
|
||||||
|
}
|
||||||
|
|
||||||
super.paint( g );
|
super.paint( g );
|
||||||
|
|
||||||
|
paintStyle( g, 0, 0, getWidth(), getHeight() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 3.3 */
|
||||||
|
protected void paintStyle( Graphics g, int x, int y, int width, int height ) {
|
||||||
if( "plain".equals( style ) )
|
if( "plain".equals( style ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
||||||
|
|
||||||
g.setColor( gripColor );
|
g.setColor( gripColor );
|
||||||
paintGrip( g, 0, 0, getWidth(), getHeight() );
|
paintGrip( g, x, y, width, height );
|
||||||
|
|
||||||
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||||
}
|
}
|
||||||
@@ -297,6 +361,29 @@ public class FlatSplitPaneUI
|
|||||||
: location == (splitPane.getWidth() - getWidth() - insets.right);
|
: location == (splitPane.getWidth() - getWidth() - insets.right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setMouseOver( boolean mouseOver ) {
|
||||||
|
super.setMouseOver( mouseOver );
|
||||||
|
repaintIfNecessary();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void prepareForDragging() {
|
||||||
|
super.prepareForDragging();
|
||||||
|
repaintIfNecessary();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void finishDraggingTo( int location ) {
|
||||||
|
super.finishDraggingTo( location );
|
||||||
|
repaintIfNecessary();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void repaintIfNecessary() {
|
||||||
|
if( hoverColor != null || pressedColor != null )
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
//---- class FlatOneTouchButton ---------------------------------------
|
//---- class FlatOneTouchButton ---------------------------------------
|
||||||
|
|
||||||
protected class FlatOneTouchButton
|
protected class FlatOneTouchButton
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ import javax.swing.UIManager;
|
|||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.StringUtils;
|
import com.formdev.flatlaf.util.StringUtils;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Support for styling components in CSS syntax.
|
* Support for styling components in CSS syntax.
|
||||||
@@ -51,6 +51,9 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
*/
|
*/
|
||||||
public class FlatStylingSupport
|
public class FlatStylingSupport
|
||||||
{
|
{
|
||||||
|
|
||||||
|
//---- annotations --------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates that a field is intended to be used by FlatLaf styling support.
|
* Indicates that a field is intended to be used by FlatLaf styling support.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -98,17 +101,56 @@ public class FlatStylingSupport
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** @since 2 */
|
//---- interfaces ---------------------------------------------------------
|
||||||
public interface StyleableUI {
|
|
||||||
Map<String, Class<?>> getStyleableInfos( JComponent c );
|
|
||||||
/** @since 2.5 */ Object getStyleableValue( JComponent c, String key );
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
public interface StyleableBorder {
|
public interface StyleableUI {
|
||||||
Object applyStyleProperty( String key, Object value );
|
Map<String, Class<?>> getStyleableInfos( JComponent c ) throws IllegalArgumentException;
|
||||||
Map<String, Class<?>> getStyleableInfos();
|
/** @since 2.5 */ Object getStyleableValue( JComponent c, String key ) throws IllegalArgumentException;
|
||||||
/** @since 2.5 */ Object getStyleableValue( String key );
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object that implements this interface is intended to support FlatLaf styling.
|
||||||
|
*
|
||||||
|
* @since 3.7
|
||||||
|
*/
|
||||||
|
public interface StyleableObject {
|
||||||
|
/**
|
||||||
|
* Applies the given value to this object.
|
||||||
|
* <p>
|
||||||
|
* The default implementation invokes {@link FlatStylingSupport#applyToAnnotatedObject(Object, String, Object)}.
|
||||||
|
*
|
||||||
|
* @param key the name of the property
|
||||||
|
* @param value the new value
|
||||||
|
* @return the old value of the property
|
||||||
|
*/
|
||||||
|
default Object applyStyleProperty( String key, Object value )
|
||||||
|
throws UnknownStyleException, IllegalArgumentException
|
||||||
|
{
|
||||||
|
return applyToAnnotatedObject( this, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a map of all styleable properties.
|
||||||
|
* The key is the name of the property and the value the type of the property.
|
||||||
|
* <p>
|
||||||
|
* The default implementation invokes {@link FlatStylingSupport#getAnnotatedStyleableInfos(Object)}.
|
||||||
|
*/
|
||||||
|
default Map<String, Class<?>> getStyleableInfos() throws IllegalArgumentException {
|
||||||
|
return getAnnotatedStyleableInfos( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current value for the given property key.
|
||||||
|
* <p>
|
||||||
|
* The default implementation invokes {@link FlatStylingSupport#getAnnotatedStyleableValue(Object, String)}.
|
||||||
|
*
|
||||||
|
* @param key the name of the property
|
||||||
|
* @return the current value of the property
|
||||||
|
*/
|
||||||
|
default Object getStyleableValue( String key ) throws IllegalArgumentException {
|
||||||
|
return getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2.5 */
|
/** @since 2.5 */
|
||||||
@@ -117,6 +159,8 @@ public class FlatStylingSupport
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---- methods ------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the style specified in client property {@link FlatClientProperties#STYLE}.
|
* Returns the style specified in client property {@link FlatClientProperties#STYLE}.
|
||||||
*/
|
*/
|
||||||
@@ -135,7 +179,9 @@ public class FlatStylingSupport
|
|||||||
return getStyle( c ) != null || getStyleClass( c ) != null;
|
return getStyle( c ) != null || getStyleClass( c ) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object getResolvedStyle( JComponent c, String type ) {
|
public static Object getResolvedStyle( JComponent c, String type )
|
||||||
|
throws IllegalArgumentException
|
||||||
|
{
|
||||||
Object style = getStyle( c );
|
Object style = getStyle( c );
|
||||||
Object styleClass = getStyleClass( c );
|
Object styleClass = getStyleClass( c );
|
||||||
Object styleForClasses = getStyleForClasses( styleClass, type );
|
Object styleForClasses = getStyleForClasses( styleClass, type );
|
||||||
@@ -175,7 +221,9 @@ public class FlatStylingSupport
|
|||||||
* @param type the type of the component
|
* @param type the type of the component
|
||||||
* @return the styles
|
* @return the styles
|
||||||
*/
|
*/
|
||||||
public static Object getStyleForClasses( Object styleClass, String type ) {
|
public static Object getStyleForClasses( Object styleClass, String type )
|
||||||
|
throws IllegalArgumentException
|
||||||
|
{
|
||||||
if( styleClass == null )
|
if( styleClass == null )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@@ -198,7 +246,9 @@ public class FlatStylingSupport
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Object getStyleForClass( String styleClass, String type ) {
|
private static Object getStyleForClass( String styleClass, String type )
|
||||||
|
throws IllegalArgumentException
|
||||||
|
{
|
||||||
return joinStyles(
|
return joinStyles(
|
||||||
UIManager.get( "[style]." + styleClass ),
|
UIManager.get( "[style]." + styleClass ),
|
||||||
UIManager.get( "[style]" + type + '.' + styleClass ) );
|
UIManager.get( "[style]" + type + '.' + styleClass ) );
|
||||||
@@ -218,7 +268,9 @@ public class FlatStylingSupport
|
|||||||
* @return new joined style
|
* @return new joined style
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings( "unchecked" )
|
@SuppressWarnings( "unchecked" )
|
||||||
public static Object joinStyles( Object style1, Object style2 ) {
|
public static Object joinStyles( Object style1, Object style2 )
|
||||||
|
throws IllegalArgumentException
|
||||||
|
{
|
||||||
if( style1 == null )
|
if( style1 == null )
|
||||||
return style2;
|
return style2;
|
||||||
if( style2 == null )
|
if( style2 == null )
|
||||||
@@ -278,6 +330,7 @@ public class FlatStylingSupport
|
|||||||
* @throws IllegalArgumentException on syntax errors
|
* @throws IllegalArgumentException on syntax errors
|
||||||
* @throws ClassCastException if value type does not fit to expected type
|
* @throws ClassCastException if value type does not fit to expected type
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings( "ReturnValueIgnored" ) // Error Prone
|
||||||
public static Map<String, Object> parseAndApply( Map<String, Object> oldStyleValues,
|
public static Map<String, Object> parseAndApply( Map<String, Object> oldStyleValues,
|
||||||
Object style, BiFunction<String, Object, Object> applyProperty )
|
Object style, BiFunction<String, Object, Object> applyProperty )
|
||||||
throws UnknownStyleException, IllegalArgumentException
|
throws UnknownStyleException, IllegalArgumentException
|
||||||
@@ -315,22 +368,24 @@ public class FlatStylingSupport
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
Map<String, Object> oldValues = new HashMap<>();
|
Map<String, Object> oldValues = new HashMap<>();
|
||||||
|
outer:
|
||||||
for( Map.Entry<String, Object> e : style.entrySet() ) {
|
for( Map.Entry<String, Object> e : style.entrySet() ) {
|
||||||
String key = e.getKey();
|
String key = e.getKey();
|
||||||
Object newValue = e.getValue();
|
Object newValue = e.getValue();
|
||||||
|
|
||||||
// handle key prefix
|
// handle key prefix
|
||||||
if( key.startsWith( "[" ) ) {
|
while( key.startsWith( "[" ) ) {
|
||||||
if( (SystemInfo.isWindows && key.startsWith( "[win]" )) ||
|
int closeIndex = key.indexOf( ']' );
|
||||||
(SystemInfo.isMacOS && key.startsWith( "[mac]" )) ||
|
if( closeIndex < 0 )
|
||||||
(SystemInfo.isLinux && key.startsWith( "[linux]" )) ||
|
continue outer;
|
||||||
(key.startsWith( "[light]" ) && !FlatLaf.isLafDark()) ||
|
|
||||||
(key.startsWith( "[dark]" ) && FlatLaf.isLafDark()) )
|
String prefix = key.substring( 0, closeIndex + 1 );
|
||||||
{
|
String lightOrDarkPrefix = FlatLaf.getUIKeyLightOrDarkPrefix( FlatLaf.isLafDark() );
|
||||||
// prefix is known and enabled --> remove prefix
|
if( !lightOrDarkPrefix.equals( prefix ) && !FlatLaf.getUIKeyPlatformPrefixes().contains( prefix ) )
|
||||||
key = key.substring( key.indexOf( ']' ) + 1 );
|
continue outer;
|
||||||
} else
|
|
||||||
continue;
|
// prefix is known and enabled --> remove prefix
|
||||||
|
key = key.substring( closeIndex + 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
Object oldValue = applyProperty.apply( key, newValue );
|
Object oldValue = applyProperty.apply( key, newValue );
|
||||||
@@ -379,7 +434,9 @@ public class FlatStylingSupport
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Object parseValue( String key, String value ) {
|
private static Object parseValue( String key, String value )
|
||||||
|
throws IllegalArgumentException
|
||||||
|
{
|
||||||
// simple reference
|
// simple reference
|
||||||
if( value.startsWith( "$" ) )
|
if( value.startsWith( "$" ) )
|
||||||
return UIManager.get( value.substring( 1 ) );
|
return UIManager.get( value.substring( 1 ) );
|
||||||
@@ -474,7 +531,9 @@ public class FlatStylingSupport
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Object applyToField( Field f, Object obj, Object value, boolean useMethodHandles ) {
|
private static Object applyToField( Field f, Object obj, Object value, boolean useMethodHandles )
|
||||||
|
throws IllegalArgumentException
|
||||||
|
{
|
||||||
checkValidField( f );
|
checkValidField( f );
|
||||||
|
|
||||||
if( useMethodHandles && obj instanceof StyleableLookupProvider ) {
|
if( useMethodHandles && obj instanceof StyleableLookupProvider ) {
|
||||||
@@ -504,7 +563,9 @@ public class FlatStylingSupport
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Object getFieldValue( Field f, Object obj, boolean useMethodHandles ) {
|
private static Object getFieldValue( Field f, Object obj, boolean useMethodHandles )
|
||||||
|
throws IllegalArgumentException
|
||||||
|
{
|
||||||
checkValidField( f );
|
checkValidField( f );
|
||||||
|
|
||||||
if( useMethodHandles && obj instanceof StyleableLookupProvider ) {
|
if( useMethodHandles && obj instanceof StyleableLookupProvider ) {
|
||||||
@@ -529,7 +590,9 @@ public class FlatStylingSupport
|
|||||||
return new IllegalArgumentException( "failed to access field '" + f.getDeclaringClass().getName() + "." + f.getName() + "'", ex );
|
return new IllegalArgumentException( "failed to access field '" + f.getDeclaringClass().getName() + "." + f.getName() + "'", ex );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkValidField( Field f ) {
|
private static void checkValidField( Field f )
|
||||||
|
throws IllegalArgumentException
|
||||||
|
{
|
||||||
if( !isValidField( f ) )
|
if( !isValidField( f ) )
|
||||||
throw new IllegalArgumentException( "field '" + f.getDeclaringClass().getName() + "." + f.getName() + "' is final or static" );
|
throw new IllegalArgumentException( "field '" + f.getDeclaringClass().getName() + "." + f.getName() + "' is final or static" );
|
||||||
}
|
}
|
||||||
@@ -539,7 +602,9 @@ public class FlatStylingSupport
|
|||||||
return (modifiers & (Modifier.FINAL|Modifier.STATIC)) == 0 && !f.isSynthetic();
|
return (modifiers & (Modifier.FINAL|Modifier.STATIC)) == 0 && !f.isSynthetic();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Field getStyleableField( StyleableField styleableField ) {
|
private static Field getStyleableField( StyleableField styleableField )
|
||||||
|
throws IllegalArgumentException
|
||||||
|
{
|
||||||
String fieldName = styleableField.fieldName();
|
String fieldName = styleableField.fieldName();
|
||||||
if( fieldName.isEmpty() )
|
if( fieldName.isEmpty() )
|
||||||
fieldName = styleableField.key();
|
fieldName = styleableField.key();
|
||||||
@@ -647,13 +712,14 @@ public class FlatStylingSupport
|
|||||||
|
|
||||||
static Object applyToAnnotatedObjectOrBorder( Object obj, String key, Object value,
|
static Object applyToAnnotatedObjectOrBorder( Object obj, String key, Object value,
|
||||||
JComponent c, AtomicBoolean borderShared )
|
JComponent c, AtomicBoolean borderShared )
|
||||||
|
throws IllegalArgumentException
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return applyToAnnotatedObject( obj, key, value );
|
return applyToAnnotatedObject( obj, key, value );
|
||||||
} catch( UnknownStyleException ex ) {
|
} catch( UnknownStyleException ex ) {
|
||||||
// apply to border
|
// apply to border
|
||||||
Border border = c.getBorder();
|
Border border = c.getBorder();
|
||||||
if( border instanceof StyleableBorder ) {
|
if( border instanceof StyleableObject ) {
|
||||||
if( borderShared.get() ) {
|
if( borderShared.get() ) {
|
||||||
border = cloneBorder( border );
|
border = cloneBorder( border );
|
||||||
c.setBorder( border );
|
c.setBorder( border );
|
||||||
@@ -661,7 +727,7 @@ public class FlatStylingSupport
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return ((StyleableBorder)border).applyStyleProperty( key, value );
|
return ((StyleableObject)border).applyStyleProperty( key, value );
|
||||||
} catch( UnknownStyleException ex2 ) {
|
} catch( UnknownStyleException ex2 ) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
@@ -689,13 +755,15 @@ public class FlatStylingSupport
|
|||||||
case FlatClientProperties.STYLE_CLASS:
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
installStyle.run();
|
installStyle.run();
|
||||||
c.revalidate();
|
c.revalidate();
|
||||||
c.repaint();
|
HiDPIUtils.repaint( c );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static Border cloneBorder( Border border ) {
|
static Border cloneBorder( Border border )
|
||||||
|
throws IllegalArgumentException
|
||||||
|
{
|
||||||
Class<? extends Border> borderClass = border.getClass();
|
Class<? extends Border> borderClass = border.getClass();
|
||||||
try {
|
try {
|
||||||
return borderClass.getDeclaredConstructor().newInstance();
|
return borderClass.getDeclaredConstructor().newInstance();
|
||||||
@@ -704,7 +772,9 @@ public class FlatStylingSupport
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Icon cloneIcon( Icon icon ) {
|
static Icon cloneIcon( Icon icon )
|
||||||
|
throws IllegalArgumentException
|
||||||
|
{
|
||||||
Class<? extends Icon> iconClass = icon.getClass();
|
Class<? extends Icon> iconClass = icon.getClass();
|
||||||
try {
|
try {
|
||||||
return iconClass.getDeclaredConstructor().newInstance();
|
return iconClass.getDeclaredConstructor().newInstance();
|
||||||
@@ -717,11 +787,15 @@ public class FlatStylingSupport
|
|||||||
* Returns a map of all fields annotated with {@link Styleable}.
|
* 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.
|
* The key is the name of the field and the value the type of the field.
|
||||||
*/
|
*/
|
||||||
public static Map<String, Class<?>> getAnnotatedStyleableInfos( Object obj ) {
|
public static Map<String, Class<?>> getAnnotatedStyleableInfos( Object obj )
|
||||||
|
throws IllegalArgumentException
|
||||||
|
{
|
||||||
return getAnnotatedStyleableInfos( obj, null );
|
return getAnnotatedStyleableInfos( obj, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Map<String, Class<?>> getAnnotatedStyleableInfos( Object obj, Border border ) {
|
public static Map<String, Class<?>> getAnnotatedStyleableInfos( Object obj, Border border )
|
||||||
|
throws IllegalArgumentException
|
||||||
|
{
|
||||||
Map<String, Class<?>> infos = new StyleableInfosMap<>();
|
Map<String, Class<?>> infos = new StyleableInfosMap<>();
|
||||||
collectAnnotatedStyleableInfos( obj, infos );
|
collectAnnotatedStyleableInfos( obj, infos );
|
||||||
collectStyleableInfos( border, infos );
|
collectStyleableInfos( border, infos );
|
||||||
@@ -732,7 +806,9 @@ public class FlatStylingSupport
|
|||||||
* Search for all fields annotated with {@link Styleable} and add them to the given map.
|
* 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.
|
* 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 ) {
|
public static void collectAnnotatedStyleableInfos( Object obj, Map<String, Class<?>> infos )
|
||||||
|
throws IllegalArgumentException
|
||||||
|
{
|
||||||
HashSet<String> processedFields = new HashSet<>();
|
HashSet<String> processedFields = new HashSet<>();
|
||||||
Class<?> cls = obj.getClass();
|
Class<?> cls = obj.getClass();
|
||||||
|
|
||||||
@@ -801,8 +877,8 @@ public class FlatStylingSupport
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void collectStyleableInfos( Border border, Map<String, Class<?>> infos ) {
|
public static void collectStyleableInfos( Border border, Map<String, Class<?>> infos ) {
|
||||||
if( border instanceof StyleableBorder )
|
if( border instanceof StyleableObject )
|
||||||
infos.putAll( ((StyleableBorder)border).getStyleableInfos() );
|
infos.putAll( ((StyleableObject)border).getStyleableInfos() );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void putAllPrefixKey( Map<String, Class<?>> infos, String keyPrefix, Map<String, Class<?>> infos2 ) {
|
public static void putAllPrefixKey( Map<String, Class<?>> infos, String keyPrefix, Map<String, Class<?>> infos2 ) {
|
||||||
@@ -810,7 +886,9 @@ public class FlatStylingSupport
|
|||||||
infos.put( keyPrefix.concat( e.getKey() ), e.getValue() );
|
infos.put( keyPrefix.concat( e.getKey() ), e.getValue() );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object getAnnotatedStyleableValue( Object obj, String key ) {
|
public static Object getAnnotatedStyleableValue( Object obj, String key )
|
||||||
|
throws IllegalArgumentException
|
||||||
|
{
|
||||||
String fieldName = keyToFieldName( key );
|
String fieldName = keyToFieldName( key );
|
||||||
Class<?> cls = obj.getClass();
|
Class<?> cls = obj.getClass();
|
||||||
|
|
||||||
@@ -848,8 +926,8 @@ public class FlatStylingSupport
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Object getAnnotatedStyleableValue( Object obj, Border border, String key ) {
|
public static Object getAnnotatedStyleableValue( Object obj, Border border, String key ) {
|
||||||
if( border instanceof StyleableBorder ) {
|
if( border instanceof StyleableObject ) {
|
||||||
Object value = ((StyleableBorder)border).getStyleableValue( key );
|
Object value = ((StyleableObject)border).getStyleableValue( key );
|
||||||
if( value != null )
|
if( value != null )
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@@ -877,7 +955,9 @@ public class FlatStylingSupport
|
|||||||
extends LinkedHashMap<K,V>
|
extends LinkedHashMap<K,V>
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public V put( K key, V value ) {
|
public V put( K key, V value )
|
||||||
|
throws IllegalArgumentException
|
||||||
|
{
|
||||||
V oldValue = super.put( key, value );
|
V oldValue = super.put( key, value );
|
||||||
if( oldValue != null )
|
if( oldValue != null )
|
||||||
throw new IllegalArgumentException( "duplicate key '" + key + "'" );
|
throw new IllegalArgumentException( "duplicate key '" + key + "'" );
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,7 @@ import java.util.function.Function;
|
|||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.border.Border;
|
||||||
import javax.swing.plaf.TableUI;
|
import javax.swing.plaf.TableUI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,8 +65,28 @@ public class FlatTableCellBorder
|
|||||||
return super.getLineColor();
|
return super.getLineColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getArc() {
|
||||||
|
if( c != null ) {
|
||||||
|
Integer selectionArc = getStyleFromTableUI( c, ui -> ui.selectionArc );
|
||||||
|
if( selectionArc != null )
|
||||||
|
return selectionArc;
|
||||||
|
}
|
||||||
|
return super.getArc();
|
||||||
|
}
|
||||||
|
|
||||||
@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( c != null ) {
|
||||||
|
Insets selectionInsets = getStyleFromTableUI( c, ui -> ui.selectionInsets );
|
||||||
|
if( selectionInsets != null ) {
|
||||||
|
x += selectionInsets.left;
|
||||||
|
y += selectionInsets.top;
|
||||||
|
width -= selectionInsets.left + selectionInsets.right;
|
||||||
|
height -= selectionInsets.top + selectionInsets.bottom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.c = c;
|
this.c = c;
|
||||||
super.paintBorder( c, g, x, y, width, height );
|
super.paintBorder( c, g, x, y, width, height );
|
||||||
this.c = null;
|
this.c = null;
|
||||||
@@ -107,17 +128,55 @@ public class FlatTableCellBorder
|
|||||||
public static class Focused
|
public static class Focused
|
||||||
extends FlatTableCellBorder
|
extends FlatTableCellBorder
|
||||||
{
|
{
|
||||||
|
@Override
|
||||||
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
|
if( c != null && c.getClass().getName().equals( "javax.swing.JTable$BooleanRenderer" ) ) {
|
||||||
|
// boolean renderer in JTable does not use Table.focusSelectedCellHighlightBorder
|
||||||
|
// if cell is selected and focused (as DefaultTableCellRenderer does)
|
||||||
|
// --> delegate to Table.focusSelectedCellHighlightBorder
|
||||||
|
// to make FlatLaf "focus indicator border hiding" work
|
||||||
|
JTable table = (JTable) SwingUtilities.getAncestorOfClass( JTable.class, c );
|
||||||
|
if( table != null &&
|
||||||
|
c.getForeground() == table.getSelectionForeground() &&
|
||||||
|
c.getBackground() == table.getSelectionBackground() )
|
||||||
|
{
|
||||||
|
Border border = UIManager.getBorder( "Table.focusSelectedCellHighlightBorder" );
|
||||||
|
if( border != null ) {
|
||||||
|
border.paintBorder( c, g, x, y, width, height );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.paintBorder( c, g, x, y, width, height );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class Selected -----------------------------------------------------
|
//---- class Selected -----------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 (Table.showCellFocusIndicator=true) or at least one selected cell is editable.
|
* The focus indicator is shown under following conditions:
|
||||||
|
* <ul>
|
||||||
|
* <li>always if enabled via UI property {@code Table.showCellFocusIndicator=true}
|
||||||
|
* <li>for row selection mode if exactly one row is selected and at least one cell in that row is editable
|
||||||
|
* <li>for column selection mode if exactly one column is selected and at least one cell in that column is editable
|
||||||
|
* <li>never for cell selection mode
|
||||||
|
* </ul>
|
||||||
|
* The reason for this logic is to hide the focus indicator when it is not needed,
|
||||||
|
* and only show it when there are editable cells and the user needs to know
|
||||||
|
* which cell is focused to start editing.
|
||||||
|
* <p>
|
||||||
|
* To avoid possible performance issues, checking for editable cells is limited
|
||||||
|
* to {@link #maxCheckCellsEditable}. If there are more cells to check,
|
||||||
|
* the focus indicator is always shown.
|
||||||
*/
|
*/
|
||||||
public static class Selected
|
public static class Selected
|
||||||
extends FlatTableCellBorder
|
extends FlatTableCellBorder
|
||||||
{
|
{
|
||||||
|
/** @since 3.1 */
|
||||||
|
public int maxCheckCellsEditable = 50;
|
||||||
|
|
||||||
@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 = getStyleFromTableUI( c, ui -> ui.showCellFocusIndicator );
|
Boolean b = getStyleFromTableUI( c, ui -> ui.showCellFocusIndicator );
|
||||||
@@ -125,7 +184,7 @@ public class FlatTableCellBorder
|
|||||||
|
|
||||||
if( !showCellFocusIndicator ) {
|
if( !showCellFocusIndicator ) {
|
||||||
JTable table = (JTable) SwingUtilities.getAncestorOfClass( JTable.class, c );
|
JTable table = (JTable) SwingUtilities.getAncestorOfClass( JTable.class, c );
|
||||||
if( table != null && !isSelectionEditable( table ) )
|
if( table != null && !shouldShowCellFocusIndicator( table ) )
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,28 +192,57 @@ public class FlatTableCellBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether at least one selected cell is editable.
|
* Returns whether focus indicator border should be shown.
|
||||||
|
*
|
||||||
|
* @since 3.1
|
||||||
*/
|
*/
|
||||||
protected boolean isSelectionEditable( JTable table ) {
|
protected boolean shouldShowCellFocusIndicator( JTable table ) {
|
||||||
if( table.getRowSelectionAllowed() ) {
|
boolean rowSelectionAllowed = table.getRowSelectionAllowed();
|
||||||
int columnCount = table.getColumnCount();
|
boolean columnSelectionAllowed = table.getColumnSelectionAllowed();
|
||||||
int[] selectedRows = table.getSelectedRows();
|
|
||||||
for( int selectedRow : selectedRows ) {
|
|
||||||
for( int column = 0; column < columnCount; column++ ) {
|
|
||||||
if( table.isCellEditable( selectedRow, column ) )
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( table.getColumnSelectionAllowed() ) {
|
// do not show for cell selection mode
|
||||||
|
// (unlikely that user wants edit cell in case that multiple cells are selected;
|
||||||
|
// if only a single cell is selected then it is clear where the focus is)
|
||||||
|
if( rowSelectionAllowed && columnSelectionAllowed )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if( rowSelectionAllowed ) {
|
||||||
|
// row selection mode
|
||||||
|
|
||||||
|
// do not show if more than one row is selected
|
||||||
|
// (unlikely that user wants edit cell in this case)
|
||||||
|
if( table.getSelectedRowCount() != 1 )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// show always if there are too many columns to check for editable
|
||||||
|
int columnCount = table.getColumnCount();
|
||||||
|
if( columnCount > maxCheckCellsEditable )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// check whether at least one selected cell is editable
|
||||||
|
int selectedRow = table.getSelectedRow();
|
||||||
|
for( int column = 0; column < columnCount; column++ ) {
|
||||||
|
if( table.isCellEditable( selectedRow, column ) )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if( columnSelectionAllowed ) {
|
||||||
|
// column selection mode
|
||||||
|
|
||||||
|
// do not show if more than one column is selected
|
||||||
|
// (unlikely that user wants edit cell in this case)
|
||||||
|
if( table.getSelectedColumnCount() != 1 )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// show always if there are too many rows to check for editable
|
||||||
int rowCount = table.getRowCount();
|
int rowCount = table.getRowCount();
|
||||||
int[] selectedColumns = table.getSelectedColumns();
|
if( rowCount > maxCheckCellsEditable )
|
||||||
for( int selectedColumn : selectedColumns ) {
|
return true;
|
||||||
for( int row = 0; row < rowCount; row++ ) {
|
|
||||||
if( table.isCellEditable( row, selectedColumn ) )
|
// check whether at least one selected cell is editable
|
||||||
return true;
|
int selectedColumn = table.getSelectedColumn();
|
||||||
}
|
for( int row = 0; row < rowCount; row++ ) {
|
||||||
|
if( table.isCellEditable( row, selectedColumn ) )
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
|
import java.awt.Container;
|
||||||
import java.awt.Cursor;
|
import java.awt.Cursor;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
@@ -28,16 +29,15 @@ import java.awt.event.MouseEvent;
|
|||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import javax.swing.CellRendererPane;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
import javax.swing.SwingConstants;
|
import javax.swing.SwingConstants;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.border.Border;
|
|
||||||
import javax.swing.event.MouseInputListener;
|
import javax.swing.event.MouseInputListener;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.UIResource;
|
|
||||||
import javax.swing.plaf.basic.BasicTableHeaderUI;
|
import javax.swing.plaf.basic.BasicTableHeaderUI;
|
||||||
import javax.swing.table.JTableHeader;
|
import javax.swing.table.JTableHeader;
|
||||||
import javax.swing.table.TableCellRenderer;
|
import javax.swing.table.TableCellRenderer;
|
||||||
@@ -45,6 +45,7 @@ import javax.swing.table.TableColumn;
|
|||||||
import javax.swing.table.TableColumnModel;
|
import javax.swing.table.TableColumnModel;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -59,6 +60,10 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*
|
*
|
||||||
* <!-- FlatTableHeaderUI -->
|
* <!-- FlatTableHeaderUI -->
|
||||||
*
|
*
|
||||||
|
* @uiDefault TableHeader.hoverBackground Color optional
|
||||||
|
* @uiDefault TableHeader.hoverForeground Color optional
|
||||||
|
* @uiDefault TableHeader.pressedBackground Color optional
|
||||||
|
* @uiDefault TableHeader.pressedForeground Color optional
|
||||||
* @uiDefault TableHeader.bottomSeparatorColor Color
|
* @uiDefault TableHeader.bottomSeparatorColor Color
|
||||||
* @uiDefault TableHeader.height int
|
* @uiDefault TableHeader.height int
|
||||||
* @uiDefault TableHeader.sortIconPosition String right (default), left, top or bottom
|
* @uiDefault TableHeader.sortIconPosition String right (default), left, top or bottom
|
||||||
@@ -81,6 +86,10 @@ public class FlatTableHeaderUI
|
|||||||
extends BasicTableHeaderUI
|
extends BasicTableHeaderUI
|
||||||
implements StyleableUI
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
|
/** @since 3.1 */ @Styleable protected Color hoverBackground;
|
||||||
|
/** @since 3.1 */ @Styleable protected Color hoverForeground;
|
||||||
|
/** @since 3.1 */ @Styleable protected Color pressedBackground;
|
||||||
|
/** @since 3.1 */ @Styleable protected Color pressedForeground;
|
||||||
@Styleable protected Color bottomSeparatorColor;
|
@Styleable protected Color bottomSeparatorColor;
|
||||||
@Styleable protected int height;
|
@Styleable protected int height;
|
||||||
@Styleable(type=String.class) protected int sortIconPosition;
|
@Styleable(type=String.class) protected int sortIconPosition;
|
||||||
@@ -106,6 +115,11 @@ public class FlatTableHeaderUI
|
|||||||
public void installUI( JComponent c ) {
|
public void installUI( JComponent c ) {
|
||||||
super.installUI( c );
|
super.installUI( c );
|
||||||
|
|
||||||
|
// replace cell renderer pane
|
||||||
|
header.remove( rendererPane );
|
||||||
|
rendererPane = new FlatTableHeaderCellRendererPane();
|
||||||
|
header.add( rendererPane );
|
||||||
|
|
||||||
installStyle();
|
installStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,6 +127,10 @@ public class FlatTableHeaderUI
|
|||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
|
hoverBackground = UIManager.getColor( "TableHeader.hoverBackground" );
|
||||||
|
hoverForeground = UIManager.getColor( "TableHeader.hoverForeground" );
|
||||||
|
pressedBackground = UIManager.getColor( "TableHeader.pressedBackground" );
|
||||||
|
pressedForeground = UIManager.getColor( "TableHeader.pressedForeground" );
|
||||||
bottomSeparatorColor = UIManager.getColor( "TableHeader.bottomSeparatorColor" );
|
bottomSeparatorColor = UIManager.getColor( "TableHeader.bottomSeparatorColor" );
|
||||||
height = UIManager.getInt( "TableHeader.height" );
|
height = UIManager.getInt( "TableHeader.height" );
|
||||||
sortIconPosition = parseSortIconPosition( UIManager.getString( "TableHeader.sortIconPosition" ) );
|
sortIconPosition = parseSortIconPosition( UIManager.getString( "TableHeader.sortIconPosition" ) );
|
||||||
@@ -122,6 +140,10 @@ public class FlatTableHeaderUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
hoverBackground = null;
|
||||||
|
hoverForeground = null;
|
||||||
|
pressedBackground = null;
|
||||||
|
pressedForeground = null;
|
||||||
bottomSeparatorColor = null;
|
bottomSeparatorColor = null;
|
||||||
|
|
||||||
oldStyleValues = null;
|
oldStyleValues = null;
|
||||||
@@ -211,6 +233,12 @@ public class FlatTableHeaderUI
|
|||||||
return super.getRolloverColumn();
|
return super.getRolloverColumn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void rolloverColumnUpdated( int oldColumn, int newColumn ) {
|
||||||
|
HiDPIUtils.repaint( header, header.getHeaderRect( oldColumn ) );
|
||||||
|
HiDPIUtils.repaint( header, header.getHeaderRect( newColumn ) );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paint( Graphics g, JComponent c ) {
|
public void paint( Graphics g, JComponent c ) {
|
||||||
fixDraggedAndResizingColumns( header );
|
fixDraggedAndResizingColumns( header );
|
||||||
@@ -243,21 +271,8 @@ public class FlatTableHeaderUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// temporary use own default renderer if necessary
|
|
||||||
FlatTableCellHeaderRenderer sortIconRenderer = null;
|
|
||||||
if( sortIconPosition != SwingConstants.RIGHT ) {
|
|
||||||
sortIconRenderer = new FlatTableCellHeaderRenderer( header.getDefaultRenderer() );
|
|
||||||
header.setDefaultRenderer( sortIconRenderer );
|
|
||||||
}
|
|
||||||
|
|
||||||
// paint header
|
// paint header
|
||||||
super.paint( g, c );
|
super.paint( g, c );
|
||||||
|
|
||||||
// restore default renderer
|
|
||||||
if( sortIconRenderer != null ) {
|
|
||||||
sortIconRenderer.reset();
|
|
||||||
header.setDefaultRenderer( sortIconRenderer.delegate );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSystemDefaultRenderer( Object headerRenderer ) {
|
private boolean isSystemDefaultRenderer( Object headerRenderer ) {
|
||||||
@@ -315,80 +330,129 @@ public class FlatTableHeaderUI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class FlatTableCellHeaderRenderer ----------------------------------
|
//---- class FlatTableHeaderCellRendererPane ------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A delegating header renderer that is only used to paint sort arrows at
|
* Cell renderer pane that is used to paint hover and pressed background/foreground
|
||||||
* top, bottom or left position.
|
* and to paint sort arrows at top, bottom or left position.
|
||||||
*/
|
*/
|
||||||
private class FlatTableCellHeaderRenderer
|
private class FlatTableHeaderCellRendererPane
|
||||||
implements TableCellRenderer, Border, UIResource
|
extends CellRendererPane
|
||||||
{
|
{
|
||||||
private final TableCellRenderer delegate;
|
private final Icon ascendingSortIcon;
|
||||||
|
private final Icon descendingSortIcon;
|
||||||
|
|
||||||
private JLabel l;
|
public FlatTableHeaderCellRendererPane() {
|
||||||
private int oldHorizontalTextPosition = -1;
|
ascendingSortIcon = UIManager.getIcon( "Table.ascendingSortIcon" );
|
||||||
private Border origBorder;
|
descendingSortIcon = UIManager.getIcon( "Table.descendingSortIcon" );
|
||||||
private Icon sortIcon;
|
|
||||||
|
|
||||||
FlatTableCellHeaderRenderer( TableCellRenderer delegate ) {
|
|
||||||
this.delegate = delegate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected,
|
public void paintComponent( Graphics g, Component c, Container p, int x, int y, int w, int h, boolean shouldValidate ) {
|
||||||
boolean hasFocus, int row, int column )
|
if( !(c instanceof JLabel) ) {
|
||||||
{
|
super.paintComponent( g, c, p, x, y, w, h, shouldValidate );
|
||||||
Component c = delegate.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column );
|
return;
|
||||||
if( !(c instanceof JLabel) )
|
|
||||||
return c;
|
|
||||||
|
|
||||||
l = (JLabel) c;
|
|
||||||
|
|
||||||
if( sortIconPosition == SwingConstants.LEFT ) {
|
|
||||||
if( oldHorizontalTextPosition < 0 )
|
|
||||||
oldHorizontalTextPosition = l.getHorizontalTextPosition();
|
|
||||||
l.setHorizontalTextPosition( SwingConstants.RIGHT );
|
|
||||||
} else {
|
|
||||||
// top or bottom
|
|
||||||
sortIcon = l.getIcon();
|
|
||||||
origBorder = l.getBorder();
|
|
||||||
l.setIcon( null );
|
|
||||||
l.setBorder( this );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return l;
|
JLabel l = (JLabel) c;
|
||||||
}
|
Color oldBackground = null;
|
||||||
|
Color oldForeground = null;
|
||||||
|
boolean oldOpaque = false;
|
||||||
|
Icon oldIcon = null;
|
||||||
|
int oldHorizontalTextPosition = -1;
|
||||||
|
|
||||||
void reset() {
|
// hover and pressed background/foreground
|
||||||
if( l != null && sortIconPosition == SwingConstants.LEFT && oldHorizontalTextPosition >= 0 )
|
TableColumn draggedColumn = header.getDraggedColumn();
|
||||||
|
Color background = null;
|
||||||
|
Color foreground = null;
|
||||||
|
if( draggedColumn != null &&
|
||||||
|
header.getTable().convertColumnIndexToView( draggedColumn.getModelIndex() )
|
||||||
|
== getColumn( x - header.getDraggedDistance(), w ) )
|
||||||
|
{
|
||||||
|
background = pressedBackground;
|
||||||
|
foreground = pressedForeground;
|
||||||
|
} else if( getRolloverColumn() >= 0 && getRolloverColumn() == getColumn( x, w ) ) {
|
||||||
|
background = hoverBackground;
|
||||||
|
foreground = hoverForeground;
|
||||||
|
}
|
||||||
|
if( background != null ) {
|
||||||
|
oldBackground = l.getBackground();
|
||||||
|
oldOpaque = l.isOpaque();
|
||||||
|
l.setBackground( FlatUIUtils.deriveColor( background, header.getBackground() ) );
|
||||||
|
l.setOpaque( true );
|
||||||
|
}
|
||||||
|
if( foreground != null ) {
|
||||||
|
oldForeground = l.getForeground();
|
||||||
|
l.setForeground( FlatUIUtils.deriveColor( foreground, header.getForeground() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort icon position
|
||||||
|
Icon icon = l.getIcon();
|
||||||
|
boolean isSortIcon = (icon != null && (icon == ascendingSortIcon || icon == descendingSortIcon));
|
||||||
|
if( isSortIcon ) {
|
||||||
|
if( sortIconPosition == SwingConstants.LEFT ) {
|
||||||
|
// left
|
||||||
|
oldHorizontalTextPosition = l.getHorizontalTextPosition();
|
||||||
|
l.setHorizontalTextPosition( SwingConstants.RIGHT );
|
||||||
|
} else if( sortIconPosition == SwingConstants.TOP || sortIconPosition == SwingConstants.BOTTOM ) {
|
||||||
|
// top or bottom
|
||||||
|
oldIcon = icon;
|
||||||
|
l.setIcon( null );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// paint renderer component
|
||||||
|
super.paintComponent( g, c, p, x, y, w, h, shouldValidate );
|
||||||
|
|
||||||
|
// paint top or bottom sort icon
|
||||||
|
if( isSortIcon && (sortIconPosition == SwingConstants.TOP || sortIconPosition == SwingConstants.BOTTOM) ) {
|
||||||
|
int xi = x + ((w - icon.getIconWidth()) / 2);
|
||||||
|
int yi = (sortIconPosition == SwingConstants.TOP)
|
||||||
|
? y + UIScale.scale( 1 )
|
||||||
|
: y + height - icon.getIconHeight()
|
||||||
|
- 1 // for gap
|
||||||
|
- (int) (1 * UIScale.getUserScaleFactor()); // for bottom border
|
||||||
|
icon.paintIcon( c, g, xi, yi );
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore modified renderer component properties
|
||||||
|
if( background != null ) {
|
||||||
|
l.setBackground( oldBackground );
|
||||||
|
l.setOpaque( oldOpaque );
|
||||||
|
}
|
||||||
|
if( foreground != null )
|
||||||
|
l.setForeground( oldForeground );
|
||||||
|
if( oldIcon != null )
|
||||||
|
l.setIcon( oldIcon );
|
||||||
|
if( oldHorizontalTextPosition >= 0 )
|
||||||
l.setHorizontalTextPosition( oldHorizontalTextPosition );
|
l.setHorizontalTextPosition( oldHorizontalTextPosition );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
* Get column index for given coordinates.
|
||||||
if( origBorder != null )
|
*/
|
||||||
origBorder.paintBorder( c, g, x, y, width, height );
|
private int getColumn( int x, int width ) {
|
||||||
|
TableColumnModel columnModel = header.getColumnModel();
|
||||||
|
int columnCount = columnModel.getColumnCount();
|
||||||
|
boolean ltr = header.getComponentOrientation().isLeftToRight();
|
||||||
|
int cx = ltr ? 0 : getWidthInRightToLef();
|
||||||
|
|
||||||
if( sortIcon != null ) {
|
for( int i = 0; i < columnCount; i++ ) {
|
||||||
int xi = x + ((width - sortIcon.getIconWidth()) / 2);
|
int cw = columnModel.getColumn( i ).getWidth();
|
||||||
int yi = (sortIconPosition == SwingConstants.TOP)
|
if( x == cx - (ltr ? 0 : cw) && width == cw )
|
||||||
? y + UIScale.scale( 1 )
|
return i;
|
||||||
: y + height - sortIcon.getIconHeight()
|
|
||||||
- 1 // for gap
|
cx += ltr ? cw : -cw;
|
||||||
- (int) (1 * UIScale.getUserScaleFactor()); // for bottom border
|
|
||||||
sortIcon.paintIcon( c, g, xi, yi );
|
|
||||||
}
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// similar to JTableHeader.getWidthInRightToLeft()
|
||||||
public Insets getBorderInsets( Component c ) {
|
private int getWidthInRightToLef() {
|
||||||
return (origBorder != null) ? origBorder.getBorderInsets( c ) : new Insets( 0, 0, 0, 0 );
|
JTable table = header.getTable();
|
||||||
}
|
return (table != null && table.getAutoResizeMode() != JTable.AUTO_RESIZE_OFF)
|
||||||
|
? table.getWidth()
|
||||||
@Override
|
: header.getWidth();
|
||||||
public boolean isBorderOpaque() {
|
|
||||||
return (origBorder != null) ? origBorder.isBorderOpaque() : false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,30 +17,57 @@
|
|||||||
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.Container;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
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.Point;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.Shape;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ComponentAdapter;
|
||||||
|
import java.awt.event.ComponentEvent;
|
||||||
|
import java.awt.event.ComponentListener;
|
||||||
import java.awt.event.FocusEvent;
|
import java.awt.event.FocusEvent;
|
||||||
import java.awt.event.FocusListener;
|
import java.awt.event.FocusListener;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import javax.swing.Action;
|
||||||
|
import javax.swing.ActionMap;
|
||||||
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JTable;
|
||||||
|
import javax.swing.JTextField;
|
||||||
import javax.swing.JViewport;
|
import javax.swing.JViewport;
|
||||||
|
import javax.swing.ListSelectionModel;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ListSelectionEvent;
|
||||||
|
import javax.swing.event.ListSelectionListener;
|
||||||
|
import javax.swing.event.TableColumnModelEvent;
|
||||||
|
import javax.swing.event.TableColumnModelListener;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicTableUI;
|
import javax.swing.plaf.basic.BasicTableUI;
|
||||||
|
import javax.swing.table.DefaultTableCellRenderer;
|
||||||
import javax.swing.table.JTableHeader;
|
import javax.swing.table.JTableHeader;
|
||||||
|
import javax.swing.table.TableCellRenderer;
|
||||||
|
import javax.swing.table.TableColumnModel;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.icons.FlatCheckBoxIcon;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatUIUtils.FlatPropertyWatcher;
|
||||||
import com.formdev.flatlaf.util.Graphics2DProxy;
|
import com.formdev.flatlaf.util.Graphics2DProxy;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
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,7 +107,10 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault Table.intercellSpacing Dimension
|
* @uiDefault Table.intercellSpacing Dimension
|
||||||
* @uiDefault Table.selectionInactiveBackground Color
|
* @uiDefault Table.selectionInactiveBackground Color
|
||||||
* @uiDefault Table.selectionInactiveForeground Color
|
* @uiDefault Table.selectionInactiveForeground Color
|
||||||
|
* @uiDefault Table.selectionInsets Insets
|
||||||
|
* @uiDefault Table.selectionArc int
|
||||||
* @uiDefault Table.paintOutsideAlternateRows boolean
|
* @uiDefault Table.paintOutsideAlternateRows boolean
|
||||||
|
* @uiDefault Table.editorSelectAllOnStartEditing boolean
|
||||||
*
|
*
|
||||||
* <!-- FlatTableCellBorder -->
|
* <!-- FlatTableCellBorder -->
|
||||||
*
|
*
|
||||||
@@ -107,6 +137,8 @@ public class FlatTableUI
|
|||||||
@Styleable protected Color selectionForeground;
|
@Styleable protected Color selectionForeground;
|
||||||
@Styleable protected Color selectionInactiveBackground;
|
@Styleable protected Color selectionInactiveBackground;
|
||||||
@Styleable protected Color selectionInactiveForeground;
|
@Styleable protected Color selectionInactiveForeground;
|
||||||
|
/** @since 3.5 */ @Styleable protected Insets selectionInsets;
|
||||||
|
/** @since 3.5 */ @Styleable protected int selectionArc;
|
||||||
|
|
||||||
// for FlatTableCellBorder
|
// for FlatTableCellBorder
|
||||||
/** @since 2 */ @Styleable protected Insets cellMargins;
|
/** @since 2 */ @Styleable protected Insets cellMargins;
|
||||||
@@ -116,8 +148,12 @@ public class FlatTableUI
|
|||||||
private boolean oldShowHorizontalLines;
|
private boolean oldShowHorizontalLines;
|
||||||
private boolean oldShowVerticalLines;
|
private boolean oldShowVerticalLines;
|
||||||
private Dimension oldIntercellSpacing;
|
private Dimension oldIntercellSpacing;
|
||||||
|
private TableCellRenderer oldBooleanRenderer;
|
||||||
|
|
||||||
private PropertyChangeListener propertyChangeListener;
|
private PropertyChangeListener propertyChangeListener;
|
||||||
|
private ComponentListener outsideAlternateRowsListener;
|
||||||
|
private ListSelectionListener rowSelectionListener;
|
||||||
|
private TableColumnModelListener columnSelectionListener;
|
||||||
private Map<String, Object> oldStyleValues;
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
@@ -144,6 +180,8 @@ public class FlatTableUI
|
|||||||
selectionForeground = UIManager.getColor( "Table.selectionForeground" );
|
selectionForeground = UIManager.getColor( "Table.selectionForeground" );
|
||||||
selectionInactiveBackground = UIManager.getColor( "Table.selectionInactiveBackground" );
|
selectionInactiveBackground = UIManager.getColor( "Table.selectionInactiveBackground" );
|
||||||
selectionInactiveForeground = UIManager.getColor( "Table.selectionInactiveForeground" );
|
selectionInactiveForeground = UIManager.getColor( "Table.selectionInactiveForeground" );
|
||||||
|
selectionInsets = UIManager.getInsets( "Table.selectionInsets" );
|
||||||
|
selectionArc = UIManager.getInt( "Table.selectionArc" );
|
||||||
|
|
||||||
toggleSelectionColors();
|
toggleSelectionColors();
|
||||||
|
|
||||||
@@ -152,18 +190,31 @@ public class FlatTableUI
|
|||||||
LookAndFeel.installProperty( table, "rowHeight", UIScale.scale( rowHeight ) );
|
LookAndFeel.installProperty( table, "rowHeight", UIScale.scale( rowHeight ) );
|
||||||
|
|
||||||
if( !showHorizontalLines ) {
|
if( !showHorizontalLines ) {
|
||||||
oldShowHorizontalLines = table.getShowHorizontalLines();
|
FlatPropertyWatcher.runIfNotChanged( table, "showHorizontalLines", () -> {
|
||||||
table.setShowHorizontalLines( false );
|
oldShowHorizontalLines = table.getShowHorizontalLines();
|
||||||
|
table.setShowHorizontalLines( false );
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
if( !showVerticalLines ) {
|
if( !showVerticalLines ) {
|
||||||
oldShowVerticalLines = table.getShowVerticalLines();
|
FlatPropertyWatcher.runIfNotChanged( table, "showVerticalLines", () -> {
|
||||||
table.setShowVerticalLines( false );
|
oldShowVerticalLines = table.getShowVerticalLines();
|
||||||
|
table.setShowVerticalLines( false );
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( intercellSpacing != null ) {
|
if( intercellSpacing != null ) {
|
||||||
oldIntercellSpacing = table.getIntercellSpacing();
|
FlatPropertyWatcher.runIfNotChanged( table, "rowMargin", () -> {
|
||||||
table.setIntercellSpacing( intercellSpacing );
|
oldIntercellSpacing = table.getIntercellSpacing();
|
||||||
|
table.setIntercellSpacing( intercellSpacing );
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// install boolean renderer
|
||||||
|
oldBooleanRenderer = table.getDefaultRenderer( Boolean.class );
|
||||||
|
if( oldBooleanRenderer instanceof UIResource )
|
||||||
|
table.setDefaultRenderer( Boolean.class, new FlatBooleanRenderer() );
|
||||||
|
else
|
||||||
|
oldBooleanRenderer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -178,14 +229,34 @@ public class FlatTableUI
|
|||||||
oldStyleValues = null;
|
oldStyleValues = null;
|
||||||
|
|
||||||
// restore old show horizontal/vertical lines (if not modified)
|
// restore old show horizontal/vertical lines (if not modified)
|
||||||
if( !showHorizontalLines && oldShowHorizontalLines && !table.getShowHorizontalLines() )
|
if( !showHorizontalLines && oldShowHorizontalLines && !table.getShowHorizontalLines() ) {
|
||||||
table.setShowHorizontalLines( true );
|
FlatPropertyWatcher.runIfNotChanged( table, "showHorizontalLines", () -> {
|
||||||
if( !showVerticalLines && oldShowVerticalLines && !table.getShowVerticalLines() )
|
table.setShowHorizontalLines( true );
|
||||||
table.setShowVerticalLines( true );
|
} );
|
||||||
|
}
|
||||||
|
if( !showVerticalLines && oldShowVerticalLines && !table.getShowVerticalLines() ) {
|
||||||
|
FlatPropertyWatcher.runIfNotChanged( table, "showVerticalLines", () -> {
|
||||||
|
table.setShowVerticalLines( true );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
// restore old intercell spacing (if not modified)
|
// restore old intercell spacing (if not modified)
|
||||||
if( intercellSpacing != null && table.getIntercellSpacing().equals( intercellSpacing ) )
|
if( intercellSpacing != null && table.getIntercellSpacing().equals( intercellSpacing ) ) {
|
||||||
table.setIntercellSpacing( oldIntercellSpacing );
|
FlatPropertyWatcher.runIfNotChanged( table, "rowMargin", () -> {
|
||||||
|
table.setIntercellSpacing( oldIntercellSpacing );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
// uninstall boolean renderer
|
||||||
|
if( table.getDefaultRenderer( Boolean.class ) instanceof FlatBooleanRenderer ) {
|
||||||
|
if( oldBooleanRenderer instanceof Component ) {
|
||||||
|
// because the old renderer component was not attached to any component hierarchy,
|
||||||
|
// its UI was not yet updated, and it is necessary to do it here
|
||||||
|
SwingUtilities.updateComponentTreeUI( (Component) oldBooleanRenderer );
|
||||||
|
}
|
||||||
|
table.setDefaultRenderer( Boolean.class, oldBooleanRenderer );
|
||||||
|
}
|
||||||
|
oldBooleanRenderer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -194,6 +265,28 @@ public class FlatTableUI
|
|||||||
|
|
||||||
propertyChangeListener = e -> {
|
propertyChangeListener = e -> {
|
||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
|
case "selectionModel":
|
||||||
|
if( rowSelectionListener != null ) {
|
||||||
|
Object oldModel = e.getOldValue();
|
||||||
|
Object newModel = e.getNewValue();
|
||||||
|
if( oldModel != null )
|
||||||
|
((ListSelectionModel)oldModel).removeListSelectionListener( rowSelectionListener );
|
||||||
|
if( newModel != null )
|
||||||
|
((ListSelectionModel)newModel).addListSelectionListener( rowSelectionListener );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "columnModel":
|
||||||
|
if( columnSelectionListener != null ) {
|
||||||
|
Object oldModel = e.getOldValue();
|
||||||
|
Object newModel = e.getNewValue();
|
||||||
|
if( oldModel != null )
|
||||||
|
((TableColumnModel)oldModel).removeColumnModelListener( columnSelectionListener );
|
||||||
|
if( newModel != null )
|
||||||
|
((TableColumnModel)newModel).addColumnModelListener( columnSelectionListener );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case FlatClientProperties.COMPONENT_FOCUS_OWNER:
|
case FlatClientProperties.COMPONENT_FOCUS_OWNER:
|
||||||
toggleSelectionColors();
|
toggleSelectionColors();
|
||||||
break;
|
break;
|
||||||
@@ -202,11 +295,14 @@ public class FlatTableUI
|
|||||||
case FlatClientProperties.STYLE_CLASS:
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
installStyle();
|
installStyle();
|
||||||
table.revalidate();
|
table.revalidate();
|
||||||
table.repaint();
|
HiDPIUtils.repaint( table );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
table.addPropertyChangeListener( propertyChangeListener );
|
table.addPropertyChangeListener( propertyChangeListener );
|
||||||
|
|
||||||
|
if( selectionArc > 0 )
|
||||||
|
installRepaintRoundedSelectionListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -215,6 +311,19 @@ public class FlatTableUI
|
|||||||
|
|
||||||
table.removePropertyChangeListener( propertyChangeListener );
|
table.removePropertyChangeListener( propertyChangeListener );
|
||||||
propertyChangeListener = null;
|
propertyChangeListener = null;
|
||||||
|
|
||||||
|
if( outsideAlternateRowsListener != null ) {
|
||||||
|
table.removeComponentListener( outsideAlternateRowsListener );
|
||||||
|
outsideAlternateRowsListener = null;
|
||||||
|
}
|
||||||
|
if( rowSelectionListener != null ) {
|
||||||
|
table.getSelectionModel().removeListSelectionListener( rowSelectionListener );
|
||||||
|
rowSelectionListener = null;
|
||||||
|
}
|
||||||
|
if( columnSelectionListener != null ) {
|
||||||
|
table.getColumnModel().removeColumnModelListener( columnSelectionListener );
|
||||||
|
columnSelectionListener = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -238,6 +347,18 @@ public class FlatTableUI
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installKeyboardActions() {
|
||||||
|
super.installKeyboardActions();
|
||||||
|
|
||||||
|
if( UIManager.getBoolean( "Table.editorSelectAllOnStartEditing" ) ) {
|
||||||
|
// get shared action map, used for all tables
|
||||||
|
ActionMap map = SwingUtilities.getUIActionMap( table );
|
||||||
|
if( map != null )
|
||||||
|
StartEditingAction.install( map, "startEditing" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
protected void installStyle() {
|
protected void installStyle() {
|
||||||
try {
|
try {
|
||||||
@@ -277,6 +398,11 @@ public class FlatTableUI
|
|||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
protected Object applyStyleProperty( String key, Object value ) {
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
if( "rowHeight".equals( key ) && value instanceof Integer )
|
||||||
|
value = UIScale.scale( (Integer) value );
|
||||||
|
else if( "selectionArc".equals( key ) && value instanceof Integer && (Integer) value > 0 )
|
||||||
|
installRepaintRoundedSelectionListeners();
|
||||||
|
|
||||||
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, table, key, value );
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, table, key, value );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,6 +464,7 @@ public class FlatTableUI
|
|||||||
|
|
||||||
double systemScaleFactor = UIScale.getSystemScaleFactor( (Graphics2D) g );
|
double systemScaleFactor = UIScale.getSystemScaleFactor( (Graphics2D) g );
|
||||||
double lineThickness = (1. / systemScaleFactor) * (int) systemScaleFactor;
|
double lineThickness = (1. / systemScaleFactor) * (int) systemScaleFactor;
|
||||||
|
double lineOffset = (1. - lineThickness) + 0.05; // adding 0.05 to fix line location in some cases
|
||||||
|
|
||||||
// Java 8 uses drawLine() to paint grid lines
|
// Java 8 uses drawLine() to paint grid lines
|
||||||
// Java 9+ uses fillRect() to paint grid lines (except for dragged column)
|
// Java 9+ uses fillRect() to paint grid lines (except for dragged column)
|
||||||
@@ -380,11 +507,11 @@ public class FlatTableUI
|
|||||||
// reduce line thickness to avoid unstable painted line thickness
|
// reduce line thickness to avoid unstable painted line thickness
|
||||||
if( lineThickness != 1 ) {
|
if( lineThickness != 1 ) {
|
||||||
if( horizontalLines && height == 1 && wasInvokedFromPaintGrid() ) {
|
if( horizontalLines && height == 1 && wasInvokedFromPaintGrid() ) {
|
||||||
super.fill( new Rectangle2D.Double( x, y, width, lineThickness ) );
|
super.fill( new Rectangle2D.Double( x, y + lineOffset, width, lineThickness ) );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if( verticalLines && width == 1 && y == 0 && wasInvokedFromPaintGrid() ) {
|
if( verticalLines && width == 1 && y == 0 && wasInvokedFromPaintGrid() ) {
|
||||||
super.fill( new Rectangle2D.Double( x, y, lineThickness, height ) );
|
super.fill( new Rectangle2D.Double( x + lineOffset, y, lineThickness, height ) );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -402,6 +529,10 @@ public class FlatTableUI
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rounded selection or selection insets
|
||||||
|
if( selectionArc > 0 || (selectionInsets != null && !FlatUIUtils.isInsetsEmpty( selectionInsets )) )
|
||||||
|
g = new RoundedSelectionGraphics( g, UIManager.getColor( "Table.alternateRowColor" ) );
|
||||||
|
|
||||||
super.paint( g, c );
|
super.paint( g, c );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,8 +578,6 @@ public class FlatTableUI
|
|||||||
boolean paintOutside = UIManager.getBoolean( "Table.paintOutsideAlternateRows" );
|
boolean paintOutside = UIManager.getBoolean( "Table.paintOutsideAlternateRows" );
|
||||||
Color alternateColor;
|
Color alternateColor;
|
||||||
if( paintOutside && (alternateColor = UIManager.getColor( "Table.alternateRowColor" )) != null ) {
|
if( paintOutside && (alternateColor = UIManager.getColor( "Table.alternateRowColor" )) != null ) {
|
||||||
g.setColor( alternateColor );
|
|
||||||
|
|
||||||
int rowCount = table.getRowCount();
|
int rowCount = table.getRowCount();
|
||||||
|
|
||||||
// paint alternating empty rows below the table
|
// paint alternating empty rows below the table
|
||||||
@@ -457,11 +586,409 @@ public class FlatTableUI
|
|||||||
int tableWidth = table.getWidth();
|
int tableWidth = table.getWidth();
|
||||||
int rowHeight = table.getRowHeight();
|
int rowHeight = table.getRowHeight();
|
||||||
|
|
||||||
|
g.setColor( alternateColor );
|
||||||
|
|
||||||
|
int x = viewport.getComponentOrientation().isLeftToRight() ? 0 : viewportWidth - tableWidth;
|
||||||
for( int y = tableHeight, row = rowCount; y < viewportHeight; y += rowHeight, row++ ) {
|
for( int y = tableHeight, row = rowCount; y < viewportHeight; y += rowHeight, row++ ) {
|
||||||
if( row % 2 != 0 )
|
if( row % 2 != 0 )
|
||||||
g.fillRect( 0, y, tableWidth, rowHeight );
|
paintAlternateRowBackground( g, -1, -1, x, y, tableWidth, rowHeight );
|
||||||
|
}
|
||||||
|
|
||||||
|
// add listener on demand
|
||||||
|
if( outsideAlternateRowsListener == null && table.getAutoResizeMode() == JTable.AUTO_RESIZE_OFF ) {
|
||||||
|
outsideAlternateRowsListener = new FlatOutsideAlternateRowsListener();
|
||||||
|
table.addComponentListener( outsideAlternateRowsListener );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paints (rounded) alternate row background.
|
||||||
|
* Supports {@link #selectionArc} and {@link #selectionInsets}.
|
||||||
|
* <p>
|
||||||
|
* <b>Note:</b> This method is only invoked if either selection arc
|
||||||
|
* is greater than zero or if selection insets are not empty.
|
||||||
|
*
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
protected void paintAlternateRowBackground( Graphics g, int row, int column, int x, int y, int width, int height ) {
|
||||||
|
Insets insets = (selectionInsets != null) ? (Insets) selectionInsets.clone() : null;
|
||||||
|
float arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight;
|
||||||
|
arcTopLeft = arcTopRight = arcBottomLeft = arcBottomRight = UIScale.scale( selectionArc / 2f );
|
||||||
|
|
||||||
|
if( column >= 0 ) {
|
||||||
|
// selection insets
|
||||||
|
|
||||||
|
// selection arc
|
||||||
|
if( column > 0 ) {
|
||||||
|
if( insets != null )
|
||||||
|
insets.left = 0;
|
||||||
|
|
||||||
|
if( table.getComponentOrientation().isLeftToRight() )
|
||||||
|
arcTopLeft = arcBottomLeft = 0;
|
||||||
|
else
|
||||||
|
arcTopRight = arcBottomRight = 0;
|
||||||
|
}
|
||||||
|
if( column < table.getColumnCount() - 1 ) {
|
||||||
|
if( insets != null )
|
||||||
|
insets.right = 0;
|
||||||
|
|
||||||
|
if( table.getComponentOrientation().isLeftToRight() )
|
||||||
|
arcTopRight = arcBottomRight = 0;
|
||||||
|
else
|
||||||
|
arcTopLeft = arcBottomLeft = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatUIUtils.paintSelection( (Graphics2D) g, x, y, width, height,
|
||||||
|
UIScale.scale( insets ), arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paints (rounded) cell selection.
|
||||||
|
* Supports {@link #selectionArc} and {@link #selectionInsets}.
|
||||||
|
* <p>
|
||||||
|
* <b>Note:</b> This method is only invoked if either selection arc
|
||||||
|
* is greater than zero or if selection insets are not empty.
|
||||||
|
*
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
protected void paintCellSelection( Graphics g, int row, int column, int x, int y, int width, int height ) {
|
||||||
|
boolean rowSelAllowed = table.getRowSelectionAllowed();
|
||||||
|
boolean colSelAllowed = table.getColumnSelectionAllowed();
|
||||||
|
boolean rowSelOnly = rowSelAllowed && !colSelAllowed;
|
||||||
|
boolean colSelOnly = colSelAllowed && !rowSelAllowed;
|
||||||
|
boolean cellOnlySel = rowSelAllowed && colSelAllowed;
|
||||||
|
|
||||||
|
// get selection state of surrounding cells
|
||||||
|
boolean leftSelected = (column > 0 && (rowSelOnly || table.isCellSelected( row, column - 1 )));
|
||||||
|
boolean topSelected = (row > 0 && (colSelOnly || table.isCellSelected( row - 1, column )));
|
||||||
|
boolean rightSelected = (column < table.getColumnCount() - 1 && (rowSelOnly || table.isCellSelected( row, column + 1 )));
|
||||||
|
boolean bottomSelected = (row < table.getRowCount() - 1 && (colSelOnly || table.isCellSelected( row + 1, column )));
|
||||||
|
if( !table.getComponentOrientation().isLeftToRight() ) {
|
||||||
|
boolean temp = leftSelected;
|
||||||
|
leftSelected = rightSelected;
|
||||||
|
rightSelected = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// selection insets
|
||||||
|
// (insets are applied to whole row if row-only selection is used,
|
||||||
|
// or to whole column if column-only selection is used,
|
||||||
|
// or to cell if cell selection is used)
|
||||||
|
Insets insets = (selectionInsets != null) ? (Insets) selectionInsets.clone() : null;
|
||||||
|
if( insets != null ) {
|
||||||
|
if( rowSelOnly && leftSelected )
|
||||||
|
insets.left = 0;
|
||||||
|
if( rowSelOnly && rightSelected )
|
||||||
|
insets.right = 0;
|
||||||
|
if( colSelOnly && topSelected )
|
||||||
|
insets.top = 0;
|
||||||
|
if( colSelOnly && bottomSelected )
|
||||||
|
insets.bottom = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// selection arc
|
||||||
|
float arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight;
|
||||||
|
arcTopLeft = arcTopRight = arcBottomLeft = arcBottomRight = UIScale.scale( selectionArc / 2f );
|
||||||
|
if( selectionArc > 0 ) {
|
||||||
|
// note that intercellSpacing is not considered as a gap because
|
||||||
|
// grid lines are usually painted to intercell space
|
||||||
|
boolean hasRowGap = (rowSelOnly || cellOnlySel) && insets != null && (insets.top != 0 || insets.bottom != 0);
|
||||||
|
boolean hasColGap = (colSelOnly || cellOnlySel) && insets != null && (insets.left != 0 || insets.right != 0);
|
||||||
|
|
||||||
|
if( leftSelected && !hasColGap )
|
||||||
|
arcTopLeft = arcBottomLeft = 0;
|
||||||
|
if( rightSelected && !hasColGap )
|
||||||
|
arcTopRight = arcBottomRight = 0;
|
||||||
|
if( topSelected && !hasRowGap )
|
||||||
|
arcTopLeft = arcTopRight = 0;
|
||||||
|
if( bottomSelected && !hasRowGap )
|
||||||
|
arcBottomLeft = arcBottomRight = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatUIUtils.paintSelection( (Graphics2D) g, x, y, width, height,
|
||||||
|
UIScale.scale( insets ), arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paints a cell selection at the given coordinates.
|
||||||
|
* The selection color must be set on the graphics context.
|
||||||
|
* <p>
|
||||||
|
* This method is intended for use in custom cell renderers to support
|
||||||
|
* {@link #selectionArc} and {@link #selectionInsets}.
|
||||||
|
*
|
||||||
|
* @since 3.5
|
||||||
|
*/
|
||||||
|
public static void paintCellSelection( JTable table, Graphics g, int row, int column, int x, int y, int width, int height ) {
|
||||||
|
if( !(table.getUI() instanceof FlatTableUI) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
FlatTableUI ui = (FlatTableUI) table.getUI();
|
||||||
|
ui.paintCellSelection( g, row, column, x, y, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installRepaintRoundedSelectionListeners() {
|
||||||
|
if( rowSelectionListener == null ) {
|
||||||
|
rowSelectionListener = this::repaintRoundedRowSelection;
|
||||||
|
table.getSelectionModel().addListSelectionListener( rowSelectionListener );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( columnSelectionListener == null ) {
|
||||||
|
columnSelectionListener = new TableColumnModelListener() {
|
||||||
|
@Override
|
||||||
|
public void columnSelectionChanged( ListSelectionEvent e ) {
|
||||||
|
repaintRoundedColumnSelection( e );
|
||||||
|
}
|
||||||
|
@Override public void columnRemoved( TableColumnModelEvent e ) {}
|
||||||
|
@Override public void columnMoved( TableColumnModelEvent e ) {}
|
||||||
|
@Override public void columnMarginChanged( ChangeEvent e ) {}
|
||||||
|
@Override public void columnAdded( TableColumnModelEvent e ) {}
|
||||||
|
};
|
||||||
|
table.getColumnModel().addColumnModelListener( columnSelectionListener );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void repaintRoundedRowSelection( ListSelectionEvent e ) {
|
||||||
|
if( selectionArc <= 0 || !table.getRowSelectionAllowed() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
int rowCount = table.getRowCount();
|
||||||
|
int columnCount = table.getColumnCount();
|
||||||
|
if( rowCount <= 0 || columnCount <= 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// repaint including rows before and after changed selection
|
||||||
|
int firstRow = Math.max( 0, Math.min( e.getFirstIndex() - 1, rowCount - 1 ) );
|
||||||
|
int lastRow = Math.max( 0, Math.min( e.getLastIndex() + 1, rowCount - 1 ) );
|
||||||
|
Rectangle firstRect = table.getCellRect( firstRow, 0, false );
|
||||||
|
Rectangle lastRect = table.getCellRect( lastRow, columnCount - 1, false );
|
||||||
|
table.repaint( firstRect.union( lastRect ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void repaintRoundedColumnSelection( ListSelectionEvent e ) {
|
||||||
|
if( selectionArc <= 0 || !table.getColumnSelectionAllowed() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
int rowCount = table.getRowCount();
|
||||||
|
int columnCount = table.getColumnCount();
|
||||||
|
if( rowCount <= 0 || columnCount <= 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// limit to selected rows for cell selection
|
||||||
|
int firstRow = 0;
|
||||||
|
int lastRow = rowCount - 1;
|
||||||
|
if( table.getRowSelectionAllowed() ) {
|
||||||
|
firstRow = table.getSelectionModel().getMinSelectionIndex();
|
||||||
|
lastRow = table.getSelectionModel().getMaxSelectionIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
// repaint including columns before and after changed selection
|
||||||
|
int firstColumn = Math.max( 0, Math.min( e.getFirstIndex() - 1, columnCount - 1 ) );
|
||||||
|
int lastColumn = Math.max( 0, Math.min( e.getLastIndex() + 1, columnCount - 1 ) );
|
||||||
|
Rectangle firstRect = table.getCellRect( firstRow, firstColumn, false );
|
||||||
|
Rectangle lastRect = table.getCellRect( lastRow, lastColumn, false );
|
||||||
|
table.repaint( firstRect.union( lastRect ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class RoundedSelectionGraphics -------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Because selection painting is done in the cell renderer, it would be
|
||||||
|
* necessary to require a FlatLaf specific renderer to implement rounded selection.
|
||||||
|
* Using a LaF specific renderer was avoided because often a custom renderer is
|
||||||
|
* already used in applications. Then either the rounded selection is not used,
|
||||||
|
* or the application has to be changed to extend a FlatLaf renderer.
|
||||||
|
* <p>
|
||||||
|
* To solve this, a graphics proxy is used that paints rounded selection
|
||||||
|
* if row/column/cell is selected and the renderer wants to fill the background.
|
||||||
|
*/
|
||||||
|
private class RoundedSelectionGraphics
|
||||||
|
extends Graphics2DProxy
|
||||||
|
{
|
||||||
|
private final Color alternateRowColor;
|
||||||
|
|
||||||
|
// used to avoid endless loop in case that paintCellSelection() invokes
|
||||||
|
// g.fillRect() with full bounds (selectionInsets is 0,0,0,0)
|
||||||
|
private boolean inPaintSelection;
|
||||||
|
|
||||||
|
RoundedSelectionGraphics( Graphics delegate, Color alternateRowColor ) {
|
||||||
|
super( (Graphics2D) delegate );
|
||||||
|
this.alternateRowColor = alternateRowColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Graphics create() {
|
||||||
|
return new RoundedSelectionGraphics( super.create(), alternateRowColor );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Graphics create( int x, int y, int width, int height ) {
|
||||||
|
return new RoundedSelectionGraphics( super.create( x, y, width, height ), alternateRowColor );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fillRect( int x, int y, int width, int height ) {
|
||||||
|
if( fillCellSelection( x, y, width, height ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
super.fillRect( x, y, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fill( Shape shape ) {
|
||||||
|
if( shape instanceof Rectangle2D ) {
|
||||||
|
Rectangle2D r = (Rectangle2D) shape;
|
||||||
|
double x = r.getX();
|
||||||
|
double y = r.getY();
|
||||||
|
double width = r.getWidth();
|
||||||
|
double height = r.getHeight();
|
||||||
|
if( x == (int) x && y == (int) y && width == (int) width && height == (int) height ) {
|
||||||
|
if( fillCellSelection( (int) x, (int) y, (int) width, (int) height ) )
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.fill( shape );
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean fillCellSelection( int x, int y, int width, int height ) {
|
||||||
|
if( inPaintSelection )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Color color;
|
||||||
|
Component rendererComponent;
|
||||||
|
if( x == 0 && y == 0 &&
|
||||||
|
((color = getColor()) == table.getSelectionBackground() ||
|
||||||
|
(alternateRowColor != null && color == alternateRowColor)) &&
|
||||||
|
(rendererComponent = findActiveRendererComponent()) != null &&
|
||||||
|
width == rendererComponent.getWidth() &&
|
||||||
|
height == rendererComponent.getHeight() )
|
||||||
|
{
|
||||||
|
Point location = rendererComponent.getLocation();
|
||||||
|
int row = table.rowAtPoint( location );
|
||||||
|
int column = table.columnAtPoint( location );
|
||||||
|
if( row >= 0 && column >= 0 ) {
|
||||||
|
inPaintSelection = true;
|
||||||
|
if( color == table.getSelectionBackground() )
|
||||||
|
paintCellSelection( this, row, column, x, y, width, height );
|
||||||
|
else
|
||||||
|
paintAlternateRowBackground( this, row, column, x, y, width, height );
|
||||||
|
inPaintSelection = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A CellRendererPane may contain multiple components, if multiple renderers
|
||||||
|
* are used. Inactive renderer components have size {@code 0x0}.
|
||||||
|
*/
|
||||||
|
private Component findActiveRendererComponent() {
|
||||||
|
int count = rendererPane.getComponentCount();
|
||||||
|
for( int i = 0; i < count; i++ ) {
|
||||||
|
Component c = rendererPane.getComponent( i );
|
||||||
|
if( c.getWidth() > 0 && c.getHeight() > 0 )
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class OutsideAlternateRowsListener ---------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used if table auto-resize-mode is off to repaint outside alternate rows
|
||||||
|
* when table width changed (column resized) or component orientation changed.
|
||||||
|
*/
|
||||||
|
private class FlatOutsideAlternateRowsListener
|
||||||
|
extends ComponentAdapter
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void componentHidden( ComponentEvent e ) {
|
||||||
|
Container viewport = SwingUtilities.getUnwrappedParent( table );
|
||||||
|
if( viewport instanceof JViewport )
|
||||||
|
HiDPIUtils.repaint( viewport );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void componentMoved( ComponentEvent e ) {
|
||||||
|
repaintAreaBelowTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void componentResized( ComponentEvent e ) {
|
||||||
|
repaintAreaBelowTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void repaintAreaBelowTable() {
|
||||||
|
Container viewport = SwingUtilities.getUnwrappedParent( table );
|
||||||
|
if( viewport instanceof JViewport ) {
|
||||||
|
int viewportHeight = viewport.getHeight();
|
||||||
|
int tableHeight = table.getHeight();
|
||||||
|
if( tableHeight < viewportHeight )
|
||||||
|
HiDPIUtils.repaint( viewport, 0, tableHeight, viewport.getWidth(), viewportHeight - tableHeight );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class FlatBooleanRenderer ------------------------------------------
|
||||||
|
|
||||||
|
private static class FlatBooleanRenderer
|
||||||
|
extends DefaultTableCellRenderer
|
||||||
|
implements UIResource
|
||||||
|
{
|
||||||
|
private boolean selected;
|
||||||
|
|
||||||
|
FlatBooleanRenderer() {
|
||||||
|
setHorizontalAlignment( SwingConstants.CENTER );
|
||||||
|
Icon icon = new FlatCheckBoxIcon() {
|
||||||
|
@Override
|
||||||
|
protected boolean isSelected( Component c ) {
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
setIcon( icon );
|
||||||
|
setDisabledIcon( icon );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setValue( Object value ) {
|
||||||
|
selected = (value != null && (Boolean) value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class StartEditingAction -------------------------------------------
|
||||||
|
|
||||||
|
private static class StartEditingAction
|
||||||
|
extends FlatUIAction
|
||||||
|
{
|
||||||
|
static void install( ActionMap map, String key ) {
|
||||||
|
Action oldAction = map.get( key );
|
||||||
|
if( oldAction == null || oldAction instanceof StartEditingAction )
|
||||||
|
return; // not found or already installed
|
||||||
|
|
||||||
|
map.put( key, new StartEditingAction( oldAction ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private StartEditingAction( Action delegate ) {
|
||||||
|
super( delegate );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed( ActionEvent e ) {
|
||||||
|
JTable table = (JTable) e.getSource();
|
||||||
|
|
||||||
|
Component oldEditorComp = table.getEditorComponent();
|
||||||
|
|
||||||
|
delegate.actionPerformed( e );
|
||||||
|
|
||||||
|
// select all text in editor if editing starts with F2 key
|
||||||
|
Component editorComp = table.getEditorComponent();
|
||||||
|
if( oldEditorComp == null && editorComp instanceof JTextField )
|
||||||
|
((JTextField)editorComp).selectAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ import com.formdev.flatlaf.util.LoggingFacade;
|
|||||||
* <!-- FlatTextAreaUI -->
|
* <!-- FlatTextAreaUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault Component.minimumWidth int
|
* @uiDefault Component.minimumWidth int
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
|
||||||
* @uiDefault TextArea.disabledBackground Color used if not enabled
|
* @uiDefault TextArea.disabledBackground Color used if not enabled
|
||||||
* @uiDefault TextArea.inactiveBackground Color used if not editable
|
* @uiDefault TextArea.inactiveBackground Color used if not editable
|
||||||
* @uiDefault TextArea.focusedBackground Color optional
|
* @uiDefault TextArea.focusedBackground Color optional
|
||||||
@@ -66,7 +65,6 @@ public class FlatTextAreaUI
|
|||||||
implements StyleableUI
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
@Styleable protected int minimumWidth;
|
@Styleable protected int minimumWidth;
|
||||||
protected boolean isIntelliJTheme;
|
|
||||||
private Color background;
|
private Color background;
|
||||||
@Styleable protected Color disabledBackground;
|
@Styleable protected Color disabledBackground;
|
||||||
@Styleable protected Color inactiveBackground;
|
@Styleable protected Color inactiveBackground;
|
||||||
@@ -103,7 +101,6 @@ public class FlatTextAreaUI
|
|||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
||||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
|
||||||
background = UIManager.getColor( "TextArea.background" );
|
background = UIManager.getColor( "TextArea.background" );
|
||||||
disabledBackground = UIManager.getColor( "TextArea.disabledBackground" );
|
disabledBackground = UIManager.getColor( "TextArea.disabledBackground" );
|
||||||
inactiveBackground = UIManager.getColor( "TextArea.inactiveBackground" );
|
inactiveBackground = UIManager.getColor( "TextArea.inactiveBackground" );
|
||||||
@@ -227,6 +224,6 @@ public class FlatTextAreaUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintBackground( Graphics g ) {
|
protected void paintBackground( Graphics g ) {
|
||||||
FlatEditorPaneUI.paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground );
|
FlatEditorPaneUI.paintBackground( g, getComponent(), focusedBackground );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ 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.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.event.DocumentEvent;
|
import javax.swing.event.DocumentEvent;
|
||||||
import javax.swing.event.DocumentListener;
|
import javax.swing.event.DocumentListener;
|
||||||
@@ -81,7 +82,6 @@ import com.formdev.flatlaf.util.LoggingFacade;
|
|||||||
* <!-- FlatTextFieldUI -->
|
* <!-- FlatTextFieldUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault Component.minimumWidth int
|
* @uiDefault Component.minimumWidth int
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
|
||||||
* @uiDefault TextField.placeholderForeground Color
|
* @uiDefault TextField.placeholderForeground Color
|
||||||
* @uiDefault TextField.focusedBackground Color optional
|
* @uiDefault TextField.focusedBackground Color optional
|
||||||
* @uiDefault TextField.iconTextGap int optional, default is 4
|
* @uiDefault TextField.iconTextGap int optional, default is 4
|
||||||
@@ -95,7 +95,6 @@ public class FlatTextFieldUI
|
|||||||
implements StyleableUI
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
@Styleable protected int minimumWidth;
|
@Styleable protected int minimumWidth;
|
||||||
protected boolean isIntelliJTheme;
|
|
||||||
private Color background;
|
private Color background;
|
||||||
@Styleable protected Color disabledBackground;
|
@Styleable protected Color disabledBackground;
|
||||||
@Styleable protected Color inactiveBackground;
|
@Styleable protected Color inactiveBackground;
|
||||||
@@ -165,7 +164,6 @@ public class FlatTextFieldUI
|
|||||||
|
|
||||||
String prefix = getPropertyPrefix();
|
String prefix = getPropertyPrefix();
|
||||||
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
||||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
|
||||||
background = UIManager.getColor( prefix + ".background" );
|
background = UIManager.getColor( prefix + ".background" );
|
||||||
disabledBackground = UIManager.getColor( prefix + ".disabledBackground" );
|
disabledBackground = UIManager.getColor( prefix + ".disabledBackground" );
|
||||||
inactiveBackground = UIManager.getColor( prefix + ".inactiveBackground" );
|
inactiveBackground = UIManager.getColor( prefix + ".inactiveBackground" );
|
||||||
@@ -176,8 +174,6 @@ public class FlatTextFieldUI
|
|||||||
defaultMargin = UIManager.getInsets( prefix + ".margin" );
|
defaultMargin = UIManager.getInsets( prefix + ".margin" );
|
||||||
|
|
||||||
LookAndFeel.installProperty( getComponent(), "opaque", false );
|
LookAndFeel.installProperty( getComponent(), "opaque", false );
|
||||||
|
|
||||||
MigLayoutVisualPadding.install( getComponent() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -195,8 +191,6 @@ public class FlatTextFieldUI
|
|||||||
|
|
||||||
oldStyleValues = null;
|
oldStyleValues = null;
|
||||||
borderShared = null;
|
borderShared = null;
|
||||||
|
|
||||||
MigLayoutVisualPadding.uninstall( getComponent() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -206,6 +200,8 @@ public class FlatTextFieldUI
|
|||||||
// necessary to update focus border and background
|
// necessary to update focus border and background
|
||||||
focusListener = new FlatUIUtils.RepaintFocusListener( getComponent(), null );
|
focusListener = new FlatUIUtils.RepaintFocusListener( getComponent(), null );
|
||||||
getComponent().addFocusListener( focusListener );
|
getComponent().addFocusListener( focusListener );
|
||||||
|
|
||||||
|
MigLayoutVisualPadding.install( getComponent() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -219,6 +215,8 @@ public class FlatTextFieldUI
|
|||||||
getComponent().getDocument().removeDocumentListener( documentListener );
|
getComponent().getDocument().removeDocumentListener( documentListener );
|
||||||
documentListener = null;
|
documentListener = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MigLayoutVisualPadding.uninstall( getComponent() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -241,7 +239,7 @@ public class FlatTextFieldUI
|
|||||||
case COMPONENT_ROUND_RECT:
|
case COMPONENT_ROUND_RECT:
|
||||||
case OUTLINE:
|
case OUTLINE:
|
||||||
case TEXT_FIELD_PADDING:
|
case TEXT_FIELD_PADDING:
|
||||||
c.repaint();
|
HiDPIUtils.repaint( c );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MINIMUM_WIDTH:
|
case MINIMUM_WIDTH:
|
||||||
@@ -252,38 +250,38 @@ public class FlatTextFieldUI
|
|||||||
case STYLE_CLASS:
|
case STYLE_CLASS:
|
||||||
installStyle();
|
installStyle();
|
||||||
c.revalidate();
|
c.revalidate();
|
||||||
c.repaint();
|
HiDPIUtils.repaint( c );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEXT_FIELD_LEADING_ICON:
|
case TEXT_FIELD_LEADING_ICON:
|
||||||
leadingIcon = (e.getNewValue() instanceof Icon) ? (Icon) e.getNewValue() : null;
|
leadingIcon = (e.getNewValue() instanceof Icon) ? (Icon) e.getNewValue() : null;
|
||||||
c.repaint();
|
HiDPIUtils.repaint( c );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEXT_FIELD_TRAILING_ICON:
|
case TEXT_FIELD_TRAILING_ICON:
|
||||||
trailingIcon = (e.getNewValue() instanceof Icon) ? (Icon) e.getNewValue() : null;
|
trailingIcon = (e.getNewValue() instanceof Icon) ? (Icon) e.getNewValue() : null;
|
||||||
c.repaint();
|
HiDPIUtils.repaint( c );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEXT_FIELD_LEADING_COMPONENT:
|
case TEXT_FIELD_LEADING_COMPONENT:
|
||||||
uninstallLeadingComponent();
|
uninstallLeadingComponent();
|
||||||
installLeadingComponent();
|
installLeadingComponent();
|
||||||
c.revalidate();
|
c.revalidate();
|
||||||
c.repaint();
|
HiDPIUtils.repaint( c );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEXT_FIELD_TRAILING_COMPONENT:
|
case TEXT_FIELD_TRAILING_COMPONENT:
|
||||||
uninstallTrailingComponent();
|
uninstallTrailingComponent();
|
||||||
installTrailingComponent();
|
installTrailingComponent();
|
||||||
c.revalidate();
|
c.revalidate();
|
||||||
c.repaint();
|
HiDPIUtils.repaint( c );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEXT_FIELD_SHOW_CLEAR_BUTTON:
|
case TEXT_FIELD_SHOW_CLEAR_BUTTON:
|
||||||
uninstallClearButton();
|
uninstallClearButton();
|
||||||
installClearButton();
|
installClearButton();
|
||||||
c.revalidate();
|
c.revalidate();
|
||||||
c.repaint();
|
HiDPIUtils.repaint( c );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "enabled":
|
case "enabled":
|
||||||
@@ -402,7 +400,7 @@ public class FlatTextFieldUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintSafely( Graphics g ) {
|
protected void paintSafely( Graphics g ) {
|
||||||
paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground );
|
paintBackground( g, getComponent(), focusedBackground );
|
||||||
paintPlaceholder( g );
|
paintPlaceholder( g );
|
||||||
|
|
||||||
if( hasLeadingIcon() || hasTrailingIcon() )
|
if( hasLeadingIcon() || hasTrailingIcon() )
|
||||||
@@ -422,7 +420,7 @@ debug*/
|
|||||||
// background is painted elsewhere
|
// background is painted elsewhere
|
||||||
}
|
}
|
||||||
|
|
||||||
static void paintBackground( Graphics g, JTextComponent c, boolean isIntelliJTheme, Color focusedBackground ) {
|
static void paintBackground( Graphics g, JTextComponent c, Color focusedBackground ) {
|
||||||
// do not paint background if:
|
// do not paint background if:
|
||||||
// - not opaque and
|
// - not opaque and
|
||||||
// - border is not a flat border and
|
// - border is not a flat border and
|
||||||
@@ -443,14 +441,14 @@ debug*/
|
|||||||
try {
|
try {
|
||||||
FlatUIUtils.setRenderingHints( g2 );
|
FlatUIUtils.setRenderingHints( g2 );
|
||||||
|
|
||||||
g2.setColor( getBackground( c, isIntelliJTheme, focusedBackground ) );
|
g2.setColor( getBackground( c, focusedBackground ) );
|
||||||
FlatUIUtils.paintComponentBackground( g2, 0, 0, c.getWidth(), c.getHeight(), focusWidth, arc );
|
FlatUIUtils.paintComponentBackground( g2, 0, 0, c.getWidth(), c.getHeight(), focusWidth, arc );
|
||||||
} finally {
|
} finally {
|
||||||
g2.dispose();
|
g2.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Color getBackground( JTextComponent c, boolean isIntelliJTheme, Color focusedBackground ) {
|
static Color getBackground( JTextComponent c, Color focusedBackground ) {
|
||||||
Color background = c.getBackground();
|
Color background = c.getBackground();
|
||||||
|
|
||||||
// always use explicitly set color
|
// always use explicitly set color
|
||||||
@@ -461,10 +459,6 @@ debug*/
|
|||||||
if( focusedBackground != null && FlatUIUtils.isPermanentFocusOwner( c ) )
|
if( focusedBackground != null && FlatUIUtils.isPermanentFocusOwner( c ) )
|
||||||
return focusedBackground;
|
return focusedBackground;
|
||||||
|
|
||||||
// for compatibility with IntelliJ themes
|
|
||||||
if( isIntelliJTheme && (!c.isEnabled() || !c.isEditable()) )
|
|
||||||
return FlatUIUtils.getParentBackground( c );
|
|
||||||
|
|
||||||
return background;
|
return background;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,10 +481,22 @@ debug*/
|
|||||||
// compute placeholder location
|
// compute placeholder location
|
||||||
Rectangle r = getVisibleEditorRect();
|
Rectangle r = getVisibleEditorRect();
|
||||||
FontMetrics fm = c.getFontMetrics( c.getFont() );
|
FontMetrics fm = c.getFontMetrics( c.getFont() );
|
||||||
String clippedPlaceholder = JavaCompatibility.getClippedString( c, fm, placeholder, r.width );
|
int x = r.x;
|
||||||
int x = r.x + (isLeftToRight() ? 0 : r.width - fm.stringWidth( clippedPlaceholder ));
|
|
||||||
int y = r.y + fm.getAscent() + ((r.height - fm.getHeight()) / 2);
|
int y = r.y + fm.getAscent() + ((r.height - fm.getHeight()) / 2);
|
||||||
|
|
||||||
|
// apply horizontal alignment to x location
|
||||||
|
String clippedPlaceholder = JavaCompatibility.getClippedString( c, fm, placeholder, r.width );
|
||||||
|
int stringWidth = fm.stringWidth( clippedPlaceholder );
|
||||||
|
int halign = (c instanceof JTextField) ? ((JTextField)c).getHorizontalAlignment() : SwingConstants.LEADING;
|
||||||
|
if( halign == SwingConstants.LEADING )
|
||||||
|
halign = isLeftToRight() ? SwingConstants.LEFT : SwingConstants.RIGHT;
|
||||||
|
else if( halign == SwingConstants.TRAILING )
|
||||||
|
halign = isLeftToRight() ? SwingConstants.RIGHT : SwingConstants.LEFT;
|
||||||
|
if( halign == SwingConstants.RIGHT )
|
||||||
|
x += r.width - stringWidth;
|
||||||
|
else if( halign == SwingConstants.CENTER )
|
||||||
|
x = Math.max( 0, x + (r.width / 2) - (stringWidth / 2) );
|
||||||
|
|
||||||
// paint placeholder
|
// paint placeholder
|
||||||
g.setColor( placeholderForeground );
|
g.setColor( placeholderForeground );
|
||||||
FlatUIUtils.drawString( c, g, clippedPlaceholder, x, y );
|
FlatUIUtils.drawString( c, g, clippedPlaceholder, x, y );
|
||||||
@@ -622,7 +628,7 @@ debug*/
|
|||||||
/**
|
/**
|
||||||
* Returns the rectangle used to paint leading and trailing icons.
|
* Returns the rectangle used to paint leading and trailing icons.
|
||||||
* It invokes {@code super.getVisibleEditorRect()} and reduces left and/or
|
* It invokes {@code super.getVisibleEditorRect()} and reduces left and/or
|
||||||
* right margin if the text field has leading or trailing icons or components.
|
* right insets if the text field has leading or trailing icons or components.
|
||||||
* Also, the preferred widths of leading and trailing components are removed.
|
* Also, the preferred widths of leading and trailing components are removed.
|
||||||
*
|
*
|
||||||
* @since 2
|
* @since 2
|
||||||
@@ -654,24 +660,24 @@ debug*/
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if a leading/trailing icons (or components) are shown, then the left/right margins are reduced
|
// if a leading/trailing icons (or components) are shown, then the left/right insets are reduced
|
||||||
// to the top margin, which places the icon nicely centered on left/right side
|
// to the top inset, which places the icon nicely centered on left/right side
|
||||||
if( leftVisible || (ltr ? hasLeadingIcon() : hasTrailingIcon()) ) {
|
if( leftVisible || (ltr ? hasLeadingIcon() : hasTrailingIcon()) ) {
|
||||||
// reduce left margin
|
// reduce left inset
|
||||||
Insets margin = getComponent().getMargin();
|
Insets insets = getComponent().getInsets();
|
||||||
int newLeftMargin = Math.min( margin.left, margin.top );
|
int newLeftInset = Math.min( insets.left, insets.top );
|
||||||
if( newLeftMargin < margin.left ) {
|
if( newLeftInset < insets.left ) {
|
||||||
int diff = scale( margin.left - newLeftMargin );
|
int diff = insets.left - newLeftInset;
|
||||||
r.x -= diff;
|
r.x -= diff;
|
||||||
r.width += diff;
|
r.width += diff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if( rightVisible || (ltr ? hasTrailingIcon() : hasLeadingIcon()) ) {
|
if( rightVisible || (ltr ? hasTrailingIcon() : hasLeadingIcon()) ) {
|
||||||
// reduce right margin
|
// reduce right inset
|
||||||
Insets margin = getComponent().getMargin();
|
Insets insets = getComponent().getInsets();
|
||||||
int newRightMargin = Math.min( margin.right, margin.top );
|
int newRightInset = Math.min( insets.right, insets.top );
|
||||||
if( newRightMargin < margin.left )
|
if( newRightInset < insets.left )
|
||||||
r.width += scale( margin.right - newRightMargin );
|
r.width += insets.right - newRightInset;
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure that width and height are not negative
|
// make sure that width and height are not negative
|
||||||
@@ -809,7 +815,7 @@ debug*/
|
|||||||
if( visible != clearButton.isVisible() ) {
|
if( visible != clearButton.isVisible() ) {
|
||||||
clearButton.setVisible( visible );
|
clearButton.setVisible( visible );
|
||||||
c.revalidate();
|
c.revalidate();
|
||||||
c.repaint();
|
HiDPIUtils.repaint( c );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user