mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2026-02-11 06:27:13 -06:00
Compare commits
596 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e869973d4 | ||
|
|
731c8962c9 | ||
|
|
294b8bb789 | ||
|
|
4f9b819f48 | ||
|
|
5318d5fa8e | ||
|
|
98b156bdde | ||
|
|
511dd02107 | ||
|
|
f1f7a2e7b6 | ||
|
|
d557cf5427 | ||
|
|
39d2941099 | ||
|
|
2a732306a1 | ||
|
|
8a72b30cbc | ||
|
|
ed9cb0f918 | ||
|
|
7e0915cb9c | ||
|
|
a51294d570 | ||
|
|
d962f218a1 | ||
|
|
7b248427f0 | ||
|
|
b99fb8b11f | ||
|
|
26250e790f | ||
|
|
b26dbe81f4 | ||
|
|
903212345b | ||
|
|
025f6564dc | ||
|
|
35f97368fa | ||
|
|
09e5c86488 | ||
|
|
8998371cae | ||
|
|
29e1dc6b55 | ||
|
|
439e63b52f | ||
|
|
eea341fb33 | ||
|
|
359eedf773 | ||
|
|
866751ffc1 | ||
|
|
38a3a0768d | ||
|
|
03b42749cd | ||
|
|
60fd78e082 | ||
|
|
9edaf58929 | ||
|
|
5000186f85 | ||
|
|
cacf0ea987 | ||
|
|
067501cbe7 | ||
|
|
9fe0cf496b | ||
|
|
9d0823038e | ||
|
|
5a05efefdd | ||
|
|
988d171bdd | ||
|
|
e6f72bf343 | ||
|
|
89c5a0c57b | ||
|
|
d97146393c | ||
|
|
1c52f1f76c | ||
|
|
9bd3a68115 | ||
|
|
f58780d36b | ||
|
|
6eb15ab437 | ||
|
|
00dc7004f5 | ||
|
|
8ec0e57235 | ||
|
|
d75dc9e70c | ||
|
|
ec2fccbb0e | ||
|
|
34861166e8 | ||
|
|
584fa0a26e | ||
|
|
6c48489d89 | ||
|
|
ba9c884a0c | ||
|
|
360f0bafe0 | ||
|
|
4327c13dca | ||
|
|
4f2256f713 | ||
|
|
5167cd368f | ||
|
|
ef7289d11a | ||
|
|
cb11d98bf7 | ||
|
|
992349da8c | ||
|
|
2e7637f274 | ||
|
|
1f8eaf4a64 | ||
|
|
46ac7a9dc7 | ||
|
|
0d86d39217 | ||
|
|
1f591f3d1b | ||
|
|
30c6ddba37 | ||
|
|
406eeaec96 | ||
|
|
2fe5652bc6 | ||
|
|
39bf68a6bd | ||
|
|
a7a4a19824 | ||
|
|
7f906ba0ea | ||
|
|
07bf6e4506 | ||
|
|
a331760321 | ||
|
|
d9c240d729 | ||
|
|
d9526c19e7 | ||
|
|
1798ccd284 | ||
|
|
ab1ce7fab1 | ||
|
|
e9b2f17171 | ||
|
|
d3bf4433b7 | ||
|
|
ba0f43455b | ||
|
|
638af4bcd7 | ||
|
|
5eab843d97 | ||
|
|
c55f0e239e | ||
|
|
32d9381745 | ||
|
|
77fc564e70 | ||
|
|
3b84314c45 | ||
|
|
5729c20386 | ||
|
|
a4d70d8095 | ||
|
|
8fcce349d5 | ||
|
|
5a94676a3a | ||
|
|
f32d72ee62 | ||
|
|
e35fc8620c | ||
|
|
277c288952 | ||
|
|
240b08e55c | ||
|
|
fe7f345661 | ||
|
|
c8db01c958 | ||
|
|
f456185f7d | ||
|
|
801b555835 | ||
|
|
eee177e64b | ||
|
|
63639f8e96 | ||
|
|
de1b0b1bb6 | ||
|
|
bbdd7fc2b4 | ||
|
|
6addb5c4b4 | ||
|
|
b47e0c88d6 | ||
|
|
d06993d940 | ||
|
|
d31f167b9e | ||
|
|
f12ee6c167 | ||
|
|
983b341f33 | ||
|
|
f3e6642f05 | ||
|
|
0a63990d21 | ||
|
|
6909bb4b03 | ||
|
|
620aa8bcee | ||
|
|
6db39d1860 | ||
|
|
1762ead89f | ||
|
|
d13ddeb944 | ||
|
|
1b5da0e1d1 | ||
|
|
7a2d0e7fcb | ||
|
|
477c3b6b1e | ||
|
|
95312c3650 | ||
|
|
98a3c4b0f5 | ||
|
|
6e990a7e31 | ||
|
|
8e49904f8d | ||
|
|
69f52c8abd | ||
|
|
d7b0754327 | ||
|
|
2a00de11f1 | ||
|
|
923cc51f3e | ||
|
|
c8f7478170 | ||
|
|
9006e835c6 | ||
|
|
f801d61929 | ||
|
|
a143e5777c | ||
|
|
bffac60bf8 | ||
|
|
bf500e46e7 | ||
|
|
4a2f79f390 | ||
|
|
c24ce7c5bc | ||
|
|
8a6a0c7971 | ||
|
|
de6e5bd800 | ||
|
|
e18a04f9e6 | ||
|
|
14fc652f4b | ||
|
|
9a876e747a | ||
|
|
f8ee8b27fb | ||
|
|
ce1a1487aa | ||
|
|
fe1e364a1d | ||
|
|
eabb052107 | ||
|
|
734f3621f1 | ||
|
|
ae8323e2f8 | ||
|
|
9612a81f2e | ||
|
|
2945a36cef | ||
|
|
b84dc5bfcc | ||
|
|
60486fd880 | ||
|
|
891091cebc | ||
|
|
1493ddcf41 | ||
|
|
4299c50537 | ||
|
|
14577c396d | ||
|
|
e9b566241d | ||
|
|
d39b08c035 | ||
|
|
69ac683c8c | ||
|
|
eafd0b3d06 | ||
|
|
310a4989dc | ||
|
|
3d0df51839 | ||
|
|
ede02aaaa5 | ||
|
|
beff149004 | ||
|
|
07db6e8fb0 | ||
|
|
46852c0780 | ||
|
|
a5e41c573f | ||
|
|
ed91aa4648 | ||
|
|
9a94395d30 | ||
|
|
04aa61c2bb | ||
|
|
035a13df54 | ||
|
|
e8a6f0ca3d | ||
|
|
1fc519b9de | ||
|
|
2bcf38e2e3 | ||
|
|
8eb44a68cb | ||
|
|
30c7b442a8 | ||
|
|
cee2211108 | ||
|
|
b7bcbccd45 | ||
|
|
d2ccb97eba | ||
|
|
39d56f2603 | ||
|
|
83e904dd2d | ||
|
|
110c787eba | ||
|
|
7c7ff289de | ||
|
|
617a35c51b | ||
|
|
73487ccf65 | ||
|
|
712bff9c99 | ||
|
|
eedfcf86aa | ||
|
|
f730848928 | ||
|
|
61d0574c5c | ||
|
|
2f01e01ec1 | ||
|
|
cbcf66df7f | ||
|
|
cfaeea039b | ||
|
|
a891d1eb54 | ||
|
|
4372052ef0 | ||
|
|
8734b062dc | ||
|
|
343451de65 | ||
|
|
144d65c776 | ||
|
|
a6815574f7 | ||
|
|
e5a116a0d4 | ||
|
|
0beef6b108 | ||
|
|
7341008449 | ||
|
|
49bd53194a | ||
|
|
baf4437efc | ||
|
|
b244f80f81 | ||
|
|
e41c91a42b | ||
|
|
b9a2e3ceac | ||
|
|
fa7dd3bdc4 | ||
|
|
9a8c68b846 | ||
|
|
698e33ddf4 | ||
|
|
909258ba14 | ||
|
|
2ad6bd1d23 | ||
|
|
510ffd41d8 | ||
|
|
4f00591c4e | ||
|
|
5b65ed87cd | ||
|
|
b0121c422d | ||
|
|
a9e9fad222 | ||
|
|
b5fc07acc7 | ||
|
|
140ebfdb92 | ||
|
|
37d0179de1 | ||
|
|
823d4b0fe2 | ||
|
|
dd1eacf4f0 | ||
|
|
86c33dd686 | ||
|
|
c6757cc61b | ||
|
|
a38cf284dd | ||
|
|
575b8e3f7f | ||
|
|
bc443f47f1 | ||
|
|
b631bcc0db | ||
|
|
5ccd92ece6 | ||
|
|
2f3c8868a7 | ||
|
|
6f7b5e8005 | ||
|
|
10d1e4b798 | ||
|
|
9d5934df14 | ||
|
|
be507de6c1 | ||
|
|
e5d3c08821 | ||
|
|
027b4ab7da | ||
|
|
fefea0d7ec | ||
|
|
33f30bfd19 | ||
|
|
e9d4b9961a | ||
|
|
b94248fe79 | ||
|
|
225975e0dd | ||
|
|
eac7492143 | ||
|
|
b3c40bf448 | ||
|
|
02f7cd77f4 | ||
|
|
7f8f3aa99b | ||
|
|
0bcdc14909 | ||
|
|
526c25a02b | ||
|
|
f48da9dab1 | ||
|
|
2e8dfda12e | ||
|
|
63da576d85 | ||
|
|
0ab4206540 | ||
|
|
212ae90401 | ||
|
|
d4e5d0be45 | ||
|
|
3520a0f1fb | ||
|
|
036090a947 | ||
|
|
dc570c683a | ||
|
|
9f85d34c91 | ||
|
|
16bf1fb6c3 | ||
|
|
47c4d508e0 | ||
|
|
e5d9060623 | ||
|
|
fdf28fc385 | ||
|
|
9015a4d56b | ||
|
|
38301454a6 | ||
|
|
9b3a22c4ca | ||
|
|
548dbc3649 | ||
|
|
3474129812 | ||
|
|
63193feebe | ||
|
|
51f22bfe75 | ||
|
|
7d0f7e1c8e | ||
|
|
dd8ab242fb | ||
|
|
60f3428da7 | ||
|
|
c6fec0a131 | ||
|
|
fdc43fc0d3 | ||
|
|
0b880aa335 | ||
|
|
74f50ec992 | ||
|
|
1bdf4532db | ||
|
|
f97783ddef | ||
|
|
1024d6fc07 | ||
|
|
3ec59d0c58 | ||
|
|
c43249316c | ||
|
|
ed5180ffd6 | ||
|
|
e9ec769340 | ||
|
|
5e16ff8dff | ||
|
|
364b6631ea | ||
|
|
48a18e53e3 | ||
|
|
bcc8282d73 | ||
|
|
15017ed49c | ||
|
|
50d36fe91b | ||
|
|
23e67a2908 | ||
|
|
0dab1b73cc | ||
|
|
3c086a92e2 | ||
|
|
647d72514b | ||
|
|
15328b4fd7 | ||
|
|
b49a498f9c | ||
|
|
8d14d5f87c | ||
|
|
a6db352ecd | ||
|
|
ccbb26c176 | ||
|
|
8f6af73541 | ||
|
|
a59f17fdb2 | ||
|
|
14222e40ad | ||
|
|
7d48bf06fe | ||
|
|
1d06a2c2e8 | ||
|
|
cf141f0e55 | ||
|
|
9113c31612 | ||
|
|
00b4e0a6fd | ||
|
|
e3cac95d37 | ||
|
|
64d850c583 | ||
|
|
2fe1b9e726 | ||
|
|
1315d847b9 | ||
|
|
b5954102b6 | ||
|
|
1c8ba0c538 | ||
|
|
be18317a6d | ||
|
|
88d2b8266e | ||
|
|
949ca5ddff | ||
|
|
3eb53b9648 | ||
|
|
e4a03ede1f | ||
|
|
cb65dc0e9d | ||
|
|
8ec907050e | ||
|
|
15ba00a902 | ||
|
|
89d0c301c2 | ||
|
|
2f47466f3b | ||
|
|
d70eca9774 | ||
|
|
95ce92fa18 | ||
|
|
b3db52b2ed | ||
|
|
c40912013d | ||
|
|
1c08e98c1c | ||
|
|
3f202a7cdc | ||
|
|
6f3aea8fc1 | ||
|
|
0896143838 | ||
|
|
ea94899a28 | ||
|
|
d2109cef86 | ||
|
|
cda146366c | ||
|
|
678b879a01 | ||
|
|
4c885c5e7b | ||
|
|
d5002b1c33 | ||
|
|
4f8b6d6b28 | ||
|
|
66dab41539 | ||
|
|
9e4940228d | ||
|
|
cbb11ebb03 | ||
|
|
073a25f381 | ||
|
|
40592ab876 | ||
|
|
bbfe624b51 | ||
|
|
a2af9e4c65 | ||
|
|
0123a8895f | ||
|
|
53854a4d13 | ||
|
|
4fdd44858f | ||
|
|
3c58879ce5 | ||
|
|
a7c6a881b3 | ||
|
|
ef065d31a0 | ||
|
|
d059d6b448 | ||
|
|
2d0a6f1bec | ||
|
|
a3cc5a1938 | ||
|
|
435068515a | ||
|
|
956001dbd7 | ||
|
|
460f0d9dee | ||
|
|
5155ec93c9 | ||
|
|
8bb8883e22 | ||
|
|
ffb7a6dfbb | ||
|
|
176de6f245 | ||
|
|
11f9740dbf | ||
|
|
42a91ba26c | ||
|
|
234003e2b1 | ||
|
|
534384438b | ||
|
|
ab51f35d5d | ||
|
|
511a4044d7 | ||
|
|
821efaff40 | ||
|
|
91bc994532 | ||
|
|
1323b46ac7 | ||
|
|
3a8b30ca8e | ||
|
|
923d58519f | ||
|
|
eabb1f84f6 | ||
|
|
cfbe44b946 | ||
|
|
81c35eab46 | ||
|
|
a1c7c29113 | ||
|
|
1293e2a074 | ||
|
|
b5deca7f22 | ||
|
|
604ba236c0 | ||
|
|
14df490b2a | ||
|
|
dd2f73e8ad | ||
|
|
56bfdc8ef9 | ||
|
|
91dbf1e144 | ||
|
|
e07ae90d09 | ||
|
|
5ef0c9aae1 | ||
|
|
aefed7c481 | ||
|
|
0d66d9f9a3 | ||
|
|
d0ffc4f979 | ||
|
|
f149d2b7cd | ||
|
|
21a12b8dd4 | ||
|
|
6c8b8e8949 | ||
|
|
539737d1c5 | ||
|
|
33ff5828da | ||
|
|
1fb0783808 | ||
|
|
b5e7aa8553 | ||
|
|
1d3ce76b27 | ||
|
|
0101171159 | ||
|
|
8b8ed0b9ff | ||
|
|
413b60e630 | ||
|
|
10b2a94c70 | ||
|
|
e337e5bbd8 | ||
|
|
6e55e0a183 | ||
|
|
8ee1d26935 | ||
|
|
80bdf69eaf | ||
|
|
18e838bffd | ||
|
|
d95b1b0ec4 | ||
|
|
d16a3c117b | ||
|
|
d04ec982ab | ||
|
|
cce99c803e | ||
|
|
19ed538573 | ||
|
|
a1f78345e6 | ||
|
|
f8c7ccf064 | ||
|
|
4d5242cd61 | ||
|
|
7ad176f98d | ||
|
|
57df7d28b5 | ||
|
|
f784ff2c84 | ||
|
|
a0f6affb68 | ||
|
|
0c679167fa | ||
|
|
4fe707e519 | ||
|
|
d83704b7cb | ||
|
|
2177ee45cc | ||
|
|
ccd4f99aea | ||
|
|
cd6b55c846 | ||
|
|
d923c8df81 | ||
|
|
59879f493e | ||
|
|
06cab0d4b5 | ||
|
|
a16db38a6f | ||
|
|
de93e19a80 | ||
|
|
47bb7d0de7 | ||
|
|
896e808db4 | ||
|
|
6fe6d1ffa0 | ||
|
|
4c6f7a66e2 | ||
|
|
4b5646ec88 | ||
|
|
66a5f350da | ||
|
|
f9e34cbab7 | ||
|
|
634f7b5ba3 | ||
|
|
7dbc6ff8a3 | ||
|
|
afccdc4749 | ||
|
|
c98ec041d4 | ||
|
|
9e0c62092e | ||
|
|
9aea006f50 | ||
|
|
c16c3759cf | ||
|
|
cbc1fe27ef | ||
|
|
f57dbf94c8 | ||
|
|
c0f15d2e6f | ||
|
|
cb525fafb6 | ||
|
|
5cae3a8141 | ||
|
|
8594e78287 | ||
|
|
5b8f922273 | ||
|
|
847b41752c | ||
|
|
7c08489cb3 | ||
|
|
605c77ecbc | ||
|
|
fd0c2a5cd1 | ||
|
|
a80790fc8e | ||
|
|
206d449d0d | ||
|
|
2323dc099f | ||
|
|
642583479f | ||
|
|
082e5842d0 | ||
|
|
c67ba02839 | ||
|
|
4c6cb7618f | ||
|
|
c15100f129 | ||
|
|
6dfb3cc84e | ||
|
|
18d8c7d086 | ||
|
|
ab3adf4ae3 | ||
|
|
7e6619af00 | ||
|
|
a7e2a10403 | ||
|
|
3a784375d0 | ||
|
|
b8c9433259 | ||
|
|
815d9d6012 | ||
|
|
feb91aa056 | ||
|
|
cd264586ca | ||
|
|
c6d561f2df | ||
|
|
6167c5f855 | ||
|
|
1a31cb96b8 | ||
|
|
9b8df64c35 | ||
|
|
a47565afec | ||
|
|
c2ee815cbe | ||
|
|
e45a2df6b6 | ||
|
|
a19979c233 | ||
|
|
e2a297fa40 | ||
|
|
df13b338b2 | ||
|
|
da9d7a0dee | ||
|
|
0374c65159 | ||
|
|
71b1e07ba6 | ||
|
|
c3781dc4b5 | ||
|
|
dc92d0913c | ||
|
|
a5adf29001 | ||
|
|
8861bfe4fa | ||
|
|
c8d280f418 | ||
|
|
09c98359af | ||
|
|
6f8a7471c2 | ||
|
|
4c141fe47c | ||
|
|
b37ff348fb | ||
|
|
09798d33b0 | ||
|
|
717ab95fbe | ||
|
|
3f616e3608 | ||
|
|
c590157561 | ||
|
|
2b50431081 | ||
|
|
6d38e44f91 | ||
|
|
9bc656a5c5 | ||
|
|
700bb9b567 | ||
|
|
8ccda81d9a | ||
|
|
3818790ced | ||
|
|
c34ce389a4 | ||
|
|
15718cdb46 | ||
|
|
10746a454a | ||
|
|
f0fd02e81f | ||
|
|
bfaac6d164 | ||
|
|
a909f1012a | ||
|
|
201581a07c | ||
|
|
8cef5ecf7e | ||
|
|
2c1075f471 | ||
|
|
1f5e08fdc6 | ||
|
|
c0408045ef | ||
|
|
c58f5a6ca7 | ||
|
|
ae445c9343 | ||
|
|
ad7ff2ba0b | ||
|
|
4b7ef6e853 | ||
|
|
87f2acc2d9 | ||
|
|
ec2fef02ed | ||
|
|
ebe0d74dbe | ||
|
|
029dc51f8b | ||
|
|
3fc85cd7b2 | ||
|
|
a46bdef079 | ||
|
|
3de489f693 | ||
|
|
eddb9eee46 | ||
|
|
5b0c96cd6d | ||
|
|
15ac77107f | ||
|
|
a7c906091c | ||
|
|
de870c546c | ||
|
|
2f3427e6ad | ||
|
|
203426bd55 | ||
|
|
16242080e0 | ||
|
|
57655d8859 | ||
|
|
62ffd57108 | ||
|
|
8db05f47b5 | ||
|
|
c684761eef | ||
|
|
0a8ece8c9c | ||
|
|
01058bde1b | ||
|
|
9c2c03cddb | ||
|
|
f0778a83a0 | ||
|
|
b86ae1f122 | ||
|
|
dfd6831b02 | ||
|
|
a4ddc13c1a | ||
|
|
fd63a1b7c2 | ||
|
|
d83c3689d0 | ||
|
|
d52bf9d318 | ||
|
|
80f56dec15 | ||
|
|
358c226b96 | ||
|
|
9de9983416 | ||
|
|
c9da4fcaf1 | ||
|
|
932ca6f9d4 | ||
|
|
4487c9985c | ||
|
|
a53ce99977 | ||
|
|
5444719895 | ||
|
|
b66139281d | ||
|
|
8925c27eb9 | ||
|
|
99be346387 | ||
|
|
81d46ba8ee | ||
|
|
ef4c467b20 | ||
|
|
44d196fb8c | ||
|
|
867c4fff58 | ||
|
|
5643546117 | ||
|
|
549832ba96 | ||
|
|
a8744b2bb4 | ||
|
|
e292d3444c | ||
|
|
ee6a1da709 | ||
|
|
8c15bc746b | ||
|
|
aebb083180 | ||
|
|
5438549b6d | ||
|
|
0077708235 | ||
|
|
2fd99ec9f3 | ||
|
|
0d266c4990 | ||
|
|
0982675b5f | ||
|
|
3bac5d3c80 | ||
|
|
58338f4848 | ||
|
|
9c261d3a3f | ||
|
|
5441ac6640 | ||
|
|
015b04a29a | ||
|
|
12ec0abf54 | ||
|
|
c8d461cdee | ||
|
|
faecffeadd | ||
|
|
b3c76c21b4 | ||
|
|
1697735162 | ||
|
|
ecb94bac6d | ||
|
|
7ebeacf16e | ||
|
|
d0079ab66b | ||
|
|
147e400bd6 | ||
|
|
c44905ea5e | ||
|
|
98b9df06fe | ||
|
|
02473080a5 | ||
|
|
c6beb9dc0a | ||
|
|
dcce14b122 | ||
|
|
a2ac24ac74 | ||
|
|
600f812f45 | ||
|
|
e945f46f25 | ||
|
|
c78c653b0a | ||
|
|
e0b3663239 | ||
|
|
3cc9c98040 |
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -15,8 +15,12 @@
|
|||||||
# BINARY FILES:
|
# BINARY FILES:
|
||||||
# Disable line ending normalize on checkin.
|
# Disable line ending normalize on checkin.
|
||||||
|
|
||||||
|
*.dll binary
|
||||||
|
*.dylib binary
|
||||||
*.gif binary
|
*.gif binary
|
||||||
*.jar binary
|
*.jar binary
|
||||||
|
*.lib binary
|
||||||
*.png binary
|
*.png binary
|
||||||
*.sketch binary
|
*.sketch binary
|
||||||
|
*.so binary
|
||||||
*.zip binary
|
*.zip binary
|
||||||
|
|||||||
8
.gitbugtraq
Normal file
8
.gitbugtraq
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# links issue numbers in git commit messages to issue tracker
|
||||||
|
# https://github.com/mstrap/bugtraq
|
||||||
|
# for SmartGit - https://www.syntevo.com/smartgit/
|
||||||
|
|
||||||
|
[bugtraq]
|
||||||
|
url = "https://github.com/JFormDesigner/FlatLaf/issues/%BUGID%"
|
||||||
|
loglinkregex = "#[0-9]{1,5}"
|
||||||
|
logregex = "[0-9]{1,5}"
|
||||||
152
.github/workflows/ci.yml
vendored
Normal file
152
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
# https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
|
||||||
|
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
tags:
|
||||||
|
- '[0-9]*'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
# test against
|
||||||
|
# - Java 1.8 (minimum requirement)
|
||||||
|
# - Java 9 (first version with JPMS)
|
||||||
|
# - Java LTS versions (11, 17, ...)
|
||||||
|
# - lastest Java version(s)
|
||||||
|
java:
|
||||||
|
- 1.8
|
||||||
|
- 9
|
||||||
|
- 11 # LTS
|
||||||
|
- 14
|
||||||
|
- 15
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- uses: gradle/wrapper-validation-action@v1
|
||||||
|
|
||||||
|
- name: Setup Java ${{ matrix.java }}
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: ${{ matrix.java }}
|
||||||
|
|
||||||
|
- name: Cache Gradle wrapper
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ~/.gradle/wrapper
|
||||||
|
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
|
||||||
|
|
||||||
|
- name: Cache Gradle cache
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/.gradle/caches
|
||||||
|
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
|
||||||
|
restore-keys: ${{ runner.os }}-gradle
|
||||||
|
|
||||||
|
- name: Build with Gradle
|
||||||
|
run: ./gradlew build
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
if: matrix.java == '11'
|
||||||
|
with:
|
||||||
|
name: FlatLaf-build-artifacts
|
||||||
|
path: |
|
||||||
|
flatlaf-*/build/libs
|
||||||
|
!**/*-javadoc.jar
|
||||||
|
!**/*-sources.jar
|
||||||
|
|
||||||
|
|
||||||
|
snapshot:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
if: |
|
||||||
|
github.event_name == 'push' &&
|
||||||
|
github.ref == 'refs/heads/main' &&
|
||||||
|
github.repository == 'JFormDesigner/FlatLaf'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup Java 11
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 11
|
||||||
|
|
||||||
|
- name: Cache Gradle wrapper
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ~/.gradle/wrapper
|
||||||
|
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
|
||||||
|
|
||||||
|
- name: Cache Gradle cache
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/.gradle/caches
|
||||||
|
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
|
||||||
|
restore-keys: ${{ runner.os }}-gradle
|
||||||
|
|
||||||
|
- name: Publish snapshot to oss.sonatype.org
|
||||||
|
run: ./gradlew publish -Dorg.gradle.internal.publish.checksums.insecure=true
|
||||||
|
env:
|
||||||
|
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
|
||||||
|
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
|
||||||
|
|
||||||
|
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
if: |
|
||||||
|
github.event_name == 'push' &&
|
||||||
|
startsWith( github.ref, 'refs/tags/' ) &&
|
||||||
|
github.repository == 'JFormDesigner/FlatLaf'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup Java 11
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 11
|
||||||
|
|
||||||
|
- name: Cache Gradle wrapper
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ~/.gradle/wrapper
|
||||||
|
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
|
||||||
|
|
||||||
|
- name: Cache Gradle cache
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/.gradle/caches
|
||||||
|
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
|
||||||
|
restore-keys: ${{ runner.os }}-gradle
|
||||||
|
|
||||||
|
- name: Release a new stable version to Maven Central
|
||||||
|
run: ./gradlew publish :flatlaf-demo:build -Drelease=true
|
||||||
|
env:
|
||||||
|
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
|
||||||
|
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
|
||||||
|
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
|
||||||
|
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Upload demo
|
||||||
|
uses: sebastianpopp/ftp-action@releases/v2
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.FTP_SERVER }}
|
||||||
|
user: ${{ secrets.FTP_USERNAME }}
|
||||||
|
password: ${{ secrets.FTP_PASSWORD }}
|
||||||
|
forceSsl: true
|
||||||
|
localDir: "flatlaf-demo/build/libs"
|
||||||
|
remoteDir: "."
|
||||||
|
options: "--only-newer --no-recursion --verbose=1"
|
||||||
58
.github/workflows/natives.yml
vendored
Normal file
58
.github/workflows/natives.yml
vendored
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
|
||||||
|
|
||||||
|
name: Native Libraries
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
tags:
|
||||||
|
- '[0-9]*'
|
||||||
|
paths:
|
||||||
|
- 'flatlaf-natives/flatlaf-natives-windows/**'
|
||||||
|
- '.github/workflows/natives.yml'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- 'flatlaf-natives/flatlaf-natives-windows/**'
|
||||||
|
- '.github/workflows/natives.yml'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Windows:
|
||||||
|
runs-on: windows-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- uses: gradle/wrapper-validation-action@v1
|
||||||
|
|
||||||
|
- name: Setup Java 1.8
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 1.8
|
||||||
|
|
||||||
|
- name: Cache Gradle wrapper
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ~/.gradle/wrapper
|
||||||
|
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
|
||||||
|
|
||||||
|
- name: Cache Gradle cache
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
!~/.gradle/caches/modules-2/modules-2.lock
|
||||||
|
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
|
||||||
|
restore-keys: ${{ runner.os }}-gradle
|
||||||
|
|
||||||
|
- name: Build with Gradle
|
||||||
|
run: ./gradlew :flatlaf-natives-windows:build
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: FlatLaf-natives-windows-build-artifacts
|
||||||
|
path: |
|
||||||
|
flatlaf-natives/flatlaf-natives-windows/build
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -9,3 +9,5 @@ out/
|
|||||||
*.iml
|
*.iml
|
||||||
*.ipr
|
*.ipr
|
||||||
*.iws
|
*.iws
|
||||||
|
.vs/
|
||||||
|
.vscode/
|
||||||
|
|||||||
39
.travis.yml
39
.travis.yml
@@ -1,39 +0,0 @@
|
|||||||
language: java
|
|
||||||
sudo: false
|
|
||||||
|
|
||||||
jdk:
|
|
||||||
- openjdk8
|
|
||||||
- openjdk9
|
|
||||||
- openjdk11
|
|
||||||
- openjdk14
|
|
||||||
|
|
||||||
before_cache:
|
|
||||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
|
||||||
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- $HOME/.gradle/caches/
|
|
||||||
- $HOME/.gradle/wrapper/
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- ./gradlew --version
|
|
||||||
- java -version
|
|
||||||
|
|
||||||
stages:
|
|
||||||
- name: test
|
|
||||||
- name: snapshot
|
|
||||||
if: branch = master AND type IN (push) AND tag IS blank
|
|
||||||
- name: release
|
|
||||||
if: type IN (push) AND tag IS present
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
include:
|
|
||||||
# publish snapshot to oss.jfrog.org
|
|
||||||
- stage: snapshot
|
|
||||||
jdk: openjdk11
|
|
||||||
script: ./gradlew artifactoryPublish
|
|
||||||
|
|
||||||
# release a new stable version to bintray
|
|
||||||
- stage: release
|
|
||||||
jdk: openjdk11
|
|
||||||
script: ./gradlew bintrayUpload -Drelease=true
|
|
||||||
503
CHANGELOG.md
503
CHANGELOG.md
@@ -1,6 +1,509 @@
|
|||||||
FlatLaf Change Log
|
FlatLaf Change Log
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
## 1.3
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- TextComponents, ComboBox and Spinner: Support different background color when
|
||||||
|
component is focused (use UI values `TextField.focusedBackground`,
|
||||||
|
`PasswordField.focusedBackground`, `FormattedTextField.focusedBackground`,
|
||||||
|
`TextArea.focusedBackground`, `TextPane.focusedBackground`,
|
||||||
|
`EditorPane.focusedBackground`, `ComboBox.focusedBackground`,
|
||||||
|
`ComboBox.buttonFocusedBackground`, `ComboBox.popupFocusedBackground` and
|
||||||
|
`Spinner.focusedBackground`). (issue #335)
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Fixed white lines at bottom and right side of window (in dark themes on HiDPI
|
||||||
|
screens with scaling enabled).
|
||||||
|
- ScrollBar: Fixed left/top arrow icon location (if visible). (issue #329)
|
||||||
|
- Spinner: Fixed up/down arrow icon location.
|
||||||
|
- ToolTip: Fixed positioning of huge tooltips. (issue #333)
|
||||||
|
|
||||||
|
|
||||||
|
## 1.2
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Renamed `Flat*Laf.install()` methods to `Flat*Laf.setup()` to avoid confusion
|
||||||
|
with `UIManager.installLookAndFeel(LookAndFeelInfo info)`. The old
|
||||||
|
`Flat*Laf.install()` methods are still there, but marked as deprecated. They
|
||||||
|
will be removed in a future version.
|
||||||
|
- Button and ToggleButton: Support borderless button style (set client property
|
||||||
|
`JButton.buttonType` to `borderless`). (PR #276)
|
||||||
|
- ComboBox: Support using as cell renderer (e.g. in `JTable`).
|
||||||
|
- DesktopPane: Improved layout of iconified internal frames in dock:
|
||||||
|
- Always placed at bottom-left in desktop pane.
|
||||||
|
- Newly iconified frames are added to the right side of the dock.
|
||||||
|
- If frame is deiconified, dock is compacted (icons move to the left).
|
||||||
|
- If dock is wider than desktop width, additional rows are used.
|
||||||
|
- If desktop pane is resized, layout of dock is updated.
|
||||||
|
- TableHeader: Moved table header column border painting from
|
||||||
|
`FlatTableHeaderUI` to new border `FlatTableHeaderBorder` to improve
|
||||||
|
compatibility with custom table header implementations. (issue #228)
|
||||||
|
- Linux: Enable text anti-aliasing if no Gnome or KDE Desktop properties are
|
||||||
|
available. (issue #218)
|
||||||
|
- IntelliJ Themes: Added "Material Theme UI Lite / GitHub Dark" theme.
|
||||||
|
- JIDE Common Layer: Improved support for `JideTabbedPane`. (PR #306)
|
||||||
|
- Extras: `FlatSVGIcon` improvements:
|
||||||
|
- Each icon can now have its own color filter. (PR #303)
|
||||||
|
- Use mapper function in color filter to dynamically map colors. (PR #303)
|
||||||
|
- Color filter supports light and dark themes.
|
||||||
|
- Getters for icon name, classloader, etc.
|
||||||
|
- Extras: UI Inspector: Show class hierarchies when pressing <kbd>Alt</kbd> key
|
||||||
|
and prettified class names (dimmed package name).
|
||||||
|
- Extras: `FlatSVGUtils.createWindowIconImages()` now returns a single
|
||||||
|
multi-resolution image that creates requested image sizes on demand from SVG
|
||||||
|
(only on Windows with Java 9+).
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- CheckBox and RadioButton: Do not fill background if used as cell renderer,
|
||||||
|
except if cell is selected or has different background color. (issue #311)
|
||||||
|
- DesktopPane:
|
||||||
|
- Fixed missing preview of iconified internal frames in dock when using a
|
||||||
|
custom desktop manager. (PR #294)
|
||||||
|
- Fixed incomplete preview of iconified internal frames in dock when switching
|
||||||
|
LaF.
|
||||||
|
- On HiDPI screens, use high-resolution images for preview of iconified
|
||||||
|
internal frames in dock.
|
||||||
|
- PopupFactory: Fixed occasional `NullPointerException` in
|
||||||
|
`FlatPopupFactory.fixToolTipLocation()`. (issue #305)
|
||||||
|
- Tree: Fill cell background if
|
||||||
|
`DefaultTreeCellRenderer.setBackgroundNonSelectionColor(Color)` was used.
|
||||||
|
(issue #322)
|
||||||
|
- IntelliJ Themes: Fixed background colors of DesktopPane and DesktopIcon in all
|
||||||
|
themes.
|
||||||
|
- Native window decorations:
|
||||||
|
- Fixed slow application startup under particular conditions. (e.g. incomplete
|
||||||
|
custom JRE) (issue #319)
|
||||||
|
- Fixed occasional double window title bar when creating many frames or
|
||||||
|
dialogs. (issue #315)
|
||||||
|
- Fixed broken maximizing window (under special conditions) when restoring
|
||||||
|
frame state at startup.
|
||||||
|
- Title icon: For multi-resolution images now use `getResolutionVariant(width,
|
||||||
|
height)` (instead of `getResolutionVariants()`) to allow creation of
|
||||||
|
requested size on demand. This also avoids creation of all resolution
|
||||||
|
variants.
|
||||||
|
- Double-click at upper-left corner of maximized frame did not close window.
|
||||||
|
(issue #326)
|
||||||
|
- Linux: Fixed/improved detection of user font settings. (issue #309)
|
||||||
|
|
||||||
|
|
||||||
|
## 1.1.2
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Native window decorations: Added API to check whether current platform
|
||||||
|
supports window decorations (`FlatLaf.supportsNativeWindowDecorations()`) and
|
||||||
|
to toggle window decorations of all windows
|
||||||
|
(`FlatLaf.setUseNativeWindowDecorations(boolean)`).
|
||||||
|
- Native window decorations: Support changing title bar background and
|
||||||
|
foreground colors per window. (set client properties
|
||||||
|
`JRootPane.titleBarBackground` and `JRootPane.titleBarForeground` on root pane
|
||||||
|
to a `java.awt.Color`).
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Native window decorations: Fixed loading of native library when using Java
|
||||||
|
Platform Module System (JPMS) for application. (issue #289)
|
||||||
|
- Native window decorations: Removed superfluous pixel-line at top of screen
|
||||||
|
when window is maximized. (issue #296)
|
||||||
|
- Window decorations: Fixed random window title bar background in cases were
|
||||||
|
background is not filled by custom window or root pane components and unified
|
||||||
|
background is enabled.
|
||||||
|
- IntelliJ Themes: Fixed window title bar background if unified background is
|
||||||
|
enabled.
|
||||||
|
- IntelliJ Themes: Fixed system colors.
|
||||||
|
- Button and ToggleButton: Do not paint background of disabled (and unselected)
|
||||||
|
toolBar buttons. (issue #292; regression since fixing #112)
|
||||||
|
- ComboBox and Spinner: Fixed too wide arrow button if component is higher than
|
||||||
|
preferred. (issue #302)
|
||||||
|
- SplitPane: `JSplitPane.setContinuousLayout(false)` did not work. (issue #301)
|
||||||
|
- TabbedPane: Fixed NPE when creating/modifying in another thread. (issue #299)
|
||||||
|
- Fixed crash when running in Webswing. (issue #290)
|
||||||
|
|
||||||
|
|
||||||
|
## 1.1.1
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Native window decorations: Support disabling native window decorations per
|
||||||
|
window. (set client property `JRootPane.useWindowDecorations` on root pane to
|
||||||
|
`false`).
|
||||||
|
- Support running on WinPE. (issue #279)
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Native window decorations: Fixed missing animations when minimizing,
|
||||||
|
maximizing or restoring a window using window title bar buttons. (issue #282)
|
||||||
|
- Native window decorations: Fixed broken maximizing window when restoring frame
|
||||||
|
state at startup. (issue #283)
|
||||||
|
- Native window decorations: Fixed double window title bar when first disposing
|
||||||
|
a window with `frame.dispose()` and then showing it again with
|
||||||
|
`frame.setVisible(true)`. (issue #277)
|
||||||
|
- Custom window decorations: Fixed NPE in `FlatTitlePane.findHorizontalGlue()`.
|
||||||
|
(issue #275)
|
||||||
|
- Custom window decorations: Fixed right aligned progress bar in embedded menu
|
||||||
|
bar was overlapping window title. (issue #272)
|
||||||
|
- Fixed missing focus indicators in heavy-weight popups. (issue #273)
|
||||||
|
- InternalFrame: Fixed translucent internal frame menu bar background if
|
||||||
|
`TitlePane.unifiedBackground` is `true`. (issue #274)
|
||||||
|
- Extras: UI Inspector: Fixed `InaccessibleObjectException` when running in Java 16.
|
||||||
|
|
||||||
|
|
||||||
|
## 1.1
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Windows 10 only:
|
||||||
|
- Native window decorations for Windows 10 enables dark frame/dialog title bar
|
||||||
|
and embedded menu bar with all JREs, while still having native Windows 10
|
||||||
|
border drop shadows, resize behavior, window snapping and system window
|
||||||
|
menu. (PR #267)
|
||||||
|
- Custom window decorations: Support right aligned components in `JFrame`
|
||||||
|
title bar with embedded menu bar (using `Box.createHorizontalGlue()`). (PR
|
||||||
|
#268)
|
||||||
|
- Custom window decorations: Improved centering of window title with embedded
|
||||||
|
menu bar. (PR #268; issue #252)
|
||||||
|
- Custom window decorations: Support unified backgrounds for window title bar,
|
||||||
|
menu bar and main content. If enabled with `UIManager.put(
|
||||||
|
"TitlePane.unifiedBackground", true );` then window title bar and menu bar
|
||||||
|
use same background color as main content. (PR #268; issue #254)
|
||||||
|
- JIDE Common Layer: Support `JideButton`, `JideLabel`, `JideSplitButton`,
|
||||||
|
`JideToggleButton` and `JideToggleSplitButton`.
|
||||||
|
- JIDE Common Layer: The library on Maven Central no longer depends on
|
||||||
|
`com.jidesoft:jide-oss:3.6.18` to avoid problems when another JIDE library
|
||||||
|
should be used. (issue #270)
|
||||||
|
- SwingX: The library on Maven Central no longer depends on
|
||||||
|
`org.swinglabs.swingx:swingx-all:1.6.5-1` to avoid problems when another
|
||||||
|
SwingX library should be used.
|
||||||
|
- Support running in [JetBrains Projector](https://jetbrains.com/projector/).
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- IntelliJ Themes: Fixed text color of CheckBoxMenuItem and RadioButtonMenuItem
|
||||||
|
in all "Arc" themes. (issue #259)
|
||||||
|
|
||||||
|
|
||||||
|
## 1.0
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Extras: UI Inspector: Tooltip is no longer limited to window bounds.
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- TabbedPane: Custom `TabbedPane.selectedForeground` color did not work when
|
||||||
|
`TabbedPane.foreground` has also custom color. (issue #257)
|
||||||
|
- FileChooser: Fixed display of date in details view if current user is selected
|
||||||
|
in "Look in" combobox. (Windows 10 only; issue #249)
|
||||||
|
- Table: Fixed wrong grid line thickness in dragged column on HiDPI screens on
|
||||||
|
Java 9+. (issue #236)
|
||||||
|
- PopupFactory: Fixed `NullPointerException` when `PopupFactory.getPopup()` is
|
||||||
|
invoked with parameter `owner` set to `null`.
|
||||||
|
|
||||||
|
|
||||||
|
## 1.0-rc3
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Extras:
|
||||||
|
- UI Inspector: Use HTML in tooltip. Display color value in same format as
|
||||||
|
used in FlatLaf properties files. Added color preview.
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Label and ToolTip: Fixed font sizes for `<code>`, `<kbd>`, `<big>`, `<small>`
|
||||||
|
and `<samp>` tags in HTML text.
|
||||||
|
- Fixed color of `<address>` tag in HTML text.
|
||||||
|
- IntelliJ Themes: Fixed table header background when dragging column in "Dark
|
||||||
|
Flat" and "Light Flat" themes.
|
||||||
|
- CheckBox: Fixed background of check boxes in JIDE `CheckBoxTree`. (regression
|
||||||
|
in 1.0-rc2)
|
||||||
|
|
||||||
|
|
||||||
|
## 1.0-rc2
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Button:
|
||||||
|
- In "Flat Light" theme, use a slightly thinner border for focused buttons
|
||||||
|
(because they already have light blue background).
|
||||||
|
- In "Flat Dark" theme, use slightly wider border for focused buttons.
|
||||||
|
- CheckBox and RadioButton: In "Flat Dark" theme, use blueish background for
|
||||||
|
focused components.
|
||||||
|
- Tree: Support disabling wide selection per component. (set client property
|
||||||
|
`JTree.wideSelection` to `false`). (PR #245)
|
||||||
|
- Tree: Support disabling selection painting per component. Then the tree cell
|
||||||
|
renderer is responsible for selection painting. (set client property
|
||||||
|
`JTree.paintSelection` to `false`).
|
||||||
|
- JIDE Common Layer: Support `JidePopupMenu`.
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Button: Fixed behavior of <kbd>Enter</kbd> key on focused button on Windows
|
||||||
|
and Linux, which now clicks the focused button (instead of the default
|
||||||
|
button).
|
||||||
|
- On Windows, this is a regression in 1.0-rc1.
|
||||||
|
- On macOS, the <kbd>Enter</kbd> key always clicks the default button, which
|
||||||
|
is the platform behavior.
|
||||||
|
- On all platforms, the default button can be always clicked with
|
||||||
|
<kbd>Ctrl+Enter</kbd> keys, even if another button is focused.
|
||||||
|
- CheckBox and RadioButton: Fill component background as soon as background
|
||||||
|
color is different to default background color, even if component is not
|
||||||
|
opaque (which is the default). This paints selection if using the component as
|
||||||
|
cell renderer in Table, Tree or List.
|
||||||
|
- TextComponents: Border of focused non-editable text components had wrong
|
||||||
|
color.
|
||||||
|
- Custom window decorations: Fixed top window border in dark themes when running
|
||||||
|
in JetBrains Runtime.
|
||||||
|
|
||||||
|
|
||||||
|
## 1.0-rc1
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Button: Disabled `Button.defaultButtonFollowsFocus` on Windows (as on other
|
||||||
|
platforms). If you like to keep the old behavior in your application, use:
|
||||||
|
`if(SystemInfo.isWindows)
|
||||||
|
UIManager.put("Button.defaultButtonFollowsFocus",true);`.
|
||||||
|
- ComboBox, Spinner and SplitPaneDivider: Added pressed feedback to arrow
|
||||||
|
buttons.
|
||||||
|
- Slider: Support per component custom thumb and track colors via
|
||||||
|
`JSlider.setForeground(Color)` and `JSlider.setBackground(Color)`.
|
||||||
|
- Slider: Improved thumb hover and pressed colors.
|
||||||
|
- TextComponent: Clip placeholder text if it does not fit into visible area. (PR
|
||||||
|
#229)
|
||||||
|
- macOS: Improved font rendering on macOS when using JetBrains Runtime. (PRs
|
||||||
|
#237, #239 and #241)
|
||||||
|
- Extras: UI defaults inspector:
|
||||||
|
- Support embedding UI defaults inspector panel into any window. See
|
||||||
|
`FlatUIDefaultsInspector.createInspectorPanel()`.
|
||||||
|
- Copy selected keys and values into clipboard via context menu.
|
||||||
|
- Support wildcard matching in filter (`*` matches any number of characters,
|
||||||
|
`?` matches a single character, `^` beginning of line, `$` end of line).
|
||||||
|
- IntelliJ Themes:
|
||||||
|
- Added hover and pressed feedback to Button, CheckBox, RadioButton and
|
||||||
|
ToggleButton. (issue #176)
|
||||||
|
- Added "Material Theme UI Lite / Moonlight" theme.
|
||||||
|
- Updated "Dracula", "Gradianto" and "Material Theme UI Lite" themes.
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Button and ToggleButton: Threat Unicode surrogate character pair as single
|
||||||
|
character and make button square. (issue #234)
|
||||||
|
- Button and ToggleButton: ToolBar buttons now respect explicitly set background
|
||||||
|
color. If no background color is set, then the button background is not
|
||||||
|
painted anymore. (issue #191)
|
||||||
|
- ToggleButton: Tab style buttons (client property `JButton.buttonType` is
|
||||||
|
`tab`) now respect explicitly set background color.
|
||||||
|
- TabbedPane: Fixed `IndexOutOfBoundsException` when using tooltip text on close
|
||||||
|
buttons and closing last/rightmost tab. (issue #235)
|
||||||
|
- TabbedPane: Fixed scrolling tabs with touchpads and high-resolution mouse
|
||||||
|
wheels.
|
||||||
|
- Extras: Added missing export of package
|
||||||
|
`com.formdev.flatlaf.extras.components` to Java 9 module descriptor.
|
||||||
|
- JIDE Common Layer:
|
||||||
|
- Invoke `LookAndFeelFactory.installJideExtension()` when using FlatLaf UI
|
||||||
|
delegates. (issue #230)
|
||||||
|
- RangeSlider: Fixed slider focused colors in IntelliJ themes.
|
||||||
|
- IntelliJ Themes:
|
||||||
|
- Fixed menu item check colors.
|
||||||
|
- Fixed `MenuItem.underlineSelectionColor`.
|
||||||
|
- Fixed List, Tree and Table `selectionInactiveForeground` in light Arc
|
||||||
|
themes.
|
||||||
|
- Fixed List and Table background colors in Material UI Lite themes.
|
||||||
|
- Fixed menu accelerator colors in Monocai theme. (issue #243)
|
||||||
|
|
||||||
|
|
||||||
|
## 0.46
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Slider and JIDE RangeSlider: Clicking on track now immediately moves the thumb
|
||||||
|
to mouse location and starts dragging the thumb. Use `UIManager.put(
|
||||||
|
"Slider.scrollOnTrackClick", true )` to enable old behavior that scrolls the
|
||||||
|
thumb when clicking on track.
|
||||||
|
- Slider: Snap to ticks is now done while dragging the thumb. Use
|
||||||
|
`UIManager.put( "Slider.snapToTicksOnReleased", true )` to enable old behavior
|
||||||
|
that snaps to ticks on mouse released.
|
||||||
|
- Extras: Added standard component extension classes that provides easy access
|
||||||
|
to FlatLaf specific client properties (see package
|
||||||
|
`com.formdev.flatlaf.extras.components`).
|
||||||
|
- Extras: Renamed tri-state check box class from
|
||||||
|
`com.formdev.flatlaf.extras.TriStateCheckBox` to
|
||||||
|
`com.formdev.flatlaf.extras.components.FlatTriStateCheckBox`. Also
|
||||||
|
changed/improved API and added javadoc.
|
||||||
|
- Extras: Renamed SVG utility class from `com.formdev.flatlaf.extras.SVGUtils`
|
||||||
|
to `com.formdev.flatlaf.extras.FlatSVGUtils`.
|
||||||
|
- IntelliJ Themes: Added flag whether a theme is dark to
|
||||||
|
`FlatAllIJThemes.INFOS`. (issue #221)
|
||||||
|
- JIDE Common Layer: Support `TristateCheckBox`.
|
||||||
|
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Slider: Fixed painting of colored track if `JSlider.inverted` is `true`.
|
||||||
|
- Table and TableHeader: Fixed missing right vertical grid line if using table
|
||||||
|
as row header in scroll pane. (issues #152 and #46)
|
||||||
|
- TableHeader: Fixed position of column separators in right-to-left component
|
||||||
|
orientation.
|
||||||
|
- ToolTip: Fixed drop shadow for wide tooltips on Windows and Java 9+. (issue
|
||||||
|
#224)
|
||||||
|
- SwingX: Fixed striping background highlighting color (e.g. alternating table
|
||||||
|
rows) in dark themes.
|
||||||
|
- Fixed: If text antialiasing is disabled (in OS system settings or via
|
||||||
|
`-Dawt.useSystemAAFontSettings=off`), then some components still did use
|
||||||
|
antialiasing to render text (not-editable ComboBox, ProgressBar, Slider,
|
||||||
|
TabbedPane and multiline ToolTip). (issue #227)
|
||||||
|
|
||||||
|
|
||||||
|
## 0.45
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Slider: New design, added hover and pressed feedback and improved customizing.
|
||||||
|
(PR #214)
|
||||||
|
- JIDE Common Layer: Support `RangeSlider`. (PR #209)
|
||||||
|
- IntelliJ Themes:
|
||||||
|
- Added "Gradianto Nature Green" theme.
|
||||||
|
- Updated "Arc Dark", "Cyan", "Dark purple", "Gradianto", "Gray", "Gruvbox"
|
||||||
|
and "One Dark" themes.
|
||||||
|
- TabbedPane: Support hiding tab area if it contains only one tab. (set client
|
||||||
|
property `JTabbedPane.hideTabAreaWithOneTab` to `true`)
|
||||||
|
- MenuBar: Support different underline menu selection style UI defaults for
|
||||||
|
`MenuBar` and `MenuItem`. (PR #217; issue #216)
|
||||||
|
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Table: Do not paint last vertical grid line if auto-resize mode is not off.
|
||||||
|
(issue #46)
|
||||||
|
- Table: Fixed unstable grid line thickness when scaled on HiDPI screens. (issue
|
||||||
|
#152)
|
||||||
|
- TabbedPane: No longer add (internal) tab close button component as child to
|
||||||
|
`JTabbedPane`. (issue #219)
|
||||||
|
- Custom window decorations: Title bar was not hidden if window is in
|
||||||
|
full-screen mode. (issue #212)
|
||||||
|
|
||||||
|
|
||||||
|
## 0.44
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- TabbedPane: In scroll tab layout, added "Show Hidden Tabs" button to trailing
|
||||||
|
side of tab area. If pressed, it shows a popup menu that contains (partly)
|
||||||
|
hidden tabs and selecting one activates that tab. (PR #190; issue #40)
|
||||||
|
- TabbedPane: Support forward/backward scroll arrow buttons on both sides of tab
|
||||||
|
area. Backward button on left side, forward button on right side. Not
|
||||||
|
applicable scroll buttons are hidden. (PR #211; issue #40)
|
||||||
|
- TabbedPane: Support specifying default tab layout policy for all tabbed panes
|
||||||
|
in the application via UI value `TabbedPane.tabLayoutPolicy`. E.g. invoke
|
||||||
|
`UIManager.put( "TabbedPane.tabLayoutPolicy", "scroll" );` to use scroll
|
||||||
|
layout.
|
||||||
|
- TabbedPane: Support tab scrolling with mouse wheel (in scroll tab layout). (PR
|
||||||
|
#187; issue #40)
|
||||||
|
- TabbedPane: Repeat scrolling as long as scroll arrow buttons are pressed. (PR
|
||||||
|
#187; issue #40)
|
||||||
|
- TabbedPane: Support adding custom components to left and right sides of tab
|
||||||
|
area. (set client property `JTabbedPane.leadingComponent` or
|
||||||
|
`JTabbedPane.trailingComponent` to a `java.awt.Component`) (PR #192; issue
|
||||||
|
#40)
|
||||||
|
- TabbedPane: Support closable tabs. (PR #193; issues #31 and #40)
|
||||||
|
- TabbedPane: Support minimum or maximum tab widths. (set client property
|
||||||
|
`JTabbedPane.minimumTabWidth` or `JTabbedPane.maximumTabWidth` to an integer)
|
||||||
|
(PR #199)
|
||||||
|
- TabbedPane: Support alignment of tab area. (set client property
|
||||||
|
`JTabbedPane.tabAreaAlignment` to `"leading"`, `"trailing"`, `"center"` or
|
||||||
|
`"fill"`) (PR #199)
|
||||||
|
- TabbedPane: Support horizontal alignment of tab title and icon. (set client
|
||||||
|
property `JTabbedPane.tabAlignment` to `SwingConstants.LEADING`,
|
||||||
|
`SwingConstants.TRAILING` or `SwingConstants.CENTER`)
|
||||||
|
- TabbedPane: Support equal and compact tab width modes. (set client property
|
||||||
|
`JTabbedPane.tabWidthMode` to `"preferred"`, `"equal"` or `"compact"`) (PR
|
||||||
|
#199)
|
||||||
|
- TabbedPane: Support left, right, top and bottom tab icon placement. (set
|
||||||
|
client property `JTabbedPane.tabIconPlacement` to `SwingConstants.LEADING`,
|
||||||
|
`SwingConstants.TRAILING`, `SwingConstants.TOP` or `SwingConstants.BOTTOM`)
|
||||||
|
(PR #199)
|
||||||
|
- Support painting separator line between window title and content (use UI value
|
||||||
|
`TitlePane.borderColor`). (issue #184)
|
||||||
|
- Extras: `FlatSVGIcon` now allows specifying icon width and height in
|
||||||
|
constructors. (issue #196)
|
||||||
|
- SplitPane: Hide not applicable expand/collapse buttons. Added tooltips to
|
||||||
|
expand/collapse buttons. (issue #198)
|
||||||
|
- SplitPane: Added grip to divider. Can be disabled with `UIManager.put(
|
||||||
|
"SplitPaneDivider.style", "plain" )`. (issue #179)
|
||||||
|
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Custom window decorations: Not visible menu bar is now ignored in layout.
|
||||||
|
- Popups using `JToolTip` components did not respect their location. (issue
|
||||||
|
#188; regression in 0.42 in fix for #164)
|
||||||
|
- IntelliJ Themes: Added suffix "(Material)" to names of all Material UI Lite
|
||||||
|
themes to avoid duplicate theme names. (issue #201)
|
||||||
|
- Extras: `FlatSVGIcon` icons were not painted in disabled labels and disabled
|
||||||
|
tabs. (issue #205)
|
||||||
|
|
||||||
|
|
||||||
|
## 0.43
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- TabbedPane: Made tabs separator color lighter in dark themes so that it is
|
||||||
|
easier to recognize the tabbed pane.
|
||||||
|
- TabbedPane: Added top and bottom tab insets to avoid that large tab icons are
|
||||||
|
painted over active tab underline.
|
||||||
|
- TabbedPane: Support hiding separator between tabs and content area (set client
|
||||||
|
property `JTabbedPane.showContentSeparator` to `false`).
|
||||||
|
- CheckBoxMenuItem and RadioButtonMenuItem: Improved checkmark background colors
|
||||||
|
of selected menu items that have also an icon. This makes it is easier to
|
||||||
|
recognize selected menu items.
|
||||||
|
- Windows: Made scaling compatible with Windows OS scaling, which distinguish
|
||||||
|
between "screen scaling" and "text scaling". (issue #175)
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- ComboBox: If using own `JTextField` as editor, default text field border is
|
||||||
|
now removed to avoid duplicate border.
|
||||||
|
- ComboBox: Limit popup width to screen width for very long items. (issue #182)
|
||||||
|
- FileChooser: Fixed localizing special Windows folders (e.g. "Documents") and
|
||||||
|
enabled hiding known file extensions (if enabled in Windows Explorer). (issue
|
||||||
|
#178)
|
||||||
|
- Spinner: Fixed `NullPointerException` in case that arrow buttons were removed
|
||||||
|
to create button-less spinner. (issue #181)
|
||||||
|
|
||||||
|
|
||||||
|
## 0.42
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Demo: Improved "SplitPane & Tabs" and "Data Components" tabs.
|
||||||
|
- Demo: Menu items "File > Open" and "File > Save As" now show file choosers.
|
||||||
|
- InternalFrame: Support draggable border for resizing frame inside of the
|
||||||
|
visible frame border. (issue #121)
|
||||||
|
- `FlatUIDefaultsInspector` added (see [FlatLaf Extras](flatlaf-extras)). A
|
||||||
|
simple UI defaults inspector that shows a window with all UI defaults used in
|
||||||
|
current theme (look and feel).
|
||||||
|
- Made disabled text color slightly lighter in dark themes for better
|
||||||
|
readability. (issue #174)
|
||||||
|
- PasswordField: Support disabling Caps Lock warning icon. (issue #172)
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- TextComponents: Fixed text color of disabled text components in dark themes.
|
||||||
|
- Custom window decorations: Fixed wrong window placement when moving window to
|
||||||
|
another screen with different scaling factor. (issue #166)
|
||||||
|
- Custom window decorations: Fixed wrong window bounds when resizing window to
|
||||||
|
another screen with different scaling factor. (issue #166)
|
||||||
|
- Fixed occasional wrong positioning of heavy weight popups when using multiple
|
||||||
|
screens with different scaling factors. (issue #166)
|
||||||
|
- ToolTip: Avoid that tooltip hides owner component. (issue #164)
|
||||||
|
|
||||||
|
|
||||||
## 0.41
|
## 0.41
|
||||||
|
|
||||||
#### New features and improvements
|
#### New features and improvements
|
||||||
|
|||||||
166
README.md
166
README.md
@@ -37,7 +37,7 @@ Requires Java 8 or newer.
|
|||||||
Download
|
Download
|
||||||
--------
|
--------
|
||||||
|
|
||||||
FlatLaf binaries are available on **JCenter** and **Maven Central**.
|
FlatLaf binaries are available on **Maven Central**.
|
||||||
|
|
||||||
If you use Maven or Gradle, add a dependency with following coordinates to your
|
If you use Maven or Gradle, add a dependency with following coordinates to your
|
||||||
build script:
|
build script:
|
||||||
@@ -48,16 +48,16 @@ build script:
|
|||||||
|
|
||||||
Otherwise download `flatlaf-<version>.jar` here:
|
Otherwise download `flatlaf-<version>.jar` here:
|
||||||
|
|
||||||
[](https://bintray.com/jformdesigner/flatlaf/flatlaf/_latestVersion)
|
[](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf)
|
||||||
|
|
||||||
|
|
||||||
### Snapshots
|
### Snapshots
|
||||||
|
|
||||||
FlatLaf snapshot binaries are available in
|
FlatLaf snapshot binaries are available on
|
||||||
[JFrog Artifactory](https://oss.jfrog.org/artifactory/oss-snapshot-local/com/formdev/).
|
[Sonatype OSSRH](https://oss.sonatype.org/content/repositories/snapshots/com/formdev/flatlaf/).
|
||||||
To access the latest snapshot, change the FlatLaf version(s) in the 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. `0.27-SNAPSHOT`) and add the repository
|
||||||
`https://oss.jfrog.org/artifactory/oss-snapshot-local` to your build (see
|
`https://oss.sonatype.org/content/repositories/snapshots/` to your build (see
|
||||||
[Maven](https://maven.apache.org/guides/mini/guide-multiple-repositories.html)
|
[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)
|
||||||
@@ -73,51 +73,127 @@ Addons
|
|||||||
- [JIDE Common Layer](flatlaf-jide-oss)
|
- [JIDE Common Layer](flatlaf-jide-oss)
|
||||||
|
|
||||||
|
|
||||||
Projects using FlatLaf
|
Getting started
|
||||||
----------------------
|
---------------
|
||||||
|
|
||||||
- [NetBeans](https://netbeans.apache.org/) 11.3
|
To use FlatLaf, add following code to your main method before you create any
|
||||||
- [jclasslib bytecode viewer](https://github.com/ingokegel/jclasslib) 5.5
|
Swing component:
|
||||||
- [KeyStore Explorer](https://keystore-explorer.org/) 5.4.3
|
|
||||||
- [OWASP Zed Attack Proxy (ZAP)](https://www.zaproxy.org/) (in weekly releases)
|
|
||||||
-  [jAlbum](https://jalbum.net/) 21 (commercial)
|
|
||||||
- [XMLmind XML Editor](https://www.xmlmind.com/xmleditor/) 9.3 (commercial)
|
|
||||||
- [Total Validator](https://www.totalvalidator.com/) 15 (commercial)
|
|
||||||
- [j-lawyer](https://github.com/jlawyerorg/j-lawyer-org)
|
|
||||||
- [MegaMek](https://github.com/MegaMek/megamek) v0.47.4 and
|
|
||||||
[MekHQ](https://github.com/MegaMek/mekhq) v0.47.5
|
|
||||||
- [GUIslice Builder](https://github.com/ImpulseAdventure/GUIslice-Builder)
|
|
||||||
0.13.b024
|
|
||||||
- [Rest Suite](https://github.com/supanadit/restsuite)
|
|
||||||
- [ControllerBuddy](https://github.com/bwRavencl/ControllerBuddy)
|
|
||||||
- [SpringRemote](https://github.com/HaleyWang/SpringRemote)
|
|
||||||
- [mendelson AS2](https://sourceforge.net/projects/mec-as2/),
|
|
||||||
[AS4](https://sourceforge.net/projects/mendelson-as4/) and
|
|
||||||
[OFTP2](https://sourceforge.net/projects/mendelson-oftp2/) (open-source) and
|
|
||||||
[mendelson AS2](https://mendelson-e-c.com/as2/),
|
|
||||||
[AS4](https://mendelson-e-c.com/as4/) and
|
|
||||||
[OFTP2](https://mendelson-e-c.com/oftp2) (commercial)
|
|
||||||
- [MeteoInfo](https://github.com/meteoinfo/MeteoInfo) 2.2
|
|
||||||
- [lsfusion platform](https://github.com/lsfusion/platform)
|
|
||||||
- [Jes - Die Java-EÜR](https://www.jes-eur.de)
|
|
||||||
- [Mapton](https://mapton.org/) 2.0
|
|
||||||
([source code](https://github.com/trixon/mapton)) based on NetBeans platform
|
|
||||||
- [Pseudo Assembler IDE](https://github.com/tomasz-herman/PseudoAssemblerIDE)
|
|
||||||
- [Sound Analysis](https://github.com/tomasz-herman/SoundAnalysis)
|
|
||||||
- [RemoteLight](https://github.com/Drumber/RemoteLight) - Multifunctional LED
|
|
||||||
Control Software
|
|
||||||
- and more...
|
|
||||||
|
|
||||||
|
~~~java
|
||||||
|
FlatLightLaf.setup();
|
||||||
|
|
||||||
Buzz
|
// create UI here...
|
||||||
----
|
~~~
|
||||||
|
|
||||||
- [What others say about FlatLaf on Twitter](https://twitter.com/search?f=live&q=flatlaf)
|
|
||||||
- [FlatLaf announcement on Reddit](https://www.reddit.com/r/java/comments/dl0hu3/flatlaf_flat_look_and_feel/)
|
|
||||||
|
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
For more information and documentation visit
|
For more information and documentation visit
|
||||||
[FlatLaf Home](https://www.formdev.com/flatlaf/)
|
[FlatLaf Home](https://www.formdev.com/flatlaf/):
|
||||||
|
|
||||||
|
- [Themes](https://www.formdev.com/flatlaf/themes/)
|
||||||
|
- [Customizing](https://www.formdev.com/flatlaf/customizing/)
|
||||||
|
- [How to Customize](https://www.formdev.com/flatlaf/how-to-customize/)
|
||||||
|
- [Properties Files](https://www.formdev.com/flatlaf/properties-files/)
|
||||||
|
- [Client Properties](https://www.formdev.com/flatlaf/client-properties/)
|
||||||
|
- [System Properties](https://www.formdev.com/flatlaf/system-properties/)
|
||||||
|
|
||||||
|
|
||||||
|
Buzz
|
||||||
|
----
|
||||||
|
|
||||||
|
- [What others say about FlatLaf on Twitter](https://twitter.com/search?f=live&q=flatlaf)
|
||||||
|
- [FlatLaf 1.0 announcement on Reddit](https://www.reddit.com/r/java/comments/lsbcwe/flatlaf_10_swing_look_and_feel/)
|
||||||
|
- [FlatLaf announcement on Reddit](https://www.reddit.com/r/java/comments/dl0hu3/flatlaf_flat_look_and_feel/)
|
||||||
|
|
||||||
|
|
||||||
|
Applications using FlatLaf
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
- [Apache NetBeans](https://netbeans.apache.org/) 11.3 - IDE for Java, PHP, HTML
|
||||||
|
and much more
|
||||||
|
- [jclasslib bytecode viewer](https://github.com/ingokegel/jclasslib) 5.5
|
||||||
|
- [KeyStore Explorer](https://keystore-explorer.org/) 5.4.3
|
||||||
|
- 
|
||||||
|
[install4j](https://www.ej-technologies.com/products/install4j/overview.html)
|
||||||
|
9.0 (**commercial**) - the powerful multi-platform Java installer builder
|
||||||
|
-  [DbVisualizer](https://www.dbvis.com/) 12.0
|
||||||
|
(**commercial**) - the universal database tool for developers, analysts and
|
||||||
|
DBAs
|
||||||
|
-  [MagicPlot](https://magicplot.com/) 3.0
|
||||||
|
(**commercial**) - Software for nonlinear fitting, plotting and data analysis
|
||||||
|
- 
|
||||||
|
[Thermo-Calc](https://thermocalc.com/products/thermo-calc/) 2021a
|
||||||
|
(**commercial**) - Thermodynamics and Properties Software
|
||||||
|
- [OWASP ZAP](https://www.zaproxy.org/) 2.10 - the worlds most widely used web
|
||||||
|
app scanner
|
||||||
|
- 
|
||||||
|
[Burp Suite Professional and Community Edition](https://portswigger.net/burp/pro)
|
||||||
|
2020.11.2 (**commercial**) - the leading software for web security testing
|
||||||
|
- 
|
||||||
|
[BurpCustomizer](https://github.com/CoreyD97/BurpCustomizer) - adds more
|
||||||
|
FlatLaf themes to Burp Suite
|
||||||
|
- [JOSM](https://josm.openstreetmap.de/) - an extensible editor for
|
||||||
|
[OpenStreetMap](https://www.openstreetmap.org/) (requires FlatLaf JOSM plugin)
|
||||||
|
- [jAlbum](https://jalbum.net/) 21 (**commercial**) - creates photo album
|
||||||
|
websites
|
||||||
|
- [XMLmind XML Editor](https://www.xmlmind.com/xmleditor/) 9.3 (**commercial**)
|
||||||
|
- [Total Validator](https://www.totalvalidator.com/) 15 (**commercial**) -
|
||||||
|
checks your website
|
||||||
|
- [j-lawyer](https://github.com/jlawyerorg/j-lawyer-org) - Kanzleisoftware
|
||||||
|
- [MegaMek](https://github.com/MegaMek/megamek) v0.47.4 and
|
||||||
|
[MekHQ](https://github.com/MegaMek/mekhq) v0.47.5 - a turn-based sci-fi board
|
||||||
|
game
|
||||||
|
- [GUIslice Builder](https://github.com/ImpulseAdventure/GUIslice-Builder)
|
||||||
|
0.13.b024 - GUI builder for
|
||||||
|
[GUIslice](https://github.com/ImpulseAdventure/GUIslice), a lightweight GUI
|
||||||
|
framework for embedded displays
|
||||||
|
- [Rest Suite](https://github.com/supanadit/restsuite) - Rest API testing
|
||||||
|
- [ControllerBuddy](https://github.com/bwRavencl/ControllerBuddy) - advanced
|
||||||
|
gamepad mapping software
|
||||||
|
- [SpringRemote](https://github.com/HaleyWang/SpringRemote) - remote Linux SSH
|
||||||
|
connections manager
|
||||||
|
- [jEnTunnel](https://github.com/ggrandes/jentunnel) - manage SSH Tunnels made
|
||||||
|
easy
|
||||||
|
- [mendelson AS2](https://sourceforge.net/projects/mec-as2/),
|
||||||
|
[AS4](https://sourceforge.net/projects/mendelson-as4/) and
|
||||||
|
[OFTP2](https://sourceforge.net/projects/mendelson-oftp2/) (open-source) and
|
||||||
|
[mendelson AS2](https://mendelson-e-c.com/as2/),
|
||||||
|
[AS4](https://mendelson-e-c.com/as4/) and
|
||||||
|
[OFTP2](https://mendelson-e-c.com/oftp2) (**commercial**)
|
||||||
|
-  [IGMAS+](https://www.gfz-potsdam.de/igmas) -
|
||||||
|
Interactive Gravity and Magnetic Application System
|
||||||
|
- [MeteoInfo](https://github.com/meteoinfo/MeteoInfo) 2.2 - GIS and scientific
|
||||||
|
computation environment for meteorological community
|
||||||
|
- [lsfusion platform](https://github.com/lsfusion/platform) 4 - information
|
||||||
|
systems development platform
|
||||||
|
- [JPass](https://github.com/gaborbata/jpass) - password manager with strong
|
||||||
|
encryption
|
||||||
|
- [Jes - Die Java-EÜR](https://www.jes-eur.de)
|
||||||
|
- [Mapton](https://mapton.org/) 2.0
|
||||||
|
([source code](https://github.com/trixon/mapton)) - some kind of map
|
||||||
|
application (based on NetBeans platform)
|
||||||
|
- [Pseudo Assembler IDE](https://github.com/tomasz-herman/PseudoAssemblerIDE) -
|
||||||
|
IDE for Pseudo-Assembler
|
||||||
|
- [Linotte](https://github.com/cpc6128/LangageLinotte) 3.1 - French programming
|
||||||
|
language created to learn programming
|
||||||
|
- [MEKA](https://github.com/Waikato/meka) 1.9.3 - multi-label classifiers and
|
||||||
|
evaluation procedures using the Weka machine learning framework
|
||||||
|
- [Shutter Encoder](https://www.shutterencoder.com/) 14.2
|
||||||
|
([source code](https://github.com/paulpacifico/shutter-encoder)) -
|
||||||
|
professional video converter and compression tool (screenshots show **old**
|
||||||
|
look)
|
||||||
|
- [Sound Analysis](https://github.com/tomasz-herman/SoundAnalysis) - analyze
|
||||||
|
sound files in time or frequency domain
|
||||||
|
- [RemoteLight](https://github.com/Drumber/RemoteLight) - multifunctional LED
|
||||||
|
control software
|
||||||
|
- [ThunderFocus](https://github.com/marcocipriani01/ThunderFocus) -
|
||||||
|
Arduino-based telescope focuser
|
||||||
|
- [Novel-Grabber](https://github.com/Flameish/Novel-Grabber) - download novels
|
||||||
|
from any webnovel and lightnovel site
|
||||||
|
- [lectureStudio](https://www.lecturestudio.org/) 4.3.1060 - digitize your
|
||||||
|
lectures with ease
|
||||||
|
- [Android Tool](https://github.com/fast-geek/Android-Tool) - makes popular adb
|
||||||
|
and fastboot commands easier to use
|
||||||
|
- and more...
|
||||||
|
|||||||
@@ -14,8 +14,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
val releaseVersion = "0.41"
|
val releaseVersion = "1.3"
|
||||||
val developmentVersion = "0.42-SNAPSHOT"
|
val developmentVersion = "1.4-SNAPSHOT"
|
||||||
|
|
||||||
version = if( java.lang.Boolean.getBoolean( "release" ) ) releaseVersion else developmentVersion
|
version = if( java.lang.Boolean.getBoolean( "release" ) ) releaseVersion else developmentVersion
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ allprojects {
|
|||||||
version = rootProject.version
|
version = rootProject.version
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,17 +40,6 @@ println( "Java ${System.getProperty( "java.version" )}" )
|
|||||||
println()
|
println()
|
||||||
|
|
||||||
|
|
||||||
extra["bintray.user"] = System.getenv( "BINTRAY_USER" ) ?: System.getProperty( "bintray.user" )
|
|
||||||
extra["bintray.key"] = System.getenv( "BINTRAY_KEY" ) ?: System.getProperty( "bintray.key" )
|
|
||||||
|
|
||||||
// if true, do not upload to bintray
|
|
||||||
extra["bintray.dryRun"] = false
|
|
||||||
|
|
||||||
// if true, uploaded artifacts are visible to all
|
|
||||||
// if false, only visible to owner when logged into bintray
|
|
||||||
extra["bintray.publish"] = true
|
|
||||||
|
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
tasks {
|
tasks {
|
||||||
withType<JavaCompile>().configureEach {
|
withType<JavaCompile>().configureEach {
|
||||||
@@ -58,19 +47,35 @@ allprojects {
|
|||||||
targetCompatibility = "1.8"
|
targetCompatibility = "1.8"
|
||||||
|
|
||||||
options.encoding = "ISO-8859-1"
|
options.encoding = "ISO-8859-1"
|
||||||
|
options.isDeprecation = false
|
||||||
}
|
}
|
||||||
|
|
||||||
withType<Jar>().configureEach {
|
withType<Jar>().configureEach {
|
||||||
// manifest for all created JARs
|
// manifest for all created JARs
|
||||||
manifest.attributes(mapOf(
|
manifest.attributes(
|
||||||
"Implementation-Vendor" to "FormDev Software GmbH",
|
"Implementation-Vendor" to "FormDev Software GmbH",
|
||||||
"Implementation-Copyright" to "Copyright (C) ${java.time.LocalDate.now().year} FormDev Software GmbH. All rights reserved.",
|
"Implementation-Copyright" to "Copyright (C) 2019-${java.time.LocalDate.now().year} FormDev Software GmbH. All rights reserved.",
|
||||||
"Implementation-Version" to project.version))
|
"Implementation-Version" to project.version
|
||||||
|
)
|
||||||
|
|
||||||
// add META-INF/LICENSE to all created JARs
|
// add META-INF/LICENSE to all created JARs
|
||||||
from("${rootDir}/LICENSE") {
|
from( "${rootDir}/LICENSE" ) {
|
||||||
into("META-INF")
|
into( "META-INF" )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
withType<Javadoc>().configureEach {
|
||||||
|
options {
|
||||||
|
this as StandardJavadocDocletOptions
|
||||||
|
|
||||||
|
title = "${project.name} $version"
|
||||||
|
header = title
|
||||||
|
isUse = true
|
||||||
|
tags = listOf( "uiDefault", "clientProperty" )
|
||||||
|
addStringOption( "Xdoclint:all,-missing", "-Xdoclint:all,-missing" )
|
||||||
|
links( "https://docs.oracle.com/en/java/javase/11/docs/api/" )
|
||||||
|
}
|
||||||
|
isFailOnError = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,15 +20,5 @@ plugins {
|
|||||||
|
|
||||||
// required for kotlin-dsl or embedded-kotlin plugins
|
// required for kotlin-dsl or embedded-kotlin plugins
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
mavenCentral()
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
// NOTE: keep plugin versions in sync with settings.gradle.kts
|
|
||||||
|
|
||||||
// "com.jfrog.bintray" plugin
|
|
||||||
implementation( "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4" )
|
|
||||||
|
|
||||||
// "com.jfrog.artifactory" plugin
|
|
||||||
implementation( "org.jfrog.buildinfo:build-info-extractor-gradle:4.13.0" )
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
add( "java9Compile", sourceSets.main.get().output )
|
||||||
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
named<JavaCompile>( "compileJava9Java" ) {
|
named<JavaCompile>( "compileJava9Java" ) {
|
||||||
sourceCompatibility = "9"
|
sourceCompatibility = "9"
|
||||||
|
|||||||
@@ -33,9 +33,17 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
create( "module-info" ) {
|
create( "module-info" ) {
|
||||||
java {
|
java {
|
||||||
// include "src/main/java" here to get compile errors if classes are
|
// include "src/main/java" and "src/main/java9" here to get compile errors if classes are
|
||||||
// used from other modules that are not specified in module dependencies
|
// used from other modules that are not specified in module dependencies
|
||||||
setSrcDirs( listOf( "src/main/module-info", "src/main/java" ) )
|
setSrcDirs( listOf( "src/main/module-info", "src/main/java", "src/main/java9" ) )
|
||||||
|
|
||||||
|
// exclude Java 8 source file if an equally named Java 9+ source file exists
|
||||||
|
exclude {
|
||||||
|
if( it.isDirectory )
|
||||||
|
return@exclude false
|
||||||
|
val java9file = file( "${projectDir}/src/main/java9/${it.path}" )
|
||||||
|
java9file.exists() && java9file != it.file
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,7 +56,8 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
|
|||||||
dependsOn( extension.paths )
|
dependsOn( extension.paths )
|
||||||
|
|
||||||
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 )
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
|
|||||||
@@ -26,8 +26,7 @@ val extension = project.extensions.create<PublishExtension>( "flatlafPublish" )
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
`maven-publish`
|
`maven-publish`
|
||||||
id( "com.jfrog.bintray" )
|
signing
|
||||||
id( "com.jfrog.artifactory" )
|
|
||||||
}
|
}
|
||||||
|
|
||||||
publishing {
|
publishing {
|
||||||
@@ -63,54 +62,51 @@ publishing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
scm {
|
scm {
|
||||||
|
connection.set( "scm:git:git://github.com/JFormDesigner/FlatLaf.git" )
|
||||||
url.set( "https://github.com/JFormDesigner/FlatLaf" )
|
url.set( "https://github.com/JFormDesigner/FlatLaf" )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issueManagement {
|
||||||
|
system.set( "GitHub" )
|
||||||
|
url.set( "https://github.com/JFormDesigner/FlatLaf/issues" )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
name = "OSSRH"
|
||||||
|
|
||||||
|
val releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
|
||||||
|
val snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots/"
|
||||||
|
url = uri( if( java.lang.Boolean.getBoolean( "release" ) ) releasesRepoUrl else snapshotsRepoUrl )
|
||||||
|
|
||||||
|
credentials {
|
||||||
|
// get from gradle.properties
|
||||||
|
val ossrhUsername: String? by project
|
||||||
|
val ossrhPassword: String? by project
|
||||||
|
|
||||||
|
username = System.getenv( "OSSRH_USERNAME" ) ?: ossrhUsername
|
||||||
|
password = System.getenv( "OSSRH_PASSWORD" ) ?: ossrhPassword
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bintray {
|
signing {
|
||||||
user = rootProject.extra["bintray.user"] as String?
|
// get from gradle.properties
|
||||||
key = rootProject.extra["bintray.key"] as String?
|
val signingKey: String? by project
|
||||||
|
val signingPassword: String? by project
|
||||||
|
|
||||||
setPublications( "maven" )
|
val key = System.getenv( "SIGNING_KEY" ) ?: signingKey
|
||||||
|
val password = System.getenv( "SIGNING_PASSWORD" ) ?: signingPassword
|
||||||
|
|
||||||
with( pkg ) {
|
useInMemoryPgpKeys( key, password )
|
||||||
repo = "flatlaf"
|
sign( publishing.publications["maven"] )
|
||||||
afterEvaluate {
|
|
||||||
this@with.name = extension.artifactId
|
|
||||||
}
|
|
||||||
setLicenses( "Apache-2.0" )
|
|
||||||
vcsUrl = "https://github.com/JFormDesigner/FlatLaf"
|
|
||||||
|
|
||||||
with( version ) {
|
|
||||||
name = project.version.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
publish = rootProject.extra["bintray.publish"] as Boolean
|
|
||||||
dryRun = rootProject.extra["bintray.dryRun"] as Boolean
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
artifactory {
|
// disable signing of snapshots
|
||||||
setContextUrl( "https://oss.jfrog.org" )
|
tasks.withType<Sign>().configureEach {
|
||||||
|
onlyIf { java.lang.Boolean.getBoolean( "release" ) }
|
||||||
publish( closureOf<org.jfrog.gradle.plugin.artifactory.dsl.PublisherConfig> {
|
|
||||||
repository( delegateClosureOf<groovy.lang.GroovyObject> {
|
|
||||||
setProperty( "repoKey", "oss-snapshot-local" )
|
|
||||||
setProperty( "username", rootProject.extra["bintray.user"] as String? )
|
|
||||||
setProperty( "password", rootProject.extra["bintray.key"] as String? )
|
|
||||||
} )
|
|
||||||
|
|
||||||
defaults( delegateClosureOf<groovy.lang.GroovyObject> {
|
|
||||||
invokeMethod( "publications", "maven" )
|
|
||||||
setProperty( "publishArtifacts", true )
|
|
||||||
setProperty( "publishPom", true )
|
|
||||||
} )
|
|
||||||
} )
|
|
||||||
|
|
||||||
resolve( delegateClosureOf<org.jfrog.gradle.plugin.artifactory.dsl.ResolverConfig> {
|
|
||||||
setProperty( "repoKey", "jcenter" )
|
|
||||||
} )
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,16 @@ java {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
|
compileJava {
|
||||||
|
// generate JNI headers
|
||||||
|
options.headerOutputDirectory.set( buildDir.resolve( "generated/jni-headers" ) )
|
||||||
|
}
|
||||||
|
|
||||||
|
processResources {
|
||||||
|
// build native libraries
|
||||||
|
dependsOn( ":flatlaf-natives-windows:assemble" )
|
||||||
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
archiveBaseName.set( "flatlaf" )
|
archiveBaseName.set( "flatlaf" )
|
||||||
|
|
||||||
@@ -35,21 +45,11 @@ tasks {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
javadoc {
|
named<Jar>( "sourcesJar" ) {
|
||||||
options {
|
|
||||||
this as StandardJavadocDocletOptions
|
|
||||||
use( true )
|
|
||||||
tags = listOf( "uiDefault", "clientProperty" )
|
|
||||||
addStringOption( "Xdoclint:all,-missing", "-Xdoclint:all,-missing" )
|
|
||||||
}
|
|
||||||
isFailOnError = false
|
|
||||||
}
|
|
||||||
|
|
||||||
named<Jar>("sourcesJar" ) {
|
|
||||||
archiveBaseName.set( "flatlaf" )
|
archiveBaseName.set( "flatlaf" )
|
||||||
}
|
}
|
||||||
|
|
||||||
named<Jar>("javadocJar" ) {
|
named<Jar>( "javadocJar" ) {
|
||||||
archiveBaseName.set( "flatlaf" )
|
archiveBaseName.set( "flatlaf" )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,19 +19,29 @@ package com.formdev.flatlaf;
|
|||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Defines/documents own client properties used in FlatLaf.
|
||||||
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public interface FlatClientProperties
|
public interface FlatClientProperties
|
||||||
{
|
{
|
||||||
|
//---- JButton ------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies type of a button.
|
* Specifies type of a 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>
|
||||||
* <strong>Allowed Values</strong> {@link #BUTTON_TYPE_SQUARE}, {@link #BUTTON_TYPE_ROUND_RECT},
|
* <strong>Allowed Values</strong>
|
||||||
* {@link #BUTTON_TYPE_TAB}, {@link #BUTTON_TYPE_HELP} and {@link BUTTON_TYPE_TOOLBAR_BUTTON}
|
* {@link #BUTTON_TYPE_SQUARE},
|
||||||
|
* {@link #BUTTON_TYPE_ROUND_RECT},
|
||||||
|
* {@link #BUTTON_TYPE_TAB},
|
||||||
|
* {@link #BUTTON_TYPE_HELP},
|
||||||
|
* {@link #BUTTON_TYPE_TOOLBAR_BUTTON} or
|
||||||
|
* {@link #BUTTON_TYPE_BORDERLESS}
|
||||||
*/
|
*/
|
||||||
String BUTTON_TYPE = "JButton.buttonType";
|
String BUTTON_TYPE = "JButton.buttonType";
|
||||||
|
|
||||||
@@ -80,6 +90,16 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String BUTTON_TYPE_TOOLBAR_BUTTON = "toolBarButton";
|
String BUTTON_TYPE_TOOLBAR_BUTTON = "toolBarButton";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paint the button without a border in the unfocused state.
|
||||||
|
* <p>
|
||||||
|
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}
|
||||||
|
*
|
||||||
|
* @see #BUTTON_TYPE
|
||||||
|
* @since 1.2
|
||||||
|
*/
|
||||||
|
String BUTTON_TYPE_BORDERLESS = "borderless";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies selected state of a checkbox.
|
* Specifies selected state of a checkbox.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -99,17 +119,19 @@ public interface FlatClientProperties
|
|||||||
/**
|
/**
|
||||||
* Specifies whether the button preferred size will be made square (quadratically).
|
* Specifies whether the button preferred size will be made square (quadratically).
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}
|
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
*/
|
*/
|
||||||
String SQUARE_SIZE = "JButton.squareSize";
|
String SQUARE_SIZE = "JButton.squareSize";
|
||||||
|
|
||||||
|
//---- JComponent ---------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies minimum width of a component.
|
* Specifies minimum width of a component.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JButton}, {@link javax.swing.JToggleButton},
|
* <strong>Component</strong> {@link javax.swing.JButton}, {@link javax.swing.JToggleButton},
|
||||||
* {@link javax.swing.JComboBox}, {@link javax.swing.JSpinner} and {@link javax.swing.text.JTextComponent}<br>
|
* {@link javax.swing.JComboBox}, {@link javax.swing.JSpinner} and {@link javax.swing.text.JTextComponent}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Integer}<br>
|
* <strong>Value type</strong> {@link java.lang.Integer}
|
||||||
*/
|
*/
|
||||||
String MINIMUM_WIDTH = "JComponent.minimumWidth";
|
String MINIMUM_WIDTH = "JComponent.minimumWidth";
|
||||||
|
|
||||||
@@ -117,10 +139,19 @@ public interface FlatClientProperties
|
|||||||
* Specifies minimum height of a component.
|
* Specifies minimum height of a component.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
|
* <strong>Component</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Integer}<br>
|
* <strong>Value type</strong> {@link java.lang.Integer}
|
||||||
*/
|
*/
|
||||||
String MINIMUM_HEIGHT = "JComponent.minimumHeight";
|
String MINIMUM_HEIGHT = "JComponent.minimumHeight";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paint the component with round edges.
|
||||||
|
* <p>
|
||||||
|
* <strong>Components</strong> {@link javax.swing.JComboBox}, {@link javax.swing.JSpinner},
|
||||||
|
* {@link javax.swing.JTextField}, {@link javax.swing.JFormattedTextField} and {@link javax.swing.JPasswordField}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
|
*/
|
||||||
|
String COMPONENT_ROUND_RECT = "JComponent.roundRect";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies the outline color of the component border.
|
* Specifies the outline color of the component border.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -129,9 +160,12 @@ public interface FlatClientProperties
|
|||||||
* {@link javax.swing.JScrollPane}, {@link javax.swing.JSpinner},
|
* {@link javax.swing.JScrollPane}, {@link javax.swing.JSpinner},
|
||||||
* {@link javax.swing.JTextField} and {@link javax.swing.JToggleButton}<br>
|
* {@link javax.swing.JTextField} and {@link javax.swing.JToggleButton}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.String} or {@link java.awt.Color} or {@link java.awt.Color}[2]<br>
|
* <strong>Value type</strong> {@link java.lang.String} or {@link java.awt.Color} or {@link java.awt.Color}[2]<br>
|
||||||
* <strong>Allowed Values</strong> {@link #OUTLINE_ERROR}, {@link #OUTLINE_WARNING},
|
* <strong>Allowed Values</strong>
|
||||||
* any color (type {@link java.awt.Color}) or an array of two colors (type {@link java.awt.Color}[2])
|
* {@link #OUTLINE_ERROR},
|
||||||
* where the first color is for focused state and the second for unfocused state
|
* {@link #OUTLINE_WARNING},
|
||||||
|
* any color (type {@link java.awt.Color}) or
|
||||||
|
* an array of two colors (type {@link java.awt.Color}[2]) where the first color
|
||||||
|
* is for focused state and the second for unfocused state
|
||||||
*/
|
*/
|
||||||
String OUTLINE = "JComponent.outline";
|
String OUTLINE = "JComponent.outline";
|
||||||
|
|
||||||
@@ -150,13 +184,25 @@ public interface FlatClientProperties
|
|||||||
String OUTLINE_WARNING = "warning";
|
String OUTLINE_WARNING = "warning";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Paint the component with round edges.
|
* Specifies a callback that is invoked to check whether a component is permanent focus owner.
|
||||||
|
* Used to paint focus indicators.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Components</strong> {@link javax.swing.JComboBox}, {@link javax.swing.JSpinner},
|
* May be useful in special cases for custom components.
|
||||||
* {@link javax.swing.JTextField}, {@link javax.swing.JFormattedTextField} and {@link javax.swing.JPasswordField}
|
* <p>
|
||||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
* Use a {@link java.util.function.Predicate} that receives the component as parameter:
|
||||||
|
* <pre>{@code
|
||||||
|
* myComponent.putClientProperty( "JComponent.focusOwner",
|
||||||
|
* (Predicate<JComponent>) c -> {
|
||||||
|
* return ...; // check here
|
||||||
|
* } );
|
||||||
|
* }</pre>
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.util.function.Predicate}<javax.swing.JComponent>
|
||||||
*/
|
*/
|
||||||
String COMPONENT_ROUND_RECT = "JComponent.roundRect";
|
String COMPONENT_FOCUS_OWNER = "JComponent.focusOwner";
|
||||||
|
|
||||||
|
//---- Popup --------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether a drop shadow is painted if the component is shown in a popup
|
* Specifies whether a drop shadow is painted if the component is shown in a popup
|
||||||
@@ -167,6 +213,17 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String POPUP_DROP_SHADOW_PAINTED = "Popup.dropShadowPainted";
|
String POPUP_DROP_SHADOW_PAINTED = "Popup.dropShadowPainted";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether a heavy weight window should be used if the component is shown in a popup
|
||||||
|
* or if the component is the owner of another component that is shown in a popup.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
|
*/
|
||||||
|
String POPUP_FORCE_HEAVY_WEIGHT = "Popup.forceHeavyWeight";
|
||||||
|
|
||||||
|
//---- JProgressBar -------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether the progress bar has always the larger height even if no string is painted.
|
* Specifies whether the progress bar has always the larger height even if no string is painted.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -183,15 +240,72 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String PROGRESS_BAR_SQUARE = "JProgressBar.square";
|
String PROGRESS_BAR_SQUARE = "JProgressBar.square";
|
||||||
|
|
||||||
|
//---- JRootPane ----------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether the menu bar is embedded into the title pane if custom
|
* Specifies whether FlatLaf native window decorations should be used
|
||||||
* window decorations are enabled. Default is {@code true}.
|
* for {@code JFrame} or {@code JDialog}.
|
||||||
|
* <p>
|
||||||
|
* Setting this enables/disables using FlatLaf native window decorations
|
||||||
|
* for the window that contains the root pane.
|
||||||
|
* <p>
|
||||||
|
* This client property has lower priority than system property
|
||||||
|
* {@link FlatSystemProperties#USE_WINDOW_DECORATIONS}, but higher priority
|
||||||
|
* than UI default {@code TitlePane.useWindowDecorations}.
|
||||||
|
* <p>
|
||||||
|
* (requires Window 10)
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
|
*
|
||||||
|
* @since 1.1.1
|
||||||
|
*/
|
||||||
|
String USE_WINDOW_DECORATIONS = "JRootPane.useWindowDecorations";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether the menu bar is embedded into the window title pane
|
||||||
|
* if window decorations are enabled.
|
||||||
|
* <p>
|
||||||
|
* Setting this enables/disables embedding
|
||||||
|
* for the window that contains the root pane.
|
||||||
|
* <p>
|
||||||
|
* This client property has lower priority than system property
|
||||||
|
* {@link FlatSystemProperties#MENUBAR_EMBEDDED}, but higher priority
|
||||||
|
* than UI default {@code TitlePane.menuBarEmbedded}.
|
||||||
|
* <p>
|
||||||
|
* (requires Window 10)
|
||||||
* <p>
|
* <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";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Background color of window title bar (requires enabled window decorations).
|
||||||
|
* <p>
|
||||||
|
* (requires Window 10)
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.awt.Color}
|
||||||
|
*
|
||||||
|
* @since 1.1.2
|
||||||
|
*/
|
||||||
|
String TITLE_BAR_BACKGROUND = "JRootPane.titleBarBackground";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Foreground color of window title bar (requires enabled window decorations).
|
||||||
|
* <p>
|
||||||
|
* (requires Window 10)
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.awt.Color}
|
||||||
|
*
|
||||||
|
* @since 1.1.2
|
||||||
|
*/
|
||||||
|
String TITLE_BAR_FOREGROUND = "JRootPane.titleBarForeground";
|
||||||
|
|
||||||
|
//---- JScrollBar / JScrollPane -------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether the decrease/increase arrow buttons of a scrollbar are shown.
|
* Specifies whether the decrease/increase arrow buttons of a scrollbar are shown.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -203,11 +317,13 @@ public interface FlatClientProperties
|
|||||||
/**
|
/**
|
||||||
* Specifies whether the scroll pane uses smooth scrolling.
|
* Specifies whether the scroll pane uses smooth scrolling.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {{@link javax.swing.JScrollPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JScrollPane}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
*/
|
*/
|
||||||
String SCROLL_PANE_SMOOTH_SCROLLING = "JScrollPane.smoothScrolling";
|
String SCROLL_PANE_SMOOTH_SCROLLING = "JScrollPane.smoothScrolling";
|
||||||
|
|
||||||
|
//---- JTabbedPane --------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether separators are shown between tabs.
|
* Specifies whether separators are shown between tabs.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -216,6 +332,14 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String TABBED_PANE_SHOW_TAB_SEPARATORS = "JTabbedPane.showTabSeparators";
|
String TABBED_PANE_SHOW_TAB_SEPARATORS = "JTabbedPane.showTabSeparators";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether the separator between tabs area and content area should be shown.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_SHOW_CONTENT_SEPARATOR = "JTabbedPane.showContentSeparator";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether a full border is painted around a tabbed pane.
|
* Specifies whether a full border is painted around a tabbed pane.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -225,19 +349,354 @@ public interface FlatClientProperties
|
|||||||
String TABBED_PANE_HAS_FULL_BORDER = "JTabbedPane.hasFullBorder";
|
String TABBED_PANE_HAS_FULL_BORDER = "JTabbedPane.hasFullBorder";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies the height of a tab.
|
* Specifies whether the tab area should be hidden if it contains only one tab.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_HIDE_TAB_AREA_WITH_ONE_TAB = "JTabbedPane.hideTabAreaWithOneTab";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the minimum width of a tab.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
|
||||||
|
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Integer}
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_MINIMUM_TAB_WIDTH = "JTabbedPane.minimumTabWidth";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the maximum width of a tab.
|
||||||
|
* <p>
|
||||||
|
* Applied only if tab does not have a custom tab component
|
||||||
|
* (see {@link javax.swing.JTabbedPane#setTabComponentAt(int, java.awt.Component)}).
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
|
||||||
|
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Integer}
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_MAXIMUM_TAB_WIDTH = "JTabbedPane.maximumTabWidth";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the minimum height of a tab.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Integer}
|
* <strong>Value type</strong> {@link java.lang.Integer}
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TAB_INSETS
|
||||||
*/
|
*/
|
||||||
String TABBED_PANE_TAB_HEIGHT = "JTabbedPane.tabHeight";
|
String TABBED_PANE_TAB_HEIGHT = "JTabbedPane.tabHeight";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the insets of a tab.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
|
||||||
|
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
|
||||||
|
* <strong>Value type</strong> {@link java.awt.Insets}
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TAB_HEIGHT
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_INSETS = "JTabbedPane.tabInsets";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the insets of the tab area.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.awt.Insets}
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_AREA_INSETS = "JTabbedPane.tabAreaInsets";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether tabs are closable.
|
||||||
|
* If set to {@code true} on a tabbed pane component, all tabs in that tabbed pane are closable.
|
||||||
|
* To make individual tabs closable, set it to {@code true} on a tab content component.
|
||||||
|
* <p>
|
||||||
|
* Note that you have to specify a callback (see {@link #TABBED_PANE_TAB_CLOSABLE})
|
||||||
|
* that is invoked when the user clicks a tab close button.
|
||||||
|
* The callback is responsible for closing the tab.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
|
||||||
|
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TAB_CLOSE_CALLBACK
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_CLOSABLE = "JTabbedPane.tabClosable";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the tooltip text used for tab close buttons.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
|
||||||
|
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.String}
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TAB_CLOSABLE
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_CLOSE_TOOLTIPTEXT = "JTabbedPane.tabCloseToolTipText";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the callback that is invoked when a tab close button is clicked.
|
||||||
|
* The callback is responsible for closing the tab.
|
||||||
|
* <p>
|
||||||
|
* Either use a {@link java.util.function.IntConsumer} that receives the tab index as parameter:
|
||||||
|
* <pre>{@code
|
||||||
|
* myTabbedPane.putClientProperty( "JTabbedPane.tabCloseCallback",
|
||||||
|
* (IntConsumer) tabIndex -> {
|
||||||
|
* // close tab here
|
||||||
|
* } );
|
||||||
|
* }</pre>
|
||||||
|
* Or use a {@link java.util.function.BiConsumer}<javax.swing.JTabbedPane, Integer>
|
||||||
|
* that receives the tabbed pane and the tab index as parameters:
|
||||||
|
* <pre>{@code
|
||||||
|
* myTabbedPane.putClientProperty( "JTabbedPane.tabCloseCallback",
|
||||||
|
* (BiConsumer<JTabbedPane, Integer>) (tabbedPane, tabIndex) -> {
|
||||||
|
* // close tab here
|
||||||
|
* } );
|
||||||
|
* }</pre>
|
||||||
|
* If you need to check whether a modifier key (e.g. Alt or Shift) was pressed
|
||||||
|
* while the user clicked the tab close button, use {@link java.awt.EventQueue#getCurrentEvent}
|
||||||
|
* to get current event, check whether it is a {@link java.awt.event.MouseEvent}
|
||||||
|
* and invoke its methods. E.g.
|
||||||
|
* <pre>{@code
|
||||||
|
* AWTEvent e = EventQueue.getCurrentEvent();
|
||||||
|
* boolean shift = (e instanceof MouseEvent) ? ((MouseEvent)e).isShiftDown() : false;
|
||||||
|
* }</pre>
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
|
||||||
|
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
|
||||||
|
* <strong>Value type</strong> {@link java.util.function.IntConsumer}
|
||||||
|
* or {@link java.util.function.BiConsumer}<javax.swing.JTabbedPane, Integer>
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TAB_CLOSABLE
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_CLOSE_CALLBACK = "JTabbedPane.tabCloseCallback";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the display policy for the "more tabs" button,
|
||||||
|
* which shows a popup menu with the (partly) hidden tabs.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||||
|
* <strong>Allowed Values</strong>
|
||||||
|
* {@link #TABBED_PANE_POLICY_NEVER} or
|
||||||
|
* {@link #TABBED_PANE_POLICY_AS_NEEDED}
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TABS_POPUP_POLICY = "JTabbedPane.tabsPopupPolicy";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the display policy for the forward/backward scroll arrow buttons.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||||
|
* <strong>Allowed Values</strong>
|
||||||
|
* {@link #TABBED_PANE_POLICY_NEVER},
|
||||||
|
* {@link #TABBED_PANE_POLICY_AS_NEEDED} or
|
||||||
|
* {@link #TABBED_PANE_POLICY_AS_NEEDED_SINGLE}
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_SCROLL_BUTTONS_POLICY = "JTabbedPane.scrollButtonsPolicy";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display never.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TABS_POPUP_POLICY
|
||||||
|
* @see #TABBED_PANE_SCROLL_BUTTONS_POLICY
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_POLICY_NEVER = "never";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display only when needed.
|
||||||
|
* <p>
|
||||||
|
* If used for {@link #TABBED_PANE_SCROLL_BUTTONS_POLICY}, both scroll arrow buttons
|
||||||
|
* are either shown or hidden. Buttons are disabled if scrolling in that
|
||||||
|
* direction is not applicable.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TABS_POPUP_POLICY
|
||||||
|
* @see #TABBED_PANE_SCROLL_BUTTONS_POLICY
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_POLICY_AS_NEEDED = "asNeeded";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display single button only when needed.
|
||||||
|
* <p>
|
||||||
|
* If scroll button placement is trailing, then this option is ignored
|
||||||
|
* and both buttons are shown or hidden as needed.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_SCROLL_BUTTONS_POLICY
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_POLICY_AS_NEEDED_SINGLE = "asNeededSingle";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the placement of the forward/backward scroll arrow buttons.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||||
|
* <strong>Allowed Values</strong>
|
||||||
|
* {@link #TABBED_PANE_PLACEMENT_BOTH} or
|
||||||
|
* {@link #TABBED_PANE_PLACEMENT_TRAILING}
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_SCROLL_BUTTONS_PLACEMENT = "JTabbedPane.scrollButtonsPlacement";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The forward/backward scroll arrow buttons are placed on both sides of the tab area.
|
||||||
|
* The backward scroll button at the left/top side.
|
||||||
|
* The forward scroll button at the right/bottom side.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_SCROLL_BUTTONS_PLACEMENT
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_PLACEMENT_BOTH = "both";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The forward/backward scroll arrow buttons are placed on the trailing side of the tab area.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_SCROLL_BUTTONS_PLACEMENT
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_PLACEMENT_TRAILING = "trailing";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the alignment of the tab area.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Integer} or {@link java.lang.String}<br>
|
||||||
|
* <strong>Allowed Values</strong>
|
||||||
|
* {@link SwingConstants#LEADING} (default)
|
||||||
|
* {@link SwingConstants#TRAILING},
|
||||||
|
* {@link SwingConstants#CENTER},
|
||||||
|
* {@link #TABBED_PANE_ALIGN_LEADING} (default),
|
||||||
|
* {@link #TABBED_PANE_ALIGN_TRAILING},
|
||||||
|
* {@link #TABBED_PANE_ALIGN_CENTER} or
|
||||||
|
* {@link #TABBED_PANE_ALIGN_FILL}
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_AREA_ALIGNMENT = "JTabbedPane.tabAreaAlignment";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the horizontal alignment of the tab title and icon.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
|
||||||
|
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Integer} or {@link java.lang.String}<br>
|
||||||
|
* <strong>Allowed Values</strong>
|
||||||
|
* {@link SwingConstants#LEADING},
|
||||||
|
* {@link SwingConstants#TRAILING},
|
||||||
|
* {@link SwingConstants#CENTER} (default),
|
||||||
|
* {@link #TABBED_PANE_ALIGN_LEADING},
|
||||||
|
* {@link #TABBED_PANE_ALIGN_TRAILING} or
|
||||||
|
* {@link #TABBED_PANE_ALIGN_CENTER} (default)
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_ALIGNMENT = "JTabbedPane.tabAlignment";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Align to the leading edge.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TAB_AREA_ALIGNMENT
|
||||||
|
* @see #TABBED_PANE_TAB_ALIGNMENT
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_ALIGN_LEADING = "leading";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Align to the trailing edge.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TAB_AREA_ALIGNMENT
|
||||||
|
* @see #TABBED_PANE_TAB_ALIGNMENT
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_ALIGN_TRAILING = "trailing";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Align to center.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TAB_AREA_ALIGNMENT
|
||||||
|
* @see #TABBED_PANE_TAB_ALIGNMENT
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_ALIGN_CENTER = "center";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stretch to fill all available space.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TAB_AREA_ALIGNMENT
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_ALIGN_FILL = "fill";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies how the tabs should be sized.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||||
|
* <strong>Allowed Values</strong>
|
||||||
|
* {@link #TABBED_PANE_TAB_WIDTH_MODE_PREFERRED} (default),
|
||||||
|
* {@link #TABBED_PANE_TAB_WIDTH_MODE_EQUAL} or
|
||||||
|
* {@link #TABBED_PANE_TAB_WIDTH_MODE_COMPACT}
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_WIDTH_MODE = "JTabbedPane.tabWidthMode";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tab width is adjusted to tab icon and title.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TAB_WIDTH_MODE
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_WIDTH_MODE_PREFERRED = "preferred";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All tabs in a tabbed pane has same width.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TAB_WIDTH_MODE
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_WIDTH_MODE_EQUAL = "equal";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unselected tabs are smaller because they show only the tab icon, but no tab title.
|
||||||
|
* Selected tabs show both.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TAB_WIDTH_MODE
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_WIDTH_MODE_COMPACT = "compact";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the tab icon placement (relative to tab title).
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Integer}<br>
|
||||||
|
* <strong>Allowed Values</strong>
|
||||||
|
* {@link SwingConstants#LEADING} (default),
|
||||||
|
* {@link SwingConstants#TRAILING},
|
||||||
|
* {@link SwingConstants#TOP} or
|
||||||
|
* {@link SwingConstants#BOTTOM}
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_ICON_PLACEMENT = "JTabbedPane.tabIconPlacement";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies a component that will be placed at the leading edge of the tabs area.
|
||||||
|
* <p>
|
||||||
|
* For top and bottom tab placement, the layed out component size will be
|
||||||
|
* the preferred component width and the tab area height.<br>
|
||||||
|
* For left and right tab placement, the layed out component size will be
|
||||||
|
* the tab area width and the preferred component height.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.awt.Component}
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_LEADING_COMPONENT = "JTabbedPane.leadingComponent";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies a component that will be placed at the trailing edge of the tabs area.
|
||||||
|
* <p>
|
||||||
|
* For top and bottom tab placement, the layed out component size will be
|
||||||
|
* the available horizontal space (minimum is preferred component width) and the tab area height.<br>
|
||||||
|
* For left and right tab placement, the layed out component size will be
|
||||||
|
* the tab area width and the available vertical space (minimum is preferred component height).
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.awt.Component}
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TRAILING_COMPONENT = "JTabbedPane.trailingComponent";
|
||||||
|
|
||||||
|
//---- JTextField ---------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether all text is selected when the text component gains focus.
|
* 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>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.String}<br>
|
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||||
* <strong>Allowed Values</strong> {@link #SELECT_ALL_ON_FOCUS_POLICY_NEVER},
|
* <strong>Allowed Values</strong>
|
||||||
|
* {@link #SELECT_ALL_ON_FOCUS_POLICY_NEVER},
|
||||||
* {@link #SELECT_ALL_ON_FOCUS_POLICY_ONCE} (default) or
|
* {@link #SELECT_ALL_ON_FOCUS_POLICY_ONCE} (default) or
|
||||||
* {@link #SELECT_ALL_ON_FOCUS_POLICY_ALWAYS}
|
* {@link #SELECT_ALL_ON_FOCUS_POLICY_ALWAYS}
|
||||||
*/
|
*/
|
||||||
@@ -274,6 +733,8 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String PLACEHOLDER_TEXT = "JTextField.placeholderText";
|
String PLACEHOLDER_TEXT = "JTextField.placeholderText";
|
||||||
|
|
||||||
|
//---- JToggleButton ------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Height of underline if toggle button type is {@link #BUTTON_TYPE_TAB}.
|
* Height of underline if toggle button type is {@link #BUTTON_TYPE_TAB}.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -298,6 +759,27 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground";
|
String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground";
|
||||||
|
|
||||||
|
//---- JTree --------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override if a tree shows a wide selection. Default is {@code true}.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTree}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
|
*/
|
||||||
|
String TREE_WIDE_SELECTION = "JTree.wideSelection";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether tree item selection is painted. Default is {@code true}.
|
||||||
|
* If set to {@code false}, then the tree cell renderer is responsible for painting selection.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTree}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
|
*/
|
||||||
|
String TREE_PAINT_SELECTION = "JTree.paintSelection";
|
||||||
|
|
||||||
|
//---- helper methods -----------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether a client property of a component has the given value.
|
* Checks whether a client property of a component has the given value.
|
||||||
*/
|
*/
|
||||||
@@ -340,14 +822,4 @@ public interface FlatClientProperties
|
|||||||
Object value = c.getClientProperty( key );
|
Object value = c.getClientProperty( key );
|
||||||
return (value instanceof Color) ? (Color) value : defaultValue;
|
return (value instanceof Color) ? (Color) value : defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int clientPropertyChoice( JComponent c, String key, String... choices ) {
|
|
||||||
Object value = c.getClientProperty( key );
|
|
||||||
for( int i = 0; i < choices.length; i++ ) {
|
|
||||||
if( choices[i].equals( value ) )
|
|
||||||
return i;
|
|
||||||
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,23 +16,50 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf;
|
package com.formdev.flatlaf;
|
||||||
|
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Flat LaF that has a dark color scheme and looks like Darcula LaF.
|
* A Flat LaF that has a dark color scheme and looks like Darcula LaF.
|
||||||
*
|
* <p>
|
||||||
* The UI defaults are loaded from FlatDarculaLaf.properties, FlatDarkLaf.properties and FlatLaf.properties
|
* The UI defaults are loaded from {@code FlatDarculaLaf.properties},
|
||||||
|
* {@code FlatDarkLaf.properties} and {@code FlatLaf.properties}.
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatDarculaLaf
|
public class FlatDarculaLaf
|
||||||
extends FlatDarkLaf
|
extends FlatDarkLaf
|
||||||
{
|
{
|
||||||
public static boolean install( ) {
|
public static final String NAME = "FlatLaf Darcula";
|
||||||
return install( new FlatDarculaLaf() );
|
|
||||||
|
/**
|
||||||
|
* Sets the application look and feel to this LaF
|
||||||
|
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||||
|
*/
|
||||||
|
public static boolean setup() {
|
||||||
|
return setup( new FlatDarculaLaf() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static boolean install() {
|
||||||
|
return setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds this look and feel to the set of available look and feels.
|
||||||
|
* <p>
|
||||||
|
* Useful if your application uses {@link UIManager#getInstalledLookAndFeels()}
|
||||||
|
* to query available LaFs and display them to the user in a combobox.
|
||||||
|
*/
|
||||||
|
public static void installLafInfo() {
|
||||||
|
installLafInfo( NAME, FlatDarculaLaf.class );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "FlatLaf Darcula";
|
return NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -16,23 +16,49 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf;
|
package com.formdev.flatlaf;
|
||||||
|
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Flat LaF that has a dark color scheme.
|
* A Flat LaF that has a dark color scheme.
|
||||||
*
|
* <p>
|
||||||
* The UI defaults are loaded from FlatDarkLaf.properties and FlatLaf.properties
|
* The UI defaults are loaded from {@code FlatDarkLaf.properties} and {@code FlatLaf.properties}.
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatDarkLaf
|
public class FlatDarkLaf
|
||||||
extends FlatLaf
|
extends FlatLaf
|
||||||
{
|
{
|
||||||
public static boolean install( ) {
|
public static final String NAME = "FlatLaf Dark";
|
||||||
return install( new FlatDarkLaf() );
|
|
||||||
|
/**
|
||||||
|
* Sets the application look and feel to this LaF
|
||||||
|
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||||
|
*/
|
||||||
|
public static boolean setup() {
|
||||||
|
return setup( new FlatDarkLaf() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static boolean install() {
|
||||||
|
return setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds this look and feel to the set of available look and feels.
|
||||||
|
* <p>
|
||||||
|
* Useful if your application uses {@link UIManager#getInstalledLookAndFeels()}
|
||||||
|
* to query available LaFs and display them to the user in a combobox.
|
||||||
|
*/
|
||||||
|
public static void installLafInfo() {
|
||||||
|
installLafInfo( NAME, FlatDarkLaf.class );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "FlatLaf Dark";
|
return NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -42,10 +42,12 @@ class FlatInputMaps
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void initBasicInputMaps( UIDefaults defaults ) {
|
private static void initBasicInputMaps( UIDefaults defaults ) {
|
||||||
|
if( SystemInfo.isMacOS ) {
|
||||||
defaults.put( "Button.focusInputMap", new UIDefaults.LazyInputMap( new Object[] {
|
defaults.put( "Button.focusInputMap", new UIDefaults.LazyInputMap( new Object[] {
|
||||||
"SPACE", "pressed",
|
"SPACE", "pressed",
|
||||||
"released SPACE", "released"
|
"released SPACE", "released"
|
||||||
} ) );
|
} ) );
|
||||||
|
}
|
||||||
|
|
||||||
modifyInputMap( defaults, "ComboBox.ancestorInputMap",
|
modifyInputMap( defaults, "ComboBox.ancestorInputMap",
|
||||||
"SPACE", "spacePopup",
|
"SPACE", "spacePopup",
|
||||||
|
|||||||
@@ -16,23 +16,50 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf;
|
package com.formdev.flatlaf;
|
||||||
|
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Flat LaF that has a light color scheme and looks like IntelliJ LaF.
|
* A Flat LaF that has a light color scheme and looks like IntelliJ LaF.
|
||||||
*
|
* <p>
|
||||||
* The UI defaults are loaded from FlatIntelliJLaf.properties, FlatLightLaf.properties and FlatLaf.properties
|
* The UI defaults are loaded from {@code FlatIntelliJLaf.properties},
|
||||||
|
* {@code FlatLightLaf.properties} and {@code FlatLaf.properties}.
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatIntelliJLaf
|
public class FlatIntelliJLaf
|
||||||
extends FlatLightLaf
|
extends FlatLightLaf
|
||||||
{
|
{
|
||||||
public static boolean install( ) {
|
public static final String NAME = "FlatLaf IntelliJ";
|
||||||
return install( new FlatIntelliJLaf() );
|
|
||||||
|
/**
|
||||||
|
* Sets the application look and feel to this LaF
|
||||||
|
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||||
|
*/
|
||||||
|
public static boolean setup() {
|
||||||
|
return setup( new FlatIntelliJLaf() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static boolean install() {
|
||||||
|
return setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds this look and feel to the set of available look and feels.
|
||||||
|
* <p>
|
||||||
|
* Useful if your application uses {@link UIManager#getInstalledLookAndFeels()}
|
||||||
|
* to query available LaFs and display them to the user in a combobox.
|
||||||
|
*/
|
||||||
|
public static void installLafInfo() {
|
||||||
|
installLafInfo( NAME, FlatIntelliJLaf.class );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "FlatLaf IntelliJ";
|
return NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -32,14 +32,13 @@ 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.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.ServiceLoader;
|
import java.util.ServiceLoader;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
@@ -48,20 +47,24 @@ import javax.swing.JDialog;
|
|||||||
import javax.swing.JFrame;
|
import javax.swing.JFrame;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.PopupFactory;
|
import javax.swing.PopupFactory;
|
||||||
|
import javax.swing.RootPaneContainer;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIDefaults;
|
import javax.swing.UIDefaults;
|
||||||
|
import javax.swing.UIDefaults.ActiveValue;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.UnsupportedLookAndFeelException;
|
import javax.swing.UnsupportedLookAndFeelException;
|
||||||
import javax.swing.UIDefaults.ActiveValue;
|
|
||||||
import javax.swing.plaf.ColorUIResource;
|
import javax.swing.plaf.ColorUIResource;
|
||||||
import javax.swing.plaf.FontUIResource;
|
import javax.swing.plaf.FontUIResource;
|
||||||
|
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.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.FlatPopupFactory;
|
import com.formdev.flatlaf.ui.FlatPopupFactory;
|
||||||
import com.formdev.flatlaf.ui.JBRCustomDecorations;
|
import com.formdev.flatlaf.ui.FlatRootPaneUI;
|
||||||
import com.formdev.flatlaf.util.GrayFilter;
|
import com.formdev.flatlaf.util.GrayFilter;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
|
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
@@ -74,7 +77,6 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
public abstract class FlatLaf
|
public abstract class FlatLaf
|
||||||
extends BasicLookAndFeel
|
extends BasicLookAndFeel
|
||||||
{
|
{
|
||||||
static final Logger LOG = Logger.getLogger( FlatLaf.class.getName() );
|
|
||||||
private static final String DESKTOPFONTHINTS = "awt.font.desktophints";
|
private static final String DESKTOPFONTHINTS = "awt.font.desktophints";
|
||||||
|
|
||||||
private static List<Object> customDefaultsSources;
|
private static List<Object> customDefaultsSources;
|
||||||
@@ -91,19 +93,38 @@ public abstract class FlatLaf
|
|||||||
|
|
||||||
private Consumer<UIDefaults> postInitialization;
|
private Consumer<UIDefaults> postInitialization;
|
||||||
|
|
||||||
private Boolean oldFrameWindowDecorated;
|
/**
|
||||||
private Boolean oldDialogWindowDecorated;
|
* Sets the application look and feel to the given LaF
|
||||||
|
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||||
public static boolean install( LookAndFeel newLookAndFeel ) {
|
*/
|
||||||
|
public static boolean setup( LookAndFeel newLookAndFeel ) {
|
||||||
try {
|
try {
|
||||||
UIManager.setLookAndFeel( newLookAndFeel );
|
UIManager.setLookAndFeel( newLookAndFeel );
|
||||||
return true;
|
return true;
|
||||||
} catch( Exception ex ) {
|
} catch( Exception ex ) {
|
||||||
LOG.log( Level.SEVERE, "FlatLaf: Failed to initialize look and feel '" + newLookAndFeel.getClass().getName() + "'.", ex );
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to setup look and feel '" + newLookAndFeel.getClass().getName() + "'.", ex );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #setup(LookAndFeel)} instead; this method will be removed in a future version
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static boolean install( LookAndFeel newLookAndFeel ) {
|
||||||
|
return setup( newLookAndFeel );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given look and feel to the set of available look and feels.
|
||||||
|
* <p>
|
||||||
|
* Useful if your application uses {@link UIManager#getInstalledLookAndFeels()}
|
||||||
|
* to query available LaFs and display them to the user in a combobox.
|
||||||
|
*/
|
||||||
|
public static void installLafInfo( String lafName, Class<? extends LookAndFeel> lafClass ) {
|
||||||
|
UIManager.installLookAndFeel( new UIManager.LookAndFeelInfo( lafName, lafClass.getName() ) );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the look and feel identifier.
|
* Returns the look and feel identifier.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -131,28 +152,28 @@ public abstract class FlatLaf
|
|||||||
* Returns whether FlatLaf supports custom window decorations.
|
* Returns whether FlatLaf supports custom window decorations.
|
||||||
* This depends on the operating system and on the used Java runtime.
|
* This depends on the operating system and on the used Java runtime.
|
||||||
* <p>
|
* <p>
|
||||||
* To use custom window decorations in your application, enable them with
|
* This method returns {@code true} on Windows 10 (see exception below), {@code false} otherwise.
|
||||||
* following code (before creating any frames or dialogs). Then custom window
|
|
||||||
* decorations are only enabled if this method returns {@code true}.
|
|
||||||
* <pre>
|
|
||||||
* JFrame.setDefaultLookAndFeelDecorated( true );
|
|
||||||
* JDialog.setDefaultLookAndFeelDecorated( true );
|
|
||||||
* </pre>
|
|
||||||
* <p>
|
* <p>
|
||||||
* Returns {@code true} on Windows 10, {@code false} otherwise.
|
* Returns also {@code false} on Windows 10 if:
|
||||||
* <p>
|
* <ul>
|
||||||
* Return also {@code false} if running on Windows 10 in
|
* <li>FlatLaf native window border support is available (requires Windows 10)</li>
|
||||||
|
* <li>running in
|
||||||
* <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime 11 (or later)</a>
|
* <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime 11 (or later)</a>
|
||||||
* (<a href="https://github.com/JetBrains/JetBrainsRuntime">source code on github</a>)
|
* (<a href="https://github.com/JetBrains/JetBrainsRuntime">source code on github</a>)
|
||||||
* and JBR supports custom window decorations. In this case, JBR custom decorations
|
* and JBR supports custom window decorations
|
||||||
* are enabled if {@link JFrame#isDefaultLookAndFeelDecorated()} or
|
* </li>
|
||||||
* {@link JDialog#isDefaultLookAndFeelDecorated()} return {@code true}.
|
* </ul>
|
||||||
|
* In this cases, custom decorations are enabled by the root pane.
|
||||||
|
* Usage of {@link JFrame#setDefaultLookAndFeelDecorated(boolean)} or
|
||||||
|
* {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} is not necessary.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean getSupportsWindowDecorations() {
|
public boolean getSupportsWindowDecorations() {
|
||||||
if( SystemInfo.isJetBrainsJVM_11_orLater &&
|
if( SystemInfo.isProjector || SystemInfo.isWebswing || SystemInfo.isWinPE )
|
||||||
SystemInfo.isWindows_10_orLater &&
|
return false;
|
||||||
JBRCustomDecorations.isSupported() )
|
|
||||||
|
if( SystemInfo.isWindows_10_orLater &&
|
||||||
|
FlatNativeWindowBorder.isSupported() )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return SystemInfo.isWindows_10_orLater;
|
return SystemInfo.isWindows_10_orLater;
|
||||||
@@ -170,6 +191,11 @@ public abstract class FlatLaf
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Icon getDisabledIcon( JComponent component, Icon icon ) {
|
public Icon getDisabledIcon( JComponent component, Icon icon ) {
|
||||||
|
if( icon instanceof DisabledIconProvider ) {
|
||||||
|
Icon disabledIcon = ((DisabledIconProvider)icon).getDisabledIcon();
|
||||||
|
return !(disabledIcon instanceof UIResource) ? new IconUIResource( disabledIcon ) : disabledIcon;
|
||||||
|
}
|
||||||
|
|
||||||
if( icon instanceof ImageIcon ) {
|
if( icon instanceof ImageIcon ) {
|
||||||
Object grayFilter = UIManager.get( "Component.grayFilter" );
|
Object grayFilter = UIManager.get( "Component.grayFilter" );
|
||||||
ImageFilter filter = (grayFilter instanceof ImageFilter)
|
ImageFilter filter = (grayFilter instanceof ImageFilter)
|
||||||
@@ -245,19 +271,9 @@ public abstract class FlatLaf
|
|||||||
Color linkColor = defaults.getColor( "Component.linkColor" );
|
Color linkColor = defaults.getColor( "Component.linkColor" );
|
||||||
if( linkColor != null ) {
|
if( linkColor != null ) {
|
||||||
new HTMLEditorKit().getStyleSheet().addRule(
|
new HTMLEditorKit().getStyleSheet().addRule(
|
||||||
String.format( "a { color: #%06x; }", linkColor.getRGB() & 0xffffff ) );
|
String.format( "a, address { color: #%06x; }", linkColor.getRGB() & 0xffffff ) );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// enable/disable window decorations, but only if system property is either
|
|
||||||
// "true" or "false"; in other cases it is not changed
|
|
||||||
Boolean useWindowDecorations = FlatSystemProperties.getBooleanStrict( FlatSystemProperties.USE_WINDOW_DECORATIONS, null );
|
|
||||||
if( useWindowDecorations != null ) {
|
|
||||||
oldFrameWindowDecorated = JFrame.isDefaultLookAndFeelDecorated();
|
|
||||||
oldDialogWindowDecorated = JDialog.isDefaultLookAndFeelDecorated();
|
|
||||||
JFrame.setDefaultLookAndFeelDecorated( useWindowDecorations );
|
|
||||||
JDialog.setDefaultLookAndFeelDecorated( useWindowDecorations );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -287,17 +303,9 @@ public abstract class FlatLaf
|
|||||||
}
|
}
|
||||||
|
|
||||||
// restore default link color
|
// restore default link color
|
||||||
new HTMLEditorKit().getStyleSheet().addRule( "a { color: blue; }" );
|
new HTMLEditorKit().getStyleSheet().addRule( "a, address { color: blue; }" );
|
||||||
postInitialization = null;
|
postInitialization = null;
|
||||||
|
|
||||||
// restore enable/disable window decorations
|
|
||||||
if( oldFrameWindowDecorated != null ) {
|
|
||||||
JFrame.setDefaultLookAndFeelDecorated( oldFrameWindowDecorated );
|
|
||||||
JDialog.setDefaultLookAndFeelDecorated( oldDialogWindowDecorated );
|
|
||||||
oldFrameWindowDecorated = null;
|
|
||||||
oldDialogWindowDecorated = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
super.uninitialize();
|
super.uninitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,9 +330,9 @@ 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 ).newInstance();
|
aquaLaf = (BasicLookAndFeel) Class.forName( aquaLafClassName ).getDeclaredConstructor().newInstance();
|
||||||
} catch( Exception ex ) {
|
} catch( Exception ex ) {
|
||||||
LOG.log( Level.SEVERE, "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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,6 +352,10 @@ public abstract class FlatLaf
|
|||||||
public UIDefaults getDefaults() {
|
public UIDefaults getDefaults() {
|
||||||
UIDefaults defaults = super.getDefaults();
|
UIDefaults defaults = super.getDefaults();
|
||||||
|
|
||||||
|
// add flag that indicates whether the LaF is light or dark
|
||||||
|
// (can be queried without using FlatLaf API)
|
||||||
|
defaults.put( "laf.dark", isDark() );
|
||||||
|
|
||||||
// add resource bundle for localized texts
|
// add resource bundle for localized texts
|
||||||
defaults.addResourceBundle( "com.formdev.flatlaf.resources.Bundle" );
|
defaults.addResourceBundle( "com.formdev.flatlaf.resources.Bundle" );
|
||||||
|
|
||||||
@@ -379,6 +391,12 @@ public abstract class FlatLaf
|
|||||||
initIconColors( defaults, isDark() );
|
initIconColors( defaults, isDark() );
|
||||||
FlatInputMaps.initInputMaps( defaults );
|
FlatInputMaps.initInputMaps( defaults );
|
||||||
|
|
||||||
|
// copy InternalFrame.icon (the Java cup) to TitlePane.icon
|
||||||
|
// (using defaults.remove() to avoid that lazy value is resolved and icon loaded here)
|
||||||
|
Object icon = defaults.remove( "InternalFrame.icon" );
|
||||||
|
defaults.put( "InternalFrame.icon", icon );
|
||||||
|
defaults.put( "TitlePane.icon", icon );
|
||||||
|
|
||||||
// get addons and sort them by priority
|
// get addons and sort them by priority
|
||||||
ServiceLoader<FlatDefaultsAddon> addonLoader = ServiceLoader.load( FlatDefaultsAddon.class );
|
ServiceLoader<FlatDefaultsAddon> addonLoader = ServiceLoader.load( FlatDefaultsAddon.class );
|
||||||
List<FlatDefaultsAddon> addons = new ArrayList<>();
|
List<FlatDefaultsAddon> addons = new ArrayList<>();
|
||||||
@@ -440,14 +458,27 @@ public abstract class FlatLaf
|
|||||||
|
|
||||||
if( SystemInfo.isWindows ) {
|
if( SystemInfo.isWindows ) {
|
||||||
Font winFont = (Font) Toolkit.getDefaultToolkit().getDesktopProperty( "win.messagebox.font" );
|
Font winFont = (Font) Toolkit.getDefaultToolkit().getDesktopProperty( "win.messagebox.font" );
|
||||||
if( winFont != null )
|
if( winFont != null ) {
|
||||||
|
if( SystemInfo.isWinPE ) {
|
||||||
|
// on WinPE use "win.defaultGUI.font", which is usually Tahoma,
|
||||||
|
// because Segoe UI font is not available on WinPE
|
||||||
|
Font winPEFont = (Font) Toolkit.getDefaultToolkit().getDesktopProperty( "win.defaultGUI.font" );
|
||||||
|
if( winPEFont != null )
|
||||||
|
uiFont = createCompositeFont( winPEFont.getFamily(), winPEFont.getStyle(), winFont.getSize() );
|
||||||
|
} else
|
||||||
uiFont = createCompositeFont( winFont.getFamily(), winFont.getStyle(), winFont.getSize() );
|
uiFont = createCompositeFont( winFont.getFamily(), winFont.getStyle(), winFont.getSize() );
|
||||||
|
}
|
||||||
|
|
||||||
} else if( SystemInfo.isMacOS ) {
|
} else if( SystemInfo.isMacOS ) {
|
||||||
String fontName;
|
String fontName;
|
||||||
if( SystemInfo.isMacOS_10_15_Catalina_orLater ) {
|
if( SystemInfo.isMacOS_10_15_Catalina_orLater ) {
|
||||||
|
if (SystemInfo.isJetBrainsJVM_11_orLater) {
|
||||||
|
// See https://youtrack.jetbrains.com/issue/JBR-1915
|
||||||
|
fontName = ".AppleSystemUIFont";
|
||||||
|
} else {
|
||||||
// use Helvetica Neue font
|
// use Helvetica Neue font
|
||||||
fontName = "Helvetica Neue";
|
fontName = "Helvetica Neue";
|
||||||
|
}
|
||||||
} else if( SystemInfo.isMacOS_10_11_ElCapitan_orLater ) {
|
} else if( SystemInfo.isMacOS_10_11_ElCapitan_orLater ) {
|
||||||
// use San Francisco Text font
|
// use San Francisco Text font
|
||||||
fontName = ".SF NS Text";
|
fontName = ".SF NS Text";
|
||||||
@@ -463,9 +494,11 @@ public abstract class FlatLaf
|
|||||||
uiFont = (font instanceof FontUIResource) ? (FontUIResource) font : new FontUIResource( font );
|
uiFont = (font instanceof FontUIResource) ? (FontUIResource) font : new FontUIResource( font );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fallback
|
||||||
if( uiFont == null )
|
if( uiFont == null )
|
||||||
uiFont = createCompositeFont( Font.SANS_SERIF, Font.PLAIN, 12 );
|
uiFont = createCompositeFont( Font.SANS_SERIF, Font.PLAIN, 12 );
|
||||||
|
|
||||||
|
// increase font size if system property "flatlaf.uiScale" is set
|
||||||
uiFont = UIScale.applyCustomScaleFactor( uiFont );
|
uiFont = UIScale.applyCustomScaleFactor( uiFont );
|
||||||
|
|
||||||
// use active value for all fonts to allow changing fonts in all components
|
// use active value for all fonts to allow changing fonts in all components
|
||||||
@@ -494,6 +527,13 @@ public abstract class FlatLaf
|
|||||||
return (font instanceof FontUIResource) ? (FontUIResource) font : new FontUIResource( font );
|
return (font instanceof FontUIResource) ? (FontUIResource) font : new FontUIResource( font );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public static ActiveValue createActiveFontValue( float scaleFactor ) {
|
||||||
|
return new ActiveFont( scaleFactor );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the default color palette for action icons and object icons to the given UIDefaults.
|
* Adds the default color palette for action icons and object icons to the given UIDefaults.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -518,8 +558,15 @@ public abstract class FlatLaf
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void putAATextInfo( UIDefaults defaults ) {
|
private void putAATextInfo( UIDefaults defaults ) {
|
||||||
if( SystemInfo.isJava_9_orLater ) {
|
if ( SystemInfo.isMacOS && SystemInfo.isJetBrainsJVM ) {
|
||||||
|
// The awt.font.desktophints property suggests sub-pixel anti-aliasing
|
||||||
|
// which renders text with too much weight on macOS in the JetBrains JRE.
|
||||||
|
// Use greyscale anti-aliasing instead.
|
||||||
|
defaults.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
|
||||||
|
} else if( SystemInfo.isJava_9_orLater ) {
|
||||||
Object desktopHints = Toolkit.getDefaultToolkit().getDesktopProperty( DESKTOPFONTHINTS );
|
Object desktopHints = Toolkit.getDefaultToolkit().getDesktopProperty( DESKTOPFONTHINTS );
|
||||||
|
if( desktopHints == null )
|
||||||
|
desktopHints = fallbackAATextInfo();
|
||||||
if( desktopHints instanceof Map ) {
|
if( desktopHints instanceof Map ) {
|
||||||
@SuppressWarnings( "unchecked" )
|
@SuppressWarnings( "unchecked" )
|
||||||
Map<Object, Object> hints = (Map<Object, Object>) desktopHints;
|
Map<Object, Object> hints = (Map<Object, Object>) desktopHints;
|
||||||
@@ -542,9 +589,52 @@ public abstract class FlatLaf
|
|||||||
Object value = Class.forName( "sun.swing.SwingUtilities2$AATextInfo" )
|
Object value = Class.forName( "sun.swing.SwingUtilities2$AATextInfo" )
|
||||||
.getMethod( "getAATextInfo", boolean.class )
|
.getMethod( "getAATextInfo", boolean.class )
|
||||||
.invoke( null, true );
|
.invoke( null, true );
|
||||||
|
if( value == null )
|
||||||
|
value = fallbackAATextInfo();
|
||||||
defaults.put( key, value );
|
defaults.put( key, value );
|
||||||
} catch( Exception ex ) {
|
} catch( Exception ex ) {
|
||||||
Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, null, ex );
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
throw new RuntimeException( ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object fallbackAATextInfo() {
|
||||||
|
// do nothing if explicitly overridden
|
||||||
|
if( System.getProperty( "awt.useSystemAAFontSettings" ) != null )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Object aaHint = null;
|
||||||
|
Integer lcdContrastHint = null;
|
||||||
|
|
||||||
|
if( SystemInfo.isLinux ) {
|
||||||
|
// see sun.awt.UNIXToolkit.getDesktopAAHints()
|
||||||
|
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
||||||
|
if( toolkit.getDesktopProperty( "gnome.Xft/Antialias" ) == null &&
|
||||||
|
toolkit.getDesktopProperty( "fontconfig/Antialias" ) == null )
|
||||||
|
{
|
||||||
|
// no Gnome or KDE Desktop properties available
|
||||||
|
// --> enable antialiasing
|
||||||
|
aaHint = RenderingHints.VALUE_TEXT_ANTIALIAS_ON;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( aaHint == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if( SystemInfo.isJava_9_orLater ) {
|
||||||
|
Map<Object, Object> hints = new HashMap<>();
|
||||||
|
hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, aaHint );
|
||||||
|
hints.put( RenderingHints.KEY_TEXT_LCD_CONTRAST, lcdContrastHint );
|
||||||
|
return hints;
|
||||||
|
} else {
|
||||||
|
// Java 8
|
||||||
|
try {
|
||||||
|
return Class.forName( "sun.swing.SwingUtilities2$AATextInfo" )
|
||||||
|
.getConstructor( Object.class, Integer.class )
|
||||||
|
.newInstance( aaHint, lcdContrastHint );
|
||||||
|
} catch( Exception ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
throw new RuntimeException( ex );
|
throw new RuntimeException( ex );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -651,7 +741,7 @@ public abstract class FlatLaf
|
|||||||
// update UI
|
// update UI
|
||||||
updateUI();
|
updateUI();
|
||||||
} catch( UnsupportedLookAndFeelException ex ) {
|
} catch( UnsupportedLookAndFeelException ex ) {
|
||||||
LOG.log( Level.SEVERE, "FlatLaf: Failed to reinitialize look and feel '" + lookAndFeel.getClass().getName() + "'.", ex );
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to reinitialize look and feel '" + lookAndFeel.getClass().getName() + "'.", ex );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
@@ -684,6 +774,79 @@ public abstract class FlatLaf
|
|||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether native window decorations are supported on current platform.
|
||||||
|
* <p>
|
||||||
|
* This requires Windows 10, but may be disabled if running in special environments
|
||||||
|
* (JetBrains Projector, Webswing or WinPE) or if loading native library fails.
|
||||||
|
* If system property {@link FlatSystemProperties#USE_WINDOW_DECORATIONS} is set to
|
||||||
|
* {@code false}, then this method also returns {@code false}.
|
||||||
|
*
|
||||||
|
* @since 1.1.2
|
||||||
|
*/
|
||||||
|
public static boolean supportsNativeWindowDecorations() {
|
||||||
|
return SystemInfo.isWindows_10_orLater && FlatNativeWindowBorder.isSupported();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether native window decorations are enabled.
|
||||||
|
*
|
||||||
|
* @since 1.1.2
|
||||||
|
*/
|
||||||
|
public static boolean isUseNativeWindowDecorations() {
|
||||||
|
return UIManager.getBoolean( "TitlePane.useWindowDecorations" );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether native window decorations are enabled.
|
||||||
|
* <p>
|
||||||
|
* Existing frames and dialogs will be updated.
|
||||||
|
*
|
||||||
|
* @since 1.1.2
|
||||||
|
*/
|
||||||
|
public static void setUseNativeWindowDecorations( boolean enabled ) {
|
||||||
|
UIManager.put( "TitlePane.useWindowDecorations", enabled );
|
||||||
|
|
||||||
|
if( !(UIManager.getLookAndFeel() instanceof FlatLaf) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// update existing frames and dialogs
|
||||||
|
for( Window w : Window.getWindows() ) {
|
||||||
|
if( isDisplayableFrameOrDialog( w ) )
|
||||||
|
FlatRootPaneUI.updateNativeWindowBorder( ((RootPaneContainer)w).getRootPane() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revalidate and repaint all displayable frames and dialogs.
|
||||||
|
*
|
||||||
|
* @since 1.1.2
|
||||||
|
*/
|
||||||
|
public static void revalidateAndRepaintAllFramesAndDialogs() {
|
||||||
|
for( Window w : Window.getWindows() ) {
|
||||||
|
if( isDisplayableFrameOrDialog( w ) ) {
|
||||||
|
w.revalidate();
|
||||||
|
w.repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repaint all displayable frames and dialogs.
|
||||||
|
*
|
||||||
|
* @since 1.1.2
|
||||||
|
*/
|
||||||
|
public static void repaintAllFramesAndDialogs() {
|
||||||
|
for( Window w : Window.getWindows() ) {
|
||||||
|
if( isDisplayableFrameOrDialog( w ) )
|
||||||
|
w.repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isDisplayableFrameOrDialog( Window w ) {
|
||||||
|
return w.isDisplayable() && (w instanceof JFrame || w instanceof JDialog);
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isShowMnemonics() {
|
public static boolean isShowMnemonics() {
|
||||||
return MnemonicHandler.isShowMnemonics();
|
return MnemonicHandler.isShowMnemonics();
|
||||||
}
|
}
|
||||||
@@ -696,6 +859,18 @@ public abstract class FlatLaf
|
|||||||
MnemonicHandler.showMnemonics( false, null );
|
MnemonicHandler.showMnemonics( false, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// do not allow overriding to avoid issues in FlatUIUtils.createSharedUI()
|
||||||
|
@Override
|
||||||
|
public final boolean equals( Object obj ) {
|
||||||
|
return super.equals( obj );
|
||||||
|
}
|
||||||
|
|
||||||
|
// do not allow overriding to avoid issues in FlatUIUtils.createSharedUI()
|
||||||
|
@Override
|
||||||
|
public final int hashCode() {
|
||||||
|
return super.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
//---- class ActiveFont ---------------------------------------------------
|
//---- class ActiveFont ---------------------------------------------------
|
||||||
|
|
||||||
private static class ActiveFont
|
private static class ActiveFont
|
||||||
@@ -715,6 +890,10 @@ public abstract class FlatLaf
|
|||||||
public Object createValue( UIDefaults table ) {
|
public Object createValue( UIDefaults table ) {
|
||||||
Font defaultFont = UIManager.getFont( "defaultFont" );
|
Font defaultFont = UIManager.getFont( "defaultFont" );
|
||||||
|
|
||||||
|
// fallback (to avoid NPE in case that this is used in another Laf)
|
||||||
|
if( defaultFont == null )
|
||||||
|
defaultFont = UIManager.getFont( "Label.font" );
|
||||||
|
|
||||||
if( lastDefaultFont != defaultFont ) {
|
if( lastDefaultFont != defaultFont ) {
|
||||||
lastDefaultFont = defaultFont;
|
lastDefaultFont = defaultFont;
|
||||||
|
|
||||||
@@ -744,4 +923,24 @@ public abstract class FlatLaf
|
|||||||
super( image );
|
super( image );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- interface DisabledIconProvider -------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A provider for disabled icons.
|
||||||
|
* <p>
|
||||||
|
* This is intended to be implemented by {@link javax.swing.Icon} implementations
|
||||||
|
* that provide the ability to paint disabled state.
|
||||||
|
* <p>
|
||||||
|
* Used in {@link FlatLaf#getDisabledIcon(JComponent, Icon)} to create a disabled icon from an enabled icon.
|
||||||
|
*/
|
||||||
|
public interface DisabledIconProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns an icon with a disabled appearance.
|
||||||
|
*
|
||||||
|
* @return a disabled icon
|
||||||
|
*/
|
||||||
|
Icon getDisabledIcon();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,23 +16,49 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf;
|
package com.formdev.flatlaf;
|
||||||
|
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Flat LaF that has a light color scheme.
|
* A Flat LaF that has a light color scheme.
|
||||||
*
|
* <p>
|
||||||
* The UI defaults are loaded from FlatLightLaf.properties and FlatLaf.properties
|
* The UI defaults are loaded from {@code FlatLightLaf.properties} and {@code FlatLaf.properties}.
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatLightLaf
|
public class FlatLightLaf
|
||||||
extends FlatLaf
|
extends FlatLaf
|
||||||
{
|
{
|
||||||
public static boolean install( ) {
|
public static final String NAME = "FlatLaf Light";
|
||||||
return install( new FlatLightLaf() );
|
|
||||||
|
/**
|
||||||
|
* Sets the application look and feel to this LaF
|
||||||
|
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||||
|
*/
|
||||||
|
public static boolean setup() {
|
||||||
|
return setup( new FlatLightLaf() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static boolean install() {
|
||||||
|
return setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds this look and feel to the set of available look and feels.
|
||||||
|
* <p>
|
||||||
|
* Useful if your application uses {@link UIManager#getInstalledLookAndFeels()}
|
||||||
|
* to query available LaFs and display them to the user in a combobox.
|
||||||
|
*/
|
||||||
|
public static void installLafInfo() {
|
||||||
|
installLafInfo( NAME, FlatLightLaf.class );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "FlatLaf Light";
|
return NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -88,6 +88,10 @@ public class FlatPropertiesLaf
|
|||||||
return dark;
|
return dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Properties getProperties() {
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ArrayList<Class<?>> getLafClassesForDefaultsLoading() {
|
protected ArrayList<Class<?>> getLafClassesForDefaultsLoading() {
|
||||||
ArrayList<Class<?>> lafClasses = new ArrayList<>();
|
ArrayList<Class<?>> lafClasses = new ArrayList<>();
|
||||||
|
|||||||
@@ -16,8 +16,7 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf;
|
package com.formdev.flatlaf;
|
||||||
|
|
||||||
import javax.swing.JDialog;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
import javax.swing.JFrame;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines/documents own system properties used in FlatLaf.
|
* Defines/documents own system properties used in FlatLaf.
|
||||||
@@ -35,6 +34,8 @@ public interface FlatSystemProperties
|
|||||||
* To replace the Java 9+ system scale factor, use system property "sun.java2d.uiScale",
|
* To replace the Java 9+ system scale factor, use system property "sun.java2d.uiScale",
|
||||||
* which has the same syntax as this one.
|
* which has the same syntax as this one.
|
||||||
* <p>
|
* <p>
|
||||||
|
* Since FlatLaf 1.1.2: Scale factors less then 100% are allowed.
|
||||||
|
* <p>
|
||||||
* <strong>Allowed Values</strong> e.g. {@code 1.5}, {@code 1.5x}, {@code 150%} or {@code 144dpi} (96dpi is 100%)<br>
|
* <strong>Allowed Values</strong> e.g. {@code 1.5}, {@code 1.5x}, {@code 150%} or {@code 144dpi} (96dpi is 100%)<br>
|
||||||
*/
|
*/
|
||||||
String UI_SCALE = "flatlaf.uiScale";
|
String UI_SCALE = "flatlaf.uiScale";
|
||||||
@@ -47,6 +48,17 @@ public interface FlatSystemProperties
|
|||||||
*/
|
*/
|
||||||
String UI_SCALE_ENABLED = "flatlaf.uiScale.enabled";
|
String UI_SCALE_ENABLED = "flatlaf.uiScale.enabled";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether values smaller than 100% are allowed for the user scale factor
|
||||||
|
* (see {@link UIScale#getUserScaleFactor()}).
|
||||||
|
* <p>
|
||||||
|
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||||
|
* <strong>Default</strong> {@code false}
|
||||||
|
*
|
||||||
|
* @since 1.1.2
|
||||||
|
*/
|
||||||
|
String UI_SCALE_ALLOW_SCALE_DOWN = "flatlaf.uiScale.allowScaleDown";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether Ubuntu font should be used on Ubuntu Linux.
|
* Specifies whether Ubuntu font should be used on Ubuntu Linux.
|
||||||
* By default, if not running in a JetBrains Runtime, the Liberation Sans font
|
* By default, if not running in a JetBrains Runtime, the Liberation Sans font
|
||||||
@@ -58,11 +70,18 @@ public interface FlatSystemProperties
|
|||||||
String USE_UBUNTU_FONT = "flatlaf.useUbuntuFont";
|
String USE_UBUNTU_FONT = "flatlaf.useUbuntuFont";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether custom look and feel window decorations should be used
|
* Specifies whether native window decorations should be used
|
||||||
* when creating {@code JFrame} or {@code JDialog}.
|
* when creating {@code JFrame} or {@code JDialog}.
|
||||||
* <p>
|
* <p>
|
||||||
* If this system property is set, FlatLaf invokes {@link JFrame#setDefaultLookAndFeelDecorated(boolean)}
|
* Setting this to {@code true} forces using native window decorations
|
||||||
* and {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} on LaF initialization.
|
* even if they are not enabled by the application.<br>
|
||||||
|
* Setting this to {@code false} disables using native window decorations.
|
||||||
|
* <p>
|
||||||
|
* This system property has higher priority than client property
|
||||||
|
* {@link FlatClientProperties#USE_WINDOW_DECORATIONS} and
|
||||||
|
* UI default {@code TitlePane.useWindowDecorations}.
|
||||||
|
* <p>
|
||||||
|
* (requires Window 10)
|
||||||
* <p>
|
* <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
|
||||||
@@ -76,8 +95,10 @@ public interface FlatSystemProperties
|
|||||||
* <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime</a>
|
* <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime</a>
|
||||||
* (based on OpenJDK).
|
* (based on OpenJDK).
|
||||||
* <p>
|
* <p>
|
||||||
* Setting this to {@code true} forces using JetBrains Runtime custom window decorations
|
* Setting this to {@code false} disables using JetBrains Runtime custom window decorations.
|
||||||
* even if they are not enabled by the application.
|
* Then FlatLaf native window decorations are used.
|
||||||
|
* <p>
|
||||||
|
* (requires Window 10)
|
||||||
* <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}
|
||||||
@@ -85,12 +106,30 @@ public interface FlatSystemProperties
|
|||||||
String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations";
|
String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether menubar is embedded into custom window decorations.
|
* Specifies whether the menu bar is embedded into the window title pane
|
||||||
|
* if window decorations are enabled.
|
||||||
|
* <p>
|
||||||
|
* Setting this to {@code true} forces embedding.<br>
|
||||||
|
* Setting this to {@code false} disables embedding.
|
||||||
|
* <p>
|
||||||
|
* This system property has higher priority than client property
|
||||||
|
* {@link FlatClientProperties#MENU_BAR_EMBEDDED} and
|
||||||
|
* UI default {@code TitlePane.menuBarEmbedded}.
|
||||||
|
* <p>
|
||||||
|
* (requires Window 10)
|
||||||
|
* <p>
|
||||||
|
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||||
|
* <strong>Default</strong> none
|
||||||
|
*/
|
||||||
|
String MENUBAR_EMBEDDED = "flatlaf.menuBarEmbedded";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether animations are enabled.
|
||||||
* <p>
|
* <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}
|
||||||
*/
|
*/
|
||||||
String MENUBAR_EMBEDDED = "flatlaf.menuBarEmbedded";
|
String ANIMATION = "flatlaf.animation";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
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;
|
||||||
@@ -29,11 +30,12 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
|
||||||
import javax.swing.UIDefaults;
|
import javax.swing.UIDefaults;
|
||||||
import javax.swing.plaf.ColorUIResource;
|
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.StringUtils;
|
import com.formdev.flatlaf.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,6 +58,8 @@ 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> colors;
|
private final Map<String, String> colors;
|
||||||
private final Map<String, Object> ui;
|
private final Map<String, Object> ui;
|
||||||
private final Map<String, Object> icons;
|
private final Map<String, Object> icons;
|
||||||
@@ -64,20 +68,28 @@ public class IntelliJTheme
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a IntelliJ .theme.json file from the given input stream,
|
* Loads a IntelliJ .theme.json file from the given input stream,
|
||||||
* creates a Laf instance for it and installs it.
|
* creates a Laf instance for it and sets it up.
|
||||||
*
|
*
|
||||||
* The input stream is automatically closed.
|
* The input stream is automatically closed.
|
||||||
* Using a buffered input stream is not necessary.
|
* Using a buffered input stream is not necessary.
|
||||||
*/
|
*/
|
||||||
public static boolean install( InputStream in ) {
|
public static boolean setup( InputStream in ) {
|
||||||
try {
|
try {
|
||||||
return FlatLaf.install( createLaf( in ) );
|
return FlatLaf.setup( createLaf( in ) );
|
||||||
} catch( Exception ex ) {
|
} catch( Exception ex ) {
|
||||||
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: Failed to load IntelliJ theme", ex );
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to load IntelliJ theme", ex );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #setup(InputStream)} instead; this method will be removed in a future version
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static boolean install( InputStream in ) {
|
||||||
|
return setup( in );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a IntelliJ .theme.json file from the given input stream and
|
* Loads a IntelliJ .theme.json file from the given input stream and
|
||||||
* creates a Laf instance for it.
|
* creates a Laf instance for it.
|
||||||
@@ -119,6 +131,8 @@ 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" );
|
||||||
|
|
||||||
colors = (Map<String, String>) json.get( "colors" );
|
colors = (Map<String, String>) json.get( "colors" );
|
||||||
ui = (Map<String, Object>) json.get( "ui" );
|
ui = (Map<String, Object>) json.get( "ui" );
|
||||||
icons = (Map<String, Object>) json.get( "icons" );
|
icons = (Map<String, Object>) json.get( "icons" );
|
||||||
@@ -156,6 +170,11 @@ public class IntelliJTheme
|
|||||||
defaults.put( "Button.disabledBackground", panelBackground );
|
defaults.put( "Button.disabledBackground", panelBackground );
|
||||||
defaults.put( "ToggleButton.disabledBackground", panelBackground );
|
defaults.put( "ToggleButton.disabledBackground", panelBackground );
|
||||||
|
|
||||||
|
// fix Button borders
|
||||||
|
copyIfNotSet( defaults, "Button.focusedBorderColor", "Component.focusedBorderColor", uiKeys );
|
||||||
|
defaults.put( "Button.hoverBorderColor", defaults.get( "Button.focusedBorderColor" ) );
|
||||||
|
defaults.put( "HelpButton.hoverBorderColor", defaults.get( "Button.focusedBorderColor" ) );
|
||||||
|
|
||||||
// IDEA uses a SVG icon for the help button, but paints the background with Button.startBackground and Button.endBackground
|
// IDEA uses a SVG icon for the help button, but paints the background with Button.startBackground and Button.endBackground
|
||||||
Object helpButtonBackground = defaults.get( "Button.startBackground" );
|
Object helpButtonBackground = defaults.get( "Button.startBackground" );
|
||||||
Object helpButtonBorderColor = defaults.get( "Button.startBorderColor" );
|
Object helpButtonBorderColor = defaults.get( "Button.startBorderColor" );
|
||||||
@@ -205,6 +224,18 @@ public class IntelliJTheme
|
|||||||
if( !uiKeys.contains( "ToggleButton.foreground" ) && uiKeys.contains( "Button.foreground" ) )
|
if( !uiKeys.contains( "ToggleButton.foreground" ) && uiKeys.contains( "Button.foreground" ) )
|
||||||
defaults.put( "ToggleButton.foreground", defaults.get( "Button.foreground" ) );
|
defaults.put( "ToggleButton.foreground", defaults.get( "Button.foreground" ) );
|
||||||
|
|
||||||
|
// fix DesktopPane background (use Panel.background and make it 5% darker/lighter)
|
||||||
|
Color desktopBackgroundBase = defaults.getColor( "Panel.background" );
|
||||||
|
Color desktopBackground = ColorFunctions.applyFunctions( desktopBackgroundBase,
|
||||||
|
new ColorFunctions.HSLIncreaseDecrease( 2, dark, 5, false, true ) );
|
||||||
|
defaults.put( "Desktop.background", new ColorUIResource( desktopBackground ) );
|
||||||
|
|
||||||
|
// fix List and Table background colors in Material UI Lite themes
|
||||||
|
if( isMaterialUILite ) {
|
||||||
|
defaults.put( "List.background", defaults.get( "Tree.background" ) );
|
||||||
|
defaults.put( "Table.background", defaults.get( "Tree.background" ) );
|
||||||
|
}
|
||||||
|
|
||||||
// limit tree row height
|
// limit tree row height
|
||||||
int rowHeight = defaults.getInt( "Tree.rowHeight" );
|
int rowHeight = defaults.getInt( "Tree.rowHeight" );
|
||||||
if( rowHeight > 22 )
|
if( rowHeight > 22 )
|
||||||
@@ -225,10 +256,18 @@ public class IntelliJTheme
|
|||||||
// remove theme specific UI defaults and remember only those for current theme
|
// remove theme specific UI defaults and remember only those for current theme
|
||||||
Map<Object, Object> themeSpecificDefaults = new HashMap<>();
|
Map<Object, Object> themeSpecificDefaults = new HashMap<>();
|
||||||
String currentThemePrefix = '[' + name.replace( ' ', '_' ) + ']';
|
String currentThemePrefix = '[' + name.replace( ' ', '_' ) + ']';
|
||||||
|
String currentThemeAndAuthorPrefix = '[' + name.replace( ' ', '_' ) + "---" + author.replace( ' ', '_' ) + ']';
|
||||||
|
String currentAuthorPrefix = "[author-" + author.replace( ' ', '_' ) + ']';
|
||||||
|
String allThemesPrefix = "[*]";
|
||||||
|
String[] prefixes = { currentThemePrefix, currentThemeAndAuthorPrefix, currentAuthorPrefix, allThemesPrefix };
|
||||||
for( String key : themeSpecificKeys ) {
|
for( String key : themeSpecificKeys ) {
|
||||||
Object value = defaults.remove( key );
|
Object value = defaults.remove( key );
|
||||||
if( key.startsWith( currentThemePrefix ) )
|
for( String prefix : prefixes ) {
|
||||||
themeSpecificDefaults.put( key.substring( currentThemePrefix.length() ), value );
|
if( key.startsWith( prefix ) ) {
|
||||||
|
themeSpecificDefaults.put( key.substring( prefix.length() ), value );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return themeSpecificDefaults;
|
return themeSpecificDefaults;
|
||||||
@@ -269,7 +308,6 @@ public class IntelliJTheme
|
|||||||
uiKeys.add( key );
|
uiKeys.add( key );
|
||||||
|
|
||||||
// fix ComboBox size and Spinner border in all Material UI Lite themes
|
// fix ComboBox size and Spinner border in all Material UI Lite themes
|
||||||
boolean isMaterialUILite = author.equals( "Mallowigi" );
|
|
||||||
if( isMaterialUILite && (key.equals( "ComboBox.padding" ) || key.equals( "Spinner.border" )) )
|
if( isMaterialUILite && (key.equals( "ComboBox.padding" ) || key.equals( "Spinner.border" )) )
|
||||||
return; // ignore
|
return; // ignore
|
||||||
|
|
||||||
@@ -302,7 +340,7 @@ public class IntelliJTheme
|
|||||||
try {
|
try {
|
||||||
uiValue = UIDefaultsLoader.parseValue( key, valueStr );
|
uiValue = UIDefaultsLoader.parseValue( key, valueStr );
|
||||||
} catch( RuntimeException ex ) {
|
} catch( RuntimeException ex ) {
|
||||||
UIDefaultsLoader.logParseError( Level.CONFIG, key, valueStr, ex );
|
UIDefaultsLoader.logParseError( key, valueStr, ex, false );
|
||||||
return; // ignore invalid value
|
return; // ignore invalid value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -322,6 +360,10 @@ public class IntelliJTheme
|
|||||||
|
|
||||||
// replace all values in UI defaults that match the wildcard key
|
// replace all values in UI defaults that match the wildcard key
|
||||||
for( Object k : defaultsKeysCache ) {
|
for( Object k : defaultsKeysCache ) {
|
||||||
|
if( k.equals( "Desktop.background" ) ||
|
||||||
|
k.equals( "DesktopIcon.background" ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
if( k instanceof String ) {
|
if( k instanceof String ) {
|
||||||
// support replacing of mapped keys
|
// support replacing of mapped keys
|
||||||
// (e.g. set ComboBox.buttonEditableBackground to *.background
|
// (e.g. set ComboBox.buttonEditableBackground to *.background
|
||||||
@@ -381,7 +423,7 @@ public class IntelliJTheme
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Because IDEA uses SVGs for check boxes and radio buttons the colors for
|
* Because IDEA uses SVGs for check boxes and radio buttons, the colors for
|
||||||
* this two components are specified in "icons > ColorPalette".
|
* this 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 UI defaults.
|
||||||
*/
|
*/
|
||||||
@@ -453,29 +495,47 @@ public class IntelliJTheme
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove hover and pressed colors
|
// update hover, pressed and focused colors
|
||||||
if( checkboxModified ) {
|
if( checkboxModified ) {
|
||||||
|
// for non-filled checkbox/radiobutton used in dark themes
|
||||||
defaults.remove( "CheckBox.icon.focusWidth" );
|
defaults.remove( "CheckBox.icon.focusWidth" );
|
||||||
defaults.remove( "CheckBox.icon.hoverBorderColor" );
|
defaults.put( "CheckBox.icon.hoverBorderColor", defaults.get( "CheckBox.icon.focusedBorderColor" ) );
|
||||||
defaults.remove( "CheckBox.icon.focusedBackground" );
|
|
||||||
defaults.remove( "CheckBox.icon.hoverBackground" );
|
|
||||||
defaults.remove( "CheckBox.icon.pressedBackground" );
|
|
||||||
defaults.remove( "CheckBox.icon.selectedFocusedBackground" );
|
|
||||||
defaults.remove( "CheckBox.icon.selectedHoverBackground" );
|
|
||||||
defaults.remove( "CheckBox.icon.selectedPressedBackground" );
|
|
||||||
|
|
||||||
|
// for filled checkbox/radiobutton used in light themes
|
||||||
defaults.remove( "CheckBox.icon[filled].focusWidth" );
|
defaults.remove( "CheckBox.icon[filled].focusWidth" );
|
||||||
defaults.remove( "CheckBox.icon[filled].hoverBorderColor" );
|
defaults.put( "CheckBox.icon[filled].hoverBorderColor", defaults.get( "CheckBox.icon[filled].focusedBorderColor" ) );
|
||||||
defaults.remove( "CheckBox.icon[filled].focusedBackground" );
|
defaults.put( "CheckBox.icon[filled].selectedFocusedBackground", defaults.get( "CheckBox.icon[filled].selectedBackground" ) );
|
||||||
defaults.remove( "CheckBox.icon[filled].hoverBackground" );
|
|
||||||
defaults.remove( "CheckBox.icon[filled].pressedBackground" );
|
if( dark ) {
|
||||||
defaults.remove( "CheckBox.icon[filled].selectedFocusedBackground" );
|
// IDEA Darcula checkBoxFocused.svg, checkBoxSelectedFocused.svg,
|
||||||
defaults.remove( "CheckBox.icon[filled].selectedHoverBackground" );
|
// radioFocused.svg and radioSelectedFocused.svg
|
||||||
defaults.remove( "CheckBox.icon[filled].selectedPressedBackground" );
|
// use opacity=".65" for the border
|
||||||
|
// --> add alpha to focused border colors
|
||||||
|
String[] focusedBorderColorKeys = new String[] {
|
||||||
|
"CheckBox.icon.focusedBorderColor",
|
||||||
|
"CheckBox.icon.selectedFocusedBorderColor",
|
||||||
|
"CheckBox.icon[filled].focusedBorderColor",
|
||||||
|
"CheckBox.icon[filled].selectedFocusedBorderColor",
|
||||||
|
};
|
||||||
|
for( String key : focusedBorderColorKeys ) {
|
||||||
|
Color color = defaults.getColor( key );
|
||||||
|
if( color != null ) {
|
||||||
|
defaults.put( key, new ColorUIResource( new Color(
|
||||||
|
(color.getRGB() & 0xffffff) | 0xa6000000, true ) ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void copyIfNotSet( UIDefaults defaults, String destKey, String srcKey, Set<String> uiKeys ) {
|
||||||
|
if( !uiKeys.contains( destKey ) )
|
||||||
|
defaults.put( destKey, defaults.get( srcKey ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Rename UI default keys (key --> value). */
|
||||||
private static Map<String, String> uiKeyMapping = new HashMap<>();
|
private static Map<String, String> uiKeyMapping = new HashMap<>();
|
||||||
|
/** Copy UI default keys (value --> key). */
|
||||||
private static Map<String, String> uiKeyCopying = new HashMap<>();
|
private static Map<String, String> uiKeyCopying = new HashMap<>();
|
||||||
private static Map<String, String> uiKeyInverseMapping = new HashMap<>();
|
private static Map<String, String> uiKeyInverseMapping = new HashMap<>();
|
||||||
private static Map<String, String> checkboxKeyMapping = new HashMap<>();
|
private static Map<String, String> checkboxKeyMapping = new HashMap<>();
|
||||||
@@ -505,6 +565,7 @@ public class IntelliJTheme
|
|||||||
uiKeyCopying.put( "CheckBoxMenuItem.margin", "MenuItem.margin" );
|
uiKeyCopying.put( "CheckBoxMenuItem.margin", "MenuItem.margin" );
|
||||||
uiKeyCopying.put( "RadioButtonMenuItem.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" );
|
||||||
@@ -529,6 +590,9 @@ public class IntelliJTheme
|
|||||||
|
|
||||||
// Slider
|
// Slider
|
||||||
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" );
|
||||||
|
|
||||||
// TitlePane
|
// TitlePane
|
||||||
uiKeyCopying.put( "TitlePane.inactiveBackground", "TitlePane.background" );
|
uiKeyCopying.put( "TitlePane.inactiveBackground", "TitlePane.background" );
|
||||||
@@ -583,7 +647,7 @@ public class IntelliJTheme
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return theme.name;
|
return getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
import java.util.logging.Level;
|
|
||||||
|
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;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
@@ -54,25 +55,39 @@ class LinuxFontPolicy
|
|||||||
|
|
||||||
String family = "";
|
String family = "";
|
||||||
int style = Font.PLAIN;
|
int style = Font.PLAIN;
|
||||||
int size = 10;
|
double dsize = 10;
|
||||||
|
|
||||||
|
// parse pango font description
|
||||||
|
// see https://developer.gnome.org/pango/1.46/pango-Fonts.html#pango-font-description-from-string
|
||||||
StringTokenizer st = new StringTokenizer( (String) fontName );
|
StringTokenizer st = new StringTokenizer( (String) fontName );
|
||||||
while( st.hasMoreTokens() ) {
|
while( st.hasMoreTokens() ) {
|
||||||
String word = st.nextToken();
|
String word = st.nextToken();
|
||||||
|
|
||||||
if( word.equalsIgnoreCase( "italic" ) )
|
// remove trailing ',' (e.g. in "Ubuntu Condensed, 11" or "Ubuntu Condensed, Bold 11")
|
||||||
|
if( word.endsWith( "," ) )
|
||||||
|
word = word.substring( 0, word.length() - 1 ).trim();
|
||||||
|
|
||||||
|
String lword = word.toLowerCase();
|
||||||
|
if( lword.equals( "italic" ) || lword.equals( "oblique" ) )
|
||||||
style |= Font.ITALIC;
|
style |= Font.ITALIC;
|
||||||
else if( word.equalsIgnoreCase( "bold" ) )
|
else if( lword.equals( "bold" ) )
|
||||||
style |= Font.BOLD;
|
style |= Font.BOLD;
|
||||||
else if( Character.isDigit( word.charAt( 0 ) ) ) {
|
else if( Character.isDigit( word.charAt( 0 ) ) ) {
|
||||||
try {
|
try {
|
||||||
size = Integer.parseInt( word );
|
dsize = Double.parseDouble( word );
|
||||||
} catch( NumberFormatException ex ) {
|
} catch( NumberFormatException ex ) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
} else
|
} else {
|
||||||
|
// remove '-' from "Semi-Bold", "Extra-Light", etc
|
||||||
|
if( lword.startsWith( "semi-" ) || lword.startsWith( "demi-" ) )
|
||||||
|
word = word.substring( 0, 4 ) + word.substring( 5 );
|
||||||
|
else if( lword.startsWith( "extra-" ) || lword.startsWith( "ultra-" ) )
|
||||||
|
word = word.substring( 0, 5 ) + word.substring( 6 );
|
||||||
|
|
||||||
family = family.isEmpty() ? word : (family + ' ' + word);
|
family = family.isEmpty() ? word : (family + ' ' + word);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Ubuntu font is rendered poorly (except if running in JetBrains VM)
|
// Ubuntu font is rendered poorly (except if running in JetBrains VM)
|
||||||
// --> use Liberation Sans font
|
// --> use Liberation Sans font
|
||||||
@@ -82,8 +97,8 @@ class LinuxFontPolicy
|
|||||||
family = "Liberation Sans";
|
family = "Liberation Sans";
|
||||||
|
|
||||||
// scale font size
|
// scale font size
|
||||||
double dsize = size * getGnomeFontScale();
|
dsize *= getGnomeFontScale();
|
||||||
size = (int) (dsize + 0.5);
|
int size = (int) (dsize + 0.5);
|
||||||
if( size < 1 )
|
if( size < 1 )
|
||||||
size = 1;
|
size = 1;
|
||||||
|
|
||||||
@@ -92,7 +107,37 @@ class LinuxFontPolicy
|
|||||||
if( logicalFamily != null )
|
if( logicalFamily != null )
|
||||||
family = logicalFamily;
|
family = logicalFamily;
|
||||||
|
|
||||||
return createFont( family, style, size, dsize );
|
return createFontEx( family, style, size, dsize );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a font for the given family, style and size.
|
||||||
|
* If the font family does not match any font on the system,
|
||||||
|
* then the last word (usually a font weight) from the family name is removed and tried again.
|
||||||
|
* E.g. family 'URW Bookman Light' is not found, but 'URW Bookman' is found.
|
||||||
|
* If still not found, then font of family 'Dialog' is returned.
|
||||||
|
*/
|
||||||
|
private static Font createFontEx( String family, int style, int size, double dsize ) {
|
||||||
|
for(;;) {
|
||||||
|
Font font = createFont( family, style, size, dsize );
|
||||||
|
|
||||||
|
// if the font family does not match any font on the system, "Dialog" family is returned
|
||||||
|
if( !"Dialog".equals( font.getFamily() ) || "Dialog".equals( family ) )
|
||||||
|
return font;
|
||||||
|
|
||||||
|
// find last word in family
|
||||||
|
int index = family.lastIndexOf( ' ' );
|
||||||
|
if( index < 0 )
|
||||||
|
return createFont( "Dialog", style, size, dsize );
|
||||||
|
|
||||||
|
// check whether last work contains some font weight (e.g. Ultra-Bold or Heavy)
|
||||||
|
String lastWord = family.substring( index + 1 ).toLowerCase();
|
||||||
|
if( lastWord.contains( "bold" ) || lastWord.contains( "heavy" ) || lastWord.contains( "black" ) )
|
||||||
|
style |= Font.BOLD;
|
||||||
|
|
||||||
|
// remove last word from family and try again
|
||||||
|
family = family.substring( 0, index );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Font createFont( String family, int style, int size, double dsize ) {
|
private static Font createFont( String family, int style, int size, double dsize ) {
|
||||||
@@ -172,7 +217,7 @@ class LinuxFontPolicy
|
|||||||
if( "1".equals( strs.get( 5 ) ) )
|
if( "1".equals( strs.get( 5 ) ) )
|
||||||
style |= Font.ITALIC;
|
style |= Font.ITALIC;
|
||||||
} catch( RuntimeException ex ) {
|
} catch( RuntimeException ex ) {
|
||||||
FlatLaf.LOG.log( Level.CONFIG, "FlatLaf: Failed to parse 'font=" + generalFont + "'.", ex );
|
LoggingFacade.INSTANCE.logConfig( "FlatLaf: Failed to parse 'font=" + generalFont + "'.", ex );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +231,7 @@ class LinuxFontPolicy
|
|||||||
if( dpi < 50 )
|
if( dpi < 50 )
|
||||||
dpi = 50;
|
dpi = 50;
|
||||||
} catch( NumberFormatException ex ) {
|
} catch( NumberFormatException ex ) {
|
||||||
FlatLaf.LOG.log( Level.CONFIG, "FlatLaf: Failed to parse 'forceFontDPI=" + forceFontDPI + "'.", ex );
|
LoggingFacade.INSTANCE.logConfig( "FlatLaf: Failed to parse 'forceFontDPI=" + forceFontDPI + "'.", ex );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,7 +270,7 @@ class LinuxFontPolicy
|
|||||||
while( (line = reader.readLine()) != null )
|
while( (line = reader.readLine()) != null )
|
||||||
lines.add( line );
|
lines.add( line );
|
||||||
} catch( IOException ex ) {
|
} catch( IOException ex ) {
|
||||||
FlatLaf.LOG.log( Level.CONFIG, "FlatLaf: Failed to read '" + filename + "'.", ex );
|
LoggingFacade.INSTANCE.logConfig( "FlatLaf: Failed to read '" + filename + "'.", ex );
|
||||||
}
|
}
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import java.awt.event.WindowEvent;
|
|||||||
import java.awt.event.WindowListener;
|
import java.awt.event.WindowListener;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
|
import javax.swing.JDialog;
|
||||||
import javax.swing.JFrame;
|
import javax.swing.JFrame;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JMenu;
|
import javax.swing.JMenu;
|
||||||
@@ -137,10 +138,17 @@ class MnemonicHandler
|
|||||||
// get menu bar and first menu
|
// get menu bar and first menu
|
||||||
Component c = e.getComponent();
|
Component c = e.getComponent();
|
||||||
JRootPane rootPane = SwingUtilities.getRootPane( c );
|
JRootPane rootPane = SwingUtilities.getRootPane( c );
|
||||||
Window window = (rootPane != null) ? SwingUtilities.getWindowAncestor( rootPane ) : null;
|
|
||||||
JMenuBar menuBar = (rootPane != null) ? rootPane.getJMenuBar() : null;
|
JMenuBar menuBar = (rootPane != null) ? rootPane.getJMenuBar() : null;
|
||||||
if( menuBar == null && window instanceof JFrame )
|
if( menuBar == null ) {
|
||||||
|
// get menu bar from frame/dialog because there
|
||||||
|
// may be multiple nested root panes in a frame/dialog
|
||||||
|
// (e.g. each internal frame has its own root pane)
|
||||||
|
Window window = SwingUtilities.getWindowAncestor( c );
|
||||||
|
if( window instanceof JFrame )
|
||||||
menuBar = ((JFrame)window).getJMenuBar();
|
menuBar = ((JFrame)window).getJMenuBar();
|
||||||
|
else if( window instanceof JDialog )
|
||||||
|
menuBar = ((JDialog)window).getJMenuBar();
|
||||||
|
}
|
||||||
JMenu firstMenu = (menuBar != null) ? menuBar.getMenu( 0 ) : null;
|
JMenu firstMenu = (menuBar != null) ? menuBar.getMenu( 0 ) : null;
|
||||||
|
|
||||||
// select first menu and show mnemonics
|
// select first menu and show mnemonics
|
||||||
|
|||||||
@@ -26,12 +26,13 @@ import java.io.InputStream;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.logging.Level;
|
|
||||||
import javax.swing.UIDefaults;
|
import javax.swing.UIDefaults;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.UIDefaults.ActiveValue;
|
import javax.swing.UIDefaults.ActiveValue;
|
||||||
@@ -46,6 +47,7 @@ import com.formdev.flatlaf.util.ColorFunctions.ColorFunction;
|
|||||||
import com.formdev.flatlaf.util.DerivedColor;
|
import com.formdev.flatlaf.util.DerivedColor;
|
||||||
import com.formdev.flatlaf.util.GrayFilter;
|
import com.formdev.flatlaf.util.GrayFilter;
|
||||||
import com.formdev.flatlaf.util.HSLColor;
|
import com.formdev.flatlaf.util.HSLColor;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.StringUtils;
|
import com.formdev.flatlaf.util.StringUtils;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
@@ -68,7 +70,9 @@ class UIDefaultsLoader
|
|||||||
private static final String VARIABLE_PREFIX = "@";
|
private static final String VARIABLE_PREFIX = "@";
|
||||||
private static final String PROPERTY_PREFIX = "$";
|
private static final String PROPERTY_PREFIX = "$";
|
||||||
private static final String OPTIONAL_PREFIX = "?";
|
private static final String OPTIONAL_PREFIX = "?";
|
||||||
private static final String GLOBAL_PREFIX = "*.";
|
private static final String WILDCARD_PREFIX = "*.";
|
||||||
|
|
||||||
|
private static int parseColorDepth;
|
||||||
|
|
||||||
static void loadDefaultsFromProperties( Class<?> lookAndFeelClass, List<FlatDefaultsAddon> addons,
|
static void loadDefaultsFromProperties( Class<?> lookAndFeelClass, List<FlatDefaultsAddon> addons,
|
||||||
Properties additionalDefaults, boolean dark, UIDefaults defaults )
|
Properties additionalDefaults, boolean dark, UIDefaults defaults )
|
||||||
@@ -109,7 +113,15 @@ class UIDefaultsLoader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// load custom properties files (usually provides by applications)
|
// collect addon class loaders
|
||||||
|
List<ClassLoader> addonClassLoaders = new ArrayList<>();
|
||||||
|
for( FlatDefaultsAddon addon : addons ) {
|
||||||
|
ClassLoader addonClassLoader = addon.getClass().getClassLoader();
|
||||||
|
if( !addonClassLoaders.contains( addonClassLoader ) )
|
||||||
|
addonClassLoaders.add( addonClassLoader );
|
||||||
|
}
|
||||||
|
|
||||||
|
// load custom properties files (usually provided by applications)
|
||||||
List<Object> customDefaultsSources = FlatLaf.getCustomDefaultsSources();
|
List<Object> customDefaultsSources = FlatLaf.getCustomDefaultsSources();
|
||||||
int size = (customDefaultsSources != null) ? customDefaultsSources.size() : 0;
|
int size = (customDefaultsSources != null) ? customDefaultsSources.size() : 0;
|
||||||
for( int i = 0; i < size; i++ ) {
|
for( int i = 0; i < size; i++ ) {
|
||||||
@@ -119,6 +131,10 @@ class UIDefaultsLoader
|
|||||||
String packageName = (String) source;
|
String packageName = (String) source;
|
||||||
ClassLoader classLoader = (ClassLoader) customDefaultsSources.get( ++i );
|
ClassLoader classLoader = (ClassLoader) customDefaultsSources.get( ++i );
|
||||||
|
|
||||||
|
// use class loader also for instantiating classes specified in values
|
||||||
|
if( classLoader != null && !addonClassLoaders.contains( classLoader ) )
|
||||||
|
addonClassLoaders.add( classLoader );
|
||||||
|
|
||||||
packageName = packageName.replace( '.', '/' );
|
packageName = packageName.replace( '.', '/' );
|
||||||
if( classLoader == null )
|
if( classLoader == null )
|
||||||
classLoader = FlatLaf.class.getClassLoader();
|
classLoader = FlatLaf.class.getClassLoader();
|
||||||
@@ -145,14 +161,6 @@ class UIDefaultsLoader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// collect addon class loaders
|
|
||||||
List<ClassLoader> addonClassLoaders = new ArrayList<>();
|
|
||||||
for( FlatDefaultsAddon addon : addons ) {
|
|
||||||
ClassLoader addonClassLoader = addon.getClass().getClassLoader();
|
|
||||||
if( !addonClassLoaders.contains( addonClassLoader ) )
|
|
||||||
addonClassLoaders.add( addonClassLoader );
|
|
||||||
}
|
|
||||||
|
|
||||||
// add additional defaults
|
// add additional defaults
|
||||||
if( additionalDefaults != null )
|
if( additionalDefaults != null )
|
||||||
properties.putAll( additionalDefaults );
|
properties.putAll( additionalDefaults );
|
||||||
@@ -192,6 +200,32 @@ class UIDefaultsLoader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get (and remove) wildcard replacements, which override all other defaults that end with same suffix
|
||||||
|
HashMap<String, String> wildcards = new HashMap<>();
|
||||||
|
Iterator<Entry<Object, Object>> it = properties.entrySet().iterator();
|
||||||
|
while( it.hasNext() ) {
|
||||||
|
Entry<Object, Object> e = it.next();
|
||||||
|
String key = (String) e.getKey();
|
||||||
|
if( key.startsWith( WILDCARD_PREFIX ) ) {
|
||||||
|
wildcards.put( key.substring( WILDCARD_PREFIX.length() ), (String) e.getValue() );
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// override UI defaults with wildcard replacements
|
||||||
|
for( Object key : defaults.keySet() ) {
|
||||||
|
int dot;
|
||||||
|
if( !(key instanceof String) ||
|
||||||
|
properties.containsKey( key ) ||
|
||||||
|
(dot = ((String)key).lastIndexOf( '.' )) < 0 )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
String wildcardKey = ((String)key).substring( dot + 1 );
|
||||||
|
String wildcardValue = wildcards.get( wildcardKey );
|
||||||
|
if( wildcardValue != null )
|
||||||
|
properties.put( key, wildcardValue );
|
||||||
|
}
|
||||||
|
|
||||||
Function<String, String> propertiesGetter = key -> {
|
Function<String, String> propertiesGetter = key -> {
|
||||||
return properties.getProperty( key );
|
return properties.getProperty( key );
|
||||||
};
|
};
|
||||||
@@ -199,53 +233,30 @@ class UIDefaultsLoader
|
|||||||
return resolveValue( value, propertiesGetter );
|
return resolveValue( value, propertiesGetter );
|
||||||
};
|
};
|
||||||
|
|
||||||
// get globals, which override all other defaults that end with same suffix
|
// parse and add properties to UI defaults
|
||||||
HashMap<String, Object> globals = new HashMap<>();
|
|
||||||
for( Map.Entry<Object, Object> e : properties.entrySet() ) {
|
for( Map.Entry<Object, Object> e : properties.entrySet() ) {
|
||||||
String key = (String) e.getKey();
|
String key = (String) e.getKey();
|
||||||
if( !key.startsWith( GLOBAL_PREFIX ) )
|
if( key.startsWith( VARIABLE_PREFIX ) )
|
||||||
continue;
|
|
||||||
|
|
||||||
String value = resolveValue( (String) e.getValue(), propertiesGetter );
|
|
||||||
try {
|
|
||||||
globals.put( key.substring( GLOBAL_PREFIX.length() ),
|
|
||||||
parseValue( key, value, null, resolver, addonClassLoaders ) );
|
|
||||||
} catch( RuntimeException ex ) {
|
|
||||||
logParseError( Level.SEVERE, key, value, ex );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// override UI defaults with globals
|
|
||||||
for( Object key : defaults.keySet() ) {
|
|
||||||
if( key instanceof String && ((String)key).contains( "." ) ) {
|
|
||||||
String skey = (String) key;
|
|
||||||
String globalKey = skey.substring( skey.lastIndexOf( '.' ) + 1 );
|
|
||||||
Object globalValue = globals.get( globalKey );
|
|
||||||
if( globalValue != null )
|
|
||||||
defaults.put( key, globalValue );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add non-global properties to UI defaults
|
|
||||||
for( Map.Entry<Object, Object> e : properties.entrySet() ) {
|
|
||||||
String key = (String) e.getKey();
|
|
||||||
if( key.startsWith( VARIABLE_PREFIX ) || key.startsWith( GLOBAL_PREFIX ) )
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
String value = resolveValue( (String) e.getValue(), propertiesGetter );
|
String value = resolveValue( (String) e.getValue(), propertiesGetter );
|
||||||
try {
|
try {
|
||||||
defaults.put( key, parseValue( key, value, null, resolver, addonClassLoaders ) );
|
defaults.put( key, parseValue( key, value, null, resolver, addonClassLoaders ) );
|
||||||
} catch( RuntimeException ex ) {
|
} catch( RuntimeException ex ) {
|
||||||
logParseError( Level.SEVERE, key, value, ex );
|
logParseError( key, value, ex, true );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch( IOException ex ) {
|
} catch( IOException ex ) {
|
||||||
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: Failed to load properties files.", ex );
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to load properties files.", ex );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void logParseError( Level level, String key, String value, RuntimeException ex ) {
|
static void logParseError( String key, String value, RuntimeException ex, boolean severe ) {
|
||||||
FlatLaf.LOG.log( level, "FlatLaf: Failed to parse: '" + key + '=' + value + '\'', ex );
|
String message = "FlatLaf: Failed to parse: '" + key + '=' + value + '\'';
|
||||||
|
if( severe )
|
||||||
|
LoggingFacade.INSTANCE.logSevere( message, ex );
|
||||||
|
else
|
||||||
|
LoggingFacade.INSTANCE.logConfig( message, ex );
|
||||||
}
|
}
|
||||||
|
|
||||||
static String resolveValue( String value, Function<String, String> propertiesGetter ) {
|
static String resolveValue( String value, Function<String, String> propertiesGetter ) {
|
||||||
@@ -336,7 +347,12 @@ class UIDefaultsLoader
|
|||||||
|
|
||||||
// determine value type from key
|
// determine value type from key
|
||||||
if( valueType == ValueType.UNKNOWN ) {
|
if( valueType == ValueType.UNKNOWN ) {
|
||||||
if( key.endsWith( "ground" ) || key.endsWith( "Color" ) )
|
if( key.endsWith( "UI" ) )
|
||||||
|
valueType = ValueType.STRING;
|
||||||
|
else if( key.endsWith( "Color" ) ||
|
||||||
|
(key.endsWith( "ground" ) &&
|
||||||
|
(key.endsWith( ".background" ) || key.endsWith( "Background" ) ||
|
||||||
|
key.endsWith( ".foreground" ) || key.endsWith( "Foreground" ))) )
|
||||||
valueType = ValueType.COLOR;
|
valueType = ValueType.COLOR;
|
||||||
else if( key.endsWith( ".border" ) || key.endsWith( "Border" ) )
|
else if( key.endsWith( ".border" ) || key.endsWith( "Border" ) )
|
||||||
valueType = ValueType.BORDER;
|
valueType = ValueType.BORDER;
|
||||||
@@ -351,8 +367,6 @@ class UIDefaultsLoader
|
|||||||
valueType = ValueType.INTEGER;
|
valueType = ValueType.INTEGER;
|
||||||
else if( key.endsWith( "Char" ) )
|
else if( key.endsWith( "Char" ) )
|
||||||
valueType = ValueType.CHARACTER;
|
valueType = ValueType.CHARACTER;
|
||||||
else if( key.endsWith( "UI" ) )
|
|
||||||
valueType = ValueType.STRING;
|
|
||||||
else if( key.endsWith( "grayFilter" ) )
|
else if( key.endsWith( "grayFilter" ) )
|
||||||
valueType = ValueType.GRAYFILTER;
|
valueType = ValueType.GRAYFILTER;
|
||||||
}
|
}
|
||||||
@@ -428,9 +442,9 @@ class UIDefaultsLoader
|
|||||||
private static Object parseInstance( String value, List<ClassLoader> addonClassLoaders ) {
|
private static Object parseInstance( String value, List<ClassLoader> addonClassLoaders ) {
|
||||||
return (LazyValue) t -> {
|
return (LazyValue) t -> {
|
||||||
try {
|
try {
|
||||||
return findClass( value, addonClassLoaders ).newInstance();
|
return findClass( value, addonClassLoaders ).getDeclaredConstructor().newInstance();
|
||||||
} catch( InstantiationException | IllegalAccessException | ClassNotFoundException ex ) {
|
} catch( Exception ex ) {
|
||||||
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: Failed to instantiate '" + value + "'.", ex );
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to instantiate '" + value + "'.", ex );
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -441,7 +455,7 @@ class UIDefaultsLoader
|
|||||||
try {
|
try {
|
||||||
return findClass( value, addonClassLoaders );
|
return findClass( value, addonClassLoaders );
|
||||||
} catch( ClassNotFoundException ex ) {
|
} catch( ClassNotFoundException ex ) {
|
||||||
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: Failed to find class '" + value + "'.", ex );
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to find class '" + value + "'.", ex );
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -572,6 +586,11 @@ class UIDefaultsLoader
|
|||||||
if( params.isEmpty() )
|
if( params.isEmpty() )
|
||||||
throw new IllegalArgumentException( "missing parameters in function '" + value + "'" );
|
throw new IllegalArgumentException( "missing parameters in function '" + value + "'" );
|
||||||
|
|
||||||
|
if( parseColorDepth > 100 )
|
||||||
|
throw new IllegalArgumentException( "endless recursion in color function '" + value + "'" );
|
||||||
|
|
||||||
|
parseColorDepth++;
|
||||||
|
try {
|
||||||
switch( function ) {
|
switch( function ) {
|
||||||
case "rgb": return parseColorRgbOrRgba( false, params, resolver, reportError );
|
case "rgb": return parseColorRgbOrRgba( false, params, resolver, reportError );
|
||||||
case "rgba": return parseColorRgbOrRgba( true, params, resolver, reportError );
|
case "rgba": return parseColorRgbOrRgba( true, params, resolver, reportError );
|
||||||
@@ -581,13 +600,20 @@ class UIDefaultsLoader
|
|||||||
case "darken": return parseColorHSLIncreaseDecrease( 2, false, params, resolver, reportError );
|
case "darken": return parseColorHSLIncreaseDecrease( 2, false, params, resolver, reportError );
|
||||||
case "saturate": return parseColorHSLIncreaseDecrease( 1, true, params, resolver, reportError );
|
case "saturate": return parseColorHSLIncreaseDecrease( 1, true, params, resolver, reportError );
|
||||||
case "desaturate": return parseColorHSLIncreaseDecrease( 1, false, params, resolver, reportError );
|
case "desaturate": return parseColorHSLIncreaseDecrease( 1, false, params, resolver, reportError );
|
||||||
|
case "fadein": return parseColorHSLIncreaseDecrease( 3, true, params, resolver, reportError );
|
||||||
|
case "fadeout": return parseColorHSLIncreaseDecrease( 3, false, params, resolver, reportError );
|
||||||
|
case "fade": return parseColorFade( params, resolver, reportError );
|
||||||
|
case "spin": return parseColorSpin( params, resolver, reportError );
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
parseColorDepth--;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new IllegalArgumentException( "unknown color function '" + value + "'" );
|
throw new IllegalArgumentException( "unknown color function '" + value + "'" );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Syntax: rgb(red,green,blue) or rgba(red,green,blue,alpha) or rgba(color,alpha)
|
* Syntax: rgb(red,green,blue) or rgba(red,green,blue,alpha)
|
||||||
* - red: an integer 0-255 or a percentage 0-100%
|
* - red: an integer 0-255 or a percentage 0-100%
|
||||||
* - green: an integer 0-255 or a percentage 0-100%
|
* - green: an integer 0-255 or a percentage 0-100%
|
||||||
* - blue: an integer 0-255 or a percentage 0-100%
|
* - blue: an integer 0-255 or a percentage 0-100%
|
||||||
@@ -598,6 +624,8 @@ class UIDefaultsLoader
|
|||||||
{
|
{
|
||||||
if( hasAlpha && params.size() == 2 ) {
|
if( hasAlpha && params.size() == 2 ) {
|
||||||
// syntax rgba(color,alpha), which allows adding alpha to any color
|
// syntax rgba(color,alpha), which allows adding alpha to any color
|
||||||
|
// NOTE: this syntax is deprecated
|
||||||
|
// use fade(color,alpha) instead
|
||||||
String colorStr = params.get( 0 );
|
String colorStr = params.get( 0 );
|
||||||
int alpha = parseInteger( params.get( 1 ), 0, 255, true );
|
int alpha = parseInteger( params.get( 1 ), 0, 255, true );
|
||||||
|
|
||||||
@@ -634,7 +662,8 @@ class UIDefaultsLoader
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Syntax: lighten(color,amount[,options]) or darken(color,amount[,options]) or
|
* Syntax: lighten(color,amount[,options]) or darken(color,amount[,options]) or
|
||||||
* saturate(color,amount[,options]) or desaturate(color,amount[,options])
|
* saturate(color,amount[,options]) or desaturate(color,amount[,options]) or
|
||||||
|
* fadein(color,amount[,options]) or fadeout(color,amount[,options])
|
||||||
* - color: a color (e.g. #f00) or a color function
|
* - color: a color (e.g. #f00) or a color function
|
||||||
* - amount: percentage 0-100%
|
* - amount: percentage 0-100%
|
||||||
* - options: [relative] [autoInverse] [noAutoInverse] [lazy] [derived]
|
* - options: [relative] [autoInverse] [noAutoInverse] [lazy] [derived]
|
||||||
@@ -674,6 +703,59 @@ class UIDefaultsLoader
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parse base color, apply function and create derived color
|
||||||
|
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Syntax: fade(color,amount[,options])
|
||||||
|
* - color: a color (e.g. #f00) or a color function
|
||||||
|
* - amount: percentage 0-100%
|
||||||
|
* - options: [derived]
|
||||||
|
*/
|
||||||
|
private static Object parseColorFade( List<String> params, Function<String, String> resolver, boolean reportError ) {
|
||||||
|
String colorStr = params.get( 0 );
|
||||||
|
int amount = parsePercentage( params.get( 1 ) );
|
||||||
|
boolean derived = false;
|
||||||
|
|
||||||
|
if( params.size() > 2 ) {
|
||||||
|
String options = params.get( 2 );
|
||||||
|
derived = options.contains( "derived" );
|
||||||
|
}
|
||||||
|
|
||||||
|
// create function
|
||||||
|
ColorFunction function = new ColorFunctions.Fade( amount );
|
||||||
|
|
||||||
|
// parse base color, apply function and create derived color
|
||||||
|
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Syntax: spin(color,angle[,options])
|
||||||
|
* - color: a color (e.g. #f00) or a color function
|
||||||
|
* - angle: number of degrees to rotate
|
||||||
|
* - options: [derived]
|
||||||
|
*/
|
||||||
|
private static Object parseColorSpin( List<String> params, Function<String, String> resolver, boolean reportError ) {
|
||||||
|
String colorStr = params.get( 0 );
|
||||||
|
int amount = parseInteger( params.get( 1 ), true );
|
||||||
|
boolean derived = false;
|
||||||
|
|
||||||
|
if( params.size() > 2 ) {
|
||||||
|
String options = params.get( 2 );
|
||||||
|
derived = options.contains( "derived" );
|
||||||
|
}
|
||||||
|
|
||||||
|
// create function
|
||||||
|
ColorFunction function = new ColorFunctions.HSLIncreaseDecrease( 0, true, amount, false, false );
|
||||||
|
|
||||||
|
// parse base color, apply function and create derived color
|
||||||
|
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object parseFunctionBaseColor( String colorStr, ColorFunction function,
|
||||||
|
boolean derived, Function<String, String> resolver, boolean reportError )
|
||||||
|
{
|
||||||
// parse base color
|
// parse base color
|
||||||
String resolvedColorStr = resolver.apply( colorStr );
|
String resolvedColorStr = resolver.apply( colorStr );
|
||||||
ColorUIResource baseColor = (ColorUIResource) parseColorOrFunction( resolvedColorStr, resolver, reportError );
|
ColorUIResource baseColor = (ColorUIResource) parseColorOrFunction( resolvedColorStr, resolver, reportError );
|
||||||
@@ -850,7 +932,7 @@ class UIDefaultsLoader
|
|||||||
|
|
||||||
Object value = UIManager.get( uiKey );
|
Object value = UIManager.get( uiKey );
|
||||||
if( value == null && !optional )
|
if( value == null && !optional )
|
||||||
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: '" + uiKey + "' not found in UI defaults." );
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: '" + uiKey + "' not found in UI defaults.", null );
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.icons;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import com.formdev.flatlaf.util.AnimatedIcon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for animated icons that scales width and height, creates and initializes
|
||||||
|
* a scaled graphics context for icon painting.
|
||||||
|
* <p>
|
||||||
|
* Subclasses do not need to scale icon painting.
|
||||||
|
* <p>
|
||||||
|
* This class does not store any state information (needed for animation) in its instance.
|
||||||
|
* Instead a client property is set on the painted component.
|
||||||
|
* This makes it possible to use a share icon instance for multiple components.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public abstract class FlatAnimatedIcon
|
||||||
|
extends FlatAbstractIcon
|
||||||
|
implements AnimatedIcon
|
||||||
|
{
|
||||||
|
public FlatAnimatedIcon( int width, int height, Color color ) {
|
||||||
|
super( width, height, color );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paintIcon( Component c, Graphics g, int x, int y ) {
|
||||||
|
super.paintIcon( c, g, x, y );
|
||||||
|
AnimatedIcon.AnimationSupport.saveIconLocation( this, c, x, y );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
|
AnimatedIcon.AnimationSupport.paintIcon( this, c, g, 0, 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,7 +27,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
/**
|
/**
|
||||||
* "ascendingSort" icon for {@link javax.swing.table.JTableHeader}.
|
* "ascendingSort" icon for {@link javax.swing.table.JTableHeader}.
|
||||||
*
|
*
|
||||||
* @uiDefault Component.arrowType String triangle (default) or chevron
|
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||||
* @uiDefault Table.sortIconColor Color
|
* @uiDefault Table.sortIconColor Color
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
@@ -35,7 +35,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
public class FlatAscendingSortIcon
|
public class FlatAscendingSortIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
{
|
{
|
||||||
protected final boolean chevron = "chevron".equals( UIManager.getString( "Component.arrowType" ) );
|
protected final boolean chevron = FlatUIUtils.isChevron( UIManager.getString( "Component.arrowType" ) );
|
||||||
protected final Color sortIconColor = UIManager.getColor( "Table.sortIconColor" );
|
protected final Color sortIconColor = UIManager.getColor( "Table.sortIconColor" );
|
||||||
|
|
||||||
public FlatAscendingSortIcon() {
|
public FlatAscendingSortIcon() {
|
||||||
|
|||||||
@@ -49,15 +49,16 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
* @uiDefault CheckBox.icon.disabledBorderColor Color
|
* @uiDefault CheckBox.icon.disabledBorderColor Color
|
||||||
* @uiDefault CheckBox.icon.disabledBackground Color
|
* @uiDefault CheckBox.icon.disabledBackground Color
|
||||||
* @uiDefault CheckBox.icon.disabledCheckmarkColor Color
|
* @uiDefault CheckBox.icon.disabledCheckmarkColor Color
|
||||||
* @uiDefault CheckBox.icon.focusedBorderColor Color
|
* @uiDefault CheckBox.icon.focusedBorderColor Color optional
|
||||||
* @uiDefault CheckBox.icon.focusedBackground Color optional
|
* @uiDefault CheckBox.icon.focusedBackground Color optional
|
||||||
* @uiDefault CheckBox.icon.selectedFocusedBorderColor Color optional
|
* @uiDefault CheckBox.icon.selectedFocusedBorderColor Color optional; CheckBox.icon.focusedBorderColor is used if not specified
|
||||||
* @uiDefault CheckBox.icon.selectedFocusedBackground Color optional
|
* @uiDefault CheckBox.icon.selectedFocusedBackground Color optional; CheckBox.icon.focusedBackground is used if not specified
|
||||||
|
* @uiDefault CheckBox.icon.selectedFocusedCheckmarkColor Color optional; CheckBox.icon.checkmarkColor is used if not specified
|
||||||
* @uiDefault CheckBox.icon.hoverBorderColor Color optional
|
* @uiDefault CheckBox.icon.hoverBorderColor Color optional
|
||||||
* @uiDefault CheckBox.icon.hoverBackground Color optional
|
* @uiDefault CheckBox.icon.hoverBackground Color optional
|
||||||
* @uiDefault CheckBox.icon.selectedHoverBackground Color optional
|
* @uiDefault CheckBox.icon.selectedHoverBackground Color optional; CheckBox.icon.hoverBackground is used if not specified
|
||||||
* @uiDefault CheckBox.icon.pressedBackground Color optional
|
* @uiDefault CheckBox.icon.pressedBackground Color optional
|
||||||
* @uiDefault CheckBox.icon.selectedPressedBackground Color optional
|
* @uiDefault CheckBox.icon.selectedPressedBackground Color optional; CheckBox.icon.pressedBackground is used if not specified
|
||||||
* @uiDefault CheckBox.arc int
|
* @uiDefault CheckBox.arc int
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
@@ -129,78 +130,108 @@ public class FlatCheckBoxIcon
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g2 ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
boolean indeterminate = c instanceof JComponent && clientPropertyEquals( (JComponent) c, SELECTED_STATE, SELECTED_STATE_INDETERMINATE );
|
boolean indeterminate = isIndeterminate( c );
|
||||||
boolean selected = indeterminate || (c instanceof AbstractButton && ((AbstractButton)c).isSelected());
|
boolean selected = indeterminate || isSelected( c );
|
||||||
boolean isFocused = FlatUIUtils.isPermanentFocusOwner( c );
|
boolean isFocused = FlatUIUtils.isPermanentFocusOwner( c );
|
||||||
|
|
||||||
// paint focused border
|
// paint focused border
|
||||||
if( isFocused && focusWidth > 0 && FlatButtonUI.isFocusPainted( c ) ) {
|
if( isFocused && focusWidth > 0 && FlatButtonUI.isFocusPainted( c ) ) {
|
||||||
g2.setColor( focusColor );
|
g.setColor( getFocusColor( c ) );
|
||||||
paintFocusBorder( g2 );
|
paintFocusBorder( c, g );
|
||||||
}
|
}
|
||||||
|
|
||||||
// paint border
|
// paint border
|
||||||
g2.setColor( FlatButtonUI.buttonStateColor( c,
|
g.setColor( getBorderColor( c, selected ) );
|
||||||
selected ? selectedBorderColor : borderColor,
|
paintBorder( c, g );
|
||||||
disabledBorderColor,
|
|
||||||
selected && selectedFocusedBorderColor != null ? selectedFocusedBorderColor : focusedBorderColor,
|
|
||||||
hoverBorderColor,
|
|
||||||
null ) );
|
|
||||||
paintBorder( g2 );
|
|
||||||
|
|
||||||
// paint background
|
// paint background
|
||||||
g2.setColor( FlatUIUtils.deriveColor( FlatButtonUI.buttonStateColor( c,
|
Color bg = FlatUIUtils.deriveColor( getBackground( c, selected ),
|
||||||
selected ? selectedBackground : background,
|
selected ? selectedBackground : background );
|
||||||
disabledBackground,
|
if( bg.getAlpha() < 255 ) {
|
||||||
(selected && selectedFocusedBackground != null) ? selectedFocusedBackground : focusedBackground,
|
// fill background with default color before filling with non-opaque background
|
||||||
(selected && selectedHoverBackground != null) ? selectedHoverBackground : hoverBackground,
|
g.setColor( selected ? selectedBackground : background );
|
||||||
(selected && selectedPressedBackground != null) ? selectedPressedBackground : pressedBackground ),
|
paintBackground( c, g );
|
||||||
background ) );
|
}
|
||||||
paintBackground( g2 );
|
g.setColor( bg );
|
||||||
|
paintBackground( c, g );
|
||||||
|
|
||||||
// paint checkmark
|
// paint checkmark
|
||||||
if( selected || indeterminate ) {
|
if( selected || indeterminate ) {
|
||||||
g2.setColor( c.isEnabled()
|
g.setColor( getCheckmarkColor( c, selected, isFocused ) );
|
||||||
? ((selected && isFocused && selectedFocusedCheckmarkColor != null)
|
|
||||||
? selectedFocusedCheckmarkColor
|
|
||||||
: checkmarkColor)
|
|
||||||
: disabledCheckmarkColor );
|
|
||||||
if( indeterminate )
|
if( indeterminate )
|
||||||
paintIndeterminate( g2 );
|
paintIndeterminate( c, g );
|
||||||
else
|
else
|
||||||
paintCheckmark( g2 );
|
paintCheckmark( c, g );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void paintFocusBorder( Graphics2D g2 ) {
|
protected void paintFocusBorder( Component c, Graphics2D g ) {
|
||||||
// the outline focus border is painted outside of the icon
|
// the outline focus border is painted outside of the icon
|
||||||
int wh = ICON_SIZE - 1 + (focusWidth * 2);
|
int wh = ICON_SIZE - 1 + (focusWidth * 2);
|
||||||
int arcwh = arc + (focusWidth * 2);
|
int arcwh = arc + (focusWidth * 2);
|
||||||
g2.fillRoundRect( -focusWidth + 1, -focusWidth, wh, wh, arcwh, arcwh );
|
g.fillRoundRect( -focusWidth + 1, -focusWidth, wh, wh, arcwh, arcwh );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void paintBorder( Graphics2D g2 ) {
|
protected void paintBorder( Component c, Graphics2D g ) {
|
||||||
int arcwh = arc;
|
int arcwh = arc;
|
||||||
g2.fillRoundRect( 1, 0, 14, 14, arcwh, arcwh );
|
g.fillRoundRect( 1, 0, 14, 14, arcwh, arcwh );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void paintBackground( Graphics2D g2 ) {
|
protected void paintBackground( Component c, Graphics2D g ) {
|
||||||
int arcwh = arc - 1;
|
int arcwh = arc - 1;
|
||||||
g2.fillRoundRect( 2, 1, 12, 12, arcwh, arcwh );
|
g.fillRoundRect( 2, 1, 12, 12, arcwh, arcwh );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void paintCheckmark( Graphics2D g2 ) {
|
protected void paintCheckmark( Component c, Graphics2D g ) {
|
||||||
Path2D.Float path = new Path2D.Float();
|
Path2D.Float path = new Path2D.Float();
|
||||||
path.moveTo( 4.5f, 7.5f );
|
path.moveTo( 4.5f, 7.5f );
|
||||||
path.lineTo( 6.6f, 10f );
|
path.lineTo( 6.6f, 10f );
|
||||||
path.lineTo( 11.25f, 3.5f );
|
path.lineTo( 11.25f, 3.5f );
|
||||||
|
|
||||||
g2.setStroke( new BasicStroke( 1.9f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
|
g.setStroke( new BasicStroke( 1.9f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
|
||||||
g2.draw( path );
|
g.draw( path );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void paintIndeterminate( Graphics2D g2 ) {
|
protected void paintIndeterminate( Component c, Graphics2D g ) {
|
||||||
g2.fill( new RoundRectangle2D.Float( 3.75f, 5.75f, 8.5f, 2.5f, 2f, 2f ) );
|
g.fill( new RoundRectangle2D.Float( 3.75f, 5.75f, 8.5f, 2.5f, 2f, 2f ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isIndeterminate( Component c ) {
|
||||||
|
return c instanceof JComponent && clientPropertyEquals( (JComponent) c, SELECTED_STATE, SELECTED_STATE_INDETERMINATE );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isSelected( Component c ) {
|
||||||
|
return c instanceof AbstractButton && ((AbstractButton)c).isSelected();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Color getFocusColor( Component c ) {
|
||||||
|
return focusColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Color getBorderColor( Component c, boolean selected ) {
|
||||||
|
return FlatButtonUI.buttonStateColor( c,
|
||||||
|
selected ? selectedBorderColor : borderColor,
|
||||||
|
disabledBorderColor,
|
||||||
|
selected && selectedFocusedBorderColor != null ? selectedFocusedBorderColor : focusedBorderColor,
|
||||||
|
hoverBorderColor,
|
||||||
|
null );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Color getBackground( Component c, boolean selected ) {
|
||||||
|
return FlatButtonUI.buttonStateColor( c,
|
||||||
|
selected ? selectedBackground : background,
|
||||||
|
disabledBackground,
|
||||||
|
(selected && selectedFocusedBackground != null) ? selectedFocusedBackground : focusedBackground,
|
||||||
|
(selected && selectedHoverBackground != null) ? selectedHoverBackground : hoverBackground,
|
||||||
|
(selected && selectedPressedBackground != null) ? selectedPressedBackground : pressedBackground );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Color getCheckmarkColor( Component c, boolean selected, boolean isFocused ) {
|
||||||
|
return c.isEnabled()
|
||||||
|
? ((selected && isFocused && selectedFocusedCheckmarkColor != null)
|
||||||
|
? selectedFocusedCheckmarkColor
|
||||||
|
: checkmarkColor)
|
||||||
|
: disabledCheckmarkColor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
/**
|
/**
|
||||||
* "descendingSort" icon for {@link javax.swing.table.JTableHeader}.
|
* "descendingSort" icon for {@link javax.swing.table.JTableHeader}.
|
||||||
*
|
*
|
||||||
* @uiDefault Component.arrowType String triangle (default) or chevron
|
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||||
* @uiDefault Table.sortIconColor Color
|
* @uiDefault Table.sortIconColor Color
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
@@ -35,7 +35,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
public class FlatDescendingSortIcon
|
public class FlatDescendingSortIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
{
|
{
|
||||||
protected final boolean chevron = "chevron".equals( UIManager.getString( "Component.arrowType" ) );
|
protected final boolean chevron = FlatUIUtils.isChevron( UIManager.getString( "Component.arrowType" ) );
|
||||||
protected final Color sortIconColor = UIManager.getColor( "Table.sortIconColor" );
|
protected final Color sortIconColor = UIManager.getColor( "Table.sortIconColor" );
|
||||||
|
|
||||||
public FlatDescendingSortIcon() {
|
public FlatDescendingSortIcon() {
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
*
|
*
|
||||||
* @uiDefault Component.focusWidth int
|
* @uiDefault Component.focusWidth int
|
||||||
* @uiDefault Component.focusColor Color
|
* @uiDefault Component.focusColor Color
|
||||||
|
* @uiDefault HelpButton.innerFocusWidth int or float optional; defaults to Component.innerFocusWidth
|
||||||
|
* @uiDefault HelpButton.borderWidth int optional; default is 1
|
||||||
* @uiDefault HelpButton.borderColor Color
|
* @uiDefault HelpButton.borderColor Color
|
||||||
* @uiDefault HelpButton.disabledBorderColor Color
|
* @uiDefault HelpButton.disabledBorderColor Color
|
||||||
* @uiDefault HelpButton.focusedBorderColor Color
|
* @uiDefault HelpButton.focusedBorderColor Color
|
||||||
@@ -50,6 +52,8 @@ public class FlatHelpButtonIcon
|
|||||||
{
|
{
|
||||||
protected final int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
protected final int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
||||||
protected final Color focusColor = UIManager.getColor( "Component.focusColor" );
|
protected final Color focusColor = UIManager.getColor( "Component.focusColor" );
|
||||||
|
protected final float innerFocusWidth = FlatUIUtils.getUIFloat( "HelpButton.innerFocusWidth", FlatUIUtils.getUIFloat( "Component.innerFocusWidth", 0 ) );
|
||||||
|
protected final int borderWidth = FlatUIUtils.getUIInt( "HelpButton.borderWidth", 1 );
|
||||||
|
|
||||||
protected final Color borderColor = UIManager.getColor( "HelpButton.borderColor" );
|
protected final Color borderColor = UIManager.getColor( "HelpButton.borderColor" );
|
||||||
protected final Color disabledBorderColor = UIManager.getColor( "HelpButton.disabledBorderColor" );
|
protected final Color disabledBorderColor = UIManager.getColor( "HelpButton.disabledBorderColor" );
|
||||||
@@ -84,12 +88,18 @@ public class FlatHelpButtonIcon
|
|||||||
boolean enabled = c.isEnabled();
|
boolean enabled = c.isEnabled();
|
||||||
boolean focused = FlatUIUtils.isPermanentFocusOwner( c );
|
boolean focused = FlatUIUtils.isPermanentFocusOwner( c );
|
||||||
|
|
||||||
// paint focused border
|
float xy = 0.5f;
|
||||||
|
float wh = iconSize - 1;
|
||||||
|
|
||||||
|
// paint outer focus border
|
||||||
if( focused && FlatButtonUI.isFocusPainted( c ) ) {
|
if( focused && FlatButtonUI.isFocusPainted( c ) ) {
|
||||||
g2.setColor( focusColor );
|
g2.setColor( focusColor );
|
||||||
g2.fill( new Ellipse2D.Float( 0.5f, 0.5f, iconSize - 1, iconSize - 1 ) );
|
g2.fill( new Ellipse2D.Float( xy, xy, wh, wh ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xy += focusWidth;
|
||||||
|
wh -= (focusWidth * 2);
|
||||||
|
|
||||||
// paint border
|
// paint border
|
||||||
g2.setColor( FlatButtonUI.buttonStateColor( c,
|
g2.setColor( FlatButtonUI.buttonStateColor( c,
|
||||||
borderColor,
|
borderColor,
|
||||||
@@ -97,7 +107,19 @@ public class FlatHelpButtonIcon
|
|||||||
focusedBorderColor,
|
focusedBorderColor,
|
||||||
hoverBorderColor,
|
hoverBorderColor,
|
||||||
null ) );
|
null ) );
|
||||||
g2.fill( new Ellipse2D.Float( focusWidth + 0.5f, focusWidth + 0.5f, 21, 21 ) );
|
g2.fill( new Ellipse2D.Float( xy, xy, wh, wh ) );
|
||||||
|
|
||||||
|
xy += borderWidth;
|
||||||
|
wh -= (borderWidth * 2);
|
||||||
|
|
||||||
|
// paint inner focus border
|
||||||
|
if( innerFocusWidth > 0 && focused && FlatButtonUI.isFocusPainted( c ) ) {
|
||||||
|
g2.setColor( focusColor );
|
||||||
|
g2.fill( new Ellipse2D.Float( xy, xy, wh, wh ) );
|
||||||
|
|
||||||
|
xy += innerFocusWidth;
|
||||||
|
wh -= (innerFocusWidth * 2);
|
||||||
|
}
|
||||||
|
|
||||||
// paint background
|
// paint background
|
||||||
g2.setColor( FlatUIUtils.deriveColor( FlatButtonUI.buttonStateColor( c,
|
g2.setColor( FlatUIUtils.deriveColor( FlatButtonUI.buttonStateColor( c,
|
||||||
@@ -106,7 +128,7 @@ public class FlatHelpButtonIcon
|
|||||||
focusedBackground,
|
focusedBackground,
|
||||||
hoverBackground,
|
hoverBackground,
|
||||||
pressedBackground ), background ) );
|
pressedBackground ), background ) );
|
||||||
g2.fill( new Ellipse2D.Float( focusWidth + 1.5f, focusWidth + 1.5f, 19, 19 ) );
|
g2.fill( new Ellipse2D.Float( xy, xy, wh, wh ) );
|
||||||
|
|
||||||
// paint question mark
|
// paint question mark
|
||||||
Path2D q = new Path2D.Float();
|
Path2D q = new Path2D.Float();
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
/**
|
/**
|
||||||
* "arrow" icon for {@link javax.swing.JMenu}.
|
* "arrow" icon for {@link javax.swing.JMenu}.
|
||||||
*
|
*
|
||||||
* @uiDefault Component.arrowType String triangle (default) or chevron
|
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||||
* @uiDefault Menu.icon.arrowColor Color
|
* @uiDefault Menu.icon.arrowColor Color
|
||||||
* @uiDefault Menu.icon.disabledArrowColor Color
|
* @uiDefault Menu.icon.disabledArrowColor Color
|
||||||
* @uiDefault Menu.selectionForeground Color
|
* @uiDefault Menu.selectionForeground Color
|
||||||
@@ -39,7 +39,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
public class FlatMenuArrowIcon
|
public class FlatMenuArrowIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
{
|
{
|
||||||
protected final boolean chevron = "chevron".equals( UIManager.getString( "Component.arrowType" ) );
|
protected final boolean chevron = FlatUIUtils.isChevron( UIManager.getString( "Component.arrowType" ) );
|
||||||
protected final Color arrowColor = UIManager.getColor( "Menu.icon.arrowColor" );
|
protected final Color arrowColor = UIManager.getColor( "Menu.icon.arrowColor" );
|
||||||
protected final Color disabledArrowColor = UIManager.getColor( "Menu.icon.disabledArrowColor" );
|
protected final Color disabledArrowColor = UIManager.getColor( "Menu.icon.disabledArrowColor" );
|
||||||
protected final Color selectionForeground = UIManager.getColor( "Menu.selectionForeground" );
|
protected final Color selectionForeground = UIManager.getColor( "Menu.selectionForeground" );
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.icons;
|
package com.formdev.flatlaf.icons;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Ellipse2D;
|
import java.awt.geom.Ellipse2D;
|
||||||
|
|
||||||
@@ -36,25 +37,25 @@ public class FlatRadioButtonIcon
|
|||||||
protected final int centerDiameter = getUIInt( "RadioButton.icon.centerDiameter", 8, style );
|
protected final int centerDiameter = getUIInt( "RadioButton.icon.centerDiameter", 8, style );
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintFocusBorder( Graphics2D g2 ) {
|
protected void paintFocusBorder( Component c, Graphics2D g ) {
|
||||||
// the outline focus border is painted outside of the icon
|
// the outline focus border is painted outside of the icon
|
||||||
int wh = ICON_SIZE + (focusWidth * 2);
|
int wh = ICON_SIZE + (focusWidth * 2);
|
||||||
g2.fillOval( -focusWidth, -focusWidth, wh, wh );
|
g.fillOval( -focusWidth, -focusWidth, wh, wh );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintBorder( Graphics2D g2 ) {
|
protected void paintBorder( Component c, Graphics2D g ) {
|
||||||
g2.fillOval( 0, 0, 15, 15 );
|
g.fillOval( 0, 0, 15, 15 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintBackground( Graphics2D g2 ) {
|
protected void paintBackground( Component c, Graphics2D g ) {
|
||||||
g2.fillOval( 1, 1, 13, 13 );
|
g.fillOval( 1, 1, 13, 13 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintCheckmark( Graphics2D g2 ) {
|
protected void paintCheckmark( Component c, Graphics2D g ) {
|
||||||
float xy = (ICON_SIZE - centerDiameter) / 2f;
|
float xy = (ICON_SIZE - centerDiameter) / 2f;
|
||||||
g2.fill( new Ellipse2D.Float( xy, xy, centerDiameter, centerDiameter ) );
|
g.fill( new Ellipse2D.Float( xy, xy, centerDiameter, centerDiameter ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.icons;
|
||||||
|
|
||||||
|
import java.awt.BasicStroke;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.geom.Line2D;
|
||||||
|
import java.awt.geom.Path2D;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import com.formdev.flatlaf.ui.FlatButtonUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "close" icon for closable tabs in {@link javax.swing.JTabbedPane}.
|
||||||
|
*
|
||||||
|
* @uiDefault TabbedPane.closeSize Dimension
|
||||||
|
* @uiDefault TabbedPane.closeArc int
|
||||||
|
* @uiDefault TabbedPane.closeCrossPlainSize float
|
||||||
|
* @uiDefault TabbedPane.closeCrossFilledSize float
|
||||||
|
* @uiDefault TabbedPane.closeCrossLineWidth float
|
||||||
|
* @uiDefault TabbedPane.closeBackground Color
|
||||||
|
* @uiDefault TabbedPane.closeForeground Color
|
||||||
|
* @uiDefault TabbedPane.closeHoverBackground Color
|
||||||
|
* @uiDefault TabbedPane.closeHoverForeground Color
|
||||||
|
* @uiDefault TabbedPane.closePressedBackground Color
|
||||||
|
* @uiDefault TabbedPane.closePressedForeground Color
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class FlatTabbedPaneCloseIcon
|
||||||
|
extends FlatAbstractIcon
|
||||||
|
{
|
||||||
|
protected final Dimension size = UIManager.getDimension( "TabbedPane.closeSize" );
|
||||||
|
protected final int arc = UIManager.getInt( "TabbedPane.closeArc" );
|
||||||
|
protected final float crossPlainSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossPlainSize", 7.5f );
|
||||||
|
protected final float crossFilledSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossFilledSize", crossPlainSize );
|
||||||
|
protected final float closeCrossLineWidth = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossLineWidth", 1f );
|
||||||
|
protected final Color background = UIManager.getColor( "TabbedPane.closeBackground" );
|
||||||
|
protected final Color foreground = UIManager.getColor( "TabbedPane.closeForeground" );
|
||||||
|
protected final Color hoverBackground = UIManager.getColor( "TabbedPane.closeHoverBackground" );
|
||||||
|
protected final Color hoverForeground = UIManager.getColor( "TabbedPane.closeHoverForeground" );
|
||||||
|
protected final Color pressedBackground = UIManager.getColor( "TabbedPane.closePressedBackground" );
|
||||||
|
protected final Color pressedForeground = UIManager.getColor( "TabbedPane.closePressedForeground" );
|
||||||
|
|
||||||
|
public FlatTabbedPaneCloseIcon() {
|
||||||
|
super( 16, 16, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
|
// paint background
|
||||||
|
Color bg = FlatButtonUI.buttonStateColor( c, background, null, null, hoverBackground, pressedBackground );
|
||||||
|
if( bg != null ) {
|
||||||
|
g.setColor( FlatUIUtils.deriveColor( bg, c.getBackground() ) );
|
||||||
|
g.fillRoundRect( (width - size.width) / 2, (height - size.height) / 2,
|
||||||
|
size.width, size.height, arc, arc );
|
||||||
|
}
|
||||||
|
|
||||||
|
// set cross color
|
||||||
|
Color fg = FlatButtonUI.buttonStateColor( c, foreground, null, null, hoverForeground, pressedForeground );
|
||||||
|
g.setColor( FlatUIUtils.deriveColor( fg, c.getForeground() ) );
|
||||||
|
|
||||||
|
float mx = width / 2;
|
||||||
|
float my = height / 2;
|
||||||
|
float r = ((bg != null) ? crossFilledSize : crossPlainSize) / 2;
|
||||||
|
|
||||||
|
// paint cross
|
||||||
|
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||||
|
path.append( new Line2D.Float( mx - r, my - r, mx + r, my + r ), false );
|
||||||
|
path.append( new Line2D.Float( mx - r, my + r, mx + r, my - r ), false );
|
||||||
|
g.setStroke( new BasicStroke( closeCrossLineWidth ) );
|
||||||
|
g.draw( path );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,7 +25,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
/**
|
/**
|
||||||
* "collapsed" icon for {@link javax.swing.JTree}.
|
* "collapsed" icon for {@link javax.swing.JTree}.
|
||||||
*
|
*
|
||||||
* @uiDefault Component.arrowType String triangle (default) or chevron
|
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||||
* @uiDefault Tree.icon.collapsedColor Color
|
* @uiDefault Tree.icon.collapsedColor Color
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
@@ -41,7 +41,7 @@ public class FlatTreeCollapsedIcon
|
|||||||
|
|
||||||
FlatTreeCollapsedIcon( Color color ) {
|
FlatTreeCollapsedIcon( Color color ) {
|
||||||
super( 11, 11, color );
|
super( 11, 11, color );
|
||||||
chevron = "chevron".equals( UIManager.getString( "Component.arrowType" ) );
|
chevron = FlatUIUtils.isChevron( UIManager.getString( "Component.arrowType" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.resources;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The only purpose of this file is to add a .class file to this package to make it non-empty.
|
||||||
|
* Otherwise the compiler outputs a warning because this package is opend in module-info.java.
|
||||||
|
* Also when using --patch-module (e.g. from an IDE), an error would occur for empty packages.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
interface EmptyPackage
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -17,16 +17,13 @@
|
|||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
import static com.formdev.flatlaf.util.UIScale.scale;
|
||||||
import java.awt.BasicStroke;
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Shape;
|
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.awt.geom.Path2D;
|
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicArrowButton;
|
import javax.swing.plaf.basic.BasicArrowButton;
|
||||||
@@ -42,42 +39,40 @@ public class FlatArrowButton
|
|||||||
{
|
{
|
||||||
public static final int DEFAULT_ARROW_WIDTH = 8;
|
public static final int DEFAULT_ARROW_WIDTH = 8;
|
||||||
|
|
||||||
private final boolean chevron;
|
protected final boolean chevron;
|
||||||
private final Color foreground;
|
protected final Color foreground;
|
||||||
private final Color disabledForeground;
|
protected final Color disabledForeground;
|
||||||
private final Color hoverForeground;
|
protected final Color hoverForeground;
|
||||||
private final Color hoverBackground;
|
protected final Color hoverBackground;
|
||||||
private final Color pressedBackground;
|
protected final Color pressedForeground;
|
||||||
|
protected final Color pressedBackground;
|
||||||
|
|
||||||
private int arrowWidth = DEFAULT_ARROW_WIDTH;
|
private int arrowWidth = DEFAULT_ARROW_WIDTH;
|
||||||
private int xOffset = 0;
|
private float xOffset = 0;
|
||||||
private int yOffset = 0;
|
private float yOffset = 0;
|
||||||
|
|
||||||
private boolean hover;
|
private boolean hover;
|
||||||
private boolean pressed;
|
private boolean pressed;
|
||||||
|
|
||||||
public FlatArrowButton( int direction, String type, Color foreground, Color disabledForeground,
|
public FlatArrowButton( int direction, String type, Color foreground, Color disabledForeground,
|
||||||
Color hoverForeground, Color hoverBackground )
|
Color hoverForeground, Color hoverBackground, Color pressedForeground, Color pressedBackground )
|
||||||
{
|
|
||||||
this( direction, type, foreground, disabledForeground, hoverForeground, hoverBackground, null );
|
|
||||||
}
|
|
||||||
|
|
||||||
public FlatArrowButton( int direction, String type, Color foreground, Color disabledForeground,
|
|
||||||
Color hoverForeground, Color hoverBackground, Color pressedBackground )
|
|
||||||
{
|
{
|
||||||
super( direction, Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE );
|
super( direction, Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE );
|
||||||
|
|
||||||
this.chevron = "chevron".equals( type );
|
this.chevron = FlatUIUtils.isChevron( type );
|
||||||
this.foreground = foreground;
|
this.foreground = foreground;
|
||||||
this.disabledForeground = disabledForeground;
|
this.disabledForeground = disabledForeground;
|
||||||
this.hoverForeground = hoverForeground;
|
this.hoverForeground = hoverForeground;
|
||||||
this.hoverBackground = hoverBackground;
|
this.hoverBackground = hoverBackground;
|
||||||
|
this.pressedForeground = pressedForeground;
|
||||||
this.pressedBackground = pressedBackground;
|
this.pressedBackground = pressedBackground;
|
||||||
|
|
||||||
setOpaque( false );
|
setOpaque( false );
|
||||||
setBorder( null );
|
setBorder( null );
|
||||||
|
|
||||||
if( hoverForeground != null || hoverBackground != null || pressedBackground != null ) {
|
if( hoverForeground != null || hoverBackground != null ||
|
||||||
|
pressedForeground != null || pressedBackground != null )
|
||||||
|
{
|
||||||
addMouseListener( new MouseAdapter() {
|
addMouseListener( new MouseAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void mouseEntered( MouseEvent e ) {
|
public void mouseEntered( MouseEvent e ) {
|
||||||
@@ -122,19 +117,19 @@ public class FlatArrowButton
|
|||||||
return pressed;
|
return pressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getXOffset() {
|
public float getXOffset() {
|
||||||
return xOffset;
|
return xOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setXOffset( int xOffset ) {
|
public void setXOffset( float xOffset ) {
|
||||||
this.xOffset = xOffset;
|
this.xOffset = xOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getYOffset() {
|
public float getYOffset() {
|
||||||
return yOffset;
|
return yOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setYOffset( int yOffset ) {
|
public void setYOffset( float yOffset ) {
|
||||||
this.yOffset = yOffset;
|
this.yOffset = yOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,6 +137,25 @@ public class FlatArrowButton
|
|||||||
return background;
|
return background;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Color deriveForeground( Color foreground ) {
|
||||||
|
return FlatUIUtils.deriveColor( foreground, this.foreground );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the color used to paint the arrow.
|
||||||
|
*
|
||||||
|
* @since 1.2
|
||||||
|
*/
|
||||||
|
protected Color getArrowColor() {
|
||||||
|
return isEnabled()
|
||||||
|
? (pressedForeground != null && isPressed()
|
||||||
|
? pressedForeground
|
||||||
|
: (hoverForeground != null && isHover()
|
||||||
|
? hoverForeground
|
||||||
|
: foreground))
|
||||||
|
: disabledForeground;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize() {
|
public Dimension getPreferredSize() {
|
||||||
return scale( super.getPreferredSize() );
|
return scale( super.getPreferredSize() );
|
||||||
@@ -154,97 +168,42 @@ public class FlatArrowButton
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paint( Graphics g ) {
|
public void paint( Graphics g ) {
|
||||||
Graphics2D g2 = (Graphics2D)g;
|
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
||||||
FlatUIUtils.setRenderingHints( g2 );
|
|
||||||
|
|
||||||
int width = getWidth();
|
|
||||||
int height = getHeight();
|
|
||||||
boolean enabled = isEnabled();
|
|
||||||
|
|
||||||
// paint hover or pressed background
|
// paint hover or pressed background
|
||||||
if( enabled ) {
|
if( isEnabled() ) {
|
||||||
Color background = (pressedBackground != null && isPressed())
|
Color background = (pressedBackground != null && isPressed())
|
||||||
? deriveBackground( pressedBackground )
|
? pressedBackground
|
||||||
: ((hoverBackground != null && isHover())
|
: (hoverBackground != null && isHover()
|
||||||
? deriveBackground( hoverBackground )
|
? hoverBackground
|
||||||
: null);
|
: null);
|
||||||
|
|
||||||
if( background != null ) {
|
if( background != null ) {
|
||||||
g.setColor( background );
|
g.setColor( deriveBackground( background ) );
|
||||||
g.fillRect( 0, 0, width, height );
|
paintBackground( (Graphics2D) g );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int direction = getDirection();
|
// paint arrow
|
||||||
|
g.setColor( deriveForeground( getArrowColor() ) );
|
||||||
|
paintArrow( (Graphics2D) g );
|
||||||
|
|
||||||
|
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void paintBackground( Graphics2D g ) {
|
||||||
|
g.fillRect( 0, 0, getWidth(), getHeight() );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void paintArrow( Graphics2D g ) {
|
||||||
boolean vert = (direction == NORTH || direction == SOUTH);
|
boolean vert = (direction == NORTH || direction == SOUTH);
|
||||||
|
int x = 0;
|
||||||
// compute width/height
|
|
||||||
int w = scale( arrowWidth + (chevron ? 0 : 1) );
|
|
||||||
int h = scale( (arrowWidth / 2) + (chevron ? 0 : 1) );
|
|
||||||
|
|
||||||
// rotate width/height
|
|
||||||
int rw = vert ? w : h;
|
|
||||||
int rh = vert ? h : w;
|
|
||||||
|
|
||||||
// chevron lines end 1px outside of width/height
|
|
||||||
if( chevron ) {
|
|
||||||
// add 1px to width/height for position calculation only
|
|
||||||
rw++;
|
|
||||||
rh++;
|
|
||||||
}
|
|
||||||
|
|
||||||
int x = Math.round( (width - rw) / 2f + scale( (float) xOffset ) );
|
|
||||||
int y = Math.round( (height - rh) / 2f + scale( (float) yOffset ) );
|
|
||||||
|
|
||||||
// move arrow for round borders
|
// move arrow for round borders
|
||||||
Container parent = getParent();
|
Container parent = getParent();
|
||||||
if( vert && parent instanceof JComponent && FlatUIUtils.hasRoundBorder( (JComponent) parent ) )
|
if( vert && parent instanceof JComponent && FlatUIUtils.hasRoundBorder( (JComponent) parent ) )
|
||||||
x -= scale( parent.getComponentOrientation().isLeftToRight() ? 1 : -1 );
|
x -= scale( parent.getComponentOrientation().isLeftToRight() ? 1 : -1 );
|
||||||
|
|
||||||
// paint arrow
|
FlatUIUtils.paintArrow( g, x, 0, getWidth(), getHeight(), getDirection(), chevron, arrowWidth, xOffset, yOffset );
|
||||||
g.setColor( enabled
|
|
||||||
? (isHover() && hoverForeground != null ? hoverForeground : foreground)
|
|
||||||
: disabledForeground );
|
|
||||||
g.translate( x, y );
|
|
||||||
/*debug
|
|
||||||
debugPaint( g2, vert, rw, rh );
|
|
||||||
debug*/
|
|
||||||
Shape arrowShape = createArrowShape( direction, chevron, w, h );
|
|
||||||
if( chevron ) {
|
|
||||||
g2.setStroke( new BasicStroke( scale( 1f ) ) );
|
|
||||||
g2.draw( arrowShape );
|
|
||||||
} else {
|
|
||||||
// triangle
|
|
||||||
g2.fill( arrowShape );
|
|
||||||
}
|
}
|
||||||
g.translate( -x, -y );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Shape createArrowShape( int direction, boolean chevron, float w, float h ) {
|
|
||||||
switch( direction ) {
|
|
||||||
case NORTH: return FlatUIUtils.createPath( !chevron, 0,h, (w / 2f),0, w,h );
|
|
||||||
case SOUTH: return FlatUIUtils.createPath( !chevron, 0,0, (w / 2f),h, w,0 );
|
|
||||||
case WEST: return FlatUIUtils.createPath( !chevron, h,0, 0,(w / 2f), h,w );
|
|
||||||
case EAST: return FlatUIUtils.createPath( !chevron, 0,0, h,(w / 2f), 0,w );
|
|
||||||
default: return new Path2D.Float();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*debug
|
|
||||||
private void debugPaint( Graphics g, boolean vert, int w, int h ) {
|
|
||||||
Color oldColor = g.getColor();
|
|
||||||
g.setColor( Color.red );
|
|
||||||
g.drawRect( 0, 0, w - 1, h - 1 );
|
|
||||||
|
|
||||||
int xy1 = -2;
|
|
||||||
int xy2 = h + 1;
|
|
||||||
for( int i = 0; i < 20; i++ ) {
|
|
||||||
g.drawRect( vert ? 0 : xy1, vert ? xy1 : 0, 0, 0 );
|
|
||||||
g.drawRect( vert ? 0 : xy2, vert ? xy2 : 0, 0, 0 );
|
|
||||||
xy1 -= 2;
|
|
||||||
xy2 += 2;
|
|
||||||
}
|
|
||||||
g.setColor( oldColor );
|
|
||||||
}
|
|
||||||
debug*/
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,20 +22,14 @@ import java.awt.Component;
|
|||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.KeyboardFocusManager;
|
|
||||||
import java.awt.Paint;
|
import java.awt.Paint;
|
||||||
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.JTable;
|
|
||||||
import javax.swing.JTextField;
|
|
||||||
import javax.swing.JTree;
|
|
||||||
import javax.swing.JViewport;
|
import javax.swing.JViewport;
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.basic.BasicBorders;
|
import javax.swing.plaf.basic.BasicBorders;
|
||||||
import javax.swing.text.JTextComponent;
|
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.util.DerivedColor;
|
import com.formdev.flatlaf.util.DerivedColor;
|
||||||
|
|
||||||
@@ -95,13 +89,15 @@ public class FlatBorder
|
|||||||
// paint outer border
|
// paint outer border
|
||||||
if( outlineColor != null || isFocused( c ) ) {
|
if( outlineColor != null || isFocused( c ) ) {
|
||||||
float innerWidth = !isCellEditor( c ) && !(c instanceof JScrollPane)
|
float innerWidth = !isCellEditor( c ) && !(c instanceof JScrollPane)
|
||||||
? (outlineColor != null ? innerOutlineWidth : innerFocusWidth)
|
? (outlineColor != null ? innerOutlineWidth : getInnerFocusWidth( c ))
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
|
if( focusWidth > 0 || innerWidth > 0 ) {
|
||||||
g2.setColor( (outlineColor != null) ? outlineColor : getFocusColor( c ) );
|
g2.setColor( (outlineColor != null) ? outlineColor : getFocusColor( c ) );
|
||||||
FlatUIUtils.paintComponentOuterBorder( g2, x, y, width, height,
|
FlatUIUtils.paintComponentOuterBorder( g2, x, y, width, height,
|
||||||
focusWidth, borderWidth + scale( innerWidth ), arc );
|
focusWidth, borderWidth + scale( innerWidth ), arc );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// paint border
|
// paint border
|
||||||
g2.setPaint( (outlineColor != null) ? outlineColor : getBorderColor( c ) );
|
g2.setPaint( (outlineColor != null) ? outlineColor : getBorderColor( c ) );
|
||||||
@@ -159,41 +155,17 @@ public class FlatBorder
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.isEnabled() && (!(c instanceof JTextComponent) || ((JTextComponent)c).isEditable());
|
return c.isEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isFocused( Component c ) {
|
protected boolean isFocused( Component c ) {
|
||||||
if( c instanceof JScrollPane ) {
|
if( c instanceof JScrollPane )
|
||||||
JViewport viewport = ((JScrollPane)c).getViewport();
|
return FlatScrollPaneUI.isPermanentFocusOwner( (JScrollPane) c );
|
||||||
Component view = (viewport != null) ? viewport.getView() : null;
|
else if( c instanceof JComboBox )
|
||||||
if( view != null ) {
|
return FlatComboBoxUI.isPermanentFocusOwner( (JComboBox<?>) c );
|
||||||
if( FlatUIUtils.isPermanentFocusOwner( view ) )
|
else if( c instanceof JSpinner )
|
||||||
return true;
|
return FlatSpinnerUI.isPermanentFocusOwner( (JSpinner) c );
|
||||||
|
else
|
||||||
if( (view instanceof JTable && ((JTable)view).isEditing()) ||
|
|
||||||
(view instanceof JTree && ((JTree)view).isEditing()) )
|
|
||||||
{
|
|
||||||
Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
|
|
||||||
if( focusOwner != null )
|
|
||||||
return SwingUtilities.isDescendingFrom( focusOwner, view );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} else if( c instanceof JComboBox && ((JComboBox<?>)c).isEditable() ) {
|
|
||||||
Component editorComponent = ((JComboBox<?>)c).getEditor().getEditorComponent();
|
|
||||||
return (editorComponent != null) ? FlatUIUtils.isPermanentFocusOwner( editorComponent ) : false;
|
|
||||||
} else if( c instanceof JSpinner ) {
|
|
||||||
if( FlatUIUtils.isPermanentFocusOwner( c ) )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
JComponent editor = ((JSpinner)c).getEditor();
|
|
||||||
if( editor instanceof JSpinner.DefaultEditor ) {
|
|
||||||
JTextField textField = ((JSpinner.DefaultEditor)editor).getTextField();
|
|
||||||
if( textField != null )
|
|
||||||
return FlatUIUtils.isPermanentFocusOwner( textField );
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} else
|
|
||||||
return FlatUIUtils.isPermanentFocusOwner( c );
|
return FlatUIUtils.isPermanentFocusOwner( c );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,6 +208,13 @@ public class FlatBorder
|
|||||||
return focusWidth;
|
return focusWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the (unscaled) thickness of the inner focus border.
|
||||||
|
*/
|
||||||
|
protected float getInnerFocusWidth( Component c ) {
|
||||||
|
return innerFocusWidth;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the (unscaled) line thickness used to compute the border insets.
|
* Returns the (unscaled) line thickness used to compute the border insets.
|
||||||
* This may be different to {@link #getBorderWidth}.
|
* This may be different to {@link #getBorderWidth}.
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault Button.default.focusColor Color
|
* @uiDefault Button.default.focusColor Color
|
||||||
* @uiDefault Button.borderWidth int
|
* @uiDefault Button.borderWidth int
|
||||||
* @uiDefault Button.default.borderWidth int
|
* @uiDefault Button.default.borderWidth int
|
||||||
|
* @uiDefault Button.innerFocusWidth int or float optional; defaults to Component.innerFocusWidth
|
||||||
* @uiDefault Button.toolbar.margin Insets
|
* @uiDefault Button.toolbar.margin Insets
|
||||||
* @uiDefault Button.toolbar.spacingInsets Insets
|
* @uiDefault Button.toolbar.spacingInsets Insets
|
||||||
* @uiDefault Button.arc int
|
* @uiDefault Button.arc int
|
||||||
@@ -65,6 +66,7 @@ public class FlatButtonBorder
|
|||||||
protected final Color defaultFocusColor = UIManager.getColor( "Button.default.focusColor" );
|
protected final Color defaultFocusColor = UIManager.getColor( "Button.default.focusColor" );
|
||||||
protected final int borderWidth = UIManager.getInt( "Button.borderWidth" );
|
protected final int borderWidth = UIManager.getInt( "Button.borderWidth" );
|
||||||
protected final int defaultBorderWidth = UIManager.getInt( "Button.default.borderWidth" );
|
protected final int defaultBorderWidth = UIManager.getInt( "Button.default.borderWidth" );
|
||||||
|
protected final float buttonInnerFocusWidth = FlatUIUtils.getUIFloat( "Button.innerFocusWidth", innerFocusWidth );
|
||||||
protected final Insets toolbarMargin = UIManager.getInsets( "Button.toolbar.margin" );
|
protected final Insets toolbarMargin = UIManager.getInsets( "Button.toolbar.margin" );
|
||||||
protected final Insets toolbarSpacingInsets = UIManager.getInsets( "Button.toolbar.spacingInsets" );
|
protected final Insets toolbarSpacingInsets = UIManager.getInsets( "Button.toolbar.spacingInsets" );
|
||||||
protected final int arc = UIManager.getInt( "Button.arc" );
|
protected final int arc = UIManager.getInt( "Button.arc" );
|
||||||
@@ -73,6 +75,7 @@ public class FlatButtonBorder
|
|||||||
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( FlatButtonUI.isContentAreaFilled( c ) &&
|
if( FlatButtonUI.isContentAreaFilled( c ) &&
|
||||||
!FlatButtonUI.isToolBarButton( c ) &&
|
!FlatButtonUI.isToolBarButton( c ) &&
|
||||||
|
(!FlatButtonUI.isBorderlessButton( c ) || FlatUIUtils.isPermanentFocusOwner( c )) &&
|
||||||
!FlatButtonUI.isHelpButton( c ) &&
|
!FlatButtonUI.isHelpButton( c ) &&
|
||||||
!FlatToggleButtonUI.isTabButton( c ) )
|
!FlatToggleButtonUI.isTabButton( c ) )
|
||||||
super.paintBorder( c, g, x, y, width, height );
|
super.paintBorder( c, g, x, y, width, height );
|
||||||
@@ -134,6 +137,11 @@ public class FlatButtonBorder
|
|||||||
return FlatToggleButtonUI.isTabButton( c ) ? 0 : super.getFocusWidth( c );
|
return FlatToggleButtonUI.isTabButton( c ) ? 0 : super.getFocusWidth( c );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected float getInnerFocusWidth( Component c ) {
|
||||||
|
return buttonInnerFocusWidth;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getBorderWidth( Component c ) {
|
protected int getBorderWidth( Component c ) {
|
||||||
return FlatButtonUI.isDefaultButton( c ) ? defaultBorderWidth : borderWidth;
|
return FlatButtonUI.isDefaultButton( c ) ? defaultBorderWidth : borderWidth;
|
||||||
|
|||||||
@@ -132,12 +132,8 @@ public class FlatButtonUI
|
|||||||
|
|
||||||
private boolean defaults_initialized = false;
|
private boolean defaults_initialized = false;
|
||||||
|
|
||||||
private static ComponentUI instance;
|
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
if( instance == null )
|
return FlatUIUtils.createSharedUI( FlatButtonUI.class, FlatButtonUI::new );
|
||||||
instance = new FlatButtonUI();
|
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -255,18 +251,29 @@ public class FlatButtonUI
|
|||||||
Icon icon = ((AbstractButton)c).getIcon();
|
Icon icon = ((AbstractButton)c).getIcon();
|
||||||
String text = ((AbstractButton)c).getText();
|
String text = ((AbstractButton)c).getText();
|
||||||
return (icon != null && (text == null || text.isEmpty())) ||
|
return (icon != null && (text == null || text.isEmpty())) ||
|
||||||
(icon == null && text != null && ("...".equals( text ) || text.length() == 1));
|
(icon == null && text != null &&
|
||||||
|
("...".equals( text ) ||
|
||||||
|
text.length() == 1 ||
|
||||||
|
(text.length() == 2 && Character.isSurrogatePair( text.charAt( 0 ), text.charAt( 1 ) ))));
|
||||||
}
|
}
|
||||||
|
|
||||||
// same indices as in parameters to clientPropertyChoice()
|
|
||||||
static final int TYPE_OTHER = -1;
|
static final int TYPE_OTHER = -1;
|
||||||
static final int TYPE_SQUARE = 0;
|
static final int TYPE_SQUARE = 0;
|
||||||
static final int TYPE_ROUND_RECT = 1;
|
static final int TYPE_ROUND_RECT = 1;
|
||||||
|
|
||||||
static int getButtonType( Component c ) {
|
static int getButtonType( Component c ) {
|
||||||
return (c instanceof AbstractButton)
|
if( !(c instanceof AbstractButton) )
|
||||||
? clientPropertyChoice( (AbstractButton) c, BUTTON_TYPE, BUTTON_TYPE_SQUARE, BUTTON_TYPE_ROUND_RECT )
|
return TYPE_OTHER;
|
||||||
: TYPE_OTHER;
|
|
||||||
|
Object value = ((AbstractButton)c).getClientProperty( BUTTON_TYPE );
|
||||||
|
if( !(value instanceof String) )
|
||||||
|
return TYPE_OTHER;
|
||||||
|
|
||||||
|
switch( (String) value ) {
|
||||||
|
case BUTTON_TYPE_SQUARE: return TYPE_SQUARE;
|
||||||
|
case BUTTON_TYPE_ROUND_RECT: return TYPE_ROUND_RECT;
|
||||||
|
default: return TYPE_OTHER;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isHelpButton( Component c ) {
|
static boolean isHelpButton( Component c ) {
|
||||||
@@ -278,6 +285,10 @@ public class FlatButtonUI
|
|||||||
(c instanceof AbstractButton && clientPropertyEquals( (AbstractButton) c, BUTTON_TYPE, BUTTON_TYPE_TOOLBAR_BUTTON ));
|
(c instanceof AbstractButton && clientPropertyEquals( (AbstractButton) c, BUTTON_TYPE, BUTTON_TYPE_TOOLBAR_BUTTON ));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static boolean isBorderlessButton( Component c ) {
|
||||||
|
return c instanceof AbstractButton && clientPropertyEquals( (AbstractButton) c, BUTTON_TYPE, BUTTON_TYPE_BORDERLESS );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update( Graphics g, JComponent c ) {
|
public void update( Graphics g, JComponent c ) {
|
||||||
// fill background if opaque to avoid garbage if user sets opaque to true
|
// fill background if opaque to avoid garbage if user sets opaque to true
|
||||||
@@ -325,8 +336,9 @@ public class FlatButtonUI
|
|||||||
|
|
||||||
// paint shadow
|
// paint shadow
|
||||||
Color shadowColor = def ? defaultShadowColor : this.shadowColor;
|
Color shadowColor = def ? defaultShadowColor : this.shadowColor;
|
||||||
if( !isToolBarButton && shadowColor != null && shadowWidth > 0 && focusWidth > 0 &&
|
if( shadowColor != null && shadowWidth > 0 && focusWidth > 0 && c.isEnabled() &&
|
||||||
!(isFocusPainted( c ) && FlatUIUtils.isPermanentFocusOwner( c )) && c.isEnabled() )
|
!isToolBarButton && !isBorderlessButton( c ) &&
|
||||||
|
!(isFocusPainted( c ) && FlatUIUtils.isPermanentFocusOwner( c )) )
|
||||||
{
|
{
|
||||||
g2.setColor( shadowColor );
|
g2.setColor( shadowColor );
|
||||||
g2.fill( new RoundRectangle2D.Float( focusWidth, focusWidth + UIScale.scale( (float) shadowWidth ),
|
g2.fill( new RoundRectangle2D.Float( focusWidth, focusWidth + UIScale.scale( (float) shadowWidth ),
|
||||||
@@ -381,36 +393,35 @@ public class FlatButtonUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Color getBackground( JComponent c ) {
|
protected Color getBackground( JComponent c ) {
|
||||||
|
boolean toolBarButton = isToolBarButton( c ) || isBorderlessButton( c );
|
||||||
|
|
||||||
|
// selected state
|
||||||
if( ((AbstractButton)c).isSelected() ) {
|
if( ((AbstractButton)c).isSelected() ) {
|
||||||
// in toolbar use same colors for disabled and enabled because
|
// in toolbar use same background colors for disabled and enabled because
|
||||||
// we assume that toolbar icon is shown disabled
|
// we assume that toolbar icon is shown disabled
|
||||||
boolean toolBarButton = isToolBarButton( c );
|
|
||||||
return buttonStateColor( c,
|
return buttonStateColor( c,
|
||||||
toolBarButton ? toolbarSelectedBackground : selectedBackground,
|
toolBarButton ? toolbarSelectedBackground : selectedBackground,
|
||||||
toolBarButton ? toolbarSelectedBackground : disabledSelectedBackground,
|
toolBarButton ? toolbarSelectedBackground : disabledSelectedBackground,
|
||||||
null, null,
|
null,
|
||||||
|
null,
|
||||||
toolBarButton ? toolbarPressedBackground : pressedBackground );
|
toolBarButton ? toolbarPressedBackground : pressedBackground );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !c.isEnabled() )
|
|
||||||
return disabledBackground;
|
|
||||||
|
|
||||||
// toolbar button
|
// toolbar button
|
||||||
if( isToolBarButton( c ) ) {
|
if( toolBarButton ) {
|
||||||
ButtonModel model = ((AbstractButton)c).getModel();
|
Color bg = c.getBackground();
|
||||||
if( model.isPressed() )
|
return buttonStateColor( c,
|
||||||
return toolbarPressedBackground;
|
isCustomBackground( bg ) ? bg : null,
|
||||||
if( model.isRollover() )
|
null,
|
||||||
return toolbarHoverBackground;
|
null,
|
||||||
|
toolbarHoverBackground,
|
||||||
// use background of toolbar
|
toolbarPressedBackground );
|
||||||
return c.getParent().getBackground();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean def = isDefaultButton( c );
|
boolean def = isDefaultButton( c );
|
||||||
return buttonStateColor( c,
|
return buttonStateColor( c,
|
||||||
getBackgroundBase( c, def ),
|
getBackgroundBase( c, def ),
|
||||||
null,
|
disabledBackground,
|
||||||
isCustomBackground( c.getBackground() ) ? null : (def ? defaultFocusedBackground : focusedBackground),
|
isCustomBackground( c.getBackground() ) ? null : (def ? defaultFocusedBackground : focusedBackground),
|
||||||
def ? defaultHoverBackground : hoverBackground,
|
def ? defaultHoverBackground : hoverBackground,
|
||||||
def ? defaultPressedBackground : pressedBackground );
|
def ? defaultPressedBackground : pressedBackground );
|
||||||
@@ -432,16 +443,18 @@ public class FlatButtonUI
|
|||||||
public static Color buttonStateColor( Component c, Color enabledColor, Color disabledColor,
|
public static Color buttonStateColor( Component c, Color enabledColor, Color disabledColor,
|
||||||
Color focusedColor, Color hoverColor, Color pressedColor )
|
Color focusedColor, Color hoverColor, Color pressedColor )
|
||||||
{
|
{
|
||||||
AbstractButton b = (c instanceof AbstractButton) ? (AbstractButton) c : null;
|
|
||||||
|
|
||||||
if( !c.isEnabled() )
|
if( !c.isEnabled() )
|
||||||
return disabledColor;
|
return disabledColor;
|
||||||
|
|
||||||
if( pressedColor != null && b != null && b.getModel().isPressed() )
|
if( c instanceof AbstractButton ) {
|
||||||
|
ButtonModel model = ((AbstractButton)c).getModel();
|
||||||
|
|
||||||
|
if( pressedColor != null && model.isPressed() )
|
||||||
return pressedColor;
|
return pressedColor;
|
||||||
|
|
||||||
if( hoverColor != null && b != null && b.getModel().isRollover() )
|
if( hoverColor != null && model.isRollover() )
|
||||||
return hoverColor;
|
return hoverColor;
|
||||||
|
}
|
||||||
|
|
||||||
if( focusedColor != null && isFocusPainted( c ) && FlatUIUtils.isPermanentFocusOwner( c ) )
|
if( focusedColor != null && isFocusPainted( c ) && FlatUIUtils.isPermanentFocusOwner( c ) )
|
||||||
return focusedColor;
|
return focusedColor;
|
||||||
@@ -453,7 +466,7 @@ public class FlatButtonUI
|
|||||||
if( !c.isEnabled() )
|
if( !c.isEnabled() )
|
||||||
return disabledText;
|
return disabledText;
|
||||||
|
|
||||||
if( ((AbstractButton)c).isSelected() && !isToolBarButton( c ) )
|
if( ((AbstractButton)c).isSelected() && !(isToolBarButton( c ) || isBorderlessButton( c )) )
|
||||||
return selectedForeground;
|
return selectedForeground;
|
||||||
|
|
||||||
// use component foreground if explicitly set
|
// use component foreground if explicitly set
|
||||||
|
|||||||
@@ -36,13 +36,15 @@ public class FlatCaret
|
|||||||
implements UIResource
|
implements UIResource
|
||||||
{
|
{
|
||||||
private final String selectAllOnFocusPolicy;
|
private final String selectAllOnFocusPolicy;
|
||||||
|
private final boolean selectAllOnMouseClick;
|
||||||
|
|
||||||
private boolean wasFocused;
|
private boolean wasFocused;
|
||||||
private boolean wasTemporaryLost;
|
private boolean wasTemporaryLost;
|
||||||
private boolean isMousePressed;
|
private boolean isMousePressed;
|
||||||
|
|
||||||
public FlatCaret( String selectAllOnFocusPolicy ) {
|
public FlatCaret( String selectAllOnFocusPolicy, boolean selectAllOnMouseClick ) {
|
||||||
this.selectAllOnFocusPolicy = selectAllOnFocusPolicy;
|
this.selectAllOnFocusPolicy = selectAllOnFocusPolicy;
|
||||||
|
this.selectAllOnMouseClick = selectAllOnMouseClick;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -61,7 +63,7 @@ public class FlatCaret
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void focusGained( FocusEvent e ) {
|
public void focusGained( FocusEvent e ) {
|
||||||
if( !wasTemporaryLost && !isMousePressed )
|
if( !wasTemporaryLost && (!isMousePressed || selectAllOnMouseClick) )
|
||||||
selectAllOnFocusGained();
|
selectAllOnFocusGained();
|
||||||
wasTemporaryLost = false;
|
wasTemporaryLost = false;
|
||||||
wasFocused = true;
|
wasFocused = true;
|
||||||
|
|||||||
@@ -42,12 +42,8 @@ import javax.swing.plaf.ComponentUI;
|
|||||||
public class FlatCheckBoxUI
|
public class FlatCheckBoxUI
|
||||||
extends FlatRadioButtonUI
|
extends FlatRadioButtonUI
|
||||||
{
|
{
|
||||||
private static ComponentUI instance;
|
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
if( instance == null )
|
return FlatUIUtils.createSharedUI( FlatCheckBoxUI.class, FlatCheckBoxUI::new );
|
||||||
instance = new FlatCheckBoxUI();
|
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -24,21 +24,25 @@ import java.awt.Container;
|
|||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.GraphicsConfiguration;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.LayoutManager;
|
import java.awt.LayoutManager;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.Shape;
|
import java.awt.Shape;
|
||||||
|
import java.awt.Toolkit;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.awt.event.FocusEvent;
|
import java.awt.event.FocusEvent;
|
||||||
import java.awt.event.FocusListener;
|
import java.awt.event.FocusListener;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
import java.awt.event.MouseListener;
|
import java.awt.event.MouseListener;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.beans.PropertyChangeEvent;
|
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
|
import javax.swing.CellRendererPane;
|
||||||
import javax.swing.ComboBoxEditor;
|
import javax.swing.ComboBoxEditor;
|
||||||
import javax.swing.DefaultListCellRenderer;
|
import javax.swing.DefaultListCellRenderer;
|
||||||
import javax.swing.InputMap;
|
import javax.swing.InputMap;
|
||||||
@@ -57,6 +61,7 @@ import javax.swing.UIManager;
|
|||||||
import javax.swing.border.AbstractBorder;
|
import javax.swing.border.AbstractBorder;
|
||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicComboBoxUI;
|
import javax.swing.plaf.basic.BasicComboBoxUI;
|
||||||
import javax.swing.plaf.basic.BasicComboPopup;
|
import javax.swing.plaf.basic.BasicComboPopup;
|
||||||
import javax.swing.plaf.basic.ComboPopup;
|
import javax.swing.plaf.basic.ComboPopup;
|
||||||
@@ -83,18 +88,22 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault ComboBox.editorColumns int
|
* @uiDefault ComboBox.editorColumns int
|
||||||
* @uiDefault ComboBox.maximumRowCount int
|
* @uiDefault ComboBox.maximumRowCount int
|
||||||
* @uiDefault ComboBox.buttonStyle String auto (default), button or none
|
* @uiDefault ComboBox.buttonStyle String auto (default), button or none
|
||||||
* @uiDefault Component.arrowType String triangle (default) or chevron
|
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
* @uiDefault Component.isIntelliJTheme boolean
|
||||||
* @uiDefault Component.borderColor Color
|
* @uiDefault Component.borderColor Color
|
||||||
* @uiDefault Component.disabledBorderColor Color
|
* @uiDefault Component.disabledBorderColor Color
|
||||||
* @uiDefault ComboBox.editableBackground Color optional; defaults to ComboBox.background
|
* @uiDefault ComboBox.editableBackground Color optional; defaults to ComboBox.background
|
||||||
|
* @uiDefault ComboBox.focusedBackground Color optional
|
||||||
* @uiDefault ComboBox.disabledBackground Color
|
* @uiDefault ComboBox.disabledBackground Color
|
||||||
* @uiDefault ComboBox.disabledForeground Color
|
* @uiDefault ComboBox.disabledForeground Color
|
||||||
* @uiDefault ComboBox.buttonBackground Color
|
* @uiDefault ComboBox.buttonBackground Color
|
||||||
* @uiDefault ComboBox.buttonEditableBackground Color
|
* @uiDefault ComboBox.buttonEditableBackground Color
|
||||||
|
* @uiDefault ComboBox.buttonFocusedBackground Color optional; defaults to ComboBox.focusedBackground
|
||||||
* @uiDefault ComboBox.buttonArrowColor Color
|
* @uiDefault ComboBox.buttonArrowColor Color
|
||||||
* @uiDefault ComboBox.buttonDisabledArrowColor Color
|
* @uiDefault ComboBox.buttonDisabledArrowColor Color
|
||||||
* @uiDefault ComboBox.buttonHoverArrowColor Color
|
* @uiDefault ComboBox.buttonHoverArrowColor Color
|
||||||
|
* @uiDefault ComboBox.buttonPressedArrowColor Color
|
||||||
|
* @uiDefault ComboBox.popupBackground Color optional
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
@@ -110,17 +119,23 @@ public class FlatComboBoxUI
|
|||||||
protected Color disabledBorderColor;
|
protected Color disabledBorderColor;
|
||||||
|
|
||||||
protected Color editableBackground;
|
protected Color editableBackground;
|
||||||
|
protected Color focusedBackground;
|
||||||
protected Color disabledBackground;
|
protected Color disabledBackground;
|
||||||
protected Color disabledForeground;
|
protected Color disabledForeground;
|
||||||
|
|
||||||
protected Color buttonBackground;
|
protected Color buttonBackground;
|
||||||
protected Color buttonEditableBackground;
|
protected Color buttonEditableBackground;
|
||||||
|
protected Color buttonFocusedBackground;
|
||||||
protected Color buttonArrowColor;
|
protected Color buttonArrowColor;
|
||||||
protected Color buttonDisabledArrowColor;
|
protected Color buttonDisabledArrowColor;
|
||||||
protected Color buttonHoverArrowColor;
|
protected Color buttonHoverArrowColor;
|
||||||
|
protected Color buttonPressedArrowColor;
|
||||||
|
|
||||||
|
protected Color popupBackground;
|
||||||
|
|
||||||
private MouseListener hoverListener;
|
private MouseListener hoverListener;
|
||||||
protected boolean hover;
|
protected boolean hover;
|
||||||
|
protected boolean pressed;
|
||||||
|
|
||||||
private WeakReference<Component> lastRendererComponent;
|
private WeakReference<Component> lastRendererComponent;
|
||||||
|
|
||||||
@@ -132,13 +147,36 @@ public class FlatComboBoxUI
|
|||||||
protected void installListeners() {
|
protected void installListeners() {
|
||||||
super.installListeners();
|
super.installListeners();
|
||||||
|
|
||||||
hoverListener = new FlatUIUtils.HoverListener( null, h -> {
|
hoverListener = new MouseAdapter() {
|
||||||
if( !comboBox.isEditable() ) {
|
@Override
|
||||||
hover = h;
|
public void mouseEntered( MouseEvent e ) {
|
||||||
if( arrowButton != null )
|
hover = true;
|
||||||
|
repaintArrowButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseExited( MouseEvent e ) {
|
||||||
|
hover = false;
|
||||||
|
repaintArrowButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mousePressed( MouseEvent e ) {
|
||||||
|
pressed = true;
|
||||||
|
repaintArrowButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseReleased( MouseEvent e ) {
|
||||||
|
pressed = false;
|
||||||
|
repaintArrowButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void repaintArrowButton() {
|
||||||
|
if( arrowButton != null && !comboBox.isEditable() )
|
||||||
arrowButton.repaint();
|
arrowButton.repaint();
|
||||||
}
|
}
|
||||||
} );
|
};
|
||||||
comboBox.addMouseListener( hoverListener );
|
comboBox.addMouseListener( hoverListener );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,14 +203,19 @@ public class FlatComboBoxUI
|
|||||||
disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
|
disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
|
||||||
|
|
||||||
editableBackground = UIManager.getColor( "ComboBox.editableBackground" );
|
editableBackground = UIManager.getColor( "ComboBox.editableBackground" );
|
||||||
|
focusedBackground = UIManager.getColor( "ComboBox.focusedBackground" );
|
||||||
disabledBackground = UIManager.getColor( "ComboBox.disabledBackground" );
|
disabledBackground = UIManager.getColor( "ComboBox.disabledBackground" );
|
||||||
disabledForeground = UIManager.getColor( "ComboBox.disabledForeground" );
|
disabledForeground = UIManager.getColor( "ComboBox.disabledForeground" );
|
||||||
|
|
||||||
buttonBackground = UIManager.getColor( "ComboBox.buttonBackground" );
|
buttonBackground = UIManager.getColor( "ComboBox.buttonBackground" );
|
||||||
|
buttonFocusedBackground = UIManager.getColor( "ComboBox.buttonFocusedBackground" );
|
||||||
buttonEditableBackground = UIManager.getColor( "ComboBox.buttonEditableBackground" );
|
buttonEditableBackground = UIManager.getColor( "ComboBox.buttonEditableBackground" );
|
||||||
buttonArrowColor = UIManager.getColor( "ComboBox.buttonArrowColor" );
|
buttonArrowColor = UIManager.getColor( "ComboBox.buttonArrowColor" );
|
||||||
buttonDisabledArrowColor = UIManager.getColor( "ComboBox.buttonDisabledArrowColor" );
|
buttonDisabledArrowColor = UIManager.getColor( "ComboBox.buttonDisabledArrowColor" );
|
||||||
buttonHoverArrowColor = UIManager.getColor( "ComboBox.buttonHoverArrowColor" );
|
buttonHoverArrowColor = UIManager.getColor( "ComboBox.buttonHoverArrowColor" );
|
||||||
|
buttonPressedArrowColor = UIManager.getColor( "ComboBox.buttonPressedArrowColor" );
|
||||||
|
|
||||||
|
popupBackground = UIManager.getColor( "ComboBox.popupBackground" );
|
||||||
|
|
||||||
// set maximumRowCount
|
// set maximumRowCount
|
||||||
int maximumRowCount = UIManager.getInt( "ComboBox.maximumRowCount" );
|
int maximumRowCount = UIManager.getInt( "ComboBox.maximumRowCount" );
|
||||||
@@ -193,14 +236,19 @@ public class FlatComboBoxUI
|
|||||||
disabledBorderColor = null;
|
disabledBorderColor = null;
|
||||||
|
|
||||||
editableBackground = null;
|
editableBackground = null;
|
||||||
|
focusedBackground = null;
|
||||||
disabledBackground = null;
|
disabledBackground = null;
|
||||||
disabledForeground = null;
|
disabledForeground = null;
|
||||||
|
|
||||||
buttonBackground = null;
|
buttonBackground = null;
|
||||||
buttonEditableBackground = null;
|
buttonEditableBackground = null;
|
||||||
|
buttonFocusedBackground = null;
|
||||||
buttonArrowColor = null;
|
buttonArrowColor = null;
|
||||||
buttonDisabledArrowColor = null;
|
buttonDisabledArrowColor = null;
|
||||||
buttonHoverArrowColor = null;
|
buttonHoverArrowColor = null;
|
||||||
|
buttonPressedArrowColor = null;
|
||||||
|
|
||||||
|
popupBackground = null;
|
||||||
|
|
||||||
MigLayoutVisualPadding.uninstall( comboBox );
|
MigLayoutVisualPadding.uninstall( comboBox );
|
||||||
}
|
}
|
||||||
@@ -212,7 +260,24 @@ public class FlatComboBoxUI
|
|||||||
public void layoutContainer( Container parent ) {
|
public void layoutContainer( Container parent ) {
|
||||||
super.layoutContainer( parent );
|
super.layoutContainer( parent );
|
||||||
|
|
||||||
if ( editor != null && padding != null ) {
|
if( arrowButton != null ) {
|
||||||
|
Insets insets = getInsets();
|
||||||
|
int buttonWidth = parent.getPreferredSize().height - insets.top - insets.bottom;
|
||||||
|
if( buttonWidth != arrowButton.getWidth() ) {
|
||||||
|
// set width of arrow button to preferred height of combobox
|
||||||
|
int xOffset = comboBox.getComponentOrientation().isLeftToRight()
|
||||||
|
? arrowButton.getWidth() - buttonWidth
|
||||||
|
: 0;
|
||||||
|
arrowButton.setBounds( arrowButton.getX() + xOffset, arrowButton.getY(),
|
||||||
|
buttonWidth, arrowButton.getHeight() );
|
||||||
|
|
||||||
|
// update editor bounds
|
||||||
|
if( editor != null )
|
||||||
|
editor.setBounds( rectangleForCurrentValue() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( editor != null && padding != null ) {
|
||||||
// fix editor bounds by subtracting padding
|
// fix editor bounds by subtracting padding
|
||||||
editor.setBounds( FlatUIUtils.subtractInsets( editor.getBounds(), padding ) );
|
editor.setBounds( FlatUIUtils.subtractInsets( editor.getBounds(), padding ) );
|
||||||
}
|
}
|
||||||
@@ -242,10 +307,9 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PropertyChangeListener createPropertyChangeListener() {
|
protected PropertyChangeListener createPropertyChangeListener() {
|
||||||
return new BasicComboBoxUI.PropertyChangeHandler() {
|
PropertyChangeListener superListener = super.createPropertyChangeListener();
|
||||||
@Override
|
return e -> {
|
||||||
public void propertyChange( PropertyChangeEvent e ) {
|
superListener.propertyChange( e );
|
||||||
super.propertyChange( e );
|
|
||||||
|
|
||||||
Object source = e.getSource();
|
Object source = e.getSource();
|
||||||
String propertyName = e.getPropertyName();
|
String propertyName = e.getPropertyName();
|
||||||
@@ -265,7 +329,6 @@ public class FlatComboBoxUI
|
|||||||
comboBox.repaint();
|
comboBox.repaint();
|
||||||
else if( FlatClientProperties.MINIMUM_WIDTH.equals( propertyName ) )
|
else if( FlatClientProperties.MINIMUM_WIDTH.equals( propertyName ) )
|
||||||
comboBox.revalidate();
|
comboBox.revalidate();
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,6 +361,10 @@ public class FlatComboBoxUI
|
|||||||
protected void configureEditor() {
|
protected void configureEditor() {
|
||||||
super.configureEditor();
|
super.configureEditor();
|
||||||
|
|
||||||
|
// remove default text field border from editor
|
||||||
|
if( editor instanceof JTextField && ((JTextField)editor).getBorder() instanceof FlatTextBorder )
|
||||||
|
((JTextField)editor).setBorder( BorderFactory.createEmptyBorder() );
|
||||||
|
|
||||||
// explicitly make non-opaque
|
// explicitly make non-opaque
|
||||||
if( editor instanceof JComponent )
|
if( editor instanceof JComponent )
|
||||||
((JComponent)editor).setOpaque( false );
|
((JComponent)editor).setOpaque( false );
|
||||||
@@ -340,13 +407,22 @@ public class FlatComboBoxUI
|
|||||||
public void update( Graphics g, JComponent c ) {
|
public void update( Graphics g, JComponent c ) {
|
||||||
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
|
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
|
||||||
float arc = FlatUIUtils.getBorderArc( c );
|
float arc = FlatUIUtils.getBorderArc( c );
|
||||||
|
boolean paintBackground = true;
|
||||||
|
|
||||||
|
// check whether used as cell renderer
|
||||||
|
boolean isCellRenderer = c.getParent() instanceof CellRendererPane;
|
||||||
|
if( isCellRenderer ) {
|
||||||
|
focusWidth = 0;
|
||||||
|
arc = 0;
|
||||||
|
paintBackground = isCellRendererBackgroundChanged();
|
||||||
|
}
|
||||||
|
|
||||||
// fill background if opaque to avoid garbage if user sets opaque to true
|
// fill background if opaque to avoid garbage if user sets opaque to true
|
||||||
if( c.isOpaque() && (focusWidth > 0 || arc > 0) )
|
if( c.isOpaque() && (focusWidth > 0 || arc > 0) )
|
||||||
FlatUIUtils.paintParentBackground( g, c );
|
FlatUIUtils.paintParentBackground( g, c );
|
||||||
|
|
||||||
Graphics2D g2 = (Graphics2D) g;
|
Graphics2D g2 = (Graphics2D) g;
|
||||||
FlatUIUtils.setRenderingHints( g2 );
|
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g2 );
|
||||||
|
|
||||||
int width = c.getWidth();
|
int width = c.getWidth();
|
||||||
int height = c.getHeight();
|
int height = c.getHeight();
|
||||||
@@ -357,12 +433,17 @@ public class FlatComboBoxUI
|
|||||||
boolean isLeftToRight = comboBox.getComponentOrientation().isLeftToRight();
|
boolean isLeftToRight = comboBox.getComponentOrientation().isLeftToRight();
|
||||||
|
|
||||||
// paint background
|
// paint background
|
||||||
|
if( paintBackground || c.isOpaque() ) {
|
||||||
g2.setColor( getBackground( enabled ) );
|
g2.setColor( getBackground( enabled ) );
|
||||||
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 ) {
|
if( enabled && !isCellRenderer ) {
|
||||||
g2.setColor( paintButton ? buttonEditableBackground : buttonBackground );
|
g2.setColor( paintButton
|
||||||
|
? buttonEditableBackground
|
||||||
|
: (buttonFocusedBackground != null || focusedBackground != null) && isPermanentFocusOwner( comboBox )
|
||||||
|
? (buttonFocusedBackground != null ? buttonFocusedBackground : focusedBackground)
|
||||||
|
: buttonBackground );
|
||||||
Shape oldClip = g2.getClip();
|
Shape oldClip = g2.getClip();
|
||||||
if( isLeftToRight )
|
if( isLeftToRight )
|
||||||
g2.clipRect( arrowX, 0, width - arrowX, height );
|
g2.clipRect( arrowX, 0, width - arrowX, height );
|
||||||
@@ -379,6 +460,10 @@ public class FlatComboBoxUI
|
|||||||
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
|
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
|
||||||
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2)) );
|
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2)) );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// avoid that the "current value" renderer is invoked with enabled antialiasing
|
||||||
|
FlatUIUtils.resetRenderingHints( g2, oldRenderingHints );
|
||||||
|
|
||||||
paint( g, c );
|
paint( g, c );
|
||||||
}
|
}
|
||||||
@@ -418,9 +503,20 @@ public class FlatComboBoxUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Color getBackground( boolean enabled ) {
|
protected Color getBackground( boolean enabled ) {
|
||||||
return enabled
|
if( enabled ) {
|
||||||
? (editableBackground != null && comboBox.isEditable() ? editableBackground : comboBox.getBackground())
|
Color background = comboBox.getBackground();
|
||||||
: (isIntelliJTheme ? FlatUIUtils.getParentBackground( comboBox ) : disabledBackground);
|
|
||||||
|
// always use explicitly set color
|
||||||
|
if( !(background instanceof UIResource) )
|
||||||
|
return background;
|
||||||
|
|
||||||
|
// focused
|
||||||
|
if( focusedBackground != null && isPermanentFocusOwner( comboBox ) )
|
||||||
|
return focusedBackground;
|
||||||
|
|
||||||
|
return (editableBackground != null && comboBox.isEditable()) ? editableBackground : background;
|
||||||
|
} else
|
||||||
|
return isIntelliJTheme ? FlatUIUtils.getParentBackground( comboBox ) : disabledBackground;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Color getForeground( boolean enabled ) {
|
protected Color getForeground( boolean enabled ) {
|
||||||
@@ -501,25 +597,61 @@ public class FlatComboBoxUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isCellRenderer() {
|
||||||
|
return comboBox.getParent() instanceof CellRendererPane;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCellRendererBackgroundChanged() {
|
||||||
|
// parent is a CellRendererPane, parentParent is e.g. a JTable
|
||||||
|
Container parentParent = comboBox.getParent().getParent();
|
||||||
|
return parentParent != null && !comboBox.getBackground().equals( parentParent.getBackground() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.3
|
||||||
|
*/
|
||||||
|
public static boolean isPermanentFocusOwner( JComboBox<?> comboBox ) {
|
||||||
|
if( comboBox.isEditable() ) {
|
||||||
|
Component editorComponent = comboBox.getEditor().getEditorComponent();
|
||||||
|
return (editorComponent != null) ? FlatUIUtils.isPermanentFocusOwner( editorComponent ) : false;
|
||||||
|
} else
|
||||||
|
return FlatUIUtils.isPermanentFocusOwner( comboBox );
|
||||||
|
}
|
||||||
|
|
||||||
//---- class FlatComboBoxButton -------------------------------------------
|
//---- class FlatComboBoxButton -------------------------------------------
|
||||||
|
|
||||||
protected class FlatComboBoxButton
|
protected class FlatComboBoxButton
|
||||||
extends FlatArrowButton
|
extends FlatArrowButton
|
||||||
{
|
{
|
||||||
protected FlatComboBoxButton() {
|
protected FlatComboBoxButton() {
|
||||||
this( SwingConstants.SOUTH, arrowType, buttonArrowColor, buttonDisabledArrowColor, buttonHoverArrowColor, null, null );
|
this( SwingConstants.SOUTH, arrowType, buttonArrowColor, buttonDisabledArrowColor,
|
||||||
|
buttonHoverArrowColor, null, buttonPressedArrowColor, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected FlatComboBoxButton( int direction, String type, Color foreground, Color disabledForeground,
|
protected FlatComboBoxButton( int direction, String type, Color foreground, Color disabledForeground,
|
||||||
Color hoverForeground, Color hoverBackground, Color pressedBackground )
|
Color hoverForeground, Color hoverBackground, Color pressedForeground, Color pressedBackground )
|
||||||
{
|
{
|
||||||
super( direction, type, foreground, disabledForeground, hoverForeground, hoverBackground, pressedBackground );
|
super( direction, type, foreground, disabledForeground,
|
||||||
|
hoverForeground, hoverBackground, pressedForeground, pressedBackground );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isHover() {
|
protected boolean isHover() {
|
||||||
return super.isHover() || (!comboBox.isEditable() ? hover : false);
|
return super.isHover() || (!comboBox.isEditable() ? hover : false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isPressed() {
|
||||||
|
return super.isPressed() || (!comboBox.isEditable() ? pressed : false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Color getArrowColor() {
|
||||||
|
if( isCellRenderer() && isCellRendererBackgroundChanged() )
|
||||||
|
return comboBox.getForeground();
|
||||||
|
|
||||||
|
return super.getArrowColor();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class FlatComboPopup -----------------------------------------------
|
//---- class FlatComboPopup -----------------------------------------------
|
||||||
@@ -563,6 +695,17 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
// make popup wider if necessary
|
// make popup wider if necessary
|
||||||
if( displayWidth > pw ) {
|
if( displayWidth > pw ) {
|
||||||
|
// limit popup width to screen width
|
||||||
|
GraphicsConfiguration gc = comboBox.getGraphicsConfiguration();
|
||||||
|
if( gc != null ) {
|
||||||
|
Rectangle screenBounds = gc.getBounds();
|
||||||
|
Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets( gc );
|
||||||
|
displayWidth = Math.min( displayWidth, screenBounds.width - screenInsets.left - screenInsets.right );
|
||||||
|
} else {
|
||||||
|
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
|
||||||
|
displayWidth = Math.min( displayWidth, screenSize.width );
|
||||||
|
}
|
||||||
|
|
||||||
int diff = displayWidth - pw;
|
int diff = displayWidth - pw;
|
||||||
pw = displayWidth;
|
pw = displayWidth;
|
||||||
|
|
||||||
@@ -587,18 +730,18 @@ public class FlatComboBoxUI
|
|||||||
super.configureList();
|
super.configureList();
|
||||||
|
|
||||||
list.setCellRenderer( new PopupListCellRenderer() );
|
list.setCellRenderer( new PopupListCellRenderer() );
|
||||||
|
if( popupBackground != null )
|
||||||
|
list.setBackground( popupBackground );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PropertyChangeListener createPropertyChangeListener() {
|
protected PropertyChangeListener createPropertyChangeListener() {
|
||||||
return new BasicComboPopup.PropertyChangeHandler() {
|
PropertyChangeListener superListener = super.createPropertyChangeListener();
|
||||||
@Override
|
return e -> {
|
||||||
public void propertyChange( PropertyChangeEvent e ) {
|
superListener.propertyChange( e );
|
||||||
super.propertyChange( e );
|
|
||||||
|
|
||||||
if( e.getPropertyName() == "renderer" )
|
if( e.getPropertyName() == "renderer" )
|
||||||
list.setCellRenderer( new PopupListCellRenderer() );
|
list.setCellRenderer( new PopupListCellRenderer() );
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,10 +16,12 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
@@ -28,11 +30,13 @@ import java.awt.Point;
|
|||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
import java.beans.PropertyVetoException;
|
import java.beans.PropertyVetoException;
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JDesktopPane;
|
||||||
import javax.swing.event.MouseInputAdapter;
|
import javax.swing.event.MouseInputAdapter;
|
||||||
import javax.swing.event.MouseInputListener;
|
import javax.swing.event.MouseInputListener;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
@@ -45,6 +49,7 @@ 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.BasicDesktopIconUI;
|
import javax.swing.plaf.basic.BasicDesktopIconUI;
|
||||||
|
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -75,11 +80,21 @@ public class FlatDesktopIconUI
|
|||||||
private JToolTip titleTip;
|
private JToolTip titleTip;
|
||||||
private ActionListener closeListener;
|
private ActionListener closeListener;
|
||||||
private MouseInputListener mouseInputListener;
|
private MouseInputListener mouseInputListener;
|
||||||
|
private PropertyChangeListener ancestorListener;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatDesktopIconUI();
|
return new FlatDesktopIconUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
// update dock icon preview if already iconified
|
||||||
|
if( c.isDisplayable() )
|
||||||
|
updateDockIconPreviewLater();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void uninstallUI( JComponent c ) {
|
public void uninstallUI( JComponent c ) {
|
||||||
super.uninstallUI( c );
|
super.uninstallUI( c );
|
||||||
@@ -136,6 +151,17 @@ public class FlatDesktopIconUI
|
|||||||
};
|
};
|
||||||
closeButton.addActionListener( closeListener );
|
closeButton.addActionListener( closeListener );
|
||||||
closeButton.addMouseListener( mouseInputListener );
|
closeButton.addMouseListener( mouseInputListener );
|
||||||
|
|
||||||
|
ancestorListener = e -> {
|
||||||
|
if( e.getNewValue() != null ) {
|
||||||
|
// update dock icon preview if desktopIcon is added to desktop (internal frame was iconified)
|
||||||
|
updateDockIconPreviewLater();
|
||||||
|
} else {
|
||||||
|
// remove preview icon to release memory
|
||||||
|
dockIcon.setIcon( null );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
desktopIcon.addPropertyChangeListener( "ancestor", ancestorListener );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -146,6 +172,9 @@ public class FlatDesktopIconUI
|
|||||||
closeButton.removeMouseListener( mouseInputListener );
|
closeButton.removeMouseListener( mouseInputListener );
|
||||||
closeListener = null;
|
closeListener = null;
|
||||||
mouseInputListener = null;
|
mouseInputListener = null;
|
||||||
|
|
||||||
|
desktopIcon.removePropertyChangeListener( "ancestor", ancestorListener );
|
||||||
|
ancestorListener = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -228,15 +257,30 @@ public class FlatDesktopIconUI
|
|||||||
return getPreferredSize( c );
|
return getPreferredSize( c );
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateDockIcon() {
|
@Override
|
||||||
|
public void update( Graphics g, JComponent c ) {
|
||||||
|
if( c.isOpaque() ) {
|
||||||
|
// fill background with color derived from desktop pane
|
||||||
|
Color background = c.getBackground();
|
||||||
|
JDesktopPane desktopPane = desktopIcon.getDesktopPane();
|
||||||
|
g.setColor( (desktopPane != null)
|
||||||
|
? FlatUIUtils.deriveColor( background, desktopPane.getBackground() )
|
||||||
|
: background );
|
||||||
|
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
|
||||||
|
}
|
||||||
|
|
||||||
|
paint( g, c );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDockIconPreviewLater() {
|
||||||
// use invoke later to make sure that components are updated when switching LaF
|
// use invoke later to make sure that components are updated when switching LaF
|
||||||
EventQueue.invokeLater( () -> {
|
EventQueue.invokeLater( () -> {
|
||||||
if( dockIcon != null )
|
if( dockIcon != null )
|
||||||
updateDockIconLater();
|
updateDockIconPreview();
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateDockIconLater() {
|
protected void updateDockIconPreview() {
|
||||||
// make sure that frame is not selected
|
// make sure that frame is not selected
|
||||||
if( frame.isSelected() ) {
|
if( frame.isSelected() ) {
|
||||||
try {
|
try {
|
||||||
@@ -246,13 +290,22 @@ public class FlatDesktopIconUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// layout internal frame title pane, which was recreated when switching Laf
|
||||||
|
// (directly invoke doLayout() because frame.validate() does not work here
|
||||||
|
// because frame is not displayable)
|
||||||
|
if( !frame.isValid() )
|
||||||
|
frame.doLayout();
|
||||||
|
for( Component c : frame.getComponents() ) {
|
||||||
|
if( !c.isValid() )
|
||||||
|
c.doLayout();
|
||||||
|
}
|
||||||
|
|
||||||
// paint internal frame to buffered image
|
// paint internal frame to buffered image
|
||||||
int frameWidth = Math.max( frame.getWidth(), 1 );
|
int frameWidth = Math.max( frame.getWidth(), 1 );
|
||||||
int frameHeight = Math.max( frame.getHeight(), 1 );
|
int frameHeight = Math.max( frame.getHeight(), 1 );
|
||||||
BufferedImage frameImage = new BufferedImage( frameWidth, frameHeight, BufferedImage.TYPE_INT_ARGB );
|
BufferedImage frameImage = new BufferedImage( frameWidth, frameHeight, BufferedImage.TYPE_INT_ARGB );
|
||||||
Graphics2D g = frameImage.createGraphics();
|
Graphics2D g = frameImage.createGraphics();
|
||||||
try {
|
try {
|
||||||
//TODO fix missing internal frame header when switching LaF
|
|
||||||
frame.paint( g );
|
frame.paint( g );
|
||||||
} finally {
|
} finally {
|
||||||
g.dispose();
|
g.dispose();
|
||||||
@@ -270,6 +323,27 @@ public class FlatDesktopIconUI
|
|||||||
|
|
||||||
// scale preview
|
// scale preview
|
||||||
Image previewImage = frameImage.getScaledInstance( previewWidth, previewHeight, Image.SCALE_SMOOTH );
|
Image previewImage = frameImage.getScaledInstance( previewWidth, previewHeight, Image.SCALE_SMOOTH );
|
||||||
|
if( MultiResolutionImageSupport.isAvailable() ) {
|
||||||
|
// On HiDPI screens, create preview images for 1x, 2x and current scale factor.
|
||||||
|
// The icon then chooses the best resolution for painting, which is usually
|
||||||
|
// the one for the current scale factor. But if changing scale factor or
|
||||||
|
// moving window to another screen with different scale factor, then another
|
||||||
|
// resolution may be used because the preview icon is not updated.
|
||||||
|
Image previewImage2x = frameImage.getScaledInstance( previewWidth * 2, previewHeight * 2, Image.SCALE_SMOOTH );
|
||||||
|
double scaleFactor = UIScale.getSystemScaleFactor( desktopIcon.getGraphicsConfiguration() );
|
||||||
|
if( scaleFactor != 1 && scaleFactor != 2 ) {
|
||||||
|
Image previewImageCurrent = frameImage.getScaledInstance(
|
||||||
|
(int) Math.round( previewWidth * scaleFactor ),
|
||||||
|
(int) Math.round( previewHeight * scaleFactor ),
|
||||||
|
Image.SCALE_SMOOTH );
|
||||||
|
|
||||||
|
// the images must be ordered by resolution
|
||||||
|
previewImage = (scaleFactor < 2)
|
||||||
|
? MultiResolutionImageSupport.create( 0, previewImage, previewImageCurrent, previewImage2x )
|
||||||
|
: MultiResolutionImageSupport.create( 0, previewImage, previewImage2x, previewImageCurrent );
|
||||||
|
} else
|
||||||
|
previewImage = MultiResolutionImageSupport.create( 0, previewImage, previewImage2x );
|
||||||
|
}
|
||||||
dockIcon.setIcon( new ImageIcon( previewImage ) );
|
dockIcon.setIcon( new ImageIcon( previewImage ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,11 +16,16 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import javax.swing.DefaultDesktopManager;
|
import java.awt.Component;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.EventQueue;
|
||||||
|
import java.awt.event.ComponentAdapter;
|
||||||
|
import java.awt.event.ComponentEvent;
|
||||||
|
import java.awt.event.ContainerEvent;
|
||||||
|
import java.awt.event.ContainerListener;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JInternalFrame;
|
import javax.swing.JInternalFrame.JDesktopIcon;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.UIResource;
|
|
||||||
import javax.swing.plaf.basic.BasicDesktopPaneUI;
|
import javax.swing.plaf.basic.BasicDesktopPaneUI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,30 +41,96 @@ import javax.swing.plaf.basic.BasicDesktopPaneUI;
|
|||||||
public class FlatDesktopPaneUI
|
public class FlatDesktopPaneUI
|
||||||
extends BasicDesktopPaneUI
|
extends BasicDesktopPaneUI
|
||||||
{
|
{
|
||||||
|
private LayoutDockListener layoutDockListener;
|
||||||
|
private boolean layoutDockPending;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatDesktopPaneUI();
|
return new FlatDesktopPaneUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDesktopManager() {
|
public void installUI( JComponent c ) {
|
||||||
desktopManager = desktop.getDesktopManager();
|
super.installUI( c );
|
||||||
if( desktopManager == null ) {
|
|
||||||
desktopManager = new FlatDesktopManager();
|
layoutDockLaterOnce();
|
||||||
desktop.setDesktopManager( desktopManager );
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installListeners() {
|
||||||
|
super.installListeners();
|
||||||
|
|
||||||
|
layoutDockListener = new LayoutDockListener();
|
||||||
|
desktop.addContainerListener( layoutDockListener );
|
||||||
|
desktop.addComponentListener( layoutDockListener );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallListeners() {
|
||||||
|
super.uninstallListeners();
|
||||||
|
|
||||||
|
desktop.removeContainerListener( layoutDockListener );
|
||||||
|
desktop.removeComponentListener( layoutDockListener );
|
||||||
|
layoutDockListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void layoutDockLaterOnce() {
|
||||||
|
if( layoutDockPending )
|
||||||
|
return;
|
||||||
|
layoutDockPending = true;
|
||||||
|
|
||||||
|
EventQueue.invokeLater( () -> {
|
||||||
|
layoutDockPending = false;
|
||||||
|
if( desktop != null )
|
||||||
|
layoutDock();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void layoutDock() {
|
||||||
|
Dimension desktopSize = desktop.getSize();
|
||||||
|
int x = 0;
|
||||||
|
int y = desktopSize.height;
|
||||||
|
int rowHeight = 0;
|
||||||
|
|
||||||
|
for( Component c : desktop.getComponents() ) {
|
||||||
|
if( !(c instanceof JDesktopIcon) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
JDesktopIcon icon = (JDesktopIcon) c;
|
||||||
|
Dimension iconSize = icon.getPreferredSize();
|
||||||
|
|
||||||
|
if( x + iconSize.width > desktopSize.width ) {
|
||||||
|
// new row
|
||||||
|
x = 0;
|
||||||
|
y -= rowHeight;
|
||||||
|
rowHeight = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
icon.setLocation( x, y - iconSize.height );
|
||||||
|
|
||||||
|
x += iconSize.width;
|
||||||
|
rowHeight = Math.max( iconSize.height, rowHeight );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class FlatDesktopManager -------------------------------------------
|
//---- class LayoutDockListener -------------------------------------------
|
||||||
|
|
||||||
private class FlatDesktopManager
|
private class LayoutDockListener
|
||||||
extends DefaultDesktopManager
|
extends ComponentAdapter
|
||||||
implements UIResource
|
implements ContainerListener
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void iconifyFrame( JInternalFrame f ) {
|
public void componentAdded( ContainerEvent e ) {
|
||||||
super.iconifyFrame( f );
|
layoutDockLaterOnce();
|
||||||
|
}
|
||||||
|
|
||||||
((FlatDesktopIconUI)f.getDesktopIcon().getUI()).updateDockIcon();
|
@Override
|
||||||
|
public void componentRemoved( ContainerEvent e ) {
|
||||||
|
layoutDockLaterOnce();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void componentResized( ComponentEvent e ) {
|
||||||
|
layoutDockLaterOnce();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,15 +17,16 @@
|
|||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
import static com.formdev.flatlaf.util.UIScale.scale;
|
||||||
|
import java.awt.Color;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.event.FocusListener;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JEditorPane;
|
import javax.swing.JEditorPane;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.UIResource;
|
|
||||||
import javax.swing.plaf.basic.BasicEditorPaneUI;
|
import javax.swing.plaf.basic.BasicEditorPaneUI;
|
||||||
import javax.swing.text.JTextComponent;
|
import javax.swing.text.JTextComponent;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
@@ -53,6 +54,7 @@ import com.formdev.flatlaf.util.HiDPIUtils;
|
|||||||
*
|
*
|
||||||
* @uiDefault Component.minimumWidth int
|
* @uiDefault Component.minimumWidth int
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
* @uiDefault Component.isIntelliJTheme boolean
|
||||||
|
* @uiDefault EditorPane.focusedBackground Color optional
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
@@ -61,8 +63,10 @@ public class FlatEditorPaneUI
|
|||||||
{
|
{
|
||||||
protected int minimumWidth;
|
protected int minimumWidth;
|
||||||
protected boolean isIntelliJTheme;
|
protected boolean isIntelliJTheme;
|
||||||
|
protected Color focusedBackground;
|
||||||
|
|
||||||
private Object oldHonorDisplayProperties;
|
private Object oldHonorDisplayProperties;
|
||||||
|
private FocusListener focusListener;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatEditorPaneUI();
|
return new FlatEditorPaneUI();
|
||||||
@@ -72,8 +76,10 @@ public class FlatEditorPaneUI
|
|||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
|
String prefix = getPropertyPrefix();
|
||||||
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
||||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
||||||
|
focusedBackground = UIManager.getColor( prefix + ".focusedBackground" );
|
||||||
|
|
||||||
// use component font and foreground for HTML text
|
// use component font and foreground for HTML text
|
||||||
oldHonorDisplayProperties = getComponent().getClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES );
|
oldHonorDisplayProperties = getComponent().getClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES );
|
||||||
@@ -84,9 +90,28 @@ public class FlatEditorPaneUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
focusedBackground = null;
|
||||||
|
|
||||||
getComponent().putClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES, oldHonorDisplayProperties );
|
getComponent().putClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES, oldHonorDisplayProperties );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installListeners() {
|
||||||
|
super.installListeners();
|
||||||
|
|
||||||
|
// necessary to update focus background
|
||||||
|
focusListener = new FlatUIUtils.RepaintFocusListener( getComponent(), c -> focusedBackground != null );
|
||||||
|
getComponent().addFocusListener( focusListener );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallListeners() {
|
||||||
|
super.uninstallListeners();
|
||||||
|
|
||||||
|
getComponent().removeFocusListener( focusListener );
|
||||||
|
focusListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void propertyChange( PropertyChangeEvent e ) {
|
protected void propertyChange( PropertyChangeEvent e ) {
|
||||||
super.propertyChange( e );
|
super.propertyChange( e );
|
||||||
@@ -128,14 +153,11 @@ public class FlatEditorPaneUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintBackground( Graphics g ) {
|
protected void paintBackground( Graphics g ) {
|
||||||
JTextComponent c = getComponent();
|
paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground );
|
||||||
|
|
||||||
// for compatibility with IntelliJ themes
|
|
||||||
if( isIntelliJTheme && (!c.isEnabled() || !c.isEditable()) && (c.getBackground() instanceof UIResource) ) {
|
|
||||||
FlatUIUtils.paintParentBackground( g, c );
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
super.paintBackground( g );
|
static void paintBackground( Graphics g, JTextComponent c, boolean isIntelliJTheme, Color focusedBackground ) {
|
||||||
|
g.setColor( FlatTextFieldUI.getBackground( c, isIntelliJTheme, focusedBackground ) );
|
||||||
|
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,4 +57,8 @@ public class FlatEmptyBorder
|
|||||||
insets.bottom = scale( bottom );
|
insets.bottom = scale( bottom );
|
||||||
return insets;
|
return insets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Insets getUnscaledBorderInsets() {
|
||||||
|
return super.getBorderInsets();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,13 +31,17 @@ import javax.swing.JComboBox;
|
|||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JFileChooser;
|
import javax.swing.JFileChooser;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JTable;
|
||||||
import javax.swing.JToggleButton;
|
import javax.swing.JToggleButton;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.filechooser.FileView;
|
import javax.swing.filechooser.FileView;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.metal.MetalFileChooserUI;
|
import javax.swing.plaf.metal.MetalFileChooserUI;
|
||||||
|
import javax.swing.table.TableCellRenderer;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.util.ScaledImageIcon;
|
import com.formdev.flatlaf.util.ScaledImageIcon;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -190,6 +194,62 @@ public class FlatFileChooserUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JPanel createDetailsView( JFileChooser fc ) {
|
||||||
|
JPanel p = super.createDetailsView( fc );
|
||||||
|
|
||||||
|
if( !SystemInfo.isWindows )
|
||||||
|
return p;
|
||||||
|
|
||||||
|
// find scroll pane
|
||||||
|
JScrollPane scrollPane = null;
|
||||||
|
for( Component c : p.getComponents() ) {
|
||||||
|
if( c instanceof JScrollPane ) {
|
||||||
|
scrollPane = (JScrollPane) c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( scrollPane == null )
|
||||||
|
return p;
|
||||||
|
|
||||||
|
// get scroll view, which should be a table
|
||||||
|
Component view = scrollPane.getViewport().getView();
|
||||||
|
if( !(view instanceof JTable) )
|
||||||
|
return p;
|
||||||
|
|
||||||
|
JTable table = (JTable) view;
|
||||||
|
|
||||||
|
// on Windows 10, the date may contain left-to-right (0x200e) and right-to-left (0x200f)
|
||||||
|
// mark characters (see https://en.wikipedia.org/wiki/Left-to-right_mark)
|
||||||
|
// when the "current user" item is selected in the "look in" combobox
|
||||||
|
// --> remove them
|
||||||
|
TableCellRenderer defaultRenderer = table.getDefaultRenderer( Object.class );
|
||||||
|
table.setDefaultRenderer( Object.class, new TableCellRenderer() {
|
||||||
|
@Override
|
||||||
|
public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected,
|
||||||
|
boolean hasFocus, int row, int column )
|
||||||
|
{
|
||||||
|
// remove left-to-right and right-to-left mark characters
|
||||||
|
if( value instanceof String && ((String)value).startsWith( "\u200e" ) ) {
|
||||||
|
String str = (String) value;
|
||||||
|
char[] buf = new char[str.length()];
|
||||||
|
int j = 0;
|
||||||
|
for( int i = 0; i < buf.length; i++ ) {
|
||||||
|
char ch = str.charAt( i );
|
||||||
|
if( ch != '\u200e' && ch != '\u200f' )
|
||||||
|
buf[j++] = ch;
|
||||||
|
}
|
||||||
|
value = new String( buf, 0, j );
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultRenderer.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column );
|
||||||
|
}
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
return UIScale.scale( super.getPreferredSize( c ) );
|
return UIScale.scale( super.getPreferredSize( c ) );
|
||||||
|
|||||||
@@ -44,7 +44,9 @@ import javax.swing.plaf.ComponentUI;
|
|||||||
* @uiDefault Component.minimumWidth int
|
* @uiDefault Component.minimumWidth int
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
* @uiDefault Component.isIntelliJTheme boolean
|
||||||
* @uiDefault FormattedTextField.placeholderForeground Color
|
* @uiDefault FormattedTextField.placeholderForeground Color
|
||||||
|
* @uiDefault FormattedTextField.focusedBackground Color optional
|
||||||
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
|
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
|
||||||
|
* @uiDefault TextComponent.selectAllOnMouseClick boolean
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ import javax.swing.plaf.basic.BasicInternalFrameUI;
|
|||||||
public class FlatInternalFrameUI
|
public class FlatInternalFrameUI
|
||||||
extends BasicInternalFrameUI
|
extends BasicInternalFrameUI
|
||||||
{
|
{
|
||||||
|
protected FlatWindowResizer windowResizer;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatInternalFrameUI( (JInternalFrame) c );
|
return new FlatInternalFrameUI( (JInternalFrame) c );
|
||||||
}
|
}
|
||||||
@@ -97,6 +99,18 @@ public class FlatInternalFrameUI
|
|||||||
super.installUI( c );
|
super.installUI( c );
|
||||||
|
|
||||||
LookAndFeel.installProperty( frame, "opaque", false );
|
LookAndFeel.installProperty( frame, "opaque", false );
|
||||||
|
|
||||||
|
windowResizer = createWindowResizer();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uninstallUI( JComponent c ) {
|
||||||
|
super.uninstallUI( c );
|
||||||
|
|
||||||
|
if( windowResizer != null ) {
|
||||||
|
windowResizer.uninstall();
|
||||||
|
windowResizer = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -104,6 +118,10 @@ public class FlatInternalFrameUI
|
|||||||
return new FlatInternalFrameTitlePane( w );
|
return new FlatInternalFrameTitlePane( w );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected FlatWindowResizer createWindowResizer() {
|
||||||
|
return new FlatWindowResizer.InternalFrameResizer( frame, this::getDesktopManager );
|
||||||
|
}
|
||||||
|
|
||||||
//---- class FlatInternalFrameBorder --------------------------------------
|
//---- class FlatInternalFrameBorder --------------------------------------
|
||||||
|
|
||||||
public static class FlatInternalFrameBorder
|
public static class FlatInternalFrameBorder
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ 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.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;
|
||||||
@@ -56,12 +59,8 @@ public class FlatLabelUI
|
|||||||
|
|
||||||
private boolean defaults_initialized = false;
|
private boolean defaults_initialized = false;
|
||||||
|
|
||||||
private static ComponentUI instance;
|
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
if( instance == null )
|
return FlatUIUtils.createSharedUI( FlatLabelUI.class, FlatLabelUI::new );
|
||||||
instance = new FlatLabelUI();
|
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -100,23 +99,37 @@ public class FlatLabelUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether text contains HTML headings and adds a special CSS rule to
|
* Checks whether text contains HTML tags that use "absolute-size" keywords
|
||||||
* re-calculate heading font sizes based on current component font size.
|
* (e.g. "x-large") for font-size in default style sheet
|
||||||
|
* (see javax/swing/text/html/default.css).
|
||||||
|
* If yes, adds a special CSS rule (BASE_SIZE) to the HTML text, which
|
||||||
|
* re-calculates font sizes based on current component font size.
|
||||||
*/
|
*/
|
||||||
static void updateHTMLRenderer( JComponent c, String text, boolean always ) {
|
static void updateHTMLRenderer( JComponent c, String text, boolean always ) {
|
||||||
if( BasicHTML.isHTMLString( text ) &&
|
if( BasicHTML.isHTMLString( text ) &&
|
||||||
c.getClientProperty( "html.disable" ) != Boolean.TRUE &&
|
c.getClientProperty( "html.disable" ) != Boolean.TRUE &&
|
||||||
text.contains( "<h" ) &&
|
needsFontBaseSize( text ) )
|
||||||
(text.contains( "<h1" ) || text.contains( "<h2" ) || text.contains( "<h3" ) ||
|
|
||||||
text.contains( "<h4" ) || text.contains( "<h5" ) || text.contains( "<h6" )) )
|
|
||||||
{
|
{
|
||||||
int headIndex = text.indexOf( "<head>" );
|
// BASE_SIZE rule is parsed in javax.swing.text.html.StyleSheet.addRule()
|
||||||
|
|
||||||
String style = "<style>BASE_SIZE " + c.getFont().getSize() + "</style>";
|
String style = "<style>BASE_SIZE " + c.getFont().getSize() + "</style>";
|
||||||
if( headIndex < 0 )
|
|
||||||
style = "<head>" + style + "</head>";
|
|
||||||
|
|
||||||
int insertIndex = headIndex >= 0 ? (headIndex + "<head>".length()) : "<html>".length();
|
String lowerText = text.toLowerCase();
|
||||||
|
int headIndex;
|
||||||
|
int styleIndex;
|
||||||
|
|
||||||
|
int insertIndex;
|
||||||
|
if( (headIndex = lowerText.indexOf( "<head>" )) >= 0 ) {
|
||||||
|
// there is a <head> tag --> insert after <head> tag
|
||||||
|
insertIndex = headIndex + "<head>".length();
|
||||||
|
} else if( (styleIndex = lowerText.indexOf( "<style>" )) >= 0 ) {
|
||||||
|
// there is a <style> tag --> insert before <style> tag
|
||||||
|
insertIndex = styleIndex;
|
||||||
|
} else {
|
||||||
|
// no <head> or <style> tag --> insert <head> tag after <html> tag
|
||||||
|
style = "<head>" + style + "</head>";
|
||||||
|
insertIndex = "<html>".length();
|
||||||
|
}
|
||||||
|
|
||||||
text = text.substring( 0, insertIndex )
|
text = text.substring( 0, insertIndex )
|
||||||
+ style
|
+ style
|
||||||
+ text.substring( insertIndex );
|
+ text.substring( insertIndex );
|
||||||
@@ -126,6 +139,44 @@ public class FlatLabelUI
|
|||||||
BasicHTML.updateRenderer( c, text );
|
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 ) {
|
||||||
return (c.getClientProperty( BasicHTML.propertyKey ) != null)
|
return (c.getClientProperty( BasicHTML.propertyKey ) != null)
|
||||||
? HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g )
|
? HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g )
|
||||||
|
|||||||
@@ -20,9 +20,7 @@ import static com.formdev.flatlaf.util.UIScale.scale;
|
|||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.geom.Rectangle2D;
|
|
||||||
import javax.swing.JMenuBar;
|
import javax.swing.JMenuBar;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
|
||||||
@@ -40,16 +38,8 @@ public class FlatMenuBarBorder
|
|||||||
|
|
||||||
@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();
|
|
||||||
try {
|
|
||||||
float lineHeight = scale( (float) 1 );
|
float lineHeight = scale( (float) 1 );
|
||||||
|
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight );
|
||||||
FlatUIUtils.setRenderingHints( g2 );
|
|
||||||
g2.setColor( borderColor );
|
|
||||||
g2.fill( new Rectangle2D.Float( x, y + height - lineHeight, width, lineHeight ) );
|
|
||||||
} finally {
|
|
||||||
g2.dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -16,17 +16,24 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Window;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.ActionMap;
|
import javax.swing.ActionMap;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JMenu;
|
import javax.swing.JMenu;
|
||||||
import javax.swing.JMenuBar;
|
import javax.swing.JMenuBar;
|
||||||
|
import javax.swing.JRootPane;
|
||||||
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.MenuElement;
|
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.plaf.ActionMapUIResource;
|
import javax.swing.plaf.ActionMapUIResource;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicMenuBarUI;
|
import javax.swing.plaf.basic.BasicMenuBarUI;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
@@ -40,6 +47,7 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
* @uiDefault MenuBar.background Color
|
* @uiDefault MenuBar.background Color
|
||||||
* @uiDefault MenuBar.foreground Color
|
* @uiDefault MenuBar.foreground Color
|
||||||
* @uiDefault MenuBar.border Border
|
* @uiDefault MenuBar.border Border
|
||||||
|
* @uiDefault TitlePane.unifiedBackground boolean
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
@@ -55,6 +63,13 @@ public class FlatMenuBarUI
|
|||||||
* Do not add any functionality here.
|
* Do not add any functionality here.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installDefaults() {
|
||||||
|
super.installDefaults();
|
||||||
|
|
||||||
|
LookAndFeel.installProperty( menuBar, "opaque", false );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installKeyboardActions() {
|
protected void installKeyboardActions() {
|
||||||
super.installKeyboardActions();
|
super.installKeyboardActions();
|
||||||
@@ -67,6 +82,44 @@ public class FlatMenuBarUI
|
|||||||
map.put( "takeFocus", new TakeFocus() );
|
map.put( "takeFocus", new TakeFocus() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update( Graphics g, JComponent c ) {
|
||||||
|
// paint background
|
||||||
|
Color background = getBackground( c );
|
||||||
|
if( background != null ) {
|
||||||
|
g.setColor( background );
|
||||||
|
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
|
||||||
|
}
|
||||||
|
|
||||||
|
paint( g, c );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Color getBackground( JComponent c ) {
|
||||||
|
Color background = c.getBackground();
|
||||||
|
|
||||||
|
// paint background if opaque or if having custom background color
|
||||||
|
if( c.isOpaque() || !(background instanceof UIResource) )
|
||||||
|
return background;
|
||||||
|
|
||||||
|
// paint background if menu bar is not the "main" menu bar
|
||||||
|
JRootPane rootPane = SwingUtilities.getRootPane( c );
|
||||||
|
if( rootPane == null || !(rootPane.getParent() instanceof Window) || rootPane.getJMenuBar() != c )
|
||||||
|
return background;
|
||||||
|
|
||||||
|
// use parent background for unified title pane
|
||||||
|
// (not storing value of "TitlePane.unifiedBackground" in class to allow changing at runtime)
|
||||||
|
if( UIManager.getBoolean( "TitlePane.unifiedBackground" ) &&
|
||||||
|
FlatNativeWindowBorder.hasCustomDecoration( (Window) rootPane.getParent() ) )
|
||||||
|
background = FlatUIUtils.getParentBackground( c );
|
||||||
|
|
||||||
|
// paint background in full screen mode
|
||||||
|
if( FlatUIUtils.isFullScreen( rootPane ) )
|
||||||
|
return background;
|
||||||
|
|
||||||
|
// do not paint background if menu bar is embedded into title pane
|
||||||
|
return FlatRootPaneUI.isMenuBarEmbedded( rootPane ) ? null : background;
|
||||||
|
}
|
||||||
|
|
||||||
//---- class TakeFocus ----------------------------------------------------
|
//---- class TakeFocus ----------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import javax.swing.UIManager;
|
|||||||
import javax.swing.plaf.basic.BasicHTML;
|
import javax.swing.plaf.basic.BasicHTML;
|
||||||
import javax.swing.text.View;
|
import javax.swing.text.View;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
|
import com.formdev.flatlaf.util.DerivedColor;
|
||||||
import com.formdev.flatlaf.util.Graphics2DProxy;
|
import com.formdev.flatlaf.util.Graphics2DProxy;
|
||||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
@@ -55,7 +56,8 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
* @uiDefault MenuItem.underlineSelectionBackground Color
|
* @uiDefault MenuItem.underlineSelectionBackground Color
|
||||||
* @uiDefault MenuItem.underlineSelectionCheckBackground Color
|
* @uiDefault MenuItem.underlineSelectionCheckBackground Color
|
||||||
* @uiDefault MenuItem.underlineSelectionColor Color
|
* @uiDefault MenuItem.underlineSelectionColor Color
|
||||||
* @uiDefault MenuItem.underlineSelectionHeight Color
|
* @uiDefault MenuItem.underlineSelectionHeight int
|
||||||
|
* @uiDefault MenuItem.selectionBackground Color
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
@@ -81,6 +83,8 @@ public class FlatMenuItemRenderer
|
|||||||
protected final Color underlineSelectionColor = UIManager.getColor( "MenuItem.underlineSelectionColor" );
|
protected final Color underlineSelectionColor = UIManager.getColor( "MenuItem.underlineSelectionColor" );
|
||||||
protected final int underlineSelectionHeight = UIManager.getInt( "MenuItem.underlineSelectionHeight" );
|
protected final int underlineSelectionHeight = UIManager.getInt( "MenuItem.underlineSelectionHeight" );
|
||||||
|
|
||||||
|
protected final Color selectionBackground = UIManager.getColor( "MenuItem.selectionBackground" );
|
||||||
|
|
||||||
protected FlatMenuItemRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
|
protected FlatMenuItemRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
|
||||||
Font acceleratorFont, String acceleratorDelimiter )
|
Font acceleratorFont, String acceleratorDelimiter )
|
||||||
{
|
{
|
||||||
@@ -246,8 +250,11 @@ public class FlatMenuItemRenderer
|
|||||||
g.setColor( Color.orange ); g.drawRect( arrowRect.x, arrowRect.y, arrowRect.width - 1, arrowRect.height - 1 );
|
g.setColor( Color.orange ); g.drawRect( arrowRect.x, arrowRect.y, arrowRect.width - 1, arrowRect.height - 1 );
|
||||||
debug*/
|
debug*/
|
||||||
|
|
||||||
paintBackground( g, selectionBackground );
|
boolean underlineSelection = isUnderlineSelection();
|
||||||
paintIcon( g, iconRect, getIconForPainting() );
|
paintBackground( g, underlineSelection ? underlineSelectionBackground : selectionBackground );
|
||||||
|
if( underlineSelection && isArmedOrSelected( menuItem ) )
|
||||||
|
paintUnderlineSelection( g, underlineSelectionColor, underlineSelectionHeight );
|
||||||
|
paintIcon( g, iconRect, getIconForPainting(), underlineSelection ? underlineSelectionCheckBackground : checkBackground );
|
||||||
paintText( g, textRect, menuItem.getText(), selectionForeground, disabledForeground );
|
paintText( g, textRect, menuItem.getText(), selectionForeground, disabledForeground );
|
||||||
paintAccelerator( g, accelRect, getAcceleratorText(), acceleratorForeground, acceleratorSelectionForeground, disabledForeground );
|
paintAccelerator( g, accelRect, getAcceleratorText(), acceleratorForeground, acceleratorSelectionForeground, disabledForeground );
|
||||||
if( !isTopLevelMenu( menuItem ) )
|
if( !isTopLevelMenu( menuItem ) )
|
||||||
@@ -257,19 +264,18 @@ debug*/
|
|||||||
protected void paintBackground( Graphics g, Color selectionBackground ) {
|
protected void paintBackground( Graphics g, Color selectionBackground ) {
|
||||||
boolean armedOrSelected = isArmedOrSelected( menuItem );
|
boolean armedOrSelected = isArmedOrSelected( menuItem );
|
||||||
if( menuItem.isOpaque() || armedOrSelected ) {
|
if( menuItem.isOpaque() || armedOrSelected ) {
|
||||||
|
// paint background
|
||||||
|
g.setColor( armedOrSelected
|
||||||
|
? deriveBackground( selectionBackground )
|
||||||
|
: menuItem.getBackground() );
|
||||||
|
g.fillRect( 0, 0, menuItem.getWidth(), menuItem.getHeight() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void paintUnderlineSelection( Graphics g, Color underlineSelectionColor, int underlineSelectionHeight ) {
|
||||||
int width = menuItem.getWidth();
|
int width = menuItem.getWidth();
|
||||||
int height = menuItem.getHeight();
|
int height = menuItem.getHeight();
|
||||||
|
|
||||||
// paint background
|
|
||||||
g.setColor( armedOrSelected
|
|
||||||
? (isUnderlineSelection()
|
|
||||||
? deriveBackground( underlineSelectionBackground )
|
|
||||||
: selectionBackground)
|
|
||||||
: menuItem.getBackground() );
|
|
||||||
g.fillRect( 0, 0, width, height );
|
|
||||||
|
|
||||||
// paint underline
|
|
||||||
if( armedOrSelected && isUnderlineSelection() ) {
|
|
||||||
int underlineHeight = scale( underlineSelectionHeight );
|
int underlineHeight = scale( underlineSelectionHeight );
|
||||||
g.setColor( underlineSelectionColor );
|
g.setColor( underlineSelectionColor );
|
||||||
if( isTopLevelMenu( menuItem ) ) {
|
if( isTopLevelMenu( menuItem ) ) {
|
||||||
@@ -283,10 +289,11 @@ debug*/
|
|||||||
g.fillRect( width - underlineHeight, 0, underlineHeight, height );
|
g.fillRect( width - underlineHeight, 0, underlineHeight, height );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Color deriveBackground( Color background ) {
|
protected Color deriveBackground( Color background ) {
|
||||||
|
if( !(background instanceof DerivedColor) )
|
||||||
|
return background;
|
||||||
|
|
||||||
Color baseColor = menuItem.isOpaque()
|
Color baseColor = menuItem.isOpaque()
|
||||||
? menuItem.getBackground()
|
? menuItem.getBackground()
|
||||||
: FlatUIUtils.getParentBackground( menuItem );
|
: FlatUIUtils.getParentBackground( menuItem );
|
||||||
@@ -294,12 +301,12 @@ debug*/
|
|||||||
return FlatUIUtils.deriveColor( background, baseColor );
|
return FlatUIUtils.deriveColor( background, baseColor );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void paintIcon( Graphics g, Rectangle iconRect, Icon icon ) {
|
protected void paintIcon( Graphics g, Rectangle iconRect, Icon icon, Color checkBackground ) {
|
||||||
// if checkbox/radiobutton menu item is selected and also has a custom icon,
|
// if checkbox/radiobutton menu item is selected and also has a custom icon,
|
||||||
// then use filled icon background to indicate selection (instead of using checkIcon)
|
// then use filled icon background to indicate selection (instead of using checkIcon)
|
||||||
if( menuItem.isSelected() && checkIcon != null && icon != checkIcon ) {
|
if( menuItem.isSelected() && checkIcon != null && icon != checkIcon ) {
|
||||||
Rectangle r = FlatUIUtils.addInsets( iconRect, scale( checkMargins ) );
|
Rectangle r = FlatUIUtils.addInsets( iconRect, scale( checkMargins ) );
|
||||||
g.setColor( deriveBackground( isUnderlineSelection() ? underlineSelectionCheckBackground : checkBackground ) );
|
g.setColor( FlatUIUtils.deriveColor( checkBackground, selectionBackground ) );
|
||||||
g.fillRect( r.x, r.y, r.width, r.height );
|
g.fillRect( r.x, r.y, r.width, r.height );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,12 @@ import javax.swing.plaf.basic.BasicMenuUI;
|
|||||||
* @uiDefault MenuItem.iconTextGap int
|
* @uiDefault MenuItem.iconTextGap int
|
||||||
* @uiDefault MenuBar.hoverBackground Color
|
* @uiDefault MenuBar.hoverBackground Color
|
||||||
*
|
*
|
||||||
|
* <!-- FlatMenuRenderer -->
|
||||||
|
*
|
||||||
|
* @uiDefault MenuBar.underlineSelectionBackground Color
|
||||||
|
* @uiDefault MenuBar.underlineSelectionColor Color
|
||||||
|
* @uiDefault MenuBar.underlineSelectionHeight int
|
||||||
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatMenuUI
|
public class FlatMenuUI
|
||||||
@@ -147,6 +153,10 @@ public class FlatMenuUI
|
|||||||
protected class FlatMenuRenderer
|
protected class FlatMenuRenderer
|
||||||
extends FlatMenuItemRenderer
|
extends FlatMenuItemRenderer
|
||||||
{
|
{
|
||||||
|
protected final Color menuBarUnderlineSelectionBackground = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionBackground", underlineSelectionBackground );
|
||||||
|
protected final Color menuBarUnderlineSelectionColor = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionColor", underlineSelectionColor );
|
||||||
|
protected final int menuBarUnderlineSelectionHeight = FlatUIUtils.getUIInt( "MenuBar.underlineSelectionHeight", underlineSelectionHeight );
|
||||||
|
|
||||||
protected FlatMenuRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
|
protected FlatMenuRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
|
||||||
Font acceleratorFont, String acceleratorDelimiter )
|
Font acceleratorFont, String acceleratorDelimiter )
|
||||||
{
|
{
|
||||||
@@ -155,6 +165,9 @@ public class FlatMenuUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintBackground( Graphics g, Color selectionBackground ) {
|
protected void paintBackground( Graphics g, Color selectionBackground ) {
|
||||||
|
if( isUnderlineSelection() && ((JMenu)menuItem).isTopLevelMenu() )
|
||||||
|
selectionBackground = menuBarUnderlineSelectionBackground;
|
||||||
|
|
||||||
ButtonModel model = menuItem.getModel();
|
ButtonModel model = menuItem.getModel();
|
||||||
if( model.isRollover() && !model.isArmed() && !model.isSelected() &&
|
if( model.isRollover() && !model.isArmed() && !model.isSelected() &&
|
||||||
model.isEnabled() && ((JMenu)menuItem).isTopLevelMenu() )
|
model.isEnabled() && ((JMenu)menuItem).isTopLevelMenu() )
|
||||||
@@ -164,5 +177,15 @@ public class FlatMenuUI
|
|||||||
} else
|
} else
|
||||||
super.paintBackground( g, selectionBackground );
|
super.paintBackground( g, selectionBackground );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintUnderlineSelection( Graphics g, Color underlineSelectionColor, int underlineSelectionHeight ) {
|
||||||
|
if( ((JMenu)menuItem).isTopLevelMenu() ) {
|
||||||
|
underlineSelectionColor = menuBarUnderlineSelectionColor;
|
||||||
|
underlineSelectionHeight = menuBarUnderlineSelectionHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.paintUnderlineSelection( g, underlineSelectionColor, underlineSelectionHeight );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,356 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.Window;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.swing.JDialog;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JRootPane;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
|
import com.formdev.flatlaf.FlatSystemProperties;
|
||||||
|
import com.formdev.flatlaf.ui.JBRCustomDecorations.JBRWindowTopBorder;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Support for custom window decorations with native window border.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public class FlatNativeWindowBorder
|
||||||
|
{
|
||||||
|
// can use window decorations if:
|
||||||
|
// - on Windows 10
|
||||||
|
// - not when running in JetBrains Projector, Webswing or WinPE
|
||||||
|
// - not disabled via system property
|
||||||
|
private static final boolean canUseWindowDecorations =
|
||||||
|
SystemInfo.isWindows_10_orLater &&
|
||||||
|
!SystemInfo.isProjector &&
|
||||||
|
!SystemInfo.isWebswing &&
|
||||||
|
!SystemInfo.isWinPE &&
|
||||||
|
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_WINDOW_DECORATIONS, true );
|
||||||
|
|
||||||
|
// check this field before using class JBRCustomDecorations to avoid unnecessary loading of that class
|
||||||
|
private static final boolean canUseJBRCustomDecorations =
|
||||||
|
canUseWindowDecorations &&
|
||||||
|
SystemInfo.isJetBrainsJVM_11_orLater &&
|
||||||
|
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, true );
|
||||||
|
|
||||||
|
private static Boolean supported;
|
||||||
|
private static Provider nativeProvider;
|
||||||
|
|
||||||
|
public static boolean isSupported() {
|
||||||
|
if( canUseJBRCustomDecorations )
|
||||||
|
return JBRCustomDecorations.isSupported();
|
||||||
|
|
||||||
|
initialize();
|
||||||
|
return supported;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object install( JRootPane rootPane ) {
|
||||||
|
if( canUseJBRCustomDecorations )
|
||||||
|
return JBRCustomDecorations.install( rootPane );
|
||||||
|
|
||||||
|
if( !isSupported() )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Check whether root pane already has a window, which is the case when
|
||||||
|
// switching from another LaF to FlatLaf.
|
||||||
|
// Also check whether the window is displayable, which is required to install
|
||||||
|
// FlatLaf native window border.
|
||||||
|
// If the window is not displayable, then it was probably closed/disposed but not yet removed
|
||||||
|
// from the list of windows that AWT maintains and returns with Window.getWindows().
|
||||||
|
// It could be also be a window that is currently hidden, but may be shown later.
|
||||||
|
Window window = SwingUtilities.windowForComponent( rootPane );
|
||||||
|
if( window != null && window.isDisplayable() )
|
||||||
|
install( window );
|
||||||
|
|
||||||
|
// Install FlatLaf native window border, which must be done late,
|
||||||
|
// when the native window is already created, because it needs access to the window.
|
||||||
|
// Uninstall FlatLaf native window border when window is disposed (or root pane removed).
|
||||||
|
// "ancestor" property change event is fired from JComponent.addNotify() and removeNotify().
|
||||||
|
PropertyChangeListener ancestorListener = e -> {
|
||||||
|
Object newValue = e.getNewValue();
|
||||||
|
if( newValue instanceof Window )
|
||||||
|
install( (Window) newValue );
|
||||||
|
else if( newValue == null && e.getOldValue() instanceof Window )
|
||||||
|
uninstall( (Window) e.getOldValue() );
|
||||||
|
};
|
||||||
|
rootPane.addPropertyChangeListener( "ancestor", ancestorListener );
|
||||||
|
return ancestorListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void install( Window window ) {
|
||||||
|
if( hasCustomDecoration( window ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// do not enable native window border if LaF provides decorations
|
||||||
|
if( UIManager.getLookAndFeel().getSupportsWindowDecorations() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( window instanceof JFrame ) {
|
||||||
|
JFrame frame = (JFrame) window;
|
||||||
|
JRootPane rootPane = frame.getRootPane();
|
||||||
|
|
||||||
|
// check whether disabled via system property, client property or UI default
|
||||||
|
if( !useWindowDecorations( rootPane ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// do not enable native window border if frame is undecorated
|
||||||
|
if( frame.isUndecorated() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// enable native window border for window
|
||||||
|
setHasCustomDecoration( frame, true );
|
||||||
|
|
||||||
|
// avoid double window title bar if enabling native window border failed
|
||||||
|
if( !hasCustomDecoration( frame ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// enable Swing window decoration
|
||||||
|
rootPane.setWindowDecorationStyle( JRootPane.FRAME );
|
||||||
|
|
||||||
|
} else if( window instanceof JDialog ) {
|
||||||
|
JDialog dialog = (JDialog) window;
|
||||||
|
JRootPane rootPane = dialog.getRootPane();
|
||||||
|
|
||||||
|
// check whether disabled via system property, client property or UI default
|
||||||
|
if( !useWindowDecorations( rootPane ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// do not enable native window border if dialog is undecorated
|
||||||
|
if( dialog.isUndecorated() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// enable native window border for window
|
||||||
|
setHasCustomDecoration( dialog, true );
|
||||||
|
|
||||||
|
// avoid double window title bar if enabling native window border failed
|
||||||
|
if( !hasCustomDecoration( dialog ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// enable Swing window decoration
|
||||||
|
rootPane.setWindowDecorationStyle( JRootPane.PLAIN_DIALOG );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uninstall( JRootPane rootPane, Object data ) {
|
||||||
|
if( canUseJBRCustomDecorations ) {
|
||||||
|
JBRCustomDecorations.uninstall( rootPane, data );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !isSupported() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// remove listener
|
||||||
|
if( data instanceof PropertyChangeListener )
|
||||||
|
rootPane.removePropertyChangeListener( "ancestor", (PropertyChangeListener) data );
|
||||||
|
|
||||||
|
// do not uninstall when switching to another FlatLaf theme and if still enabled
|
||||||
|
if( UIManager.getLookAndFeel() instanceof FlatLaf && useWindowDecorations( rootPane ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// uninstall native window border
|
||||||
|
Window window = SwingUtilities.windowForComponent( rootPane );
|
||||||
|
if( window != null )
|
||||||
|
uninstall( window );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void uninstall( Window window ) {
|
||||||
|
if( !hasCustomDecoration( window ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// disable native window border for window
|
||||||
|
setHasCustomDecoration( window, false );
|
||||||
|
|
||||||
|
if( window instanceof JFrame ) {
|
||||||
|
JFrame frame = (JFrame) window;
|
||||||
|
|
||||||
|
// disable Swing window decoration
|
||||||
|
frame.getRootPane().setWindowDecorationStyle( JRootPane.NONE );
|
||||||
|
|
||||||
|
} else if( window instanceof JDialog ) {
|
||||||
|
JDialog dialog = (JDialog) window;
|
||||||
|
|
||||||
|
// disable Swing window decoration
|
||||||
|
dialog.getRootPane().setWindowDecorationStyle( JRootPane.NONE );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean useWindowDecorations( JRootPane rootPane ) {
|
||||||
|
return FlatUIUtils.getBoolean( rootPane,
|
||||||
|
FlatSystemProperties.USE_WINDOW_DECORATIONS,
|
||||||
|
FlatClientProperties.USE_WINDOW_DECORATIONS,
|
||||||
|
"TitlePane.useWindowDecorations",
|
||||||
|
false );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasCustomDecoration( Window window ) {
|
||||||
|
if( canUseJBRCustomDecorations )
|
||||||
|
return JBRCustomDecorations.hasCustomDecoration( window );
|
||||||
|
|
||||||
|
if( !isSupported() )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return nativeProvider.hasCustomDecoration( window );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setHasCustomDecoration( Window window, boolean hasCustomDecoration ) {
|
||||||
|
if( canUseJBRCustomDecorations ) {
|
||||||
|
JBRCustomDecorations.setHasCustomDecoration( window, hasCustomDecoration );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !isSupported() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
nativeProvider.setHasCustomDecoration( window, hasCustomDecoration );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight,
|
||||||
|
List<Rectangle> hitTestSpots, Rectangle appIconBounds )
|
||||||
|
{
|
||||||
|
if( canUseJBRCustomDecorations ) {
|
||||||
|
JBRCustomDecorations.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !isSupported() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
nativeProvider.setTitleBarHeight( window, titleBarHeight );
|
||||||
|
nativeProvider.setTitleBarHitTestSpots( window, hitTestSpots );
|
||||||
|
nativeProvider.setTitleBarAppIconBounds( window, appIconBounds );
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean showWindow( Window window, int cmd ) {
|
||||||
|
if( canUseJBRCustomDecorations || !isSupported() )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return nativeProvider.showWindow( window, cmd );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void initialize() {
|
||||||
|
if( supported != null )
|
||||||
|
return;
|
||||||
|
supported = false;
|
||||||
|
|
||||||
|
if( !canUseWindowDecorations )
|
||||||
|
return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
/*
|
||||||
|
Class<?> cls = Class.forName( "com.formdev.flatlaf.natives.jna.windows.FlatWindowsNativeWindowBorder" );
|
||||||
|
Method m = cls.getMethod( "getInstance" );
|
||||||
|
setNativeProvider( (Provider) m.invoke( null ) );
|
||||||
|
*/
|
||||||
|
setNativeProvider( FlatWindowsNativeWindowBorder.getInstance() );
|
||||||
|
} catch( Exception ex ) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1.1
|
||||||
|
*/
|
||||||
|
public static void setNativeProvider( Provider provider ) {
|
||||||
|
if( nativeProvider != null )
|
||||||
|
throw new IllegalStateException();
|
||||||
|
|
||||||
|
nativeProvider = provider;
|
||||||
|
supported = (nativeProvider != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- interface Provider -------------------------------------------------
|
||||||
|
|
||||||
|
public interface Provider
|
||||||
|
{
|
||||||
|
boolean hasCustomDecoration( Window window );
|
||||||
|
void setHasCustomDecoration( Window window, boolean hasCustomDecoration );
|
||||||
|
void setTitleBarHeight( Window window, int titleBarHeight );
|
||||||
|
void setTitleBarHitTestSpots( Window window, List<Rectangle> hitTestSpots );
|
||||||
|
void setTitleBarAppIconBounds( Window window, Rectangle appIconBounds );
|
||||||
|
|
||||||
|
// commands for showWindow(); values must match Win32 API
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
|
||||||
|
int SW_MAXIMIZE = 3;
|
||||||
|
int SW_MINIMIZE = 6;
|
||||||
|
int SW_RESTORE = 9;
|
||||||
|
boolean showWindow( Window window, int cmd );
|
||||||
|
|
||||||
|
boolean isColorizationColorAffectsBorders();
|
||||||
|
Color getColorizationColor();
|
||||||
|
int getColorizationColorBalance();
|
||||||
|
|
||||||
|
void addChangeListener( ChangeListener l );
|
||||||
|
void removeChangeListener( ChangeListener l );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class WindowTopBorder -------------------------------------------
|
||||||
|
|
||||||
|
static class WindowTopBorder
|
||||||
|
extends JBRCustomDecorations.JBRWindowTopBorder
|
||||||
|
{
|
||||||
|
private static WindowTopBorder instance;
|
||||||
|
|
||||||
|
static JBRWindowTopBorder getInstance() {
|
||||||
|
if( canUseJBRCustomDecorations )
|
||||||
|
return JBRWindowTopBorder.getInstance();
|
||||||
|
|
||||||
|
if( instance == null )
|
||||||
|
instance = new WindowTopBorder();
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void installListeners() {
|
||||||
|
nativeProvider.addChangeListener( e -> {
|
||||||
|
update();
|
||||||
|
|
||||||
|
// repaint top borders of all windows
|
||||||
|
for( Window window : Window.getWindows() ) {
|
||||||
|
if( window.isDisplayable() )
|
||||||
|
window.repaint( 0, 0, window.getWidth(), 1 );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean isColorizationColorAffectsBorders() {
|
||||||
|
return nativeProvider.isColorizationColorAffectsBorders();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Color getColorizationColor() {
|
||||||
|
return nativeProvider.getColorizationColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int getColorizationColorBalance() {
|
||||||
|
return nativeProvider.getColorizationColorBalance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,11 +35,7 @@ import javax.swing.plaf.basic.BasicPanelUI;
|
|||||||
public class FlatPanelUI
|
public class FlatPanelUI
|
||||||
extends BasicPanelUI
|
extends BasicPanelUI
|
||||||
{
|
{
|
||||||
private static ComponentUI instance;
|
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
if( instance == null )
|
return FlatUIUtils.createSharedUI( FlatPanelUI.class, FlatPanelUI::new );
|
||||||
instance = new FlatPanelUI();
|
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,8 +60,11 @@ import com.formdev.flatlaf.util.HiDPIUtils;
|
|||||||
* @uiDefault Component.minimumWidth int
|
* @uiDefault Component.minimumWidth int
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
* @uiDefault Component.isIntelliJTheme boolean
|
||||||
* @uiDefault PasswordField.placeholderForeground Color
|
* @uiDefault PasswordField.placeholderForeground Color
|
||||||
|
* @uiDefault PasswordField.focusedBackground Color optional
|
||||||
|
* @uiDefault PasswordField.showCapsLock boolean
|
||||||
* @uiDefault PasswordField.capsLockIcon Icon
|
* @uiDefault PasswordField.capsLockIcon Icon
|
||||||
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
|
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
|
||||||
|
* @uiDefault TextComponent.selectAllOnMouseClick boolean
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
@@ -71,6 +74,8 @@ public class FlatPasswordFieldUI
|
|||||||
protected int minimumWidth;
|
protected int minimumWidth;
|
||||||
protected boolean isIntelliJTheme;
|
protected boolean isIntelliJTheme;
|
||||||
protected Color placeholderForeground;
|
protected Color placeholderForeground;
|
||||||
|
protected Color focusedBackground;
|
||||||
|
protected boolean showCapsLock;
|
||||||
protected Icon capsLockIcon;
|
protected Icon capsLockIcon;
|
||||||
|
|
||||||
private FocusListener focusListener;
|
private FocusListener focusListener;
|
||||||
@@ -88,6 +93,8 @@ public class FlatPasswordFieldUI
|
|||||||
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
||||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
||||||
placeholderForeground = UIManager.getColor( prefix + ".placeholderForeground" );
|
placeholderForeground = UIManager.getColor( prefix + ".placeholderForeground" );
|
||||||
|
focusedBackground = UIManager.getColor( prefix + ".focusedBackground" );
|
||||||
|
showCapsLock = UIManager.getBoolean( "PasswordField.showCapsLock" );
|
||||||
capsLockIcon = UIManager.getIcon( "PasswordField.capsLockIcon" );
|
capsLockIcon = UIManager.getIcon( "PasswordField.capsLockIcon" );
|
||||||
|
|
||||||
LookAndFeel.installProperty( getComponent(), "opaque", false );
|
LookAndFeel.installProperty( getComponent(), "opaque", false );
|
||||||
@@ -100,6 +107,7 @@ public class FlatPasswordFieldUI
|
|||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
placeholderForeground = null;
|
placeholderForeground = null;
|
||||||
|
focusedBackground = null;
|
||||||
capsLockIcon = null;
|
capsLockIcon = null;
|
||||||
|
|
||||||
MigLayoutVisualPadding.uninstall( getComponent() );
|
MigLayoutVisualPadding.uninstall( getComponent() );
|
||||||
@@ -109,7 +117,10 @@ public class FlatPasswordFieldUI
|
|||||||
protected void installListeners() {
|
protected void installListeners() {
|
||||||
super.installListeners();
|
super.installListeners();
|
||||||
|
|
||||||
focusListener = new FlatUIUtils.RepaintFocusListener( getComponent() );
|
// necessary to update focus border and background
|
||||||
|
focusListener = new FlatUIUtils.RepaintFocusListener( getComponent(), null );
|
||||||
|
|
||||||
|
// update caps lock indicator
|
||||||
capsLockListener = new KeyAdapter() {
|
capsLockListener = new KeyAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void keyPressed( KeyEvent e ) {
|
public void keyPressed( KeyEvent e ) {
|
||||||
@@ -141,7 +152,8 @@ public class FlatPasswordFieldUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Caret createCaret() {
|
protected Caret createCaret() {
|
||||||
return new FlatCaret( UIManager.getString( "TextComponent.selectAllOnFocusPolicy" ) );
|
return new FlatCaret( UIManager.getString( "TextComponent.selectAllOnFocusPolicy" ),
|
||||||
|
UIManager.getBoolean( "TextComponent.selectAllOnMouseClick" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -152,7 +164,7 @@ public class FlatPasswordFieldUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintSafely( Graphics g ) {
|
protected void paintSafely( Graphics g ) {
|
||||||
FlatTextFieldUI.paintBackground( g, getComponent(), isIntelliJTheme );
|
FlatTextFieldUI.paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground );
|
||||||
FlatTextFieldUI.paintPlaceholder( g, getComponent(), placeholderForeground );
|
FlatTextFieldUI.paintPlaceholder( g, getComponent(), placeholderForeground );
|
||||||
paintCapsLock( g );
|
paintCapsLock( g );
|
||||||
|
|
||||||
@@ -160,6 +172,9 @@ public class FlatPasswordFieldUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void paintCapsLock( Graphics g ) {
|
protected void paintCapsLock( Graphics g ) {
|
||||||
|
if( !showCapsLock )
|
||||||
|
return;
|
||||||
|
|
||||||
JTextComponent c = getComponent();
|
JTextComponent c = getComponent();
|
||||||
if( !FlatUIUtils.isPermanentFocusOwner( c ) ||
|
if( !FlatUIUtils.isPermanentFocusOwner( c ) ||
|
||||||
!Toolkit.getDefaultToolkit().getLockingKeyState( KeyEvent.VK_CAPS_LOCK ) )
|
!Toolkit.getDefaultToolkit().getLockingKeyState( KeyEvent.VK_CAPS_LOCK ) )
|
||||||
|
|||||||
@@ -20,10 +20,16 @@ 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.GraphicsConfiguration;
|
||||||
|
import java.awt.GraphicsDevice;
|
||||||
|
import java.awt.GraphicsEnvironment;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
|
import java.awt.MouseInfo;
|
||||||
import java.awt.Panel;
|
import java.awt.Panel;
|
||||||
import java.awt.Point;
|
import java.awt.Point;
|
||||||
|
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;
|
||||||
@@ -33,6 +39,7 @@ import javax.swing.JComponent;
|
|||||||
import javax.swing.JLayeredPane;
|
import javax.swing.JLayeredPane;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JToolTip;
|
import javax.swing.JToolTip;
|
||||||
|
import javax.swing.JWindow;
|
||||||
import javax.swing.Popup;
|
import javax.swing.Popup;
|
||||||
import javax.swing.PopupFactory;
|
import javax.swing.PopupFactory;
|
||||||
import javax.swing.RootPaneContainer;
|
import javax.swing.RootPaneContainer;
|
||||||
@@ -41,6 +48,7 @@ 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.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A popup factory that adds drop shadows to popups on Windows.
|
* A popup factory that adds drop shadows to popups on Windows.
|
||||||
@@ -59,39 +67,112 @@ public class FlatPopupFactory
|
|||||||
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
|
||||||
{
|
{
|
||||||
if( !isDropShadowPainted( owner, contents ) )
|
Point pt = fixToolTipLocation( owner, contents, x, y );
|
||||||
return new NonFlashingPopup( super.getPopup( owner, contents, x, y ), contents );
|
if( pt != null ) {
|
||||||
|
x = pt.x;
|
||||||
|
y = pt.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean forceHeavyWeight = isOptionEnabled( owner, contents, FlatClientProperties.POPUP_FORCE_HEAVY_WEIGHT, "Popup.forceHeavyWeight" );
|
||||||
|
|
||||||
|
if( !isOptionEnabled( owner, contents, FlatClientProperties.POPUP_DROP_SHADOW_PAINTED, "Popup.dropShadowPainted" ) || SystemInfo.isProjector || SystemInfo.isWebswing )
|
||||||
|
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), contents );
|
||||||
|
|
||||||
// macOS and Linux adds drop shadow to heavy weight popups
|
// macOS and Linux adds drop shadow to heavy weight popups
|
||||||
if( SystemInfo.isMacOS || SystemInfo.isLinux ) {
|
if( SystemInfo.isMacOS || SystemInfo.isLinux )
|
||||||
Popup popup = getHeavyWeightPopup( owner, contents, x, y );
|
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), contents );
|
||||||
if( popup == null )
|
|
||||||
popup = super.getPopup( owner, contents, x, y );
|
|
||||||
return new NonFlashingPopup( popup, contents );
|
|
||||||
}
|
|
||||||
|
|
||||||
// create drop shadow popup
|
// create drop shadow popup
|
||||||
return new DropShadowPopup( super.getPopup( owner, contents, x, y ), owner, contents );
|
return new DropShadowPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), owner, contents );
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isDropShadowPainted( Component owner, Component contents ) {
|
/**
|
||||||
Boolean b = isDropShadowPainted( owner );
|
* Creates a popup for the screen that the owner component is on.
|
||||||
if( b != null )
|
* <p>
|
||||||
return b;
|
* PopupFactory caches heavy weight popup windows and reuses them.
|
||||||
|
* On a dual screen setup, if the popup owner has moved from one screen to the other one,
|
||||||
|
* then the cached heavy weight popup window may be connected to the wrong screen.
|
||||||
|
* If the two screens use different scaling factors, then the popup location and size
|
||||||
|
* is scaled when the popup becomes visible, which shows the popup in the wrong location
|
||||||
|
* (or on wrong screen). The re-scaling is done in WWindowPeer.setBounds() (Java 9+).
|
||||||
|
* <p>
|
||||||
|
* To fix this, dispose popup windows that are on wrong screen and get new popup.
|
||||||
|
* <p>
|
||||||
|
* This is a workaround for https://bugs.openjdk.java.net/browse/JDK-8224608
|
||||||
|
*/
|
||||||
|
private Popup getPopupForScreenOfOwner( Component owner, Component contents, int x, int y, boolean forceHeavyWeight )
|
||||||
|
throws IllegalArgumentException
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
b = isDropShadowPainted( contents );
|
for(;;) {
|
||||||
if( b != null )
|
// create new or get cached popup
|
||||||
return b;
|
Popup popup = forceHeavyWeight
|
||||||
|
? getHeavyWeightPopup( owner, contents, x, y )
|
||||||
|
: super.getPopup( owner, contents, x, y );
|
||||||
|
|
||||||
return UIManager.getBoolean( "Popup.dropShadowPainted" );
|
// get heavy weight popup window; is null for non-heavy weight popup
|
||||||
|
Window popupWindow = SwingUtilities.windowForComponent( contents );
|
||||||
|
|
||||||
|
// check whether heavy weight popup window is on same screen as owner component
|
||||||
|
if( popupWindow == null ||
|
||||||
|
owner == null ||
|
||||||
|
popupWindow.getGraphicsConfiguration() == owner.getGraphicsConfiguration() )
|
||||||
|
return popup;
|
||||||
|
|
||||||
|
// remove contents component from popup window
|
||||||
|
if( popupWindow instanceof JWindow )
|
||||||
|
((JWindow)popupWindow).getContentPane().removeAll();
|
||||||
|
|
||||||
|
// dispose unused popup
|
||||||
|
// (do not invoke popup.hide() because this would cache the popup window)
|
||||||
|
popupWindow.dispose();
|
||||||
|
|
||||||
|
// avoid endless loop (should newer happen; PopupFactory cache size is 5)
|
||||||
|
if( ++count > 10 )
|
||||||
|
return popup;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Boolean isDropShadowPainted( Component c ) {
|
/**
|
||||||
if( !(c instanceof JComponent) )
|
* Shows the given popup and, if necessary, fixes the location of a heavy weight popup window.
|
||||||
return null;
|
* <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 an 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();
|
||||||
|
|
||||||
Object value = ((JComponent)c).getClientProperty( FlatClientProperties.POPUP_DROP_SHADOW_PAINTED );
|
popup.show();
|
||||||
return (value instanceof Boolean ) ? (Boolean) value : null;
|
|
||||||
|
// 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 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -127,6 +208,67 @@ public class FlatPopupFactory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Usually ToolTipManager places a tooltip at (mouseLocation.x, mouseLocation.y + 20).
|
||||||
|
* In case that the tooltip would be partly outside of the screen,
|
||||||
|
* the ToolTipManager changes the location so that the entire tooltip fits on screen.
|
||||||
|
* But this can place the tooltip under the mouse location and hide the owner component.
|
||||||
|
* <p>
|
||||||
|
* This method checks whether the current mouse location is within tooltip bounds
|
||||||
|
* and corrects the y-location so that the tooltip is placed above the mouse location.
|
||||||
|
*/
|
||||||
|
private Point fixToolTipLocation( Component owner, Component contents, int x, int y ) {
|
||||||
|
if( !(contents instanceof JToolTip) || !wasInvokedFromToolTipManager() )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
PointerInfo pointerInfo = MouseInfo.getPointerInfo();
|
||||||
|
if( pointerInfo == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Point mouseLocation = pointerInfo.getLocation();
|
||||||
|
Dimension tipSize = contents.getPreferredSize();
|
||||||
|
|
||||||
|
// check whether mouse location is within tooltip bounds
|
||||||
|
Rectangle tipBounds = new Rectangle( x, y, tipSize.width, tipSize.height );
|
||||||
|
if( !tipBounds.contains( mouseLocation ) )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// find GraphicsConfiguration at mouse location (similar to ToolTipManager.getDrawingGC())
|
||||||
|
GraphicsConfiguration gc = null;
|
||||||
|
for( GraphicsDevice device : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices() ) {
|
||||||
|
GraphicsConfiguration dgc = device.getDefaultConfiguration();
|
||||||
|
if( dgc.getBounds().contains( mouseLocation ) ) {
|
||||||
|
gc = dgc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( gc == null )
|
||||||
|
gc = owner.getGraphicsConfiguration();
|
||||||
|
if( gc == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Rectangle screenBounds = gc.getBounds();
|
||||||
|
Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets( gc );
|
||||||
|
int screenTop = screenBounds.y + screenInsets.top;
|
||||||
|
|
||||||
|
// place tooltip above mouse location if there is enough space
|
||||||
|
int newY = mouseLocation.y - tipSize.height - UIScale.scale( 20 );
|
||||||
|
if( newY < screenTop )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new Point( x, newY );
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean wasInvokedFromToolTipManager() {
|
||||||
|
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
||||||
|
for( StackTraceElement stackTraceElement : stackTrace ) {
|
||||||
|
if( "javax.swing.ToolTipManager".equals( stackTraceElement.getClassName() ) &&
|
||||||
|
"showTipWindow".equals( stackTraceElement.getMethodName() ) )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//---- class NonFlashingPopup ---------------------------------------------
|
//---- class NonFlashingPopup ---------------------------------------------
|
||||||
|
|
||||||
private class NonFlashingPopup
|
private class NonFlashingPopup
|
||||||
@@ -157,20 +299,21 @@ public class FlatPopupFactory
|
|||||||
@Override
|
@Override
|
||||||
public void show() {
|
public void show() {
|
||||||
if( delegate != null ) {
|
if( delegate != null ) {
|
||||||
delegate.show();
|
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
|
||||||
if( contents instanceof JToolTip ) {
|
if( contents instanceof JToolTip && popupWindow == null ) {
|
||||||
Container parent = contents.getParent();
|
Container parent = contents.getParent();
|
||||||
if( parent instanceof JPanel ) {
|
if( parent instanceof JPanel ) {
|
||||||
Dimension prefSize = parent.getPreferredSize();
|
Dimension prefSize = parent.getPreferredSize();
|
||||||
if( !prefSize.equals( parent.getSize() ) ) {
|
if( !prefSize.equals( parent.getSize() ) ) {
|
||||||
Container panel = SwingUtilities.getAncestorOfClass( Panel.class, parent );
|
Container mediumWeightPanel = SwingUtilities.getAncestorOfClass( Panel.class, parent );
|
||||||
if( panel != null )
|
Container c = (mediumWeightPanel != null)
|
||||||
panel.setSize( prefSize ); // for medium weight popup
|
? mediumWeightPanel // medium weight popup
|
||||||
else
|
: parent; // light weight popup
|
||||||
parent.setSize( prefSize ); // for light weight popup
|
c.setSize( prefSize );
|
||||||
|
c.validate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -249,7 +392,7 @@ public class FlatPopupFactory
|
|||||||
// 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 = getHeavyWeightPopup( owner, dropShadowPanel, x, y );
|
dropShadowDelegate = getPopupForScreenOfOwner( owner, dropShadowPanel, x, y, true );
|
||||||
|
|
||||||
// make drop shadow popup window translucent
|
// make drop shadow popup window translucent
|
||||||
dropShadowWindow = SwingUtilities.windowForComponent( dropShadowPanel );
|
dropShadowWindow = SwingUtilities.windowForComponent( dropShadowPanel );
|
||||||
@@ -291,7 +434,7 @@ public class FlatPopupFactory
|
|||||||
@Override
|
@Override
|
||||||
public void show() {
|
public void show() {
|
||||||
if( dropShadowDelegate != null )
|
if( dropShadowDelegate != null )
|
||||||
dropShadowDelegate.show();
|
showPopupAndFixLocation( dropShadowDelegate, dropShadowWindow );
|
||||||
|
|
||||||
if( mediumWeightPanel != null )
|
if( mediumWeightPanel != null )
|
||||||
showMediumWeightDropShadow();
|
showMediumWeightDropShadow();
|
||||||
@@ -339,10 +482,10 @@ public class FlatPopupFactory
|
|||||||
|
|
||||||
mediumWeightShown = true;
|
mediumWeightShown = true;
|
||||||
|
|
||||||
Window window = SwingUtilities.windowForComponent( owner );
|
if( owner == null )
|
||||||
if( window == null )
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
Window window = SwingUtilities.windowForComponent( owner );
|
||||||
if( !(window instanceof RootPaneContainer) )
|
if( !(window instanceof RootPaneContainer) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -38,12 +38,8 @@ import javax.swing.plaf.ComponentUI;
|
|||||||
public class FlatPopupMenuSeparatorUI
|
public class FlatPopupMenuSeparatorUI
|
||||||
extends FlatSeparatorUI
|
extends FlatSeparatorUI
|
||||||
{
|
{
|
||||||
private static ComponentUI instance;
|
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
if( instance == null )
|
return FlatUIUtils.createSharedUI( FlatPopupMenuSeparatorUI.class, FlatPopupMenuSeparatorUI::new );
|
||||||
instance = new FlatPopupMenuSeparatorUI();
|
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ public class FlatProgressBarUI
|
|||||||
? 0
|
? 0
|
||||||
: Math.min( UIScale.scale( this.arc ), horizontal ? height : width );
|
: Math.min( UIScale.scale( this.arc ), horizontal ? height : width );
|
||||||
|
|
||||||
FlatUIUtils.setRenderingHints( (Graphics2D) g );
|
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
||||||
|
|
||||||
// paint track
|
// paint track
|
||||||
RoundRectangle2D.Float trackShape = new RoundRectangle2D.Float( x, y, width, height, arc, arc );
|
RoundRectangle2D.Float trackShape = new RoundRectangle2D.Float( x, y, width, height, arc, arc );
|
||||||
@@ -163,6 +163,7 @@ public class FlatProgressBarUI
|
|||||||
((Graphics2D)g).fill( trackShape );
|
((Graphics2D)g).fill( trackShape );
|
||||||
|
|
||||||
// paint progress
|
// paint progress
|
||||||
|
int amountFull = 0;
|
||||||
if( progressBar.isIndeterminate() ) {
|
if( progressBar.isIndeterminate() ) {
|
||||||
boxRect = getBox( boxRect );
|
boxRect = getBox( boxRect );
|
||||||
if( boxRect != null ) {
|
if( boxRect != null ) {
|
||||||
@@ -170,11 +171,8 @@ public class FlatProgressBarUI
|
|||||||
((Graphics2D)g).fill( new RoundRectangle2D.Float( boxRect.x, boxRect.y,
|
((Graphics2D)g).fill( new RoundRectangle2D.Float( boxRect.x, boxRect.y,
|
||||||
boxRect.width, boxRect.height, arc, arc ) );
|
boxRect.width, boxRect.height, arc, arc ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( progressBar.isStringPainted() )
|
|
||||||
paintString( g, x, y, width, height, 0, insets );
|
|
||||||
} else {
|
} else {
|
||||||
int amountFull = getAmountFull( insets, width, height );
|
amountFull = getAmountFull( insets, width, height );
|
||||||
|
|
||||||
RoundRectangle2D.Float progressShape = horizontal
|
RoundRectangle2D.Float progressShape = horizontal
|
||||||
? new RoundRectangle2D.Float( c.getComponentOrientation().isLeftToRight() ? x : x + (width - amountFull),
|
? new RoundRectangle2D.Float( c.getComponentOrientation().isLeftToRight() ? x : x + (width - amountFull),
|
||||||
@@ -189,11 +187,13 @@ public class FlatProgressBarUI
|
|||||||
((Graphics2D)g).fill( area );
|
((Graphics2D)g).fill( area );
|
||||||
} else
|
} else
|
||||||
((Graphics2D)g).fill( progressShape );
|
((Graphics2D)g).fill( progressShape );
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||||
|
|
||||||
if( progressBar.isStringPainted() )
|
if( progressBar.isStringPainted() )
|
||||||
paintString( g, x, y, width, height, amountFull, insets );
|
paintString( g, x, y, width, height, amountFull, insets );
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintString( Graphics g, int x, int y, int width, int height, int amountFull, Insets b ) {
|
protected void paintString( Graphics g, int x, int y, int width, int height, int amountFull, Insets b ) {
|
||||||
|
|||||||
@@ -18,16 +18,18 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
import static com.formdev.flatlaf.util.UIScale.scale;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.Container;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
|
import java.util.Objects;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
|
import javax.swing.CellRendererPane;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.UIResource;
|
|
||||||
import javax.swing.plaf.basic.BasicRadioButtonUI;
|
import javax.swing.plaf.basic.BasicRadioButtonUI;
|
||||||
import com.formdev.flatlaf.icons.FlatCheckBoxIcon;
|
import com.formdev.flatlaf.icons.FlatCheckBoxIcon;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
@@ -58,14 +60,12 @@ public class FlatRadioButtonUI
|
|||||||
protected int iconTextGap;
|
protected int iconTextGap;
|
||||||
protected Color disabledText;
|
protected Color disabledText;
|
||||||
|
|
||||||
|
private Color defaultBackground;
|
||||||
|
|
||||||
private boolean defaults_initialized = false;
|
private boolean defaults_initialized = false;
|
||||||
|
|
||||||
private static ComponentUI instance;
|
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
if( instance == null )
|
return FlatUIUtils.createSharedUI( FlatRadioButtonUI.class, FlatRadioButtonUI::new );
|
||||||
instance = new FlatRadioButtonUI();
|
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -78,6 +78,8 @@ public class FlatRadioButtonUI
|
|||||||
iconTextGap = FlatUIUtils.getUIInt( prefix + "iconTextGap", 4 );
|
iconTextGap = FlatUIUtils.getUIInt( prefix + "iconTextGap", 4 );
|
||||||
disabledText = UIManager.getColor( prefix + "disabledText" );
|
disabledText = UIManager.getColor( prefix + "disabledText" );
|
||||||
|
|
||||||
|
defaultBackground = UIManager.getColor( prefix + "background" );
|
||||||
|
|
||||||
defaults_initialized = true;
|
defaults_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,10 +123,11 @@ public class FlatRadioButtonUI
|
|||||||
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 if
|
||||||
// - contentAreaFilled is true and
|
// - contentAreaFilled is true and
|
||||||
// - if background was explicitly set to a non-UIResource color
|
// - if background color is different to default background color
|
||||||
|
// (this paints selection if using the component as cell renderer)
|
||||||
if( !c.isOpaque() &&
|
if( !c.isOpaque() &&
|
||||||
((AbstractButton)c).isContentAreaFilled() &&
|
((AbstractButton)c).isContentAreaFilled() &&
|
||||||
!(c.getBackground() instanceof UIResource) )
|
!Objects.equals( c.getBackground(), getDefaultBackground( c ) ) )
|
||||||
{
|
{
|
||||||
g.setColor( c.getBackground() );
|
g.setColor( c.getBackground() );
|
||||||
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
|
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
|
||||||
@@ -161,6 +164,18 @@ public class FlatRadioButtonUI
|
|||||||
FlatButtonUI.paintText( g, b, textRect, text, b.isEnabled() ? b.getForeground() : disabledText );
|
FlatButtonUI.paintText( g, b, textRect, text, b.isEnabled() ? b.getForeground() : disabledText );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default background color of the component.
|
||||||
|
* If the component is used as cell renderer (e.g. in JTable),
|
||||||
|
* then the background color of the renderer container is returned.
|
||||||
|
*/
|
||||||
|
private Color getDefaultBackground( JComponent c ) {
|
||||||
|
Container parent = c.getParent();
|
||||||
|
return (parent instanceof CellRendererPane && parent.getParent() != null)
|
||||||
|
? parent.getParent().getBackground()
|
||||||
|
: defaultBackground;
|
||||||
|
}
|
||||||
|
|
||||||
private int getIconFocusWidth( JComponent c ) {
|
private int getIconFocusWidth( JComponent c ) {
|
||||||
AbstractButton b = (AbstractButton) c;
|
AbstractButton b = (AbstractButton) c;
|
||||||
return (b.getIcon() == null && getDefaultIcon() instanceof FlatCheckBoxIcon)
|
return (b.getIcon() == null && getDefaultIcon() instanceof FlatCheckBoxIcon)
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ import java.awt.Insets;
|
|||||||
import java.awt.LayoutManager;
|
import java.awt.LayoutManager;
|
||||||
import java.awt.LayoutManager2;
|
import java.awt.LayoutManager2;
|
||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
|
import java.awt.event.HierarchyEvent;
|
||||||
|
import java.awt.event.HierarchyListener;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
@@ -37,14 +39,17 @@ import javax.swing.JMenuBar;
|
|||||||
import javax.swing.JRootPane;
|
import javax.swing.JRootPane;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.border.Border;
|
||||||
import javax.swing.plaf.BorderUIResource;
|
import javax.swing.plaf.BorderUIResource;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import javax.swing.plaf.RootPaneUI;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicRootPaneUI;
|
import javax.swing.plaf.basic.BasicRootPaneUI;
|
||||||
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.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JRootPane}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JRootPane}.
|
||||||
@@ -54,6 +59,7 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
* @uiDefault RootPane.border Border
|
* @uiDefault RootPane.border Border
|
||||||
* @uiDefault RootPane.activeBorderColor Color
|
* @uiDefault RootPane.activeBorderColor Color
|
||||||
* @uiDefault RootPane.inactiveBorderColor Color
|
* @uiDefault RootPane.inactiveBorderColor Color
|
||||||
|
* @uiDefault TitlePane.borderColor Color optional
|
||||||
*
|
*
|
||||||
* <!-- FlatWindowResizer -->
|
* <!-- FlatWindowResizer -->
|
||||||
*
|
*
|
||||||
@@ -67,15 +73,15 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
public class FlatRootPaneUI
|
public class FlatRootPaneUI
|
||||||
extends BasicRootPaneUI
|
extends BasicRootPaneUI
|
||||||
{
|
{
|
||||||
// check this field before using class JBRCustomDecorations to avoid unnecessary loading of that class
|
protected final Color borderColor = UIManager.getColor( "TitlePane.borderColor" );
|
||||||
static final boolean canUseJBRCustomDecorations
|
|
||||||
= SystemInfo.isJetBrainsJVM_11_orLater && SystemInfo.isWindows_10_orLater;
|
|
||||||
|
|
||||||
protected JRootPane rootPane;
|
protected JRootPane rootPane;
|
||||||
protected FlatTitlePane titlePane;
|
protected FlatTitlePane titlePane;
|
||||||
protected FlatWindowResizer windowResizer;
|
protected FlatWindowResizer windowResizer;
|
||||||
|
|
||||||
|
private Object nativeWindowBorderData;
|
||||||
private LayoutManager oldLayout;
|
private LayoutManager oldLayout;
|
||||||
|
private HierarchyListener hierarchyListener;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatRootPaneUI();
|
return new FlatRootPaneUI();
|
||||||
@@ -89,15 +95,25 @@ public class FlatRootPaneUI
|
|||||||
|
|
||||||
if( rootPane.getWindowDecorationStyle() != JRootPane.NONE )
|
if( rootPane.getWindowDecorationStyle() != JRootPane.NONE )
|
||||||
installClientDecorations();
|
installClientDecorations();
|
||||||
|
else
|
||||||
|
installBorder();
|
||||||
|
|
||||||
if( canUseJBRCustomDecorations )
|
installNativeWindowBorder();
|
||||||
JBRCustomDecorations.install( rootPane );
|
}
|
||||||
|
|
||||||
|
protected void installBorder() {
|
||||||
|
if( borderColor != null ) {
|
||||||
|
Border b = rootPane.getBorder();
|
||||||
|
if( b == null || b instanceof UIResource )
|
||||||
|
rootPane.setBorder( new FlatWindowTitleBorder( borderColor ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void uninstallUI( JComponent c ) {
|
public void uninstallUI( JComponent c ) {
|
||||||
super.uninstallUI( c );
|
super.uninstallUI( c );
|
||||||
|
|
||||||
|
uninstallNativeWindowBorder();
|
||||||
uninstallClientDecorations();
|
uninstallClientDecorations();
|
||||||
rootPane = null;
|
rootPane = null;
|
||||||
}
|
}
|
||||||
@@ -119,18 +135,76 @@ public class FlatRootPaneUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// enable dark window appearance on macOS when running in JetBrains Runtime
|
// enable dark window appearance on macOS when running in JetBrains Runtime
|
||||||
if( SystemInfo.isJetBrainsJVM && SystemInfo.isMacOS_10_14_Mojave_orLater ) {
|
if( SystemInfo.isJetBrainsJVM && SystemInfo.isMacOS_10_14_Mojave_orLater )
|
||||||
LookAndFeel laf = UIManager.getLookAndFeel();
|
c.putClientProperty( "jetbrains.awt.windowDarkAppearance", FlatLaf.isLafDark() );
|
||||||
boolean isDark = laf instanceof FlatLaf && ((FlatLaf)laf).isDark();
|
|
||||||
c.putClientProperty( "jetbrains.awt.windowDarkAppearance", isDark );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installListeners( JRootPane root ) {
|
||||||
|
super.installListeners( root );
|
||||||
|
|
||||||
|
if( SystemInfo.isJava_9_orLater ) {
|
||||||
|
// On HiDPI screens, where scaling is used, there may be white lines at the
|
||||||
|
// bottom and at the right side of the window when it is initially shown.
|
||||||
|
// This is very disturbing in dark themes, but hard to notice in light themes.
|
||||||
|
// Seems to be a rounding issue when Swing adds dirty region of window
|
||||||
|
// using RepaintManager.nativeAddDirtyRegion().
|
||||||
|
hierarchyListener = e -> {
|
||||||
|
if( (e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0 &&
|
||||||
|
rootPane.getParent() instanceof Window )
|
||||||
|
{
|
||||||
|
// add whole root pane to dirty regions when window is initially shown
|
||||||
|
rootPane.getParent().repaint( rootPane.getX(), rootPane.getY(),
|
||||||
|
rootPane.getWidth(), rootPane.getHeight() );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
root.addHierarchyListener( hierarchyListener );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallListeners( JRootPane root ) {
|
||||||
|
super.uninstallListeners( root );
|
||||||
|
|
||||||
|
if( SystemInfo.isJava_9_orLater ) {
|
||||||
|
root.removeHierarchyListener( hierarchyListener );
|
||||||
|
hierarchyListener = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1.2
|
||||||
|
*/
|
||||||
|
protected void installNativeWindowBorder() {
|
||||||
|
nativeWindowBorderData = FlatNativeWindowBorder.install( rootPane );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1.2
|
||||||
|
*/
|
||||||
|
protected void uninstallNativeWindowBorder() {
|
||||||
|
FlatNativeWindowBorder.uninstall( rootPane, nativeWindowBorderData );
|
||||||
|
nativeWindowBorderData = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1.2
|
||||||
|
*/
|
||||||
|
public static void updateNativeWindowBorder( JRootPane rootPane ) {
|
||||||
|
RootPaneUI rui = rootPane.getUI();
|
||||||
|
if( !(rui instanceof FlatRootPaneUI) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
FlatRootPaneUI ui = (FlatRootPaneUI) rui;
|
||||||
|
ui.uninstallNativeWindowBorder();
|
||||||
|
ui.installNativeWindowBorder();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void installClientDecorations() {
|
protected void installClientDecorations() {
|
||||||
boolean isJBRSupported = canUseJBRCustomDecorations && JBRCustomDecorations.isSupported();
|
boolean isNativeWindowBorderSupported = FlatNativeWindowBorder.isSupported();
|
||||||
|
|
||||||
// install border
|
// install border
|
||||||
if( rootPane.getWindowDecorationStyle() != JRootPane.NONE && !isJBRSupported )
|
if( rootPane.getWindowDecorationStyle() != JRootPane.NONE && !isNativeWindowBorderSupported )
|
||||||
LookAndFeel.installBorder( rootPane, "RootPane.border" );
|
LookAndFeel.installBorder( rootPane, "RootPane.border" );
|
||||||
else
|
else
|
||||||
LookAndFeel.uninstallBorder( rootPane );
|
LookAndFeel.uninstallBorder( rootPane );
|
||||||
@@ -143,7 +217,7 @@ public class FlatRootPaneUI
|
|||||||
rootPane.setLayout( createRootLayout() );
|
rootPane.setLayout( createRootLayout() );
|
||||||
|
|
||||||
// install window resizer
|
// install window resizer
|
||||||
if( !isJBRSupported )
|
if( !isNativeWindowBorderSupported )
|
||||||
windowResizer = createWindowResizer();
|
windowResizer = createWindowResizer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +246,7 @@ public class FlatRootPaneUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected FlatWindowResizer createWindowResizer() {
|
protected FlatWindowResizer createWindowResizer() {
|
||||||
return new FlatWindowResizer( rootPane );
|
return new FlatWindowResizer.WindowResizer( rootPane );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected FlatTitlePane createTitlePane() {
|
protected FlatTitlePane createTitlePane() {
|
||||||
@@ -203,6 +277,12 @@ public class FlatRootPaneUI
|
|||||||
uninstallClientDecorations();
|
uninstallClientDecorations();
|
||||||
if( rootPane.getWindowDecorationStyle() != JRootPane.NONE )
|
if( rootPane.getWindowDecorationStyle() != JRootPane.NONE )
|
||||||
installClientDecorations();
|
installClientDecorations();
|
||||||
|
else
|
||||||
|
installBorder();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.USE_WINDOW_DECORATIONS:
|
||||||
|
updateNativeWindowBorder( rootPane );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FlatClientProperties.MENU_BAR_EMBEDDED:
|
case FlatClientProperties.MENU_BAR_EMBEDDED:
|
||||||
@@ -212,9 +292,22 @@ public class FlatRootPaneUI
|
|||||||
rootPane.repaint();
|
rootPane.repaint();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.TITLE_BAR_BACKGROUND:
|
||||||
|
case FlatClientProperties.TITLE_BAR_FOREGROUND:
|
||||||
|
if( titlePane != null )
|
||||||
|
titlePane.titleBarColorsChanged();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static boolean isMenuBarEmbedded( JRootPane rootPane ) {
|
||||||
|
RootPaneUI ui = rootPane.getUI();
|
||||||
|
return ui instanceof FlatRootPaneUI &&
|
||||||
|
((FlatRootPaneUI)ui).titlePane != null &&
|
||||||
|
((FlatRootPaneUI)ui).titlePane.isMenuBarEmbedded();
|
||||||
|
}
|
||||||
|
|
||||||
//---- class FlatRootLayout -----------------------------------------------
|
//---- class FlatRootLayout -----------------------------------------------
|
||||||
|
|
||||||
protected class FlatRootLayout
|
protected class FlatRootLayout
|
||||||
@@ -252,8 +345,9 @@ public class FlatRootPaneUI
|
|||||||
int width = Math.max( titlePaneSize.width, contentSize.width );
|
int width = Math.max( titlePaneSize.width, contentSize.width );
|
||||||
int height = titlePaneSize.height + contentSize.height;
|
int height = titlePaneSize.height + contentSize.height;
|
||||||
if( titlePane == null || !titlePane.isMenuBarEmbedded() ) {
|
if( titlePane == null || !titlePane.isMenuBarEmbedded() ) {
|
||||||
Dimension menuBarSize = (rootPane.getJMenuBar() != null)
|
JMenuBar menuBar = rootPane.getJMenuBar();
|
||||||
? getSizeFunc.apply( rootPane.getJMenuBar() )
|
Dimension menuBarSize = (menuBar != null && menuBar.isVisible())
|
||||||
|
? getSizeFunc.apply( menuBar )
|
||||||
: new Dimension();
|
: new Dimension();
|
||||||
|
|
||||||
width = Math.max( width, menuBarSize.width );
|
width = Math.max( width, menuBarSize.width );
|
||||||
@@ -270,6 +364,7 @@ public class FlatRootPaneUI
|
|||||||
@Override
|
@Override
|
||||||
public void layoutContainer( Container parent ) {
|
public void layoutContainer( Container parent ) {
|
||||||
JRootPane rootPane = (JRootPane) parent;
|
JRootPane rootPane = (JRootPane) parent;
|
||||||
|
boolean isFullScreen = FlatUIUtils.isFullScreen( rootPane );
|
||||||
|
|
||||||
Insets insets = rootPane.getInsets();
|
Insets insets = rootPane.getInsets();
|
||||||
int x = insets.left;
|
int x = insets.left;
|
||||||
@@ -284,14 +379,15 @@ public class FlatRootPaneUI
|
|||||||
|
|
||||||
int nextY = 0;
|
int nextY = 0;
|
||||||
if( titlePane != null ) {
|
if( titlePane != null ) {
|
||||||
Dimension prefSize = titlePane.getPreferredSize();
|
int prefHeight = !isFullScreen ? titlePane.getPreferredSize().height : 0;
|
||||||
titlePane.setBounds( 0, 0, width, prefSize.height );
|
titlePane.setBounds( 0, 0, width, prefHeight );
|
||||||
nextY += prefSize.height;
|
nextY += prefHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
JMenuBar menuBar = rootPane.getJMenuBar();
|
JMenuBar menuBar = rootPane.getJMenuBar();
|
||||||
if( menuBar != null ) {
|
if( menuBar != null && menuBar.isVisible() ) {
|
||||||
if( titlePane != null && titlePane.isMenuBarEmbedded() ) {
|
boolean embedded = !isFullScreen && titlePane != null && titlePane.isMenuBarEmbedded();
|
||||||
|
if( embedded ) {
|
||||||
titlePane.validate();
|
titlePane.validate();
|
||||||
menuBar.setBounds( titlePane.getMenuBarBounds() );
|
menuBar.setBounds( titlePane.getMenuBarBounds() );
|
||||||
} else {
|
} else {
|
||||||
@@ -328,6 +424,9 @@ public class FlatRootPaneUI
|
|||||||
|
|
||||||
//---- class FlatWindowBorder ---------------------------------------------
|
//---- class FlatWindowBorder ---------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Window border used for non-native window decorations.
|
||||||
|
*/
|
||||||
public static class FlatWindowBorder
|
public static class FlatWindowBorder
|
||||||
extends BorderUIResource.EmptyBorderUIResource
|
extends BorderUIResource.EmptyBorderUIResource
|
||||||
{
|
{
|
||||||
@@ -341,8 +440,8 @@ public class FlatRootPaneUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
if( isWindowMaximized( c ) ) {
|
if( isWindowMaximized( c ) || FlatUIUtils.isFullScreen( c ) ) {
|
||||||
// hide border if window is maximized
|
// hide border if window is maximized or full screen
|
||||||
insets.top = insets.left = insets.bottom = insets.right = 0;
|
insets.top = insets.left = insets.bottom = insets.right = 0;
|
||||||
return insets;
|
return insets;
|
||||||
} else
|
} else
|
||||||
@@ -351,7 +450,7 @@ public class FlatRootPaneUI
|
|||||||
|
|
||||||
@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( isWindowMaximized( c ) )
|
if( isWindowMaximized( c ) || FlatUIUtils.isFullScreen( c ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Container parent = c.getParent();
|
Container parent = c.getParent();
|
||||||
@@ -372,4 +471,42 @@ public class FlatRootPaneUI
|
|||||||
: false;
|
: false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- class FlatWindowTitleBorder ----------------------------------------
|
||||||
|
|
||||||
|
private static class FlatWindowTitleBorder
|
||||||
|
extends BorderUIResource.EmptyBorderUIResource
|
||||||
|
{
|
||||||
|
private final Color borderColor;
|
||||||
|
|
||||||
|
FlatWindowTitleBorder( Color borderColor ) {
|
||||||
|
super( 0, 0, 0, 0 );
|
||||||
|
this.borderColor = borderColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
|
if( showBorder( c ) ) {
|
||||||
|
float lineHeight = UIScale.scale( (float) 1 );
|
||||||
|
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y, width, lineHeight );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
|
insets.set( showBorder( c ) ? 1 : 0, 0, 0, 0 );
|
||||||
|
return insets;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean showBorder( Component c ) {
|
||||||
|
Container parent = c.getParent();
|
||||||
|
return
|
||||||
|
(parent instanceof JFrame &&
|
||||||
|
(((JFrame)parent).getJMenuBar() == null ||
|
||||||
|
!((JFrame)parent).getJMenuBar().isVisible())) ||
|
||||||
|
(parent instanceof JDialog &&
|
||||||
|
(((JDialog)parent).getJMenuBar() == null ||
|
||||||
|
!((JDialog)parent).getJMenuBar().isVisible()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,12 +19,10 @@ package com.formdev.flatlaf.ui;
|
|||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.beans.PropertyChangeEvent;
|
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import javax.swing.InputMap;
|
import javax.swing.InputMap;
|
||||||
@@ -65,7 +63,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault ScrollBar.pressedTrackColor Color optional
|
* @uiDefault ScrollBar.pressedTrackColor Color optional
|
||||||
* @uiDefault ScrollBar.pressedThumbColor Color optional
|
* @uiDefault ScrollBar.pressedThumbColor Color optional
|
||||||
* @uiDefault ScrollBar.pressedThumbWithTrack boolean
|
* @uiDefault ScrollBar.pressedThumbWithTrack boolean
|
||||||
* @uiDefault Component.arrowType String triangle (default) or chevron
|
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||||
* @uiDefault ScrollBar.showButtons boolean
|
* @uiDefault ScrollBar.showButtons boolean
|
||||||
* @uiDefault ScrollBar.buttonArrowColor Color
|
* @uiDefault ScrollBar.buttonArrowColor Color
|
||||||
* @uiDefault ScrollBar.buttonDisabledArrowColor Color
|
* @uiDefault ScrollBar.buttonDisabledArrowColor Color
|
||||||
@@ -142,6 +140,12 @@ public class FlatScrollBarUI
|
|||||||
buttonDisabledArrowColor = UIManager.getColor( "ScrollBar.buttonDisabledArrowColor" );
|
buttonDisabledArrowColor = UIManager.getColor( "ScrollBar.buttonDisabledArrowColor" );
|
||||||
hoverButtonBackground = UIManager.getColor( "ScrollBar.hoverButtonBackground" );
|
hoverButtonBackground = UIManager.getColor( "ScrollBar.hoverButtonBackground" );
|
||||||
pressedButtonBackground = UIManager.getColor( "ScrollBar.pressedButtonBackground" );
|
pressedButtonBackground = UIManager.getColor( "ScrollBar.pressedButtonBackground" );
|
||||||
|
|
||||||
|
// fallback (e.g. when used in NetBeans GUI builder)
|
||||||
|
if( trackInsets == null )
|
||||||
|
trackInsets = new Insets( 0, 0, 0, 0 );
|
||||||
|
if( thumbInsets == null )
|
||||||
|
thumbInsets = new Insets( 0, 0, 0, 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -163,10 +167,9 @@ public class FlatScrollBarUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PropertyChangeListener createPropertyChangeListener() {
|
protected PropertyChangeListener createPropertyChangeListener() {
|
||||||
return new BasicScrollBarUI.PropertyChangeHandler() {
|
PropertyChangeListener superListener = super.createPropertyChangeListener();
|
||||||
@Override
|
return e -> {
|
||||||
public void propertyChange( PropertyChangeEvent e ) {
|
superListener.propertyChange( e );
|
||||||
super.propertyChange( e );
|
|
||||||
|
|
||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
case FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS:
|
case FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS:
|
||||||
@@ -187,7 +190,6 @@ public class FlatScrollBarUI
|
|||||||
SwingUtilities.replaceUIInputMap( scrollbar, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap );
|
SwingUtilities.replaceUIInputMap( scrollbar, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,8 +217,9 @@ public class FlatScrollBarUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paint( Graphics g, JComponent c ) {
|
public void paint( Graphics g, JComponent c ) {
|
||||||
FlatUIUtils.setRenderingHints( (Graphics2D) g );
|
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
||||||
super.paint( g, c );
|
super.paint( g, c );
|
||||||
|
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -351,13 +354,14 @@ public class FlatScrollBarUI
|
|||||||
{
|
{
|
||||||
protected FlatScrollBarButton( int direction ) {
|
protected FlatScrollBarButton( int direction ) {
|
||||||
this( direction, arrowType, buttonArrowColor, buttonDisabledArrowColor,
|
this( direction, arrowType, buttonArrowColor, buttonDisabledArrowColor,
|
||||||
null, hoverButtonBackground, pressedButtonBackground );
|
null, hoverButtonBackground, null, pressedButtonBackground );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected FlatScrollBarButton( int direction, String type, Color foreground, Color disabledForeground,
|
protected FlatScrollBarButton( int direction, String type, Color foreground, Color disabledForeground,
|
||||||
Color hoverForeground, Color hoverBackground, Color pressedBackground )
|
Color hoverForeground, Color hoverBackground, Color pressedForeground, Color pressedBackground )
|
||||||
{
|
{
|
||||||
super( direction, type, foreground, disabledForeground, hoverForeground, hoverBackground, pressedBackground );
|
super( direction, type, foreground, disabledForeground,
|
||||||
|
hoverForeground, hoverBackground, pressedForeground, pressedBackground );
|
||||||
|
|
||||||
setArrowWidth( FlatArrowButton.DEFAULT_ARROW_WIDTH - 2 );
|
setArrowWidth( FlatArrowButton.DEFAULT_ARROW_WIDTH - 2 );
|
||||||
setFocusable( false );
|
setFocusable( false );
|
||||||
|
|||||||
@@ -14,17 +14,12 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
* Smooth scrolling code partly based on code from IntelliJ IDEA Community Edition,
|
|
||||||
* which is licensed under the Apache 2.0 license. Copyright 2000-2016 JetBrains s.r.o.
|
|
||||||
* See: https://github.com/JetBrains/intellij-community/blob/31e1b5a8e43219b9571951bab6457cfb3012e3ef/platform/platform-api/src/com/intellij/ui/components/SmoothScrollPane.java#L141-L185
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
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.awt.KeyboardFocusManager;
|
||||||
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;
|
||||||
@@ -40,11 +35,13 @@ import javax.swing.JComponent;
|
|||||||
import javax.swing.JScrollBar;
|
import javax.swing.JScrollBar;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
|
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.Scrollable;
|
import javax.swing.Scrollable;
|
||||||
import javax.swing.SwingConstants;
|
import javax.swing.SwingConstants;
|
||||||
|
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.BasicScrollPaneUI;
|
import javax.swing.plaf.basic.BasicScrollPaneUI;
|
||||||
@@ -111,9 +108,8 @@ public class FlatScrollPaneUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected MouseWheelListener createMouseWheelListener() {
|
protected MouseWheelListener createMouseWheelListener() {
|
||||||
return new BasicScrollPaneUI.MouseWheelHandler() {
|
MouseWheelListener superListener = super.createMouseWheelListener();
|
||||||
@Override
|
return e -> {
|
||||||
public void mouseWheelMoved( MouseWheelEvent e ) {
|
|
||||||
if( isSmoothScrollingEnabled() &&
|
if( isSmoothScrollingEnabled() &&
|
||||||
scrollpane.isWheelScrollingEnabled() &&
|
scrollpane.isWheelScrollingEnabled() &&
|
||||||
e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL &&
|
e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL &&
|
||||||
@@ -122,8 +118,7 @@ public class FlatScrollPaneUI
|
|||||||
{
|
{
|
||||||
mouseWheelMovedSmooth( e );
|
mouseWheelMovedSmooth( e );
|
||||||
} else
|
} else
|
||||||
super.mouseWheelMoved( e );
|
superListener.mouseWheelMoved( e );
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,8 +133,6 @@ public class FlatScrollPaneUI
|
|||||||
return UIManager.getBoolean( "ScrollPane.smoothScrolling" );
|
return UIManager.getBoolean( "ScrollPane.smoothScrolling" );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final double EPSILON = 1e-5d;
|
|
||||||
|
|
||||||
private void mouseWheelMovedSmooth( MouseWheelEvent e ) {
|
private void mouseWheelMovedSmooth( MouseWheelEvent e ) {
|
||||||
// return if there is no viewport
|
// return if there is no viewport
|
||||||
JViewport viewport = scrollpane.getViewport();
|
JViewport viewport = scrollpane.getViewport();
|
||||||
@@ -160,24 +153,22 @@ public class FlatScrollPaneUI
|
|||||||
// get precise wheel rotation
|
// get precise wheel rotation
|
||||||
double rotation = e.getPreciseWheelRotation();
|
double rotation = e.getPreciseWheelRotation();
|
||||||
|
|
||||||
// get unit and block increment
|
// get unit increment
|
||||||
int unitIncrement;
|
int unitIncrement;
|
||||||
int blockIncrement;
|
|
||||||
int orientation = scrollbar.getOrientation();
|
int orientation = scrollbar.getOrientation();
|
||||||
Component view = viewport.getView();
|
Component view = viewport.getView();
|
||||||
if( view instanceof Scrollable ) {
|
if( view instanceof Scrollable ) {
|
||||||
Scrollable scrollable = (Scrollable) view;
|
Scrollable scrollable = (Scrollable) view;
|
||||||
|
|
||||||
// Use (0, 0) view position to obtain constant unit increment of first item
|
// Use (0, 0) view position to obtain a constant unit increment of first item.
|
||||||
// (which might otherwise be variable on smaller-than-unit scrolling).
|
// Unit increment may be different for each item.
|
||||||
Rectangle visibleRect = new Rectangle( viewport.getViewSize() );
|
Rectangle visibleRect = new Rectangle( viewport.getViewSize() );
|
||||||
unitIncrement = scrollable.getScrollableUnitIncrement( visibleRect, orientation, 1 );
|
unitIncrement = scrollable.getScrollableUnitIncrement( visibleRect, orientation, 1 );
|
||||||
blockIncrement = scrollable.getScrollableBlockIncrement( visibleRect, orientation, 1 );
|
|
||||||
|
|
||||||
if( unitIncrement > 0 ) {
|
if( unitIncrement > 0 ) {
|
||||||
// For the case that the first item (e.g. in a list) is larger
|
// For the case that the first item (e.g. in a list) is larger
|
||||||
// than the other items, get the unit increment of the second item
|
// than the other items (e.g. themes list in FlatLaf Demo),
|
||||||
// and use the smaller one.
|
// get the unit increment of the second item and use the smaller one.
|
||||||
if( orientation == SwingConstants.VERTICAL ) {
|
if( orientation == SwingConstants.VERTICAL ) {
|
||||||
visibleRect.y += unitIncrement;
|
visibleRect.y += unitIncrement;
|
||||||
visibleRect.height -= unitIncrement;
|
visibleRect.height -= unitIncrement;
|
||||||
@@ -192,61 +183,66 @@ public class FlatScrollPaneUI
|
|||||||
} else {
|
} else {
|
||||||
int direction = rotation < 0 ? -1 : 1;
|
int direction = rotation < 0 ? -1 : 1;
|
||||||
unitIncrement = scrollbar.getUnitIncrement( direction );
|
unitIncrement = scrollbar.getUnitIncrement( direction );
|
||||||
blockIncrement = scrollbar.getBlockIncrement( direction );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// limit scroll amount (number of units to scroll) for small viewports
|
// get viewport width/height (the visible width/height)
|
||||||
// (e.g. vertical scrolling in file chooser)
|
|
||||||
int scrollAmount = e.getScrollAmount();
|
|
||||||
int viewportWH = (orientation == SwingConstants.VERTICAL)
|
int viewportWH = (orientation == SwingConstants.VERTICAL)
|
||||||
? viewport.getHeight()
|
? viewport.getHeight()
|
||||||
: viewport.getWidth();
|
: viewport.getWidth();
|
||||||
if( unitIncrement * scrollAmount > viewportWH )
|
|
||||||
scrollAmount = Math.max( viewportWH / unitIncrement, 1 );
|
// limit scroll increment to viewport width/height
|
||||||
|
// - if scroll amount is set to a large value in OS settings
|
||||||
|
// - for large unit increments in small viewports (e.g. horizontal scrolling in file chooser)
|
||||||
|
int scrollIncrement = Math.min( unitIncrement * e.getScrollAmount(), viewportWH );
|
||||||
|
|
||||||
// compute relative delta
|
// compute relative delta
|
||||||
double delta = rotation * scrollAmount * unitIncrement;
|
double delta = rotation * scrollIncrement;
|
||||||
boolean adjustDelta = Math.abs( rotation ) < (1.0 + EPSILON);
|
int idelta = (int) Math.round( delta );
|
||||||
double adjustedDelta = adjustDelta
|
|
||||||
? Math.max( -blockIncrement, Math.min( delta, blockIncrement ) )
|
// scroll at least one pixel to avoid "hanging"
|
||||||
: delta;
|
// - for "super-low-speed" scrolling (move fingers very slowly on trackpad)
|
||||||
|
// - if unit increment is very small (e.g. 1 if scroll view does not implement
|
||||||
|
// javax.swing.Scrollable interface)
|
||||||
|
if( idelta == 0 ) {
|
||||||
|
if( rotation > 0 )
|
||||||
|
idelta = 1;
|
||||||
|
else if( rotation < 0 )
|
||||||
|
idelta = -1;
|
||||||
|
}
|
||||||
|
|
||||||
// compute new value
|
// compute new value
|
||||||
int value = scrollbar.getValue();
|
int value = scrollbar.getValue();
|
||||||
double minDelta = scrollbar.getMinimum() - value;
|
int minValue = scrollbar.getMinimum();
|
||||||
double maxDelta = scrollbar.getMaximum() - scrollbar.getModel().getExtent() - value;
|
int maxValue = scrollbar.getMaximum() - scrollbar.getModel().getExtent();
|
||||||
double boundedDelta = Math.max( minDelta, Math.min( adjustedDelta, maxDelta ) );
|
int newValue = Math.max( minValue, Math.min( value + idelta, maxValue ) );
|
||||||
int newValue = value + (int) Math.round( boundedDelta );
|
|
||||||
|
|
||||||
// set new value
|
// set new value
|
||||||
if( newValue != value )
|
if( newValue != value )
|
||||||
scrollbar.setValue( newValue );
|
scrollbar.setValue( newValue );
|
||||||
|
|
||||||
/*debug
|
/*debug
|
||||||
System.out.println( String.format( "%4d %9f / %4d %4d / %12f %5s %12f / %4d %4d %4d / %12f %12f %12f / %4d",
|
System.out.println( String.format( "%s %4d %9f / %3d * %d = %3d [%3d] / %8.2f %5d / %4d --> %4d [%d, %d]",
|
||||||
|
(orientation == SwingConstants.VERTICAL) ? "V" : "H",
|
||||||
e.getWheelRotation(),
|
e.getWheelRotation(),
|
||||||
e.getPreciseWheelRotation(),
|
e.getPreciseWheelRotation(),
|
||||||
unitIncrement,
|
unitIncrement,
|
||||||
blockIncrement,
|
e.getScrollAmount(),
|
||||||
|
scrollIncrement,
|
||||||
|
viewportWH,
|
||||||
delta,
|
delta,
|
||||||
adjustDelta,
|
idelta,
|
||||||
adjustedDelta,
|
|
||||||
value,
|
value,
|
||||||
scrollbar.getMinimum(),
|
newValue,
|
||||||
scrollbar.getMaximum(),
|
minValue,
|
||||||
minDelta,
|
maxValue ) );
|
||||||
maxDelta,
|
|
||||||
boundedDelta,
|
|
||||||
newValue ) );
|
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PropertyChangeListener createPropertyChangeListener() {
|
protected PropertyChangeListener createPropertyChangeListener() {
|
||||||
return new BasicScrollPaneUI.PropertyChangeHandler() {
|
PropertyChangeListener superListener = super.createPropertyChangeListener();
|
||||||
@Override
|
return e -> {
|
||||||
public void propertyChange( PropertyChangeEvent e ) {
|
superListener.propertyChange( e );
|
||||||
super.propertyChange( e );
|
|
||||||
|
|
||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
case FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS:
|
case FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS:
|
||||||
@@ -278,7 +274,6 @@ public class FlatScrollPaneUI
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,6 +332,31 @@ public class FlatScrollPaneUI
|
|||||||
paint( g, c );
|
paint( g, c );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.3
|
||||||
|
*/
|
||||||
|
public static boolean isPermanentFocusOwner( JScrollPane scrollPane ) {
|
||||||
|
JViewport viewport = scrollPane.getViewport();
|
||||||
|
Component view = (viewport != null) ? viewport.getView() : null;
|
||||||
|
if( view == null )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// check whether view is focus owner
|
||||||
|
if( FlatUIUtils.isPermanentFocusOwner( view ) )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// check whether editor component in JTable or JTree is focus owner
|
||||||
|
if( (view instanceof JTable && ((JTable)view).isEditing()) ||
|
||||||
|
(view instanceof JTree && ((JTree)view).isEditing()) )
|
||||||
|
{
|
||||||
|
Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
|
||||||
|
if( focusOwner != null )
|
||||||
|
return SwingUtilities.isDescendingFrom( focusOwner, view );
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//---- class Handler ------------------------------------------------------
|
//---- class Handler ------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -358,11 +378,13 @@ public class FlatScrollPaneUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void focusGained( FocusEvent e ) {
|
public void focusGained( FocusEvent e ) {
|
||||||
|
// necessary to update focus border
|
||||||
scrollpane.repaint();
|
scrollpane.repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void focusLost( FocusEvent e ) {
|
public void focusLost( FocusEvent e ) {
|
||||||
|
// necessary to update focus border
|
||||||
scrollpane.repaint();
|
scrollpane.repaint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,12 +52,8 @@ public class FlatSeparatorUI
|
|||||||
|
|
||||||
private boolean defaults_initialized = false;
|
private boolean defaults_initialized = false;
|
||||||
|
|
||||||
private static ComponentUI instance;
|
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
if( instance == null )
|
return FlatUIUtils.createSharedUI( FlatSeparatorUI.class, FlatSeparatorUI::new );
|
||||||
instance = new FlatSeparatorUI();
|
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -18,17 +18,23 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
|
import java.awt.FontMetrics;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.event.MouseListener;
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.Shape;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.geom.Ellipse2D;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
import java.awt.geom.RoundRectangle2D;
|
import java.awt.geom.RoundRectangle2D;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JSlider;
|
import javax.swing.JSlider;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicSliderUI;
|
import javax.swing.plaf.basic.BasicSliderUI;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,29 +55,49 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* <!-- FlatSliderUI -->
|
* <!-- FlatSliderUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault Slider.trackWidth int
|
* @uiDefault Slider.trackWidth int
|
||||||
* @uiDefault Slider.thumbWidth int
|
* @uiDefault Slider.thumbSize Dimension
|
||||||
|
* @uiDefault Slider.focusWidth int
|
||||||
|
* @uiDefault Slider.trackValueColor Color optional; defaults to Slider.thumbColor
|
||||||
* @uiDefault Slider.trackColor Color
|
* @uiDefault Slider.trackColor Color
|
||||||
* @uiDefault Slider.thumbColor Color
|
* @uiDefault Slider.thumbColor Color
|
||||||
|
* @uiDefault Slider.thumbBorderColor Color optional; if null, no border is painted
|
||||||
* @uiDefault Slider.focusedColor Color optional; defaults to Component.focusColor
|
* @uiDefault Slider.focusedColor Color optional; defaults to Component.focusColor
|
||||||
* @uiDefault Slider.hoverColor Color optional; defaults to Slider.focusedColor
|
* @uiDefault Slider.focusedThumbBorderColor Color optional; defaults to Component.focusedBorderColor
|
||||||
* @uiDefault Slider.disabledForeground Color used for track and thumb is disabled
|
* @uiDefault Slider.hoverThumbColor Color optional
|
||||||
|
* @uiDefault Slider.pressedThumbColor Color optional
|
||||||
|
* @uiDefault Slider.disabledTrackColor Color
|
||||||
|
* @uiDefault Slider.disabledThumbColor Color
|
||||||
|
* @uiDefault Slider.disabledThumbBorderColor Color optional; defaults to Component.disabledBorderColor
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatSliderUI
|
public class FlatSliderUI
|
||||||
extends BasicSliderUI
|
extends BasicSliderUI
|
||||||
{
|
{
|
||||||
private int trackWidth;
|
protected int trackWidth;
|
||||||
private int thumbWidth;
|
protected Dimension thumbSize;
|
||||||
|
protected int focusWidth;
|
||||||
|
|
||||||
private Color trackColor;
|
protected Color trackValueColor;
|
||||||
private Color thumbColor;
|
protected Color trackColor;
|
||||||
private Color focusColor;
|
protected Color thumbColor;
|
||||||
private Color hoverColor;
|
protected Color thumbBorderColor;
|
||||||
private Color disabledForeground;
|
protected Color focusBaseColor;
|
||||||
|
protected Color focusedColor;
|
||||||
|
protected Color focusedThumbBorderColor;
|
||||||
|
protected Color hoverThumbColor;
|
||||||
|
protected Color pressedThumbColor;
|
||||||
|
protected Color disabledTrackColor;
|
||||||
|
protected Color disabledThumbColor;
|
||||||
|
protected Color disabledThumbBorderColor;
|
||||||
|
|
||||||
private MouseListener hoverListener;
|
private Color defaultBackground;
|
||||||
private boolean hover;
|
private Color defaultForeground;
|
||||||
|
|
||||||
|
protected boolean thumbHover;
|
||||||
|
protected boolean thumbPressed;
|
||||||
|
|
||||||
|
private Object[] oldRenderingHints;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatSliderUI();
|
return new FlatSliderUI();
|
||||||
@@ -81,24 +107,6 @@ public class FlatSliderUI
|
|||||||
super( null );
|
super( null );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void installListeners( JSlider slider ) {
|
|
||||||
super.installListeners( slider );
|
|
||||||
|
|
||||||
hoverListener = new FlatUIUtils.HoverListener( slider, h -> {
|
|
||||||
hover = h;
|
|
||||||
} );
|
|
||||||
slider.addMouseListener( hoverListener );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void uninstallListeners( JSlider slider ) {
|
|
||||||
super.uninstallListeners( slider );
|
|
||||||
|
|
||||||
slider.removeMouseListener( hoverListener );
|
|
||||||
hoverListener = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults( JSlider slider ) {
|
protected void installDefaults( JSlider slider ) {
|
||||||
super.installDefaults( slider );
|
super.installDefaults( slider );
|
||||||
@@ -106,24 +114,71 @@ public class FlatSliderUI
|
|||||||
LookAndFeel.installProperty( slider, "opaque", false );
|
LookAndFeel.installProperty( slider, "opaque", false );
|
||||||
|
|
||||||
trackWidth = UIManager.getInt( "Slider.trackWidth" );
|
trackWidth = UIManager.getInt( "Slider.trackWidth" );
|
||||||
thumbWidth = UIManager.getInt( "Slider.thumbWidth" );
|
thumbSize = UIManager.getDimension( "Slider.thumbSize" );
|
||||||
|
if( thumbSize == null ) {
|
||||||
|
// fallback for compatibility with old versions
|
||||||
|
int thumbWidth = UIManager.getInt( "Slider.thumbWidth" );
|
||||||
|
thumbSize = new Dimension( thumbWidth, thumbWidth );
|
||||||
|
}
|
||||||
|
focusWidth = FlatUIUtils.getUIInt( "Slider.focusWidth", 4 );
|
||||||
|
|
||||||
|
trackValueColor = FlatUIUtils.getUIColor( "Slider.trackValueColor", "Slider.thumbColor" );
|
||||||
trackColor = UIManager.getColor( "Slider.trackColor" );
|
trackColor = UIManager.getColor( "Slider.trackColor" );
|
||||||
thumbColor = UIManager.getColor( "Slider.thumbColor" );
|
thumbColor = UIManager.getColor( "Slider.thumbColor" );
|
||||||
focusColor = FlatUIUtils.getUIColor( "Slider.focusedColor", "Component.focusColor" );
|
thumbBorderColor = UIManager.getColor( "Slider.thumbBorderColor" );
|
||||||
hoverColor = FlatUIUtils.getUIColor( "Slider.hoverColor", focusColor );
|
focusBaseColor = UIManager.getColor( "Component.focusColor" );
|
||||||
disabledForeground = UIManager.getColor( "Slider.disabledForeground" );
|
focusedColor = FlatUIUtils.getUIColor( "Slider.focusedColor", focusBaseColor );
|
||||||
|
focusedThumbBorderColor = FlatUIUtils.getUIColor( "Slider.focusedThumbBorderColor", "Component.focusedBorderColor" );
|
||||||
|
hoverThumbColor = UIManager.getColor( "Slider.hoverThumbColor" );
|
||||||
|
pressedThumbColor = UIManager.getColor( "Slider.pressedThumbColor" );
|
||||||
|
disabledTrackColor = UIManager.getColor( "Slider.disabledTrackColor" );
|
||||||
|
disabledThumbColor = UIManager.getColor( "Slider.disabledThumbColor" );
|
||||||
|
disabledThumbBorderColor = FlatUIUtils.getUIColor( "Slider.disabledThumbBorderColor", "Component.disabledBorderColor" );
|
||||||
|
|
||||||
|
defaultBackground = UIManager.getColor( "Slider.background" );
|
||||||
|
defaultForeground = UIManager.getColor( "Slider.foreground" );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void uninstallDefaults( JSlider slider ) {
|
protected void uninstallDefaults( JSlider slider ) {
|
||||||
super.uninstallDefaults( slider );
|
super.uninstallDefaults( slider );
|
||||||
|
|
||||||
|
trackValueColor = null;
|
||||||
trackColor = null;
|
trackColor = null;
|
||||||
thumbColor = null;
|
thumbColor = null;
|
||||||
focusColor = null;
|
thumbBorderColor = null;
|
||||||
hoverColor = null;
|
focusBaseColor = null;
|
||||||
disabledForeground = null;
|
focusedColor = null;
|
||||||
|
focusedThumbBorderColor = null;
|
||||||
|
hoverThumbColor = null;
|
||||||
|
pressedThumbColor = null;
|
||||||
|
disabledTrackColor = null;
|
||||||
|
disabledThumbColor = null;
|
||||||
|
disabledThumbBorderColor = null;
|
||||||
|
|
||||||
|
defaultBackground = null;
|
||||||
|
defaultForeground = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TrackListener createTrackListener( JSlider slider ) {
|
||||||
|
return new FlatTrackListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBaseline( JComponent c, int width, int height ) {
|
||||||
|
if( c == null )
|
||||||
|
throw new NullPointerException();
|
||||||
|
if( width < 0 || height < 0 )
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
|
||||||
|
// no baseline for vertical orientation
|
||||||
|
if( slider.getOrientation() == JSlider.VERTICAL )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// compute a baseline so that the track is vertically centered
|
||||||
|
FontMetrics fm = slider.getFontMetrics( slider.getFont() );
|
||||||
|
return trackRect.y + Math.round( (trackRect.height - fm.getHeight()) / 2f ) + fm.getAscent() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -153,14 +208,50 @@ public class FlatSliderUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Dimension getThumbSize() {
|
protected Dimension getThumbSize() {
|
||||||
return new Dimension( UIScale.scale( thumbWidth ), UIScale.scale( thumbWidth ) );
|
return calcThumbSize( slider, thumbSize, focusWidth );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dimension calcThumbSize( JSlider slider, Dimension thumbSize, int focusWidth ) {
|
||||||
|
int fw = UIScale.scale( focusWidth );
|
||||||
|
int w = UIScale.scale( thumbSize.width ) + fw + fw;
|
||||||
|
int h = UIScale.scale( thumbSize.height ) + fw + fw;
|
||||||
|
return (slider.getOrientation() == JSlider.HORIZONTAL)
|
||||||
|
? new Dimension( w, h )
|
||||||
|
: new Dimension( h, w );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paint( Graphics g, JComponent c ) {
|
public void paint( Graphics g, JComponent c ) {
|
||||||
FlatUIUtils.setRenderingHints( (Graphics2D) g );
|
oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
||||||
|
|
||||||
|
/*debug
|
||||||
|
g.setColor( Color.gray );
|
||||||
|
g.drawRect( 0, 0, c.getWidth() - 1, c.getHeight() - 1 );
|
||||||
|
g.setColor( Color.orange );
|
||||||
|
g.drawRect( focusRect.x, focusRect.y, focusRect.width - 1, focusRect.height - 1 );
|
||||||
|
g.setColor( Color.magenta );
|
||||||
|
g.drawRect( contentRect.x, contentRect.y, contentRect.width - 1, contentRect.height - 1 );
|
||||||
|
g.setColor( Color.blue );
|
||||||
|
g.drawRect( trackRect.x, trackRect.y, trackRect.width - 1, trackRect.height - 1 );
|
||||||
|
g.setColor( Color.red );
|
||||||
|
g.drawRect( thumbRect.x, thumbRect.y, thumbRect.width - 1, thumbRect.height - 1 );
|
||||||
|
g.setColor( Color.green );
|
||||||
|
g.drawRect( tickRect.x, tickRect.y, tickRect.width - 1, tickRect.height - 1 );
|
||||||
|
g.setColor( Color.red );
|
||||||
|
g.drawRect( labelRect.x, labelRect.y, labelRect.width - 1, labelRect.height - 1 );
|
||||||
|
debug*/
|
||||||
|
|
||||||
super.paint( g, c );
|
super.paint( g, c );
|
||||||
|
|
||||||
|
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||||
|
oldRenderingHints = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paintLabels( Graphics g ) {
|
||||||
|
FlatUIUtils.runWithoutRenderingHints( g, oldRenderingHints, () -> {
|
||||||
|
super.paintLabels( g );
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -201,50 +292,326 @@ public class FlatSliderUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
if( coloredTrack != null ) {
|
if( coloredTrack != null ) {
|
||||||
g.setColor( FlatUIUtils.deriveColor( FlatUIUtils.isPermanentFocusOwner( slider ) ? focusColor : (hover ? hoverColor : thumbColor), thumbColor ) );
|
if( slider.getInverted() ) {
|
||||||
|
RoundRectangle2D temp = track;
|
||||||
|
track = coloredTrack;
|
||||||
|
coloredTrack = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
g.setColor( getTrackValueColor() );
|
||||||
((Graphics2D)g).fill( coloredTrack );
|
((Graphics2D)g).fill( coloredTrack );
|
||||||
}
|
}
|
||||||
|
|
||||||
g.setColor( enabled ? trackColor : disabledForeground );
|
g.setColor( enabled ? getTrackColor() : disabledTrackColor );
|
||||||
((Graphics2D)g).fill( track );
|
((Graphics2D)g).fill( track );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paintThumb( Graphics g ) {
|
public void paintThumb( Graphics g ) {
|
||||||
g.setColor( FlatUIUtils.deriveColor( slider.isEnabled()
|
Color thumbColor = getThumbColor();
|
||||||
? (FlatUIUtils.isPermanentFocusOwner( slider ) ? focusColor : (hover ? hoverColor : thumbColor))
|
Color color = stateColor( slider, thumbHover, thumbPressed,
|
||||||
: disabledForeground,
|
thumbColor, disabledThumbColor, null, hoverThumbColor, pressedThumbColor );
|
||||||
thumbColor ) );
|
color = FlatUIUtils.deriveColor( color, thumbColor );
|
||||||
|
|
||||||
if( isRoundThumb() )
|
Color foreground = slider.getForeground();
|
||||||
g.fillOval( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height );
|
Color borderColor = (thumbBorderColor != null && foreground == defaultForeground)
|
||||||
else {
|
? stateColor( slider, false, false, thumbBorderColor, disabledThumbBorderColor, focusedThumbBorderColor, null, null )
|
||||||
double w = thumbRect.width;
|
: null;
|
||||||
double h = thumbRect.height;
|
|
||||||
double wh = w / 2;
|
|
||||||
|
|
||||||
Path2D thumb = FlatUIUtils.createPath( 0,0, w,0, w,(h - wh), wh,h, 0,(h - wh) );
|
Color focusedColor = FlatUIUtils.deriveColor( this.focusedColor,
|
||||||
|
(foreground != defaultForeground) ? foreground : focusBaseColor );
|
||||||
|
|
||||||
|
paintThumb( g, slider, thumbRect, isRoundThumb(), color, borderColor, focusedColor, focusWidth );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void paintThumb( Graphics g, JSlider slider, Rectangle thumbRect, boolean roundThumb,
|
||||||
|
Color thumbColor, Color thumbBorderColor, Color focusedColor, int focusWidth )
|
||||||
|
{
|
||||||
|
double systemScaleFactor = UIScale.getSystemScaleFactor( (Graphics2D) g );
|
||||||
|
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
|
||||||
|
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
|
||||||
|
HiDPIUtils.paintAtScale1x( (Graphics2D) g, thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height,
|
||||||
|
(g2d, x2, y2, width2, height2, scaleFactor) -> {
|
||||||
|
paintThumbImpl( g, slider, x2, y2, width2, height2,
|
||||||
|
roundThumb, thumbColor, thumbBorderColor, focusedColor,
|
||||||
|
(float) (focusWidth * scaleFactor) );
|
||||||
|
} );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
paintThumbImpl( g, slider, thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height,
|
||||||
|
roundThumb, thumbColor, thumbBorderColor, focusedColor, focusWidth );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void paintThumbImpl( Graphics g, JSlider slider, int x, int y, int width, int height,
|
||||||
|
boolean roundThumb, Color thumbColor, Color thumbBorderColor, Color focusedColor, float focusWidth )
|
||||||
|
{
|
||||||
|
int fw = Math.round( UIScale.scale( focusWidth ) );
|
||||||
|
int tx = x + fw;
|
||||||
|
int ty = y + fw;
|
||||||
|
int tw = width - fw - fw;
|
||||||
|
int th = height - fw - fw;
|
||||||
|
boolean focused = FlatUIUtils.isPermanentFocusOwner( slider );
|
||||||
|
|
||||||
|
if( roundThumb ) {
|
||||||
|
// paint thumb focus border
|
||||||
|
if( focused ) {
|
||||||
|
g.setColor( focusedColor );
|
||||||
|
((Graphics2D)g).fill( createRoundThumbShape( x, y, width, height ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( thumbBorderColor != null ) {
|
||||||
|
// paint thumb border
|
||||||
|
g.setColor( thumbBorderColor );
|
||||||
|
((Graphics2D)g).fill( createRoundThumbShape( tx, ty, tw, th ) );
|
||||||
|
|
||||||
|
// paint thumb background
|
||||||
|
float lw = UIScale.scale( 1f );
|
||||||
|
g.setColor( thumbColor );
|
||||||
|
((Graphics2D)g).fill( createRoundThumbShape( tx + lw, ty + lw,
|
||||||
|
tw - lw - lw, th - lw - lw ) );
|
||||||
|
} else {
|
||||||
|
// paint thumb background
|
||||||
|
g.setColor( thumbColor );
|
||||||
|
((Graphics2D)g).fill( createRoundThumbShape( tx, ty, tw, th ) );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
Graphics2D g2 = (Graphics2D) g.create();
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
try {
|
try {
|
||||||
g2.translate( thumbRect.x, thumbRect.y );
|
g2.translate( x, y );
|
||||||
if( slider.getOrientation() == JSlider.VERTICAL ) {
|
if( slider.getOrientation() == JSlider.VERTICAL ) {
|
||||||
if( slider.getComponentOrientation().isLeftToRight() ) {
|
if( slider.getComponentOrientation().isLeftToRight() ) {
|
||||||
g2.translate( 0, thumbRect.height );
|
g2.translate( 0, height );
|
||||||
g2.rotate( Math.toRadians( 270 ) );
|
g2.rotate( Math.toRadians( 270 ) );
|
||||||
} else {
|
} else {
|
||||||
g2.translate( thumbRect.width, 0 );
|
g2.translate( width, 0 );
|
||||||
g2.rotate( Math.toRadians( 90 ) );
|
g2.rotate( Math.toRadians( 90 ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rotate thumb width/height
|
||||||
|
int temp = tw;
|
||||||
|
tw = th;
|
||||||
|
th = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// paint thumb focus border
|
||||||
|
if( focused ) {
|
||||||
|
g2.setColor( focusedColor );
|
||||||
|
g2.fill( createDirectionalThumbShape( 0, 0,
|
||||||
|
tw + fw + fw, th + fw + fw + (fw * 0.4142f), fw ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( thumbBorderColor != null ) {
|
||||||
|
// paint thumb border
|
||||||
|
g2.setColor( thumbBorderColor );
|
||||||
|
g2.fill( createDirectionalThumbShape( fw, fw, tw, th, 0 ) );
|
||||||
|
|
||||||
|
// paint thumb background
|
||||||
|
float lw = UIScale.scale( 1f );
|
||||||
|
g2.setColor( thumbColor );
|
||||||
|
g2.fill( createDirectionalThumbShape( fw + lw, fw + lw,
|
||||||
|
tw - lw - lw, th - lw - lw - (lw * 0.4142f), 0 ) );
|
||||||
|
} else {
|
||||||
|
// paint thumb background
|
||||||
|
g2.setColor( thumbColor );
|
||||||
|
g2.fill( createDirectionalThumbShape( fw, fw, tw, th, 0 ) );
|
||||||
}
|
}
|
||||||
g2.fill( thumb );
|
|
||||||
} finally {
|
} finally {
|
||||||
g2.dispose();
|
g2.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isRoundThumb() {
|
public static Shape createRoundThumbShape( float x, float y, float w, float h ) {
|
||||||
|
if( w == h )
|
||||||
|
return new Ellipse2D.Float( x, y, w, h );
|
||||||
|
else {
|
||||||
|
float arc = Math.min( w, h );
|
||||||
|
return new RoundRectangle2D.Float( x, y, w, h, arc, arc );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Shape createDirectionalThumbShape( float x, float y, float w, float h, float arc ) {
|
||||||
|
float wh = w / 2;
|
||||||
|
|
||||||
|
Path2D path = new Path2D.Float();
|
||||||
|
path.moveTo( x + wh, y + h );
|
||||||
|
path.lineTo( x, y + (h - wh) );
|
||||||
|
path.lineTo( x, y + arc );
|
||||||
|
path.quadTo( x, y, x + arc, y );
|
||||||
|
path.lineTo( x + (w - arc), y );
|
||||||
|
path.quadTo( x + w, y, x + w, y + arc );
|
||||||
|
path.lineTo( x + w, y + (h - wh) );
|
||||||
|
path.closePath();
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Color getTrackValueColor() {
|
||||||
|
Color foreground = slider.getForeground();
|
||||||
|
return (foreground != defaultForeground) ? foreground : trackValueColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Color getTrackColor() {
|
||||||
|
Color backround = slider.getBackground();
|
||||||
|
return (backround != defaultBackground) ? backround : trackColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Color getThumbColor() {
|
||||||
|
Color foreground = slider.getForeground();
|
||||||
|
return (foreground != defaultForeground) ? foreground : thumbColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Color stateColor( JSlider slider, boolean hover, boolean pressed,
|
||||||
|
Color enabledColor, Color disabledColor, Color focusedColor, Color hoverColor, Color pressedColor )
|
||||||
|
{
|
||||||
|
if( disabledColor != null && !slider.isEnabled() )
|
||||||
|
return disabledColor;
|
||||||
|
if( pressedColor != null && pressed )
|
||||||
|
return pressedColor;
|
||||||
|
if( hoverColor != null && hover )
|
||||||
|
return hoverColor;
|
||||||
|
if( focusedColor != null && FlatUIUtils.isPermanentFocusOwner( slider ) )
|
||||||
|
return focusedColor;
|
||||||
|
return enabledColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isRoundThumb() {
|
||||||
return !slider.getPaintTicks() && !slider.getPaintLabels();
|
return !slider.getPaintTicks() && !slider.getPaintLabels();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setThumbLocation( int x, int y ) {
|
||||||
|
if( !isRoundThumb() ) {
|
||||||
|
// the needle of the directional thumb is painted outside of thumbRect
|
||||||
|
// --> must increase repaint rectangle
|
||||||
|
|
||||||
|
// set new thumb location and compute union of old and new thumb bounds
|
||||||
|
Rectangle r = new Rectangle( thumbRect );
|
||||||
|
thumbRect.setLocation( x, y );
|
||||||
|
SwingUtilities.computeUnion( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, r );
|
||||||
|
|
||||||
|
// increase union rectangle for repaint
|
||||||
|
int extra = (int) Math.ceil( UIScale.scale( focusWidth ) * 0.4142f );
|
||||||
|
if( slider.getOrientation() == JSlider.HORIZONTAL )
|
||||||
|
r.height += extra;
|
||||||
|
else {
|
||||||
|
r.width += extra;
|
||||||
|
if( !slider.getComponentOrientation().isLeftToRight() )
|
||||||
|
r.x -= extra;
|
||||||
|
}
|
||||||
|
|
||||||
|
slider.repaint( r );
|
||||||
|
} else
|
||||||
|
super.setThumbLocation( x, y );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class FlatTrackListener --------------------------------------------
|
||||||
|
|
||||||
|
protected class FlatTrackListener
|
||||||
|
extends TrackListener
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void mouseEntered( MouseEvent e ) {
|
||||||
|
setThumbHover( isOverThumb( e ) );
|
||||||
|
super.mouseEntered( e );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseExited( MouseEvent e ) {
|
||||||
|
setThumbHover( false );
|
||||||
|
super.mouseExited( e );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseMoved( MouseEvent e ) {
|
||||||
|
setThumbHover( isOverThumb( e ) );
|
||||||
|
super.mouseMoved( e );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mousePressed( MouseEvent e ) {
|
||||||
|
setThumbPressed( isOverThumb( e ) );
|
||||||
|
|
||||||
|
if( !slider.isEnabled() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// use "old" behavior when clicking on track
|
||||||
|
if( UIManager.getBoolean( "Slider.scrollOnTrackClick" ) ) {
|
||||||
|
super.mousePressed( e );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "new" behavior set thumb to mouse location when clicking on track
|
||||||
|
|
||||||
|
int x = e.getX();
|
||||||
|
int y = e.getY();
|
||||||
|
|
||||||
|
// clicked on thumb --> let super class do the work
|
||||||
|
calculateGeometry();
|
||||||
|
if( thumbRect.contains( x, y ) ) {
|
||||||
|
super.mousePressed( e );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( UIManager.getBoolean( "Slider.onlyLeftMouseButtonDrag" ) &&
|
||||||
|
!SwingUtilities.isLeftMouseButton( e ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// move the mouse event coordinates to the center of the thumb
|
||||||
|
int tx = thumbRect.x + (thumbRect.width / 2) - x;
|
||||||
|
int ty = thumbRect.y + (thumbRect.height / 2) - y;
|
||||||
|
e.translatePoint( tx, ty );
|
||||||
|
|
||||||
|
// invoke super mousePressed() to start dragging thumb
|
||||||
|
super.mousePressed( e );
|
||||||
|
|
||||||
|
// move the mouse event coordinates back to current mouse location
|
||||||
|
e.translatePoint( -tx, -ty );
|
||||||
|
|
||||||
|
// invoke mouseDragged() to update thumb location
|
||||||
|
mouseDragged( e );
|
||||||
|
|
||||||
|
setThumbPressed( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseReleased( MouseEvent e ) {
|
||||||
|
setThumbPressed( false );
|
||||||
|
super.mouseReleased( e );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseDragged( MouseEvent e ) {
|
||||||
|
super.mouseDragged( e );
|
||||||
|
|
||||||
|
if( isDragging() &&
|
||||||
|
slider.getSnapToTicks() &&
|
||||||
|
slider.isEnabled() &&
|
||||||
|
!UIManager.getBoolean( "Slider.snapToTicksOnReleased" ) )
|
||||||
|
{
|
||||||
|
calculateThumbLocation();
|
||||||
|
slider.repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setThumbHover( boolean hover ) {
|
||||||
|
if( hover != thumbHover ) {
|
||||||
|
thumbHover = hover;
|
||||||
|
slider.repaint( thumbRect );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setThumbPressed( boolean pressed ) {
|
||||||
|
if( pressed != thumbPressed ) {
|
||||||
|
thumbPressed = pressed;
|
||||||
|
slider.repaint( thumbRect );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isOverThumb( MouseEvent e ) {
|
||||||
|
return e != null && slider.isEnabled() && thumbRect.contains( e.getX(), e.getY() );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import javax.swing.LookAndFeel;
|
|||||||
import javax.swing.SwingConstants;
|
import javax.swing.SwingConstants;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicSpinnerUI;
|
import javax.swing.plaf.basic.BasicSpinnerUI;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
|
||||||
@@ -59,16 +60,18 @@ import com.formdev.flatlaf.FlatClientProperties;
|
|||||||
*
|
*
|
||||||
* @uiDefault Component.minimumWidth int
|
* @uiDefault Component.minimumWidth int
|
||||||
* @uiDefault Spinner.buttonStyle String button (default) or none
|
* @uiDefault Spinner.buttonStyle String button (default) or none
|
||||||
* @uiDefault Component.arrowType String triangle (default) or chevron
|
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
* @uiDefault Component.isIntelliJTheme boolean
|
||||||
* @uiDefault Component.borderColor Color
|
* @uiDefault Component.borderColor Color
|
||||||
* @uiDefault Component.disabledBorderColor Color
|
* @uiDefault Component.disabledBorderColor Color
|
||||||
* @uiDefault Spinner.disabledBackground Color
|
* @uiDefault Spinner.disabledBackground Color
|
||||||
* @uiDefault Spinner.disabledForeground Color
|
* @uiDefault Spinner.disabledForeground Color
|
||||||
|
* @uiDefault Spinner.focusedBackground Color optional
|
||||||
* @uiDefault Spinner.buttonBackground Color
|
* @uiDefault Spinner.buttonBackground Color
|
||||||
* @uiDefault Spinner.buttonArrowColor Color
|
* @uiDefault Spinner.buttonArrowColor Color
|
||||||
* @uiDefault Spinner.buttonDisabledArrowColor Color
|
* @uiDefault Spinner.buttonDisabledArrowColor Color
|
||||||
* @uiDefault Spinner.buttonHoverArrowColor Color
|
* @uiDefault Spinner.buttonHoverArrowColor Color
|
||||||
|
* @uiDefault Spinner.buttonPressedArrowColor Color
|
||||||
* @uiDefault Spinner.padding Insets
|
* @uiDefault Spinner.padding Insets
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
@@ -86,10 +89,12 @@ public class FlatSpinnerUI
|
|||||||
protected Color disabledBorderColor;
|
protected Color disabledBorderColor;
|
||||||
protected Color disabledBackground;
|
protected Color disabledBackground;
|
||||||
protected Color disabledForeground;
|
protected Color disabledForeground;
|
||||||
|
protected Color focusedBackground;
|
||||||
protected Color buttonBackground;
|
protected Color buttonBackground;
|
||||||
protected Color buttonArrowColor;
|
protected Color buttonArrowColor;
|
||||||
protected Color buttonDisabledArrowColor;
|
protected Color buttonDisabledArrowColor;
|
||||||
protected Color buttonHoverArrowColor;
|
protected Color buttonHoverArrowColor;
|
||||||
|
protected Color buttonPressedArrowColor;
|
||||||
protected Insets padding;
|
protected Insets padding;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
@@ -110,10 +115,12 @@ public class FlatSpinnerUI
|
|||||||
disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
|
disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
|
||||||
disabledBackground = UIManager.getColor( "Spinner.disabledBackground" );
|
disabledBackground = UIManager.getColor( "Spinner.disabledBackground" );
|
||||||
disabledForeground = UIManager.getColor( "Spinner.disabledForeground" );
|
disabledForeground = UIManager.getColor( "Spinner.disabledForeground" );
|
||||||
|
focusedBackground = UIManager.getColor( "Spinner.focusedBackground" );
|
||||||
buttonBackground = UIManager.getColor( "Spinner.buttonBackground" );
|
buttonBackground = UIManager.getColor( "Spinner.buttonBackground" );
|
||||||
buttonArrowColor = UIManager.getColor( "Spinner.buttonArrowColor" );
|
buttonArrowColor = UIManager.getColor( "Spinner.buttonArrowColor" );
|
||||||
buttonDisabledArrowColor = UIManager.getColor( "Spinner.buttonDisabledArrowColor" );
|
buttonDisabledArrowColor = UIManager.getColor( "Spinner.buttonDisabledArrowColor" );
|
||||||
buttonHoverArrowColor = UIManager.getColor( "Spinner.buttonHoverArrowColor" );
|
buttonHoverArrowColor = UIManager.getColor( "Spinner.buttonHoverArrowColor" );
|
||||||
|
buttonPressedArrowColor = UIManager.getColor( "Spinner.buttonPressedArrowColor" );
|
||||||
padding = UIManager.getInsets( "Spinner.padding" );
|
padding = UIManager.getInsets( "Spinner.padding" );
|
||||||
|
|
||||||
// scale
|
// scale
|
||||||
@@ -130,10 +137,12 @@ public class FlatSpinnerUI
|
|||||||
disabledBorderColor = null;
|
disabledBorderColor = null;
|
||||||
disabledBackground = null;
|
disabledBackground = null;
|
||||||
disabledForeground = null;
|
disabledForeground = null;
|
||||||
|
focusedBackground = null;
|
||||||
buttonBackground = null;
|
buttonBackground = null;
|
||||||
buttonArrowColor = null;
|
buttonArrowColor = null;
|
||||||
buttonDisabledArrowColor = null;
|
buttonDisabledArrowColor = null;
|
||||||
buttonHoverArrowColor = null;
|
buttonHoverArrowColor = null;
|
||||||
|
buttonPressedArrowColor = null;
|
||||||
padding = null;
|
padding = null;
|
||||||
|
|
||||||
MigLayoutVisualPadding.uninstall( spinner );
|
MigLayoutVisualPadding.uninstall( spinner );
|
||||||
@@ -217,10 +226,34 @@ public class FlatSpinnerUI
|
|||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.3
|
||||||
|
*/
|
||||||
|
public static boolean isPermanentFocusOwner( JSpinner spinner ) {
|
||||||
|
if( FlatUIUtils.isPermanentFocusOwner( spinner ) )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
JTextField textField = getEditorTextField( spinner.getEditor() );
|
||||||
|
return (textField != null)
|
||||||
|
? FlatUIUtils.isPermanentFocusOwner( textField )
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
|
||||||
protected Color getBackground( boolean enabled ) {
|
protected Color getBackground( boolean enabled ) {
|
||||||
return enabled
|
if( enabled ) {
|
||||||
? spinner.getBackground()
|
Color background = spinner.getBackground();
|
||||||
: (isIntelliJTheme ? FlatUIUtils.getParentBackground( spinner ) : disabledBackground);
|
|
||||||
|
// always use explicitly set color
|
||||||
|
if( !(background instanceof UIResource) )
|
||||||
|
return background;
|
||||||
|
|
||||||
|
// focused
|
||||||
|
if( focusedBackground != null && isPermanentFocusOwner( spinner ) )
|
||||||
|
return focusedBackground;
|
||||||
|
|
||||||
|
return background;
|
||||||
|
} else
|
||||||
|
return isIntelliJTheme ? FlatUIUtils.getParentBackground( spinner ) : disabledBackground;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Color getForeground( boolean enabled ) {
|
protected Color getForeground( boolean enabled ) {
|
||||||
@@ -244,9 +277,9 @@ public class FlatSpinnerUI
|
|||||||
|
|
||||||
private Component createArrowButton( int direction, String name ) {
|
private Component createArrowButton( int direction, String name ) {
|
||||||
FlatArrowButton button = new FlatArrowButton( direction, arrowType, buttonArrowColor,
|
FlatArrowButton button = new FlatArrowButton( direction, arrowType, buttonArrowColor,
|
||||||
buttonDisabledArrowColor, buttonHoverArrowColor, null );
|
buttonDisabledArrowColor, buttonHoverArrowColor, null, buttonPressedArrowColor, null );
|
||||||
button.setName( name );
|
button.setName( name );
|
||||||
button.setYOffset( (direction == SwingConstants.NORTH) ? 1 : -1 );
|
button.setYOffset( (direction == SwingConstants.NORTH) ? 1.25f : -1.25f );
|
||||||
if( direction == SwingConstants.NORTH )
|
if( direction == SwingConstants.NORTH )
|
||||||
installNextButtonListeners( button );
|
installNextButtonListeners( button );
|
||||||
else
|
else
|
||||||
@@ -264,23 +297,27 @@ public class FlatSpinnerUI
|
|||||||
FlatUIUtils.paintParentBackground( g, c );
|
FlatUIUtils.paintParentBackground( g, c );
|
||||||
|
|
||||||
Graphics2D g2 = (Graphics2D) g;
|
Graphics2D g2 = (Graphics2D) g;
|
||||||
FlatUIUtils.setRenderingHints( g2 );
|
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g2 );
|
||||||
|
|
||||||
int width = c.getWidth();
|
int width = c.getWidth();
|
||||||
int height = c.getHeight();
|
int height = c.getHeight();
|
||||||
Component nextButton = getHandler().nextButton;
|
|
||||||
int arrowX = nextButton.getX();
|
|
||||||
int arrowWidth = nextButton.getWidth();
|
|
||||||
boolean paintButton = !"none".equals( buttonStyle );
|
|
||||||
boolean enabled = spinner.isEnabled();
|
boolean enabled = spinner.isEnabled();
|
||||||
boolean isLeftToRight = spinner.getComponentOrientation().isLeftToRight();
|
|
||||||
|
|
||||||
// paint background
|
// paint background
|
||||||
g2.setColor( getBackground( enabled ) );
|
g2.setColor( getBackground( enabled ) );
|
||||||
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
|
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
|
||||||
|
|
||||||
|
// paint button background and separator
|
||||||
|
boolean paintButton = !"none".equals( buttonStyle );
|
||||||
|
Handler handler = getHandler();
|
||||||
|
if( paintButton && (handler.nextButton != null || handler.previousButton != null) ) {
|
||||||
|
Component button = (handler.nextButton != null) ? handler.nextButton : handler.previousButton;
|
||||||
|
int arrowX = button.getX();
|
||||||
|
int arrowWidth = button.getWidth();
|
||||||
|
boolean isLeftToRight = spinner.getComponentOrientation().isLeftToRight();
|
||||||
|
|
||||||
// paint arrow buttons background
|
// paint arrow buttons background
|
||||||
if( paintButton && enabled ) {
|
if( enabled ) {
|
||||||
g2.setColor( buttonBackground );
|
g2.setColor( buttonBackground );
|
||||||
Shape oldClip = g2.getClip();
|
Shape oldClip = g2.getClip();
|
||||||
if( isLeftToRight )
|
if( isLeftToRight )
|
||||||
@@ -292,7 +329,6 @@ public class FlatSpinnerUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// paint vertical line between value and arrow buttons
|
// paint vertical line between value and arrow buttons
|
||||||
if( paintButton ) {
|
|
||||||
g2.setColor( enabled ? borderColor : disabledBorderColor );
|
g2.setColor( enabled ? borderColor : disabledBorderColor );
|
||||||
float lw = scale( 1f );
|
float lw = scale( 1f );
|
||||||
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
|
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
|
||||||
@@ -300,6 +336,8 @@ public class FlatSpinnerUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
paint( g, c );
|
paint( g, c );
|
||||||
|
|
||||||
|
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class Handler ------------------------------------------------------
|
//---- class Handler ------------------------------------------------------
|
||||||
@@ -359,15 +397,15 @@ public class FlatSpinnerUI
|
|||||||
|
|
||||||
if( nextButton == null && previousButton == null ) {
|
if( nextButton == null && previousButton == null ) {
|
||||||
if( editor != null )
|
if( editor != null )
|
||||||
editor.setBounds( r );
|
editor.setBounds( FlatUIUtils.subtractInsets( r, padding ) );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle editorRect = new Rectangle( r );
|
Rectangle editorRect = new Rectangle( r );
|
||||||
Rectangle buttonsRect = new Rectangle( r );
|
Rectangle buttonsRect = new Rectangle( r );
|
||||||
|
|
||||||
// make button area square
|
// make button area square (if spinner has preferred height)
|
||||||
int buttonsWidth = r.height;
|
int buttonsWidth = parent.getPreferredSize().height - insets.top - insets.bottom;
|
||||||
buttonsRect.width = buttonsWidth;
|
buttonsRect.width = buttonsWidth;
|
||||||
|
|
||||||
if( parent.getComponentOrientation().isLeftToRight() ) {
|
if( parent.getComponentOrientation().isLeftToRight() ) {
|
||||||
@@ -396,6 +434,7 @@ public class FlatSpinnerUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void focusGained( FocusEvent e ) {
|
public void focusGained( FocusEvent e ) {
|
||||||
|
// necessary to update focus border
|
||||||
spinner.repaint();
|
spinner.repaint();
|
||||||
|
|
||||||
// if spinner gained focus, transfer it to the editor text field
|
// if spinner gained focus, transfer it to the editor text field
|
||||||
@@ -408,6 +447,7 @@ public class FlatSpinnerUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void focusLost( FocusEvent e ) {
|
public void focusLost( FocusEvent e ) {
|
||||||
|
// necessary to update focus border
|
||||||
spinner.repaint();
|
spinner.repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,11 +17,17 @@
|
|||||||
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.Cursor;
|
import java.awt.Cursor;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Insets;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JSplitPane;
|
import javax.swing.JSplitPane;
|
||||||
import javax.swing.SwingConstants;
|
import javax.swing.SwingConstants;
|
||||||
|
import javax.swing.ToolTipManager;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicSplitPaneDivider;
|
import javax.swing.plaf.basic.BasicSplitPaneDivider;
|
||||||
@@ -40,12 +46,21 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault SplitPaneDivider.border Border
|
* @uiDefault SplitPaneDivider.border Border
|
||||||
* @uiDefault SplitPaneDivider.draggingColor Color only used if continuousLayout is false
|
* @uiDefault SplitPaneDivider.draggingColor Color only used if continuousLayout is false
|
||||||
*
|
*
|
||||||
|
* <!-- JSplitPane -->
|
||||||
|
*
|
||||||
|
* @uiDefault SplitPane.continuousLayout boolean
|
||||||
|
*
|
||||||
* <!-- FlatSplitPaneUI -->
|
* <!-- FlatSplitPaneUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault Component.arrowType String triangle (default) or chevron
|
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||||
* @uiDefault SplitPane.continuousLayout boolean
|
|
||||||
* @uiDefault SplitPaneDivider.oneTouchArrowColor Color
|
* @uiDefault SplitPaneDivider.oneTouchArrowColor Color
|
||||||
* @uiDefault SplitPaneDivider.oneTouchHoverArrowColor Color
|
* @uiDefault SplitPaneDivider.oneTouchHoverArrowColor Color
|
||||||
|
* @uiDefault SplitPaneDivider.oneTouchPressedArrowColor Color
|
||||||
|
* @uiDefault SplitPaneDivider.style String grip (default) or plain
|
||||||
|
* @uiDefault SplitPaneDivider.gripColor Color
|
||||||
|
* @uiDefault SplitPaneDivider.gripDotCount int
|
||||||
|
* @uiDefault SplitPaneDivider.gripDotSize int
|
||||||
|
* @uiDefault SplitPaneDivider.gripGap int
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
@@ -53,9 +68,9 @@ public class FlatSplitPaneUI
|
|||||||
extends BasicSplitPaneUI
|
extends BasicSplitPaneUI
|
||||||
{
|
{
|
||||||
protected String arrowType;
|
protected String arrowType;
|
||||||
private Boolean continuousLayout;
|
protected Color oneTouchArrowColor;
|
||||||
private Color oneTouchArrowColor;
|
protected Color oneTouchHoverArrowColor;
|
||||||
private Color oneTouchHoverArrowColor;
|
protected Color oneTouchPressedArrowColor;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatSplitPaneUI();
|
return new FlatSplitPaneUI();
|
||||||
@@ -69,15 +84,18 @@ public class FlatSplitPaneUI
|
|||||||
// used in there on LaF switching
|
// used in there on LaF switching
|
||||||
oneTouchArrowColor = UIManager.getColor( "SplitPaneDivider.oneTouchArrowColor" );
|
oneTouchArrowColor = UIManager.getColor( "SplitPaneDivider.oneTouchArrowColor" );
|
||||||
oneTouchHoverArrowColor = UIManager.getColor( "SplitPaneDivider.oneTouchHoverArrowColor" );
|
oneTouchHoverArrowColor = UIManager.getColor( "SplitPaneDivider.oneTouchHoverArrowColor" );
|
||||||
|
oneTouchPressedArrowColor = UIManager.getColor( "SplitPaneDivider.oneTouchPressedArrowColor" );
|
||||||
|
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
continuousLayout = (Boolean) UIManager.get( "SplitPane.continuousLayout" );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isContinuousLayout() {
|
protected void uninstallDefaults() {
|
||||||
return super.isContinuousLayout() || (continuousLayout != null && Boolean.TRUE.equals( continuousLayout ));
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
oneTouchArrowColor = null;
|
||||||
|
oneTouchHoverArrowColor = null;
|
||||||
|
oneTouchPressedArrowColor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -90,8 +108,16 @@ public class FlatSplitPaneUI
|
|||||||
protected class FlatSplitPaneDivider
|
protected class FlatSplitPaneDivider
|
||||||
extends BasicSplitPaneDivider
|
extends BasicSplitPaneDivider
|
||||||
{
|
{
|
||||||
|
protected final String style = UIManager.getString( "SplitPaneDivider.style" );
|
||||||
|
protected final Color gripColor = UIManager.getColor( "SplitPaneDivider.gripColor" );
|
||||||
|
protected final int gripDotCount = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotCount", 3 );
|
||||||
|
protected final int gripDotSize = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotSize", 3 );
|
||||||
|
protected final int gripGap = FlatUIUtils.getUIInt( "SplitPaneDivider.gripGap", 2 );
|
||||||
|
|
||||||
protected FlatSplitPaneDivider( BasicSplitPaneUI ui ) {
|
protected FlatSplitPaneDivider( BasicSplitPaneUI ui ) {
|
||||||
super( ui );
|
super( ui );
|
||||||
|
|
||||||
|
setLayout( new FlatDividerLayout() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -109,16 +135,67 @@ public class FlatSplitPaneUI
|
|||||||
return new FlatOneTouchButton( false );
|
return new FlatOneTouchButton( false );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
super.propertyChange( e );
|
||||||
|
|
||||||
|
switch( e.getPropertyName() ) {
|
||||||
|
case JSplitPane.DIVIDER_LOCATION_PROPERTY:
|
||||||
|
// necessary to show/hide one-touch buttons on expand/collapse
|
||||||
|
revalidate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paint( Graphics g ) {
|
||||||
|
super.paint( g );
|
||||||
|
|
||||||
|
if( "plain".equals( style ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
||||||
|
|
||||||
|
g.setColor( gripColor );
|
||||||
|
paintGrip( g, 0, 0, getWidth(), getHeight() );
|
||||||
|
|
||||||
|
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void paintGrip( Graphics g, int x, int y, int width, int height ) {
|
||||||
|
FlatUIUtils.paintGrip( g, x, y, width, height,
|
||||||
|
splitPane.getOrientation() == JSplitPane.VERTICAL_SPLIT,
|
||||||
|
gripDotCount, gripDotSize, gripGap, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isLeftCollapsed() {
|
||||||
|
int location = splitPane.getDividerLocation();
|
||||||
|
Insets insets = splitPane.getInsets();
|
||||||
|
return (orientation == JSplitPane.VERTICAL_SPLIT)
|
||||||
|
? location == insets.top
|
||||||
|
: location == insets.left;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isRightCollapsed() {
|
||||||
|
int location = splitPane.getDividerLocation();
|
||||||
|
Insets insets = splitPane.getInsets();
|
||||||
|
return (orientation == JSplitPane.VERTICAL_SPLIT)
|
||||||
|
? location == (splitPane.getHeight() - getHeight() - insets.bottom)
|
||||||
|
: location == (splitPane.getWidth() - getWidth() - insets.right);
|
||||||
|
}
|
||||||
|
|
||||||
//---- class FlatOneTouchButton ---------------------------------------
|
//---- class FlatOneTouchButton ---------------------------------------
|
||||||
|
|
||||||
private class FlatOneTouchButton
|
protected class FlatOneTouchButton
|
||||||
extends FlatArrowButton
|
extends FlatArrowButton
|
||||||
{
|
{
|
||||||
private final boolean left;
|
protected final boolean left;
|
||||||
|
|
||||||
public FlatOneTouchButton( boolean left ) {
|
protected FlatOneTouchButton( boolean left ) {
|
||||||
super( SwingConstants.NORTH, arrowType, oneTouchArrowColor, null, oneTouchHoverArrowColor, null );
|
super( SwingConstants.NORTH, arrowType, oneTouchArrowColor, null,
|
||||||
|
oneTouchHoverArrowColor, null, oneTouchPressedArrowColor, null );
|
||||||
setCursor( Cursor.getPredefinedCursor( Cursor.DEFAULT_CURSOR ) );
|
setCursor( Cursor.getPredefinedCursor( Cursor.DEFAULT_CURSOR ) );
|
||||||
|
ToolTipManager.sharedInstance().registerComponent( this );
|
||||||
|
|
||||||
this.left = left;
|
this.left = left;
|
||||||
}
|
}
|
||||||
@@ -129,7 +206,67 @@ public class FlatSplitPaneUI
|
|||||||
? (left ? SwingConstants.NORTH : SwingConstants.SOUTH)
|
? (left ? SwingConstants.NORTH : SwingConstants.SOUTH)
|
||||||
: (left ? SwingConstants.WEST : SwingConstants.EAST);
|
: (left ? SwingConstants.WEST : SwingConstants.EAST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getToolTipText( MouseEvent e ) {
|
||||||
|
String key = (orientation == JSplitPane.VERTICAL_SPLIT)
|
||||||
|
? (left
|
||||||
|
? (isRightCollapsed()
|
||||||
|
? "SplitPaneDivider.expandBottomToolTipText"
|
||||||
|
: "SplitPaneDivider.collapseTopToolTipText")
|
||||||
|
: (isLeftCollapsed()
|
||||||
|
? "SplitPaneDivider.expandTopToolTipText"
|
||||||
|
: "SplitPaneDivider.collapseBottomToolTipText"))
|
||||||
|
: (left
|
||||||
|
? (isRightCollapsed()
|
||||||
|
? "SplitPaneDivider.expandRightToolTipText"
|
||||||
|
: "SplitPaneDivider.collapseLeftToolTipText")
|
||||||
|
: (isLeftCollapsed()
|
||||||
|
? "SplitPaneDivider.expandLeftToolTipText"
|
||||||
|
: "SplitPaneDivider.collapseRightToolTipText"));
|
||||||
|
|
||||||
|
// get text from client property
|
||||||
|
Object value = splitPane.getClientProperty( key );
|
||||||
|
if( value instanceof String )
|
||||||
|
return (String) value;
|
||||||
|
|
||||||
|
// get text from bundle
|
||||||
|
return UIManager.getString( key, getLocale() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- class FlatDividerLayout ----------------------------------------
|
||||||
|
|
||||||
|
protected class FlatDividerLayout
|
||||||
|
extends DividerLayout
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void layoutContainer( Container c ) {
|
||||||
|
super.layoutContainer( c );
|
||||||
|
|
||||||
|
if( leftButton == null || rightButton == null || !splitPane.isOneTouchExpandable() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// increase side of buttons, which makes them easier to hit by the user
|
||||||
|
// and avoids cut arrows at small divider sizes
|
||||||
|
int extraSize = UIScale.scale( 4 );
|
||||||
|
if( orientation == JSplitPane.VERTICAL_SPLIT ) {
|
||||||
|
leftButton.setSize( leftButton.getWidth() + extraSize, leftButton.getHeight() );
|
||||||
|
rightButton.setBounds( leftButton.getX() + leftButton.getWidth(), rightButton.getY(),
|
||||||
|
rightButton.getWidth() + extraSize, rightButton.getHeight() );
|
||||||
|
} else {
|
||||||
|
leftButton.setSize( leftButton.getWidth(), leftButton.getHeight() + extraSize );
|
||||||
|
rightButton.setBounds( rightButton.getX(), leftButton.getY() + leftButton.getHeight(),
|
||||||
|
rightButton.getWidth(), rightButton.getHeight() + extraSize );
|
||||||
|
}
|
||||||
|
|
||||||
|
// hide buttons if not applicable
|
||||||
|
boolean leftCollapsed = isLeftCollapsed();
|
||||||
|
if( leftCollapsed )
|
||||||
|
rightButton.setLocation( leftButton.getLocation() );
|
||||||
|
leftButton.setVisible( !leftCollapsed );
|
||||||
|
rightButton.setVisible( !isRightCollapsed() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Container;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.geom.Rectangle2D;
|
||||||
|
import javax.swing.JScrollBar;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.table.JTableHeader;
|
||||||
|
import javax.swing.table.TableColumn;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cell border for {@code sun.swing.table.DefaultTableCellHeaderRenderer}
|
||||||
|
* (used by {@link javax.swing.table.JTableHeader}).
|
||||||
|
* <p>
|
||||||
|
* Uses separate cell margins from UI defaults to allow easy customizing.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
* @since 1.2
|
||||||
|
*/
|
||||||
|
public class FlatTableHeaderBorder
|
||||||
|
extends FlatEmptyBorder
|
||||||
|
{
|
||||||
|
protected Color separatorColor = UIManager.getColor( "TableHeader.separatorColor" );
|
||||||
|
protected Color bottomSeparatorColor = UIManager.getColor( "TableHeader.bottomSeparatorColor" );
|
||||||
|
|
||||||
|
public FlatTableHeaderBorder() {
|
||||||
|
super( UIManager.getInsets( "TableHeader.cellMargins" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
|
JTableHeader header = (JTableHeader) SwingUtilities.getAncestorOfClass( JTableHeader.class, c );
|
||||||
|
boolean leftToRight = (header != null ? header : c).getComponentOrientation().isLeftToRight();
|
||||||
|
boolean paintLeft = !leftToRight;
|
||||||
|
boolean paintRight = leftToRight;
|
||||||
|
|
||||||
|
if( header != null ) {
|
||||||
|
int hx = SwingUtilities.convertPoint( c, x, y, header ).x;
|
||||||
|
if( isDraggedColumn( header, hx ) )
|
||||||
|
paintLeft = paintRight = true;
|
||||||
|
else {
|
||||||
|
if( hx <= 0 && !leftToRight && hideTrailingVerticalLine( header ) )
|
||||||
|
paintLeft = false;
|
||||||
|
if( hx + width >= header.getWidth() && leftToRight && hideTrailingVerticalLine( header ) )
|
||||||
|
paintRight = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float lineWidth = UIScale.scale( 1f );
|
||||||
|
|
||||||
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
|
try {
|
||||||
|
FlatUIUtils.setRenderingHints( g2 );
|
||||||
|
|
||||||
|
// paint column separator lines
|
||||||
|
g2.setColor( separatorColor );
|
||||||
|
if( paintLeft )
|
||||||
|
g2.fill( new Rectangle2D.Float( x, y, lineWidth, height - lineWidth ) );
|
||||||
|
if( paintRight )
|
||||||
|
g2.fill( new Rectangle2D.Float( x + width - lineWidth, y, lineWidth, height - lineWidth ) );
|
||||||
|
|
||||||
|
// paint bottom line
|
||||||
|
g2.setColor( bottomSeparatorColor );
|
||||||
|
g2.fill( new Rectangle2D.Float( x, y + height - lineWidth, width, lineWidth ) );
|
||||||
|
} finally {
|
||||||
|
g2.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isDraggedColumn( JTableHeader header, int x ) {
|
||||||
|
TableColumn draggedColumn = header.getDraggedColumn();
|
||||||
|
if( draggedColumn == null )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int draggedDistance = header.getDraggedDistance();
|
||||||
|
if( draggedDistance == 0 )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int columnCount = header.getColumnModel().getColumnCount();
|
||||||
|
for( int i = 0; i < columnCount; i++ ) {
|
||||||
|
if( header.getHeaderRect( i ).x + draggedDistance == x )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean hideTrailingVerticalLine( JTableHeader header ) {
|
||||||
|
Container viewport = header.getParent();
|
||||||
|
Container viewportParent = (viewport != null) ? viewport.getParent() : null;
|
||||||
|
if( !(viewportParent instanceof JScrollPane) )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
JScrollBar vsb = ((JScrollPane)viewportParent).getVerticalScrollBar();
|
||||||
|
if( vsb == null || !vsb.isVisible() )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// if "ScrollPane.fillUpperCorner" is true, then javax.swing.ScrollPaneLayout
|
||||||
|
// extends the vertical scrollbar into the upper right/left corner
|
||||||
|
return vsb.getY() == viewport.getY();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,18 +18,15 @@ 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.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.Rectangle;
|
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
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.JScrollPane;
|
|
||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
import javax.swing.SwingConstants;
|
import javax.swing.SwingConstants;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
@@ -38,7 +35,6 @@ import javax.swing.plaf.ComponentUI;
|
|||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicTableHeaderUI;
|
import javax.swing.plaf.basic.BasicTableHeaderUI;
|
||||||
import javax.swing.table.TableCellRenderer;
|
import javax.swing.table.TableCellRenderer;
|
||||||
import javax.swing.table.TableColumn;
|
|
||||||
import javax.swing.table.TableColumnModel;
|
import javax.swing.table.TableColumnModel;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -53,17 +49,21 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*
|
*
|
||||||
* <!-- FlatTableHeaderUI -->
|
* <!-- FlatTableHeaderUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault TableHeader.separatorColor Color
|
|
||||||
* @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
|
||||||
*
|
*
|
||||||
|
* <!-- FlatTableHeaderBorder -->
|
||||||
|
*
|
||||||
|
* @uiDefault TableHeader.cellMargins Insets
|
||||||
|
* @uiDefault TableHeader.separatorColor Color
|
||||||
|
* @uiDefault TableHeader.bottomSeparatorColor Color
|
||||||
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatTableHeaderUI
|
public class FlatTableHeaderUI
|
||||||
extends BasicTableHeaderUI
|
extends BasicTableHeaderUI
|
||||||
{
|
{
|
||||||
protected Color separatorColor;
|
|
||||||
protected Color bottomSeparatorColor;
|
protected Color bottomSeparatorColor;
|
||||||
protected int height;
|
protected int height;
|
||||||
protected int sortIconPosition;
|
protected int sortIconPosition;
|
||||||
@@ -76,7 +76,6 @@ public class FlatTableHeaderUI
|
|||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
separatorColor = UIManager.getColor( "TableHeader.separatorColor" );
|
|
||||||
bottomSeparatorColor = UIManager.getColor( "TableHeader.bottomSeparatorColor" );
|
bottomSeparatorColor = UIManager.getColor( "TableHeader.bottomSeparatorColor" );
|
||||||
height = UIManager.getInt( "TableHeader.height" );
|
height = UIManager.getInt( "TableHeader.height" );
|
||||||
switch( Objects.toString( UIManager.getString( "TableHeader.sortIconPosition" ), "right" ) ) {
|
switch( Objects.toString( UIManager.getString( "TableHeader.sortIconPosition" ), "right" ) ) {
|
||||||
@@ -92,24 +91,38 @@ public class FlatTableHeaderUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
separatorColor = null;
|
|
||||||
bottomSeparatorColor = null;
|
bottomSeparatorColor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paint( Graphics g, JComponent c ) {
|
public void paint( Graphics g, JComponent c ) {
|
||||||
// do not paint borders if JTableHeader.setDefaultRenderer() was used
|
TableColumnModel columnModel = header.getColumnModel();
|
||||||
|
if( columnModel.getColumnCount() <= 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// compute total width of all columns
|
||||||
|
int columnCount = columnModel.getColumnCount();
|
||||||
|
int totalWidth = 0;
|
||||||
|
for( int i = 0; i < columnCount; i++ )
|
||||||
|
totalWidth += columnModel.getColumn( i ).getWidth();
|
||||||
|
|
||||||
|
if( totalWidth < header.getWidth() ) {
|
||||||
|
// do not paint bottom separator if JTableHeader.setDefaultRenderer() was used
|
||||||
TableCellRenderer defaultRenderer = header.getDefaultRenderer();
|
TableCellRenderer defaultRenderer = header.getDefaultRenderer();
|
||||||
boolean paintBorders = isSystemDefaultRenderer( defaultRenderer );
|
boolean paintBottomSeparator = isSystemDefaultRenderer( defaultRenderer );
|
||||||
if( !paintBorders && header.getColumnModel().getColumnCount() > 0 ) {
|
if( !paintBottomSeparator && header.getTable() != null ) {
|
||||||
// check whether the renderer delegates to the system default renderer
|
// check whether the renderer delegates to the system default renderer
|
||||||
Component rendererComponent = defaultRenderer.getTableCellRendererComponent(
|
Component rendererComponent = defaultRenderer.getTableCellRendererComponent(
|
||||||
header.getTable(), "", false, false, -1, 0 );
|
header.getTable(), "", false, false, -1, 0 );
|
||||||
paintBorders = isSystemDefaultRenderer( rendererComponent );
|
paintBottomSeparator = isSystemDefaultRenderer( rendererComponent );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( paintBorders )
|
if( paintBottomSeparator ) {
|
||||||
paintColumnBorders( g, c );
|
int w = c.getWidth() - totalWidth;
|
||||||
|
int x = header.getComponentOrientation().isLeftToRight() ? c.getWidth() - w : 0;
|
||||||
|
paintBottomSeparator( g, c, x, w );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// temporary use own default renderer if necessary
|
// temporary use own default renderer if necessary
|
||||||
FlatTableCellHeaderRenderer sortIconRenderer = null;
|
FlatTableCellHeaderRenderer sortIconRenderer = null;
|
||||||
@@ -126,9 +139,6 @@ public class FlatTableHeaderUI
|
|||||||
sortIconRenderer.reset();
|
sortIconRenderer.reset();
|
||||||
header.setDefaultRenderer( sortIconRenderer.delegate );
|
header.setDefaultRenderer( sortIconRenderer.delegate );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( paintBorders )
|
|
||||||
paintDraggedColumnBorders( g, c );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSystemDefaultRenderer( Object headerRenderer ) {
|
private boolean isSystemDefaultRenderer( Object headerRenderer ) {
|
||||||
@@ -137,14 +147,8 @@ public class FlatTableHeaderUI
|
|||||||
rendererClassName.equals( "sun.swing.FilePane$AlignableTableHeaderRenderer" );
|
rendererClassName.equals( "sun.swing.FilePane$AlignableTableHeaderRenderer" );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void paintColumnBorders( Graphics g, JComponent c ) {
|
protected void paintBottomSeparator( Graphics g, JComponent c, int x, int w ) {
|
||||||
int width = c.getWidth();
|
|
||||||
int height = c.getHeight();
|
|
||||||
float lineWidth = UIScale.scale( 1f );
|
float lineWidth = UIScale.scale( 1f );
|
||||||
float topLineIndent = lineWidth;
|
|
||||||
float bottomLineIndent = lineWidth * 3;
|
|
||||||
TableColumnModel columnModel = header.getColumnModel();
|
|
||||||
int columnCount = columnModel.getColumnCount();
|
|
||||||
|
|
||||||
Graphics2D g2 = (Graphics2D) g.create();
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
try {
|
try {
|
||||||
@@ -152,71 +156,7 @@ public class FlatTableHeaderUI
|
|||||||
|
|
||||||
// paint bottom line
|
// paint bottom line
|
||||||
g2.setColor( bottomSeparatorColor );
|
g2.setColor( bottomSeparatorColor );
|
||||||
g2.fill( new Rectangle2D.Float( 0, height - lineWidth, width, lineWidth ) );
|
g2.fill( new Rectangle2D.Float( x, c.getHeight() - lineWidth, w, lineWidth ) );
|
||||||
|
|
||||||
// paint column separator lines
|
|
||||||
g2.setColor( separatorColor );
|
|
||||||
|
|
||||||
int sepCount = columnCount;
|
|
||||||
if( header.getTable() != null && header.getTable().getAutoResizeMode() != JTable.AUTO_RESIZE_OFF && !isVerticalScrollBarVisible() )
|
|
||||||
sepCount--;
|
|
||||||
|
|
||||||
if( header.getComponentOrientation().isLeftToRight() ) {
|
|
||||||
int x = 0;
|
|
||||||
for( int i = 0; i < sepCount; i++ ) {
|
|
||||||
x += columnModel.getColumn( i ).getWidth();
|
|
||||||
g2.fill( new Rectangle2D.Float( x - lineWidth, topLineIndent, lineWidth, height - bottomLineIndent ) );
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
int x = width;
|
|
||||||
for( int i = 0; i < sepCount; i++ ) {
|
|
||||||
x -= columnModel.getColumn( i ).getWidth();
|
|
||||||
g2.fill( new Rectangle2D.Float( x - (i < sepCount - 1 ? lineWidth : 0),
|
|
||||||
topLineIndent, lineWidth, height - bottomLineIndent ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
g2.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void paintDraggedColumnBorders( Graphics g, JComponent c ) {
|
|
||||||
TableColumn draggedColumn = header.getDraggedColumn();
|
|
||||||
if( draggedColumn == null )
|
|
||||||
return;
|
|
||||||
|
|
||||||
// find index of dragged column
|
|
||||||
TableColumnModel columnModel = header.getColumnModel();
|
|
||||||
int columnCount = columnModel.getColumnCount();
|
|
||||||
int draggedColumnIndex = -1;
|
|
||||||
for( int i = 0; i < columnCount; i++ ) {
|
|
||||||
if( columnModel.getColumn( i ) == draggedColumn ) {
|
|
||||||
draggedColumnIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( draggedColumnIndex < 0 )
|
|
||||||
return;
|
|
||||||
|
|
||||||
float lineWidth = UIScale.scale( 1f );
|
|
||||||
float topLineIndent = lineWidth;
|
|
||||||
float bottomLineIndent = lineWidth * 3;
|
|
||||||
Rectangle r = header.getHeaderRect( draggedColumnIndex );
|
|
||||||
r.x += header.getDraggedDistance();
|
|
||||||
|
|
||||||
Graphics2D g2 = (Graphics2D) g.create();
|
|
||||||
try {
|
|
||||||
FlatUIUtils.setRenderingHints( g2 );
|
|
||||||
|
|
||||||
// paint dragged bottom line
|
|
||||||
g2.setColor( bottomSeparatorColor );
|
|
||||||
g2.fill( new Rectangle2D.Float( r.x, r.y + r.height - lineWidth, r.width, lineWidth ) );
|
|
||||||
|
|
||||||
// paint dragged column separator lines
|
|
||||||
g2.setColor( separatorColor );
|
|
||||||
g2.fill( new Rectangle2D.Float( r.x, topLineIndent, lineWidth, r.height - bottomLineIndent ) );
|
|
||||||
g2.fill( new Rectangle2D.Float( r.x + r.width - lineWidth, r.y + topLineIndent, lineWidth, r.height - bottomLineIndent ) );
|
|
||||||
} finally {
|
} finally {
|
||||||
g2.dispose();
|
g2.dispose();
|
||||||
}
|
}
|
||||||
@@ -230,22 +170,6 @@ public class FlatTableHeaderUI
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isVerticalScrollBarVisible() {
|
|
||||||
JScrollPane scrollPane = getScrollPane();
|
|
||||||
return (scrollPane != null && scrollPane.getVerticalScrollBar() != null)
|
|
||||||
? scrollPane.getVerticalScrollBar().isVisible()
|
|
||||||
: false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private JScrollPane getScrollPane() {
|
|
||||||
Container parent = header.getParent();
|
|
||||||
if( parent == null )
|
|
||||||
return null;
|
|
||||||
|
|
||||||
parent = parent.getParent();
|
|
||||||
return (parent instanceof JScrollPane) ? (JScrollPane) parent : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
//---- class FlatTableCellHeaderRenderer ----------------------------------
|
//---- class FlatTableCellHeaderRenderer ----------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -17,17 +17,25 @@
|
|||||||
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.EventQueue;
|
import java.awt.EventQueue;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
import java.awt.event.FocusEvent;
|
import java.awt.event.FocusEvent;
|
||||||
import java.awt.event.FocusListener;
|
import java.awt.event.FocusListener;
|
||||||
import javax.swing.JCheckBox;
|
import java.awt.geom.Rectangle2D;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JViewport;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicTableUI;
|
import javax.swing.plaf.basic.BasicTableUI;
|
||||||
import javax.swing.table.TableCellRenderer;
|
import javax.swing.table.JTableHeader;
|
||||||
|
import com.formdev.flatlaf.util.Graphics2DProxy;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -129,12 +137,6 @@ public class FlatTableUI
|
|||||||
oldIntercellSpacing = table.getIntercellSpacing();
|
oldIntercellSpacing = table.getIntercellSpacing();
|
||||||
table.setIntercellSpacing( intercellSpacing );
|
table.setIntercellSpacing( intercellSpacing );
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkbox is non-opaque in FlatLaf and therefore would not paint selection
|
|
||||||
// --> make checkbox renderer opaque (but opaque in Metal or Windows LaF)
|
|
||||||
TableCellRenderer booleanRenderer = table.getDefaultRenderer( Boolean.class );
|
|
||||||
if( booleanRenderer instanceof JCheckBox )
|
|
||||||
((JCheckBox)booleanRenderer).setOpaque( true );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -203,4 +205,125 @@ public class FlatTableUI
|
|||||||
table.setSelectionForeground( selectionInactiveForeground );
|
table.setSelectionForeground( selectionInactiveForeground );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paint( Graphics g, JComponent c ) {
|
||||||
|
boolean horizontalLines = table.getShowHorizontalLines();
|
||||||
|
boolean verticalLines = table.getShowVerticalLines();
|
||||||
|
if( horizontalLines || verticalLines ) {
|
||||||
|
// fix grid painting issues in BasicTableUI
|
||||||
|
// - do not paint last vertical grid line if line is on right edge of scroll pane
|
||||||
|
// - fix unstable grid line thickness when scaled at 125%, 150%, 175%, 225%, ...
|
||||||
|
// which paints either 1px or 2px lines depending on location
|
||||||
|
// - on Java 9+, fix wrong grid line thickness in dragged column
|
||||||
|
|
||||||
|
boolean hideLastVerticalLine = hideLastVerticalLine();
|
||||||
|
int tableWidth = table.getWidth();
|
||||||
|
JTableHeader header = table.getTableHeader();
|
||||||
|
boolean isDragging = (header != null && header.getDraggedColumn() != null);
|
||||||
|
|
||||||
|
double systemScaleFactor = UIScale.getSystemScaleFactor( (Graphics2D) g );
|
||||||
|
double lineThickness = (1. / systemScaleFactor) * (int) systemScaleFactor;
|
||||||
|
|
||||||
|
// Java 8 uses drawLine() to paint grid lines
|
||||||
|
// Java 9+ uses fillRect() to paint grid lines (except for dragged column)
|
||||||
|
g = new Graphics2DProxy( (Graphics2D) g ) {
|
||||||
|
@Override
|
||||||
|
public void drawLine( int x1, int y1, int x2, int y2 ) {
|
||||||
|
// do not paint last vertical line
|
||||||
|
if( hideLastVerticalLine && verticalLines &&
|
||||||
|
x1 == x2 && y1 == 0 && x1 == tableWidth - 1 &&
|
||||||
|
wasInvokedFromPaintGrid() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// on Java 9+, fix wrong grid line thickness in dragged column
|
||||||
|
if( isDragging &&
|
||||||
|
SystemInfo.isJava_9_orLater &&
|
||||||
|
((horizontalLines && y1 == y2) || (verticalLines && x1 == x2)) &&
|
||||||
|
wasInvokedFromPaintDraggedArea() )
|
||||||
|
{
|
||||||
|
if( y1 == y2 ) {
|
||||||
|
// horizontal grid line
|
||||||
|
super.fill( new Rectangle2D.Double( x1, y1, x2 - x1 + 1, lineThickness ) );
|
||||||
|
} else if( x1 == x2 ) {
|
||||||
|
// vertical grid line
|
||||||
|
super.fill( new Rectangle2D.Double( x1, y1, lineThickness, y2 - y1 + 1 ) );
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.drawLine( x1, y1, x2, y2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fillRect( int x, int y, int width, int height ) {
|
||||||
|
// do not paint last vertical line
|
||||||
|
if( hideLastVerticalLine && verticalLines &&
|
||||||
|
width == 1 && y == 0 && x == tableWidth - 1 &&
|
||||||
|
wasInvokedFromPaintGrid() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// reduce line thickness to avoid unstable painted line thickness
|
||||||
|
if( lineThickness != 1 ) {
|
||||||
|
if( horizontalLines && height == 1 && wasInvokedFromPaintGrid() ) {
|
||||||
|
super.fill( new Rectangle2D.Double( x, y, width, lineThickness ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if( verticalLines && width == 1 && y == 0 && wasInvokedFromPaintGrid() ) {
|
||||||
|
super.fill( new Rectangle2D.Double( x, y, lineThickness, height ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.fillRect( x, y, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean wasInvokedFromPaintGrid() {
|
||||||
|
return wasInvokedFromMethod( "paintGrid" );
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean wasInvokedFromPaintDraggedArea() {
|
||||||
|
return wasInvokedFromMethod( "paintDraggedArea" );
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean wasInvokedFromMethod( String methodName ) {
|
||||||
|
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
||||||
|
for( int i = 0; i < 10 || i < stackTrace.length; i++ ) {
|
||||||
|
if( "javax.swing.plaf.basic.BasicTableUI".equals( stackTrace[i].getClassName() ) ) {
|
||||||
|
String methodName2 = stackTrace[i].getMethodName();
|
||||||
|
if( "paintCell".equals( methodName2 ) )
|
||||||
|
return false;
|
||||||
|
if( methodName.equals( methodName2 ) )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
super.paint( g, c );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean hideLastVerticalLine() {
|
||||||
|
Container viewport = SwingUtilities.getUnwrappedParent( table );
|
||||||
|
Container viewportParent = (viewport != null) ? viewport.getParent() : null;
|
||||||
|
if( !(viewportParent instanceof JScrollPane) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// do not hide last vertical line if table is smaller than viewport
|
||||||
|
if( table.getX() + table.getWidth() < viewport.getWidth() )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// in left-to-right:
|
||||||
|
// - do not hide last vertical line if table used as row header in scroll pane
|
||||||
|
// in right-to-left:
|
||||||
|
// - hide last vertical line if table used as row header in scroll pane
|
||||||
|
// - do not hide last vertical line if table is in center and scroll pane has row header
|
||||||
|
JScrollPane scrollPane = (JScrollPane) viewportParent;
|
||||||
|
JViewport rowHeader = scrollPane.getRowHeader();
|
||||||
|
return scrollPane.getComponentOrientation().isLeftToRight()
|
||||||
|
? (viewport != rowHeader)
|
||||||
|
: (viewport == rowHeader || rowHeader == null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import java.awt.Color;
|
|||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.event.FocusListener;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JTextArea;
|
import javax.swing.JTextArea;
|
||||||
@@ -52,6 +53,7 @@ import com.formdev.flatlaf.util.HiDPIUtils;
|
|||||||
* @uiDefault Component.isIntelliJTheme boolean
|
* @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
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
@@ -63,6 +65,9 @@ public class FlatTextAreaUI
|
|||||||
protected Color background;
|
protected Color background;
|
||||||
protected Color disabledBackground;
|
protected Color disabledBackground;
|
||||||
protected Color inactiveBackground;
|
protected Color inactiveBackground;
|
||||||
|
protected Color focusedBackground;
|
||||||
|
|
||||||
|
private FocusListener focusListener;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatTextAreaUI();
|
return new FlatTextAreaUI();
|
||||||
@@ -84,6 +89,7 @@ public class FlatTextAreaUI
|
|||||||
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" );
|
||||||
|
focusedBackground = UIManager.getColor( "TextArea.focusedBackground" );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -93,6 +99,24 @@ public class FlatTextAreaUI
|
|||||||
background = null;
|
background = null;
|
||||||
disabledBackground = null;
|
disabledBackground = null;
|
||||||
inactiveBackground = null;
|
inactiveBackground = null;
|
||||||
|
focusedBackground = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installListeners() {
|
||||||
|
super.installListeners();
|
||||||
|
|
||||||
|
// necessary to update focus background
|
||||||
|
focusListener = new FlatUIUtils.RepaintFocusListener( getComponent(), c -> focusedBackground != null );
|
||||||
|
getComponent().addFocusListener( focusListener );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallListeners() {
|
||||||
|
super.uninstallListeners();
|
||||||
|
|
||||||
|
getComponent().removeFocusListener( focusListener );
|
||||||
|
focusListener = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -156,14 +180,6 @@ public class FlatTextAreaUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintBackground( Graphics g ) {
|
protected void paintBackground( Graphics g ) {
|
||||||
JTextComponent c = getComponent();
|
FlatEditorPaneUI.paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground );
|
||||||
|
|
||||||
// for compatibility with IntelliJ themes
|
|
||||||
if( isIntelliJTheme && (!c.isEnabled() || !c.isEditable()) && (c.getBackground() instanceof UIResource) ) {
|
|
||||||
FlatUIUtils.paintParentBackground( g, c );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
super.paintBackground( g );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import javax.swing.text.Caret;
|
|||||||
import javax.swing.text.JTextComponent;
|
import javax.swing.text.JTextComponent;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
|
import com.formdev.flatlaf.util.JavaCompatibility;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JTextField}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JTextField}.
|
||||||
@@ -63,7 +64,9 @@ import com.formdev.flatlaf.util.HiDPIUtils;
|
|||||||
* @uiDefault Component.minimumWidth int
|
* @uiDefault Component.minimumWidth int
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
* @uiDefault Component.isIntelliJTheme boolean
|
||||||
* @uiDefault TextField.placeholderForeground Color
|
* @uiDefault TextField.placeholderForeground Color
|
||||||
|
* @uiDefault TextField.focusedBackground Color optional
|
||||||
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
|
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
|
||||||
|
* @uiDefault TextComponent.selectAllOnMouseClick boolean
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
@@ -73,6 +76,7 @@ public class FlatTextFieldUI
|
|||||||
protected int minimumWidth;
|
protected int minimumWidth;
|
||||||
protected boolean isIntelliJTheme;
|
protected boolean isIntelliJTheme;
|
||||||
protected Color placeholderForeground;
|
protected Color placeholderForeground;
|
||||||
|
protected Color focusedBackground;
|
||||||
|
|
||||||
private FocusListener focusListener;
|
private FocusListener focusListener;
|
||||||
|
|
||||||
@@ -88,6 +92,7 @@ public class FlatTextFieldUI
|
|||||||
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
||||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
||||||
placeholderForeground = UIManager.getColor( prefix + ".placeholderForeground" );
|
placeholderForeground = UIManager.getColor( prefix + ".placeholderForeground" );
|
||||||
|
focusedBackground = UIManager.getColor( prefix + ".focusedBackground" );
|
||||||
|
|
||||||
LookAndFeel.installProperty( getComponent(), "opaque", false );
|
LookAndFeel.installProperty( getComponent(), "opaque", false );
|
||||||
|
|
||||||
@@ -99,6 +104,7 @@ public class FlatTextFieldUI
|
|||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
placeholderForeground = null;
|
placeholderForeground = null;
|
||||||
|
focusedBackground = null;
|
||||||
|
|
||||||
MigLayoutVisualPadding.uninstall( getComponent() );
|
MigLayoutVisualPadding.uninstall( getComponent() );
|
||||||
}
|
}
|
||||||
@@ -107,7 +113,8 @@ public class FlatTextFieldUI
|
|||||||
protected void installListeners() {
|
protected void installListeners() {
|
||||||
super.installListeners();
|
super.installListeners();
|
||||||
|
|
||||||
focusListener = new FlatUIUtils.RepaintFocusListener( getComponent() );
|
// necessary to update focus border and background
|
||||||
|
focusListener = new FlatUIUtils.RepaintFocusListener( getComponent(), null );
|
||||||
getComponent().addFocusListener( focusListener );
|
getComponent().addFocusListener( focusListener );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +128,8 @@ public class FlatTextFieldUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Caret createCaret() {
|
protected Caret createCaret() {
|
||||||
return new FlatCaret( UIManager.getString( "TextComponent.selectAllOnFocusPolicy" ) );
|
return new FlatCaret( UIManager.getString( "TextComponent.selectAllOnFocusPolicy"),
|
||||||
|
UIManager.getBoolean( "TextComponent.selectAllOnMouseClick" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -145,7 +153,7 @@ public class FlatTextFieldUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintSafely( Graphics g ) {
|
protected void paintSafely( Graphics g ) {
|
||||||
paintBackground( g, getComponent(), isIntelliJTheme );
|
paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground );
|
||||||
paintPlaceholder( g, getComponent(), placeholderForeground );
|
paintPlaceholder( g, getComponent(), placeholderForeground );
|
||||||
|
|
||||||
super.paintSafely( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ) );
|
super.paintSafely( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ) );
|
||||||
@@ -156,7 +164,7 @@ public class FlatTextFieldUI
|
|||||||
// background is painted elsewhere
|
// background is painted elsewhere
|
||||||
}
|
}
|
||||||
|
|
||||||
static void paintBackground( Graphics g, JTextComponent c, boolean isIntelliJTheme ) {
|
static void paintBackground( Graphics g, JTextComponent c, boolean isIntelliJTheme, 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
|
||||||
@@ -177,18 +185,31 @@ public class FlatTextFieldUI
|
|||||||
try {
|
try {
|
||||||
FlatUIUtils.setRenderingHints( g2 );
|
FlatUIUtils.setRenderingHints( g2 );
|
||||||
|
|
||||||
Color background = c.getBackground();
|
g2.setColor( getBackground( c, isIntelliJTheme, focusedBackground ) );
|
||||||
g2.setColor( !(background instanceof UIResource)
|
|
||||||
? background
|
|
||||||
: (isIntelliJTheme && (!c.isEnabled() || !c.isEditable())
|
|
||||||
? FlatUIUtils.getParentBackground( c )
|
|
||||||
: background) );
|
|
||||||
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 ) {
|
||||||
|
Color background = c.getBackground();
|
||||||
|
|
||||||
|
// always use explicitly set color
|
||||||
|
if( !(background instanceof UIResource) )
|
||||||
|
return background;
|
||||||
|
|
||||||
|
// focused
|
||||||
|
if( focusedBackground != null && FlatUIUtils.isPermanentFocusOwner( c ) )
|
||||||
|
return focusedBackground;
|
||||||
|
|
||||||
|
// for compatibility with IntelliJ themes
|
||||||
|
if( isIntelliJTheme && (!c.isEnabled() || !c.isEditable()) )
|
||||||
|
return FlatUIUtils.getParentBackground( c );
|
||||||
|
|
||||||
|
return background;
|
||||||
|
}
|
||||||
|
|
||||||
static void paintPlaceholder( Graphics g, JTextComponent c, Color placeholderForeground ) {
|
static void paintPlaceholder( Graphics g, JTextComponent c, Color placeholderForeground ) {
|
||||||
// check whether text component is empty
|
// check whether text component is empty
|
||||||
if( c.getDocument().getLength() > 0 )
|
if( c.getDocument().getLength() > 0 )
|
||||||
@@ -211,7 +232,9 @@ public class FlatTextFieldUI
|
|||||||
|
|
||||||
// paint placeholder
|
// paint placeholder
|
||||||
g.setColor( placeholderForeground );
|
g.setColor( placeholderForeground );
|
||||||
FlatUIUtils.drawString( c, g, (String) placeholder, x, y );
|
String clippedPlaceholder = JavaCompatibility.getClippedString( jc, fm,
|
||||||
|
(String) placeholder, c.getWidth() - insets.left - insets.right );
|
||||||
|
FlatUIUtils.drawString( c, g, clippedPlaceholder, x, y );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -16,17 +16,17 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.event.FocusListener;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JEditorPane;
|
import javax.swing.JEditorPane;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.UIResource;
|
|
||||||
import javax.swing.plaf.basic.BasicTextPaneUI;
|
import javax.swing.plaf.basic.BasicTextPaneUI;
|
||||||
import javax.swing.text.JTextComponent;
|
|
||||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -51,6 +51,7 @@ import com.formdev.flatlaf.util.HiDPIUtils;
|
|||||||
*
|
*
|
||||||
* @uiDefault Component.minimumWidth int
|
* @uiDefault Component.minimumWidth int
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
* @uiDefault Component.isIntelliJTheme boolean
|
||||||
|
* @uiDefault TextPane.focusedBackground Color optional
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
@@ -59,8 +60,10 @@ public class FlatTextPaneUI
|
|||||||
{
|
{
|
||||||
protected int minimumWidth;
|
protected int minimumWidth;
|
||||||
protected boolean isIntelliJTheme;
|
protected boolean isIntelliJTheme;
|
||||||
|
protected Color focusedBackground;
|
||||||
|
|
||||||
private Object oldHonorDisplayProperties;
|
private Object oldHonorDisplayProperties;
|
||||||
|
private FocusListener focusListener;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatTextPaneUI();
|
return new FlatTextPaneUI();
|
||||||
@@ -70,8 +73,10 @@ public class FlatTextPaneUI
|
|||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
|
String prefix = getPropertyPrefix();
|
||||||
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
||||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
||||||
|
focusedBackground = UIManager.getColor( prefix + ".focusedBackground" );
|
||||||
|
|
||||||
// use component font and foreground for HTML text
|
// use component font and foreground for HTML text
|
||||||
oldHonorDisplayProperties = getComponent().getClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES );
|
oldHonorDisplayProperties = getComponent().getClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES );
|
||||||
@@ -82,9 +87,28 @@ public class FlatTextPaneUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
focusedBackground = null;
|
||||||
|
|
||||||
getComponent().putClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES, oldHonorDisplayProperties );
|
getComponent().putClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES, oldHonorDisplayProperties );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installListeners() {
|
||||||
|
super.installListeners();
|
||||||
|
|
||||||
|
// necessary to update focus background
|
||||||
|
focusListener = new FlatUIUtils.RepaintFocusListener( getComponent(), c -> focusedBackground != null );
|
||||||
|
getComponent().addFocusListener( focusListener );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallListeners() {
|
||||||
|
super.uninstallListeners();
|
||||||
|
|
||||||
|
getComponent().removeFocusListener( focusListener );
|
||||||
|
focusListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void propertyChange( PropertyChangeEvent e ) {
|
protected void propertyChange( PropertyChangeEvent e ) {
|
||||||
super.propertyChange( e );
|
super.propertyChange( e );
|
||||||
@@ -108,14 +132,6 @@ public class FlatTextPaneUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintBackground( Graphics g ) {
|
protected void paintBackground( Graphics g ) {
|
||||||
JTextComponent c = getComponent();
|
FlatEditorPaneUI.paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground );
|
||||||
|
|
||||||
// for compatibility with IntelliJ themes
|
|
||||||
if( isIntelliJTheme && (!c.isEnabled() || !c.isEditable()) && (c.getBackground() instanceof UIResource) ) {
|
|
||||||
FlatUIUtils.paintParentBackground( g, c );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
super.paintBackground( g );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import static com.formdev.flatlaf.FlatClientProperties.*;
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
@@ -47,6 +48,7 @@ import java.util.List;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import javax.accessibility.AccessibleContext;
|
import javax.accessibility.AccessibleContext;
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
|
import javax.swing.Box;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
@@ -63,7 +65,7 @@ import javax.swing.border.AbstractBorder;
|
|||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.FlatSystemProperties;
|
import com.formdev.flatlaf.FlatSystemProperties;
|
||||||
import com.formdev.flatlaf.ui.JBRCustomDecorations.JBRWindowTopBorder;
|
import com.formdev.flatlaf.ui.FlatNativeWindowBorder.WindowTopBorder;
|
||||||
import com.formdev.flatlaf.util.ScaledImageIcon;
|
import com.formdev.flatlaf.util.ScaledImageIcon;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
@@ -76,12 +78,17 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault TitlePane.foreground Color
|
* @uiDefault TitlePane.foreground Color
|
||||||
* @uiDefault TitlePane.inactiveForeground Color
|
* @uiDefault TitlePane.inactiveForeground Color
|
||||||
* @uiDefault TitlePane.embeddedForeground Color
|
* @uiDefault TitlePane.embeddedForeground Color
|
||||||
|
* @uiDefault TitlePane.borderColor Color optional
|
||||||
|
* @uiDefault TitlePane.unifiedBackground boolean
|
||||||
* @uiDefault TitlePane.iconSize Dimension
|
* @uiDefault TitlePane.iconSize Dimension
|
||||||
* @uiDefault TitlePane.iconMargins Insets
|
* @uiDefault TitlePane.iconMargins Insets
|
||||||
* @uiDefault TitlePane.titleMargins Insets
|
* @uiDefault TitlePane.titleMargins Insets
|
||||||
* @uiDefault TitlePane.menuBarMargins Insets
|
|
||||||
* @uiDefault TitlePane.menuBarEmbedded boolean
|
* @uiDefault TitlePane.menuBarEmbedded boolean
|
||||||
* @uiDefault TitlePane.buttonMaximizedHeight int
|
* @uiDefault TitlePane.buttonMaximizedHeight int
|
||||||
|
* @uiDefault TitlePane.centerTitle boolean
|
||||||
|
* @uiDefault TitlePane.centerTitleIfMenuBarEmbedded boolean
|
||||||
|
* @uiDefault TitlePane.menuBarTitleGap int
|
||||||
|
* @uiDefault TitlePane.icon Icon
|
||||||
* @uiDefault TitlePane.closeIcon Icon
|
* @uiDefault TitlePane.closeIcon Icon
|
||||||
* @uiDefault TitlePane.iconifyIcon Icon
|
* @uiDefault TitlePane.iconifyIcon Icon
|
||||||
* @uiDefault TitlePane.maximizeIcon Icon
|
* @uiDefault TitlePane.maximizeIcon Icon
|
||||||
@@ -97,10 +104,13 @@ public class FlatTitlePane
|
|||||||
protected final Color activeForeground = UIManager.getColor( "TitlePane.foreground" );
|
protected final Color activeForeground = UIManager.getColor( "TitlePane.foreground" );
|
||||||
protected final Color inactiveForeground = UIManager.getColor( "TitlePane.inactiveForeground" );
|
protected final Color inactiveForeground = UIManager.getColor( "TitlePane.inactiveForeground" );
|
||||||
protected final Color embeddedForeground = UIManager.getColor( "TitlePane.embeddedForeground" );
|
protected final Color embeddedForeground = UIManager.getColor( "TitlePane.embeddedForeground" );
|
||||||
|
protected final Color borderColor = UIManager.getColor( "TitlePane.borderColor" );
|
||||||
|
|
||||||
protected final Insets menuBarMargins = UIManager.getInsets( "TitlePane.menuBarMargins" );
|
|
||||||
protected final Dimension iconSize = UIManager.getDimension( "TitlePane.iconSize" );
|
protected final Dimension iconSize = UIManager.getDimension( "TitlePane.iconSize" );
|
||||||
protected final int buttonMaximizedHeight = UIManager.getInt( "TitlePane.buttonMaximizedHeight" );
|
protected final int buttonMaximizedHeight = UIManager.getInt( "TitlePane.buttonMaximizedHeight" );
|
||||||
|
protected final boolean centerTitle = UIManager.getBoolean( "TitlePane.centerTitle" );
|
||||||
|
protected final boolean centerTitleIfMenuBarEmbedded = FlatUIUtils.getUIBoolean( "TitlePane.centerTitleIfMenuBarEmbedded", true );
|
||||||
|
protected final int menuBarTitleGap = FlatUIUtils.getUIInt( "TitlePane.menuBarTitleGap", 20 );
|
||||||
|
|
||||||
protected final JRootPane rootPane;
|
protected final JRootPane rootPane;
|
||||||
|
|
||||||
@@ -145,9 +155,15 @@ public class FlatTitlePane
|
|||||||
protected void addSubComponents() {
|
protected void addSubComponents() {
|
||||||
leftPanel = new JPanel();
|
leftPanel = new JPanel();
|
||||||
iconLabel = new JLabel();
|
iconLabel = new JLabel();
|
||||||
titleLabel = new JLabel();
|
titleLabel = new JLabel() {
|
||||||
|
@Override
|
||||||
|
public void updateUI() {
|
||||||
|
setUI( new FlatTitleLabelUI() );
|
||||||
|
}
|
||||||
|
};
|
||||||
iconLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.iconMargins" ) ) );
|
iconLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.iconMargins" ) ) );
|
||||||
titleLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.titleMargins" ) ) );
|
titleLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.titleMargins" ) ) );
|
||||||
|
titleLabel.setHorizontalAlignment( SwingConstants.CENTER );
|
||||||
|
|
||||||
leftPanel.setLayout( new BoxLayout( leftPanel, BoxLayout.LINE_AXIS ) );
|
leftPanel.setLayout( new BoxLayout( leftPanel, BoxLayout.LINE_AXIS ) );
|
||||||
leftPanel.setOpaque( false );
|
leftPanel.setOpaque( false );
|
||||||
@@ -157,9 +173,7 @@ public class FlatTitlePane
|
|||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize() {
|
public Dimension getPreferredSize() {
|
||||||
JMenuBar menuBar = rootPane.getJMenuBar();
|
JMenuBar menuBar = rootPane.getJMenuBar();
|
||||||
return (menuBar != null && isMenuBarEmbedded())
|
return hasVisibleEmbeddedMenuBar( menuBar ) ? menuBar.getPreferredSize() : new Dimension();
|
||||||
? FlatUIUtils.addInsets( menuBar.getPreferredSize(), UIScale.scale( menuBarMargins ) )
|
|
||||||
: new Dimension();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
leftPanel.add( menuBarPlaceholder );
|
leftPanel.add( menuBarPlaceholder );
|
||||||
@@ -182,6 +196,20 @@ public class FlatTitlePane
|
|||||||
if( !getComponentOrientation().isLeftToRight() )
|
if( !getComponentOrientation().isLeftToRight() )
|
||||||
leftPanel.setLocation( leftPanel.getX() + (oldWidth - newWidth), leftPanel.getY() );
|
leftPanel.setLocation( leftPanel.getX() + (oldWidth - newWidth), leftPanel.getY() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If menu bar is embedded and contains a horizontal glue component,
|
||||||
|
// then move the title label to the same location as the glue component
|
||||||
|
// and give it the same width.
|
||||||
|
// This allows placing any component on the trailing side of the title pane.
|
||||||
|
JMenuBar menuBar = rootPane.getJMenuBar();
|
||||||
|
if( hasVisibleEmbeddedMenuBar( menuBar ) ) {
|
||||||
|
Component horizontalGlue = findHorizontalGlue( menuBar );
|
||||||
|
if( horizontalGlue != null ) {
|
||||||
|
Point glueLocation = SwingUtilities.convertPoint( horizontalGlue, 0, 0, titleLabel );
|
||||||
|
titleLabel.setBounds( titleLabel.getX() + glueLocation.x, titleLabel.getY(),
|
||||||
|
horizontalGlue.getWidth(), titleLabel.getHeight() );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
@@ -238,10 +266,17 @@ public class FlatTitlePane
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void activeChanged( boolean active ) {
|
protected void activeChanged( boolean active ) {
|
||||||
boolean hasEmbeddedMenuBar = rootPane.getJMenuBar() != null && isMenuBarEmbedded();
|
Color background = clientPropertyColor( rootPane, TITLE_BAR_BACKGROUND, null );
|
||||||
Color background = FlatUIUtils.nonUIResource( active ? activeBackground : inactiveBackground );
|
Color foreground = clientPropertyColor( rootPane, TITLE_BAR_FOREGROUND, null );
|
||||||
Color foreground = FlatUIUtils.nonUIResource( active ? activeForeground : inactiveForeground );
|
Color titleForeground = foreground;
|
||||||
Color titleForeground = (hasEmbeddedMenuBar && active) ? FlatUIUtils.nonUIResource( embeddedForeground ) : foreground;
|
if( background == null )
|
||||||
|
background = FlatUIUtils.nonUIResource( active ? activeBackground : inactiveBackground );
|
||||||
|
if( foreground == null ) {
|
||||||
|
foreground = FlatUIUtils.nonUIResource( active ? activeForeground : inactiveForeground );
|
||||||
|
titleForeground = (active && hasVisibleEmbeddedMenuBar( rootPane.getJMenuBar() ))
|
||||||
|
? FlatUIUtils.nonUIResource( embeddedForeground )
|
||||||
|
: foreground;
|
||||||
|
}
|
||||||
|
|
||||||
setBackground( background );
|
setBackground( background );
|
||||||
titleLabel.setForeground( titleForeground );
|
titleLabel.setForeground( titleForeground );
|
||||||
@@ -250,8 +285,6 @@ public class FlatTitlePane
|
|||||||
restoreButton.setForeground( foreground );
|
restoreButton.setForeground( foreground );
|
||||||
closeButton.setForeground( foreground );
|
closeButton.setForeground( foreground );
|
||||||
|
|
||||||
titleLabel.setHorizontalAlignment( hasEmbeddedMenuBar ? SwingConstants.CENTER : SwingConstants.LEADING );
|
|
||||||
|
|
||||||
// this is necessary because hover/pressed colors are derived from background color
|
// this is necessary because hover/pressed colors are derived from background color
|
||||||
iconifyButton.setBackground( background );
|
iconifyButton.setBackground( background );
|
||||||
maximizeButton.setBackground( background );
|
maximizeButton.setBackground( background );
|
||||||
@@ -318,10 +351,10 @@ public class FlatTitlePane
|
|||||||
|
|
||||||
// set icon
|
// set icon
|
||||||
if( !images.isEmpty() )
|
if( !images.isEmpty() )
|
||||||
iconLabel.setIcon( FlatTitlePaneIcon.create( images, iconSize ) );
|
iconLabel.setIcon( new FlatTitlePaneIcon( images, iconSize ) );
|
||||||
else {
|
else {
|
||||||
// no icon set on window --> use default icon
|
// no icon set on window --> use default icon
|
||||||
Icon defaultIcon = UIManager.getIcon( "InternalFrame.icon" );
|
Icon defaultIcon = UIManager.getIcon( "TitlePane.icon" );
|
||||||
if( defaultIcon != null && (defaultIcon.getIconWidth() == 0 || defaultIcon.getIconHeight() == 0) )
|
if( defaultIcon != null && (defaultIcon.getIconWidth() == 0 || defaultIcon.getIconHeight() == 0) )
|
||||||
defaultIcon = null;
|
defaultIcon = null;
|
||||||
if( defaultIcon != null ) {
|
if( defaultIcon != null ) {
|
||||||
@@ -335,7 +368,7 @@ public class FlatTitlePane
|
|||||||
// show/hide icon
|
// show/hide icon
|
||||||
iconLabel.setVisible( hasIcon );
|
iconLabel.setVisible( hasIcon );
|
||||||
|
|
||||||
updateJBRHitTestSpotsAndTitleBarHeightLater();
|
updateNativeTitleBarHeightAndHitTestSpotsLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -353,7 +386,7 @@ public class FlatTitlePane
|
|||||||
installWindowListeners();
|
installWindowListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateJBRHitTestSpotsAndTitleBarHeightLater();
|
updateNativeTitleBarHeightAndHitTestSpotsLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -392,11 +425,23 @@ public class FlatTitlePane
|
|||||||
window.removeComponentListener( handler );
|
window.removeComponentListener( handler );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this title pane currently has an visible and embedded menubar.
|
||||||
|
*/
|
||||||
|
protected boolean hasVisibleEmbeddedMenuBar( JMenuBar menuBar ) {
|
||||||
|
return menuBar != null && menuBar.isVisible() && isMenuBarEmbedded();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the menubar should be embedded into the title pane.
|
||||||
|
*/
|
||||||
protected boolean isMenuBarEmbedded() {
|
protected boolean isMenuBarEmbedded() {
|
||||||
// not storing value of "TitlePane.menuBarEmbedded" in class to allow changing at runtime
|
// not storing value of "TitlePane.menuBarEmbedded" in class to allow changing at runtime
|
||||||
return UIManager.getBoolean( "TitlePane.menuBarEmbedded" ) &&
|
return FlatUIUtils.getBoolean( rootPane,
|
||||||
FlatClientProperties.clientPropertyBoolean( rootPane, FlatClientProperties.MENU_BAR_EMBEDDED, true ) &&
|
FlatSystemProperties.MENUBAR_EMBEDDED,
|
||||||
FlatSystemProperties.getBoolean( FlatSystemProperties.MENUBAR_EMBEDDED, true );
|
FlatClientProperties.MENU_BAR_EMBEDDED,
|
||||||
|
"TitlePane.menuBarEmbedded",
|
||||||
|
false );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Rectangle getMenuBarBounds() {
|
protected Rectangle getMenuBarBounds() {
|
||||||
@@ -410,18 +455,51 @@ public class FlatTitlePane
|
|||||||
Insets borderInsets = getBorder().getBorderInsets( this );
|
Insets borderInsets = getBorder().getBorderInsets( this );
|
||||||
bounds.height += borderInsets.bottom;
|
bounds.height += borderInsets.bottom;
|
||||||
|
|
||||||
return FlatUIUtils.subtractInsets( bounds, UIScale.scale( getMenuBarMargins() ) );
|
// If menu bar is embedded and contains a horizontal glue component,
|
||||||
|
// then make the menu bar wider so that it completely overlaps the title label.
|
||||||
|
// Since the menu bar is not opaque, the title label is still visible.
|
||||||
|
// The title label is moved to the location of the glue component by the layout manager.
|
||||||
|
// This allows placing any component on the trailing side of the title pane.
|
||||||
|
Component horizontalGlue = findHorizontalGlue( rootPane.getJMenuBar() );
|
||||||
|
if( horizontalGlue != null ) {
|
||||||
|
boolean leftToRight = getComponentOrientation().isLeftToRight();
|
||||||
|
int titleWidth = leftToRight
|
||||||
|
? buttonPanel.getX() - (leftPanel.getX() + leftPanel.getWidth())
|
||||||
|
: leftPanel.getX() - (buttonPanel.getX() + buttonPanel.getWidth());
|
||||||
|
titleWidth = Math.max( titleWidth, 0 ); // title width may be negative
|
||||||
|
bounds.width += titleWidth;
|
||||||
|
if( !leftToRight )
|
||||||
|
bounds.x -= titleWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Insets getMenuBarMargins() {
|
return bounds;
|
||||||
return getComponentOrientation().isLeftToRight()
|
}
|
||||||
? menuBarMargins
|
|
||||||
: new Insets( menuBarMargins.top, menuBarMargins.right, menuBarMargins.bottom, menuBarMargins.left );
|
protected Component findHorizontalGlue( JMenuBar menuBar ) {
|
||||||
|
if( menuBar == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
int count = menuBar.getComponentCount();
|
||||||
|
for( int i = count - 1; i >= 0; i-- ) {
|
||||||
|
Component c = menuBar.getComponent( i );
|
||||||
|
if( c instanceof Box.Filler && c.getMaximumSize().width >= Short.MAX_VALUE )
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void titleBarColorsChanged() {
|
||||||
|
activeChanged( window == null || window.isActive() );
|
||||||
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void menuBarChanged() {
|
protected void menuBarChanged() {
|
||||||
menuBarPlaceholder.invalidate();
|
menuBarPlaceholder.invalidate();
|
||||||
|
|
||||||
|
// necessary for the case that an embedded menu bar is made invisible
|
||||||
|
// and a border color is specified
|
||||||
|
repaint();
|
||||||
|
|
||||||
// update title foreground color
|
// update title foreground color
|
||||||
EventQueue.invokeLater( () -> {
|
EventQueue.invokeLater( () -> {
|
||||||
activeChanged( window == null || window.isActive() );
|
activeChanged( window == null || window.isActive() );
|
||||||
@@ -429,7 +507,8 @@ public class FlatTitlePane
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void menuBarLayouted() {
|
protected void menuBarLayouted() {
|
||||||
updateJBRHitTestSpotsAndTitleBarHeightLater();
|
updateNativeTitleBarHeightAndHitTestSpotsLater();
|
||||||
|
revalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*debug
|
/*debug
|
||||||
@@ -442,16 +521,27 @@ public class FlatTitlePane
|
|||||||
g.drawLine( 0, debugTitleBarHeight, getWidth(), debugTitleBarHeight );
|
g.drawLine( 0, debugTitleBarHeight, getWidth(), debugTitleBarHeight );
|
||||||
}
|
}
|
||||||
if( debugHitTestSpots != null ) {
|
if( debugHitTestSpots != null ) {
|
||||||
g.setColor( Color.blue );
|
g.setColor( Color.red );
|
||||||
|
Point offset = SwingUtilities.convertPoint( this, 0, 0, window );
|
||||||
for( Rectangle r : debugHitTestSpots )
|
for( Rectangle r : debugHitTestSpots )
|
||||||
g.drawRect( r.x, r.y, r.width, r.height );
|
g.drawRect( r.x - offset.x, r.y - offset.y, r.width - 1, r.height - 1 );
|
||||||
|
}
|
||||||
|
if( debugAppIconBounds != null ) {
|
||||||
|
g.setColor( Color.blue);
|
||||||
|
Point offset = SwingUtilities.convertPoint( this, 0, 0, window );
|
||||||
|
Rectangle r = debugAppIconBounds;
|
||||||
|
g.drawRect( r.x - offset.x, r.y - offset.y, r.width - 1, r.height - 1 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug*/
|
debug*/
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintComponent( Graphics g ) {
|
protected void paintComponent( Graphics g ) {
|
||||||
g.setColor( getBackground() );
|
// not storing value of "TitlePane.unifiedBackground" in class to allow changing at runtime
|
||||||
|
g.setColor( (UIManager.getBoolean( "TitlePane.unifiedBackground" ) &&
|
||||||
|
clientPropertyColor( rootPane, TITLE_BAR_BACKGROUND, null ) == null)
|
||||||
|
? FlatUIUtils.getParentBackground( this )
|
||||||
|
: getBackground() );
|
||||||
g.fillRect( 0, 0, getWidth(), getHeight() );
|
g.fillRect( 0, 0, getWidth(), getHeight() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -469,11 +559,13 @@ debug*/
|
|||||||
* Iconifies the window.
|
* Iconifies the window.
|
||||||
*/
|
*/
|
||||||
protected void iconify() {
|
protected void iconify() {
|
||||||
if( window instanceof Frame ) {
|
if( !(window instanceof Frame) )
|
||||||
|
return;
|
||||||
|
|
||||||
Frame frame = (Frame) window;
|
Frame frame = (Frame) window;
|
||||||
|
if( !FlatNativeWindowBorder.showWindow( window, FlatNativeWindowBorder.Provider.SW_MINIMIZE ) )
|
||||||
frame.setExtendedState( frame.getExtendedState() | Frame.ICONIFIED );
|
frame.setExtendedState( frame.getExtendedState() | Frame.ICONIFIED );
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximizes the window.
|
* Maximizes the window.
|
||||||
@@ -490,6 +582,7 @@ debug*/
|
|||||||
rootPane.putClientProperty( "_flatlaf.maximizedBoundsUpToDate", true );
|
rootPane.putClientProperty( "_flatlaf.maximizedBoundsUpToDate", true );
|
||||||
|
|
||||||
// maximize window
|
// maximize window
|
||||||
|
if( !FlatNativeWindowBorder.showWindow( frame, FlatNativeWindowBorder.Provider.SW_MAXIMIZE ) )
|
||||||
frame.setExtendedState( frame.getExtendedState() | Frame.MAXIMIZED_BOTH );
|
frame.setExtendedState( frame.getExtendedState() | Frame.MAXIMIZED_BOTH );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -497,9 +590,9 @@ debug*/
|
|||||||
Frame frame = (Frame) window;
|
Frame frame = (Frame) window;
|
||||||
|
|
||||||
// set maximized bounds to avoid that maximized window overlaps Windows task bar
|
// set maximized bounds to avoid that maximized window overlaps Windows task bar
|
||||||
// (if not running in JBR and if not modified from the application)
|
// (if not having native window border and if not modified from the application)
|
||||||
Rectangle oldMaximizedBounds = frame.getMaximizedBounds();
|
Rectangle oldMaximizedBounds = frame.getMaximizedBounds();
|
||||||
if( !hasJBRCustomDecoration() &&
|
if( !hasNativeCustomDecoration() &&
|
||||||
(oldMaximizedBounds == null ||
|
(oldMaximizedBounds == null ||
|
||||||
Objects.equals( oldMaximizedBounds, rootPane.getClientProperty( "_flatlaf.maximizedBounds" ) )) )
|
Objects.equals( oldMaximizedBounds, rootPane.getClientProperty( "_flatlaf.maximizedBounds" ) )) )
|
||||||
{
|
{
|
||||||
@@ -578,8 +671,11 @@ debug*/
|
|||||||
* Restores the window size.
|
* Restores the window size.
|
||||||
*/
|
*/
|
||||||
protected void restore() {
|
protected void restore() {
|
||||||
if( window instanceof Frame ) {
|
if( !(window instanceof Frame) )
|
||||||
|
return;
|
||||||
|
|
||||||
Frame frame = (Frame) window;
|
Frame frame = (Frame) window;
|
||||||
|
if( !FlatNativeWindowBorder.showWindow( window, FlatNativeWindowBorder.Provider.SW_RESTORE ) ) {
|
||||||
int state = frame.getExtendedState();
|
int state = frame.getExtendedState();
|
||||||
frame.setExtendedState( ((state & Frame.ICONIFIED) != 0)
|
frame.setExtendedState( ((state & Frame.ICONIFIED) != 0)
|
||||||
? (state & ~Frame.ICONIFIED)
|
? (state & ~Frame.ICONIFIED)
|
||||||
@@ -595,65 +691,133 @@ debug*/
|
|||||||
window.dispatchEvent( new WindowEvent( window, WindowEvent.WINDOW_CLOSING ) );
|
window.dispatchEvent( new WindowEvent( window, WindowEvent.WINDOW_CLOSING ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean hasJBRCustomDecoration() {
|
private boolean hasJBRCustomDecoration() {
|
||||||
return FlatRootPaneUI.canUseJBRCustomDecorations &&
|
return window != null && JBRCustomDecorations.hasCustomDecoration( window );
|
||||||
window != null &&
|
|
||||||
JBRCustomDecorations.hasCustomDecoration( window );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void updateJBRHitTestSpotsAndTitleBarHeightLater() {
|
/**
|
||||||
|
* Returns whether windows uses native window border and has custom decorations enabled.
|
||||||
|
*/
|
||||||
|
protected boolean hasNativeCustomDecoration() {
|
||||||
|
return window != null && FlatNativeWindowBorder.hasCustomDecoration( window );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateNativeTitleBarHeightAndHitTestSpotsLater() {
|
||||||
EventQueue.invokeLater( () -> {
|
EventQueue.invokeLater( () -> {
|
||||||
updateJBRHitTestSpotsAndTitleBarHeight();
|
updateNativeTitleBarHeightAndHitTestSpots();
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void updateJBRHitTestSpotsAndTitleBarHeight() {
|
protected void updateNativeTitleBarHeightAndHitTestSpots() {
|
||||||
if( !isDisplayable() )
|
if( !isDisplayable() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if( !hasJBRCustomDecoration() )
|
if( !hasNativeCustomDecoration() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
List<Rectangle> hitTestSpots = new ArrayList<>();
|
|
||||||
if( iconLabel.isVisible() )
|
|
||||||
addJBRHitTestSpot( iconLabel, false, hitTestSpots );
|
|
||||||
addJBRHitTestSpot( buttonPanel, false, hitTestSpots );
|
|
||||||
addJBRHitTestSpot( menuBarPlaceholder, true, hitTestSpots );
|
|
||||||
|
|
||||||
int titleBarHeight = getHeight();
|
int titleBarHeight = getHeight();
|
||||||
// slightly reduce height so that component receives mouseExit events
|
// slightly reduce height so that component receives mouseExit events
|
||||||
if( titleBarHeight > 0 )
|
if( titleBarHeight > 0 )
|
||||||
titleBarHeight--;
|
titleBarHeight--;
|
||||||
|
|
||||||
JBRCustomDecorations.setHitTestSpotsAndTitleBarHeight( window, hitTestSpots, titleBarHeight );
|
List<Rectangle> hitTestSpots = new ArrayList<>();
|
||||||
|
Rectangle appIconBounds = null;
|
||||||
|
if( iconLabel.isVisible() ) {
|
||||||
|
// compute real icon size (without insets; 1px wider for easier hitting)
|
||||||
|
Point location = SwingUtilities.convertPoint( iconLabel, 0, 0, window );
|
||||||
|
Insets iconInsets = iconLabel.getInsets();
|
||||||
|
Rectangle iconBounds = new Rectangle(
|
||||||
|
location.x + iconInsets.left - 1,
|
||||||
|
location.y + iconInsets.top - 1,
|
||||||
|
iconLabel.getWidth() - iconInsets.left - iconInsets.right + 2,
|
||||||
|
iconLabel.getHeight() - iconInsets.top - iconInsets.bottom + 2 );
|
||||||
|
|
||||||
|
// if frame is maximized, increase icon bounds to upper-left corner
|
||||||
|
// of window to allow closing window via double-click in upper-left corner
|
||||||
|
if( window instanceof Frame &&
|
||||||
|
(((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0 )
|
||||||
|
{
|
||||||
|
iconBounds.height += iconBounds.y;
|
||||||
|
iconBounds.y = 0;
|
||||||
|
|
||||||
|
if( window.getComponentOrientation().isLeftToRight() ) {
|
||||||
|
iconBounds.width += iconBounds.x;
|
||||||
|
iconBounds.x = 0;
|
||||||
|
} else
|
||||||
|
iconBounds.width += iconInsets.right;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( hasJBRCustomDecoration() )
|
||||||
|
hitTestSpots.add( iconBounds );
|
||||||
|
else
|
||||||
|
appIconBounds = iconBounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle r = getNativeHitTestSpot( buttonPanel );
|
||||||
|
if( r != null )
|
||||||
|
hitTestSpots.add( r );
|
||||||
|
|
||||||
|
JMenuBar menuBar = rootPane.getJMenuBar();
|
||||||
|
if( hasVisibleEmbeddedMenuBar( menuBar ) ) {
|
||||||
|
r = getNativeHitTestSpot( menuBarPlaceholder );
|
||||||
|
if( r != null ) {
|
||||||
|
Component horizontalGlue = findHorizontalGlue( menuBar );
|
||||||
|
if( horizontalGlue != null ) {
|
||||||
|
// If menu bar is embedded and contains a horizontal glue component,
|
||||||
|
// then split the hit test spot into two spots so that
|
||||||
|
// the glue component area can used to move the window.
|
||||||
|
|
||||||
|
Point glueLocation = SwingUtilities.convertPoint( horizontalGlue, 0, 0, window );
|
||||||
|
Rectangle r2;
|
||||||
|
if( getComponentOrientation().isLeftToRight() ) {
|
||||||
|
int trailingWidth = (r.x + r.width - HIT_TEST_SPOT_GROW) - glueLocation.x;
|
||||||
|
r.width -= trailingWidth;
|
||||||
|
r2 = new Rectangle( glueLocation.x + horizontalGlue.getWidth(), r.y, trailingWidth, r.height );
|
||||||
|
} else {
|
||||||
|
int leadingWidth = (glueLocation.x + horizontalGlue.getWidth()) - (r.x + HIT_TEST_SPOT_GROW);
|
||||||
|
r.x += leadingWidth;
|
||||||
|
r.width -= leadingWidth;
|
||||||
|
r2 = new Rectangle( glueLocation.x -leadingWidth, r.y, leadingWidth, r.height );
|
||||||
|
}
|
||||||
|
r2.grow( HIT_TEST_SPOT_GROW, HIT_TEST_SPOT_GROW );
|
||||||
|
hitTestSpots.add( r2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
hitTestSpots.add( r );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatNativeWindowBorder.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots, appIconBounds );
|
||||||
|
|
||||||
/*debug
|
/*debug
|
||||||
debugHitTestSpots = hitTestSpots;
|
|
||||||
debugTitleBarHeight = titleBarHeight;
|
debugTitleBarHeight = titleBarHeight;
|
||||||
|
debugHitTestSpots = hitTestSpots;
|
||||||
|
debugAppIconBounds = appIconBounds;
|
||||||
repaint();
|
repaint();
|
||||||
debug*/
|
debug*/
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addJBRHitTestSpot( JComponent c, boolean subtractMenuBarMargins, List<Rectangle> hitTestSpots ) {
|
protected Rectangle getNativeHitTestSpot( JComponent c ) {
|
||||||
Dimension size = c.getSize();
|
Dimension size = c.getSize();
|
||||||
if( size.width <= 0 || size.height <= 0 )
|
if( size.width <= 0 || size.height <= 0 )
|
||||||
return;
|
return null;
|
||||||
|
|
||||||
Point location = SwingUtilities.convertPoint( c, 0, 0, window );
|
Point location = SwingUtilities.convertPoint( c, 0, 0, window );
|
||||||
Rectangle r = new Rectangle( location, size );
|
Rectangle r = new Rectangle( location, size );
|
||||||
if( subtractMenuBarMargins )
|
|
||||||
r = FlatUIUtils.subtractInsets( r, UIScale.scale( getMenuBarMargins() ) );
|
|
||||||
// slightly increase rectangle so that component receives mouseExit events
|
// slightly increase rectangle so that component receives mouseExit events
|
||||||
r.grow( 2, 2 );
|
r.grow( HIT_TEST_SPOT_GROW, HIT_TEST_SPOT_GROW );
|
||||||
hitTestSpots.add( r );
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final int HIT_TEST_SPOT_GROW = 2;
|
||||||
|
|
||||||
/*debug
|
/*debug
|
||||||
private List<Rectangle> debugHitTestSpots;
|
|
||||||
private int debugTitleBarHeight;
|
private int debugTitleBarHeight;
|
||||||
|
private List<Rectangle> debugHitTestSpots;
|
||||||
|
private Rectangle debugAppIconBounds;
|
||||||
debug*/
|
debug*/
|
||||||
|
|
||||||
//---- class TitlePaneBorder ----------------------------------------------
|
//---- class FlatTitlePaneBorder ------------------------------------------
|
||||||
|
|
||||||
protected class FlatTitlePaneBorder
|
protected class FlatTitlePaneBorder
|
||||||
extends AbstractBorder
|
extends AbstractBorder
|
||||||
@@ -662,33 +826,84 @@ debug*/
|
|||||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
super.getBorderInsets( c, insets );
|
super.getBorderInsets( c, insets );
|
||||||
|
|
||||||
// if menu bar is embedded, add bottom insets of menu bar border
|
|
||||||
Border menuBarBorder = getMenuBarBorder();
|
Border menuBarBorder = getMenuBarBorder();
|
||||||
if( menuBarBorder != null ) {
|
if( menuBarBorder != null ) {
|
||||||
|
// if menu bar is embedded, add bottom insets of menu bar border
|
||||||
Insets menuBarInsets = menuBarBorder.getBorderInsets( c );
|
Insets menuBarInsets = menuBarBorder.getBorderInsets( c );
|
||||||
insets.bottom += menuBarInsets.bottom;
|
insets.bottom += menuBarInsets.bottom;
|
||||||
}
|
} else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) )
|
||||||
|
insets.bottom += UIScale.scale( 1 );
|
||||||
|
|
||||||
if( hasJBRCustomDecoration() )
|
if( hasNativeCustomDecoration() && !isWindowMaximized( c ) )
|
||||||
insets = FlatUIUtils.addInsets( insets, JBRWindowTopBorder.getInstance().getBorderInsets() );
|
insets = FlatUIUtils.addInsets( insets, WindowTopBorder.getInstance().getBorderInsets() );
|
||||||
|
|
||||||
return insets;
|
return insets;
|
||||||
}
|
}
|
||||||
|
|
||||||
@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 menu bar is embedded, paint menu bar border
|
// paint bottom border
|
||||||
Border menuBarBorder = getMenuBarBorder();
|
Border menuBarBorder = getMenuBarBorder();
|
||||||
if( menuBarBorder != null )
|
if( menuBarBorder != null ) {
|
||||||
|
// if menu bar is embedded, paint menu bar border
|
||||||
menuBarBorder.paintBorder( c, g, x, y, width, height );
|
menuBarBorder.paintBorder( c, g, x, y, width, height );
|
||||||
|
} else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) ) {
|
||||||
|
// paint border between title pane and content if border color is specified
|
||||||
|
float lineHeight = UIScale.scale( (float) 1 );
|
||||||
|
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight );
|
||||||
|
}
|
||||||
|
|
||||||
if( hasJBRCustomDecoration() )
|
if( hasNativeCustomDecoration() && !isWindowMaximized( c ) )
|
||||||
JBRWindowTopBorder.getInstance().paintBorder( c, g, x, y, width, height );
|
WindowTopBorder.getInstance().paintBorder( c, g, x, y, width, height );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Border getMenuBarBorder() {
|
protected Border getMenuBarBorder() {
|
||||||
JMenuBar menuBar = rootPane.getJMenuBar();
|
JMenuBar menuBar = rootPane.getJMenuBar();
|
||||||
return (menuBar != null && isMenuBarEmbedded()) ? menuBar.getBorder() : null;
|
return hasVisibleEmbeddedMenuBar( menuBar ) ? menuBar.getBorder() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isWindowMaximized( Component c ) {
|
||||||
|
return window instanceof Frame
|
||||||
|
? (((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class FlatTitleLabelUI ---------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
protected class FlatTitleLabelUI
|
||||||
|
extends FlatLabelUI
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected void paintEnabledText( JLabel l, Graphics g, String s, int textX, int textY ) {
|
||||||
|
boolean hasEmbeddedMenuBar = hasVisibleEmbeddedMenuBar( rootPane.getJMenuBar() );
|
||||||
|
int labelWidth = l.getWidth();
|
||||||
|
int textWidth = labelWidth - (textX * 2);
|
||||||
|
int gap = UIScale.scale( menuBarTitleGap );
|
||||||
|
|
||||||
|
// The passed in textX coordinate is always to horizontally center the text within the label bounds.
|
||||||
|
// Modify textX so that the text is painted either centered within the window bounds or leading aligned.
|
||||||
|
boolean center = hasEmbeddedMenuBar ? centerTitleIfMenuBarEmbedded : centerTitle;
|
||||||
|
if( center ) {
|
||||||
|
// If window is wide enough, center title within window bounds.
|
||||||
|
// Otherwise leave it centered within free space (label bounds).
|
||||||
|
int centeredTextX = ((l.getParent().getWidth() - textWidth) / 2) - l.getX();
|
||||||
|
if( centeredTextX >= gap && centeredTextX + textWidth <= labelWidth - gap )
|
||||||
|
textX = centeredTextX;
|
||||||
|
} else {
|
||||||
|
// leading aligned
|
||||||
|
boolean leftToRight = getComponentOrientation().isLeftToRight();
|
||||||
|
Insets insets = l.getInsets();
|
||||||
|
int leadingInset = hasEmbeddedMenuBar ? gap : (leftToRight ? insets.left : insets.right);
|
||||||
|
int leadingTextX = leftToRight ? leadingInset : labelWidth - leadingInset - textWidth;
|
||||||
|
if( leftToRight ? leadingTextX < textX : leadingTextX > textX )
|
||||||
|
textX = leadingTextX;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.paintEnabledText( l, g, s, textX, textY );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -717,7 +932,7 @@ debug*/
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "componentOrientation":
|
case "componentOrientation":
|
||||||
updateJBRHitTestSpotsAndTitleBarHeightLater();
|
updateNativeTitleBarHeightAndHitTestSpotsLater();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -727,10 +942,10 @@ debug*/
|
|||||||
@Override
|
@Override
|
||||||
public void windowActivated( WindowEvent e ) {
|
public void windowActivated( WindowEvent e ) {
|
||||||
activeChanged( true );
|
activeChanged( true );
|
||||||
updateJBRHitTestSpotsAndTitleBarHeight();
|
updateNativeTitleBarHeightAndHitTestSpots();
|
||||||
|
|
||||||
if( hasJBRCustomDecoration() )
|
if( hasNativeCustomDecoration() )
|
||||||
JBRWindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this );
|
WindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this );
|
||||||
|
|
||||||
repaintWindowBorder();
|
repaintWindowBorder();
|
||||||
}
|
}
|
||||||
@@ -738,10 +953,10 @@ debug*/
|
|||||||
@Override
|
@Override
|
||||||
public void windowDeactivated( WindowEvent e ) {
|
public void windowDeactivated( WindowEvent e ) {
|
||||||
activeChanged( false );
|
activeChanged( false );
|
||||||
updateJBRHitTestSpotsAndTitleBarHeight();
|
updateNativeTitleBarHeightAndHitTestSpots();
|
||||||
|
|
||||||
if( hasJBRCustomDecoration() )
|
if( hasNativeCustomDecoration() )
|
||||||
JBRWindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this );
|
WindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this );
|
||||||
|
|
||||||
repaintWindowBorder();
|
repaintWindowBorder();
|
||||||
}
|
}
|
||||||
@@ -749,13 +964,12 @@ debug*/
|
|||||||
@Override
|
@Override
|
||||||
public void windowStateChanged( WindowEvent e ) {
|
public void windowStateChanged( WindowEvent e ) {
|
||||||
frameStateChanged();
|
frameStateChanged();
|
||||||
updateJBRHitTestSpotsAndTitleBarHeight();
|
updateNativeTitleBarHeightAndHitTestSpots();
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- interface MouseListener ----
|
//---- interface MouseListener ----
|
||||||
|
|
||||||
private int lastXOnScreen;
|
private Point dragOffset;
|
||||||
private int lastYOnScreen;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseClicked( MouseEvent e ) {
|
public void mouseClicked( MouseEvent e ) {
|
||||||
@@ -763,7 +977,7 @@ debug*/
|
|||||||
if( e.getSource() == iconLabel ) {
|
if( e.getSource() == iconLabel ) {
|
||||||
// double-click on icon closes window
|
// double-click on icon closes window
|
||||||
close();
|
close();
|
||||||
} else if( !hasJBRCustomDecoration() &&
|
} else if( !hasNativeCustomDecoration() &&
|
||||||
window instanceof Frame &&
|
window instanceof Frame &&
|
||||||
((Frame)window).isResizable() )
|
((Frame)window).isResizable() )
|
||||||
{
|
{
|
||||||
@@ -779,8 +993,10 @@ debug*/
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mousePressed( MouseEvent e ) {
|
public void mousePressed( MouseEvent e ) {
|
||||||
lastXOnScreen = e.getXOnScreen();
|
if( window == null )
|
||||||
lastYOnScreen = e.getYOnScreen();
|
return; // should newer occur
|
||||||
|
|
||||||
|
dragOffset = SwingUtilities.convertPoint( FlatTitlePane.this, e.getPoint(), window );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void mouseReleased( MouseEvent e ) {}
|
@Override public void mouseReleased( MouseEvent e ) {}
|
||||||
@@ -791,46 +1007,45 @@ debug*/
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseDragged( MouseEvent e ) {
|
public void mouseDragged( MouseEvent e ) {
|
||||||
if( hasJBRCustomDecoration() )
|
if( window == null )
|
||||||
return; // do nothing if running in JBR
|
return; // should newer occur
|
||||||
|
|
||||||
int xOnScreen = e.getXOnScreen();
|
if( hasNativeCustomDecoration() )
|
||||||
int yOnScreen = e.getYOnScreen();
|
return; // do nothing if having native window border
|
||||||
if( lastXOnScreen == xOnScreen && lastYOnScreen == yOnScreen )
|
|
||||||
return;
|
|
||||||
|
|
||||||
// restore window if it is maximized
|
// restore window if it is maximized
|
||||||
if( window instanceof Frame ) {
|
if( window instanceof Frame ) {
|
||||||
Frame frame = (Frame) window;
|
Frame frame = (Frame) window;
|
||||||
int state = frame.getExtendedState();
|
int state = frame.getExtendedState();
|
||||||
if( (state & Frame.MAXIMIZED_BOTH) != 0 ) {
|
if( (state & Frame.MAXIMIZED_BOTH) != 0 ) {
|
||||||
int maximizedX = window.getX();
|
int maximizedWidth = window.getWidth();
|
||||||
int maximizedY = window.getY();
|
|
||||||
|
|
||||||
// restore window size, which also moves window to pre-maximized location
|
// restore window size, which also moves window to pre-maximized location
|
||||||
frame.setExtendedState( state & ~Frame.MAXIMIZED_BOTH );
|
frame.setExtendedState( state & ~Frame.MAXIMIZED_BOTH );
|
||||||
|
|
||||||
|
// fix drag offset to ensure that window remains under mouse position
|
||||||
|
// for the case that dragging starts in the right area of the maximized window
|
||||||
int restoredWidth = window.getWidth();
|
int restoredWidth = window.getWidth();
|
||||||
int newX = maximizedX;
|
int center = restoredWidth / 2;
|
||||||
JComponent rightComp = getComponentOrientation().isLeftToRight() ? buttonPanel : leftPanel;
|
if( dragOffset.x > center ) {
|
||||||
if( xOnScreen >= maximizedX + restoredWidth - rightComp.getWidth() - 10 )
|
// this is same/similar to what Windows 10 does
|
||||||
newX = xOnScreen + rightComp.getWidth() + 10 - restoredWidth;
|
if( dragOffset.x > maximizedWidth - center )
|
||||||
|
dragOffset.x = restoredWidth - (maximizedWidth - dragOffset.x);
|
||||||
// move window near mouse
|
else
|
||||||
window.setLocation( newX, maximizedY );
|
dragOffset.x = center;
|
||||||
return;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute new window location
|
// compute new window location
|
||||||
int newX = window.getX() + (xOnScreen - lastXOnScreen);
|
int newX = e.getXOnScreen() - dragOffset.x;
|
||||||
int newY = window.getY() + (yOnScreen - lastYOnScreen);
|
int newY = e.getYOnScreen() - dragOffset.y;
|
||||||
|
|
||||||
|
if( newX == window.getX() && newY == window.getY() )
|
||||||
|
return;
|
||||||
|
|
||||||
// move window
|
// move window
|
||||||
window.setLocation( newX, newY );
|
window.setLocation( newX, newY );
|
||||||
|
|
||||||
lastXOnScreen = xOnScreen;
|
|
||||||
lastYOnScreen = yOnScreen;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void mouseMoved( MouseEvent e ) {}
|
@Override public void mouseMoved( MouseEvent e ) {}
|
||||||
@@ -839,7 +1054,7 @@ debug*/
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void componentResized( ComponentEvent e ) {
|
public void componentResized( ComponentEvent e ) {
|
||||||
updateJBRHitTestSpotsAndTitleBarHeightLater();
|
updateNativeTitleBarHeightAndHitTestSpotsLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -20,8 +20,6 @@ import java.awt.Dimension;
|
|||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.swing.Icon;
|
|
||||||
import javax.swing.ImageIcon;
|
|
||||||
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
|
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
|
||||||
import com.formdev.flatlaf.util.ScaledImageIcon;
|
import com.formdev.flatlaf.util.ScaledImageIcon;
|
||||||
|
|
||||||
@@ -31,40 +29,43 @@ import com.formdev.flatlaf.util.ScaledImageIcon;
|
|||||||
public class FlatTitlePaneIcon
|
public class FlatTitlePaneIcon
|
||||||
extends ScaledImageIcon
|
extends ScaledImageIcon
|
||||||
{
|
{
|
||||||
public static Icon create( List<Image> images, Dimension size ) {
|
private final List<Image> images;
|
||||||
// collect all images including multi-resolution variants
|
|
||||||
|
/**
|
||||||
|
* @since 1.2
|
||||||
|
*/
|
||||||
|
public FlatTitlePaneIcon( List<Image> images, Dimension size ) {
|
||||||
|
super( null, size.width, size.height );
|
||||||
|
this.images = images;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Image getResolutionVariant( int destImageWidth, int destImageHeight ) {
|
||||||
|
// collect all images including multi-resolution variants for requested size
|
||||||
List<Image> allImages = new ArrayList<>();
|
List<Image> allImages = new ArrayList<>();
|
||||||
for( Image image : images ) {
|
for( Image image : images ) {
|
||||||
if( MultiResolutionImageSupport.isMultiResolutionImage( image ) )
|
if( MultiResolutionImageSupport.isMultiResolutionImage( image ) )
|
||||||
allImages.addAll( MultiResolutionImageSupport.getResolutionVariants( image ) );
|
allImages.add( MultiResolutionImageSupport.getResolutionVariant( image, destImageWidth, destImageHeight ) );
|
||||||
else
|
else
|
||||||
allImages.add( image );
|
allImages.add( image );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( allImages.size() == 1 )
|
||||||
|
return allImages.get( 0 );
|
||||||
|
|
||||||
// sort images by size
|
// sort images by size
|
||||||
allImages.sort( (image1, image2) -> {
|
allImages.sort( (image1, image2) -> {
|
||||||
return image1.getWidth( null ) - image2.getWidth( null );
|
return image1.getWidth( null ) - image2.getWidth( null );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
// create icon
|
// search for optimal image size
|
||||||
return new FlatTitlePaneIcon( allImages, size );
|
for( Image image : allImages ) {
|
||||||
}
|
|
||||||
|
|
||||||
private final List<Image> images;
|
|
||||||
|
|
||||||
private FlatTitlePaneIcon( List<Image> images, Dimension size ) {
|
|
||||||
super( new ImageIcon( images.get( 0 ) ), size.width, size.height );
|
|
||||||
this.images = images;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Image getResolutionVariant( int destImageWidth, int destImageHeight ) {
|
|
||||||
for( Image image : images ) {
|
|
||||||
if( destImageWidth <= image.getWidth( null ) &&
|
if( destImageWidth <= image.getWidth( null ) &&
|
||||||
destImageHeight <= image.getHeight( null ) )
|
destImageHeight <= image.getHeight( null ) )
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
return images.get( images.size() - 1 );
|
// use largest image
|
||||||
|
return allImages.get( allImages.size() - 1 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,12 +82,8 @@ public class FlatToggleButtonUI
|
|||||||
|
|
||||||
private boolean defaults_initialized = false;
|
private boolean defaults_initialized = false;
|
||||||
|
|
||||||
private static ComponentUI instance;
|
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
if( instance == null )
|
return FlatUIUtils.createSharedUI( FlatToggleButtonUI.class, FlatToggleButtonUI::new );
|
||||||
instance = new FlatToggleButtonUI();
|
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -150,10 +146,17 @@ public class FlatToggleButtonUI
|
|||||||
int height = c.getHeight();
|
int height = c.getHeight();
|
||||||
int width = c.getWidth();
|
int width = c.getWidth();
|
||||||
boolean selected = ((AbstractButton)c).isSelected();
|
boolean selected = ((AbstractButton)c).isSelected();
|
||||||
|
Color enabledColor = selected ? clientPropertyColor( c, TAB_BUTTON_SELECTED_BACKGROUND, tabSelectedBackground ) : null;
|
||||||
|
|
||||||
|
// use component background if explicitly set
|
||||||
|
if( enabledColor == null ) {
|
||||||
|
Color bg = c.getBackground();
|
||||||
|
if( isCustomBackground( bg ) )
|
||||||
|
enabledColor = bg;
|
||||||
|
}
|
||||||
|
|
||||||
// paint background
|
// paint background
|
||||||
Color background = buttonStateColor( c,
|
Color background = buttonStateColor( c, enabledColor,
|
||||||
selected ? clientPropertyColor( c, TAB_BUTTON_SELECTED_BACKGROUND, tabSelectedBackground ) : null,
|
|
||||||
null, tabFocusBackground, tabHoverBackground, null );
|
null, tabFocusBackground, tabHoverBackground, null );
|
||||||
if( background != null ) {
|
if( background != null ) {
|
||||||
g.setColor( background );
|
g.setColor( background );
|
||||||
|
|||||||
@@ -16,15 +16,16 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
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;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
|
import java.awt.Rectangle;
|
||||||
import javax.swing.JToolBar;
|
import javax.swing.JToolBar;
|
||||||
import javax.swing.SwingConstants;
|
import javax.swing.SwingConstants;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Border for {@link javax.swing.JToolBar}.
|
* Border for {@link javax.swing.JToolBar}.
|
||||||
@@ -39,7 +40,7 @@ public class FlatToolBarBorder
|
|||||||
{
|
{
|
||||||
private static final int DOT_COUNT = 4;
|
private static final int DOT_COUNT = 4;
|
||||||
private static final int DOT_SIZE = 2;
|
private static final int DOT_SIZE = 2;
|
||||||
private static final int GRIP_WIDTH = DOT_SIZE * 3;
|
private static final int GRIP_SIZE = DOT_SIZE * 3;
|
||||||
|
|
||||||
protected final Color gripColor = UIManager.getColor( "ToolBar.gripColor" );
|
protected final Color gripColor = UIManager.getColor( "ToolBar.gripColor" );
|
||||||
|
|
||||||
@@ -64,35 +65,27 @@ public class FlatToolBarBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void paintGrip( Component c, Graphics g, int x, int y, int width, int height ) {
|
protected void paintGrip( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
int dotSize = scale( DOT_SIZE );
|
Rectangle r = calculateGripBounds( c, x, y, width, height );
|
||||||
int gapSize = dotSize;
|
FlatUIUtils.paintGrip( g, r.x, r.y, r.width, r.height,
|
||||||
int gripSize = (dotSize * DOT_COUNT) + ((gapSize * (DOT_COUNT - 1)));
|
((JToolBar)c).getOrientation() == SwingConstants.VERTICAL,
|
||||||
|
DOT_COUNT, DOT_SIZE, DOT_SIZE, false );
|
||||||
// include toolbar margin in grip position calculation
|
|
||||||
Insets insets = getBorderInsets( c );
|
|
||||||
|
|
||||||
// calculate grip position
|
|
||||||
boolean horizontal = ((JToolBar)c).getOrientation() == SwingConstants.HORIZONTAL;
|
|
||||||
if( horizontal ) {
|
|
||||||
if( c.getComponentOrientation().isLeftToRight() )
|
|
||||||
x += insets.left - (dotSize * 2);
|
|
||||||
else
|
|
||||||
x += width - insets.right + dotSize;
|
|
||||||
y += Math.round( (height - gripSize) / 2f );
|
|
||||||
} else {
|
|
||||||
// vertical
|
|
||||||
x += Math.round( (width - gripSize) / 2f );
|
|
||||||
y += insets.top - (dotSize * 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// paint dots
|
protected Rectangle calculateGripBounds( Component c, int x, int y, int width, int height ) {
|
||||||
for( int i = 0; i < DOT_COUNT; i++ ) {
|
// include toolbar margin in grip bounds calculation
|
||||||
g.fillOval( x, y, dotSize, dotSize );
|
Insets insets = super.getBorderInsets( c, new Insets( 0, 0, 0, 0 ) );
|
||||||
if( horizontal )
|
Rectangle r = FlatUIUtils.subtractInsets( new Rectangle( x, y, width, height ), insets );
|
||||||
y += dotSize + gapSize;
|
|
||||||
else
|
// calculate grip bounds
|
||||||
x += dotSize + gapSize;
|
int gripSize = UIScale.scale( GRIP_SIZE );
|
||||||
}
|
if( ((JToolBar)c).getOrientation() == SwingConstants.HORIZONTAL ) {
|
||||||
|
if( !c.getComponentOrientation().isLeftToRight() )
|
||||||
|
r.x = r.x + r.width - gripSize;
|
||||||
|
r.width = gripSize;
|
||||||
|
} else
|
||||||
|
r.height = gripSize;
|
||||||
|
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -101,7 +94,7 @@ public class FlatToolBarBorder
|
|||||||
|
|
||||||
// add grip inset if floatable
|
// add grip inset if floatable
|
||||||
if( c instanceof JToolBar && ((JToolBar)c).isFloatable() ) {
|
if( c instanceof JToolBar && ((JToolBar)c).isFloatable() ) {
|
||||||
int gripInset = scale( GRIP_WIDTH );
|
int gripInset = UIScale.scale( GRIP_SIZE );
|
||||||
if( ((JToolBar)c).getOrientation() == SwingConstants.HORIZONTAL ) {
|
if( ((JToolBar)c).getOrientation() == SwingConstants.HORIZONTAL ) {
|
||||||
if( c.getComponentOrientation().isLeftToRight() )
|
if( c.getComponentOrientation().isLeftToRight() )
|
||||||
insets.left += gripInset;
|
insets.left += gripInset;
|
||||||
|
|||||||
@@ -50,12 +50,8 @@ public class FlatToolBarSeparatorUI
|
|||||||
|
|
||||||
private boolean defaults_initialized = false;
|
private boolean defaults_initialized = false;
|
||||||
|
|
||||||
private static ComponentUI instance;
|
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
if( instance == null )
|
return FlatUIUtils.createSharedUI( FlatToolBarSeparatorUI.class, FlatToolBarSeparatorUI::new );
|
||||||
instance = new FlatToolBarSeparatorUI();
|
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -110,13 +106,15 @@ public class FlatToolBarSeparatorUI
|
|||||||
float lineWidth = scale( 1f );
|
float lineWidth = scale( 1f );
|
||||||
float offset = scale( 2f );
|
float offset = scale( 2f );
|
||||||
|
|
||||||
FlatUIUtils.setRenderingHints( (Graphics2D) g );
|
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
||||||
g.setColor( separatorColor );
|
g.setColor( separatorColor );
|
||||||
|
|
||||||
if( isVertical( c ) )
|
if( isVertical( c ) )
|
||||||
((Graphics2D)g).fill( new Rectangle2D.Float( Math.round( (width - lineWidth) / 2f ), offset, lineWidth, height - (offset * 2) ) );
|
((Graphics2D)g).fill( new Rectangle2D.Float( Math.round( (width - lineWidth) / 2f ), offset, lineWidth, height - (offset * 2) ) );
|
||||||
else
|
else
|
||||||
((Graphics2D)g).fill( new Rectangle2D.Float( offset, Math.round( (height - lineWidth) / 2f ), width - (offset * 2), lineWidth ) );
|
((Graphics2D)g).fill( new Rectangle2D.Float( offset, Math.round( (height - lineWidth) / 2f ), width - (offset * 2), lineWidth ) );
|
||||||
|
|
||||||
|
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isVertical( JComponent c ) {
|
private boolean isVertical( JComponent c ) {
|
||||||
|
|||||||
@@ -52,12 +52,8 @@ public class FlatToolTipUI
|
|||||||
{
|
{
|
||||||
private static PropertyChangeListener sharedPropertyChangedListener;
|
private static PropertyChangeListener sharedPropertyChangedListener;
|
||||||
|
|
||||||
private static ComponentUI instance;
|
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
if( instance == null )
|
return FlatUIUtils.createSharedUI( FlatToolTipUI.class, FlatToolTipUI::new );
|
||||||
instance = new FlatToolTipUI();
|
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -75,7 +71,7 @@ public class FlatToolTipUI
|
|||||||
if( sharedPropertyChangedListener == null ) {
|
if( sharedPropertyChangedListener == null ) {
|
||||||
sharedPropertyChangedListener = e -> {
|
sharedPropertyChangedListener = e -> {
|
||||||
String name = e.getPropertyName();
|
String name = e.getPropertyName();
|
||||||
if( name == "text" || name == "font" || name == "foreground" ) {
|
if( name == "tiptext" || name == "font" || name == "foreground" ) {
|
||||||
JToolTip toolTip = (JToolTip) e.getSource();
|
JToolTip toolTip = (JToolTip) e.getSource();
|
||||||
FlatLabelUI.updateHTMLRenderer( toolTip, toolTip.getTipText(), false );
|
FlatLabelUI.updateHTMLRenderer( toolTip, toolTip.getTipText(), false );
|
||||||
}
|
}
|
||||||
@@ -120,7 +116,6 @@ public class FlatToolTipUI
|
|||||||
FontMetrics fm = c.getFontMetrics( c.getFont() );
|
FontMetrics fm = c.getFontMetrics( c.getFont() );
|
||||||
Insets insets = c.getInsets();
|
Insets insets = c.getInsets();
|
||||||
|
|
||||||
FlatUIUtils.setRenderingHints( (Graphics2D) g );
|
|
||||||
g.setColor( c.getForeground() );
|
g.setColor( c.getForeground() );
|
||||||
|
|
||||||
List<String> lines = StringUtils.split( ((JToolTip)c).getTipText(), '\n' );
|
List<String> lines = StringUtils.split( ((JToolTip)c).getTipText(), '\n' );
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import static com.formdev.flatlaf.FlatClientProperties.*;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
@@ -23,10 +25,11 @@ import java.awt.Insets;
|
|||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.awt.event.MouseListener;
|
import java.awt.event.MouseListener;
|
||||||
import java.beans.PropertyChangeEvent;
|
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import javax.swing.CellRendererPane;
|
import javax.swing.CellRendererPane;
|
||||||
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JTree;
|
import javax.swing.JTree;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
@@ -104,6 +107,8 @@ public class FlatTreeUI
|
|||||||
protected boolean wideSelection;
|
protected boolean wideSelection;
|
||||||
protected boolean showCellFocusIndicator;
|
protected boolean showCellFocusIndicator;
|
||||||
|
|
||||||
|
private Color defaultCellNonSelectionBackground;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatTreeUI();
|
return new FlatTreeUI();
|
||||||
}
|
}
|
||||||
@@ -122,6 +127,8 @@ public class FlatTreeUI
|
|||||||
wideSelection = UIManager.getBoolean( "Tree.wideSelection" );
|
wideSelection = UIManager.getBoolean( "Tree.wideSelection" );
|
||||||
showCellFocusIndicator = UIManager.getBoolean( "Tree.showCellFocusIndicator" );
|
showCellFocusIndicator = UIManager.getBoolean( "Tree.showCellFocusIndicator" );
|
||||||
|
|
||||||
|
defaultCellNonSelectionBackground = UIManager.getColor( "Tree.textBackground" );
|
||||||
|
|
||||||
// scale
|
// scale
|
||||||
int rowHeight = FlatUIUtils.getUIInt( "Tree.rowHeight", 16 );
|
int rowHeight = FlatUIUtils.getUIInt( "Tree.rowHeight", 16 );
|
||||||
if( rowHeight > 0 )
|
if( rowHeight > 0 )
|
||||||
@@ -141,13 +148,12 @@ public class FlatTreeUI
|
|||||||
selectionInactiveBackground = null;
|
selectionInactiveBackground = null;
|
||||||
selectionInactiveForeground = null;
|
selectionInactiveForeground = null;
|
||||||
selectionBorderColor = null;
|
selectionBorderColor = null;
|
||||||
|
|
||||||
|
defaultCellNonSelectionBackground = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected MouseListener createMouseListener() {
|
protected MouseListener createMouseListener() {
|
||||||
if( !wideSelection )
|
|
||||||
return super.createMouseListener();
|
|
||||||
|
|
||||||
return new BasicTreeUI.MouseHandler() {
|
return new BasicTreeUI.MouseHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void mousePressed( MouseEvent e ) {
|
public void mousePressed( MouseEvent e ) {
|
||||||
@@ -165,7 +171,7 @@ public class FlatTreeUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
private MouseEvent handleWideMouseEvent( MouseEvent e ) {
|
private MouseEvent handleWideMouseEvent( MouseEvent e ) {
|
||||||
if( !tree.isEnabled() || !SwingUtilities.isLeftMouseButton( e ) || e.isConsumed() )
|
if( !isWideSelection() || !tree.isEnabled() || !SwingUtilities.isLeftMouseButton( e ) || e.isConsumed() )
|
||||||
return e;
|
return e;
|
||||||
|
|
||||||
int x = e.getX();
|
int x = e.getX();
|
||||||
@@ -192,19 +198,27 @@ public class FlatTreeUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PropertyChangeListener createPropertyChangeListener() {
|
protected PropertyChangeListener createPropertyChangeListener() {
|
||||||
if( !wideSelection )
|
PropertyChangeListener superListener = super.createPropertyChangeListener();
|
||||||
return super.createPropertyChangeListener();
|
return e -> {
|
||||||
|
superListener.propertyChange( e );
|
||||||
|
|
||||||
return new BasicTreeUI.PropertyChangeHandler() {
|
if( e.getSource() == tree ) {
|
||||||
@Override
|
switch( e.getPropertyName() ) {
|
||||||
public void propertyChange( PropertyChangeEvent e ) {
|
case TREE_WIDE_SELECTION:
|
||||||
super.propertyChange( e );
|
case TREE_PAINT_SELECTION:
|
||||||
|
tree.repaint();
|
||||||
|
break;
|
||||||
|
|
||||||
if( e.getSource() == tree && e.getPropertyName() == "dropLocation" ) {
|
case "dropLocation":
|
||||||
|
if( isWideSelection() ) {
|
||||||
JTree.DropLocation oldValue = (JTree.DropLocation) e.getOldValue();
|
JTree.DropLocation oldValue = (JTree.DropLocation) e.getOldValue();
|
||||||
repaintWideDropLocation( oldValue );
|
repaintWideDropLocation( oldValue );
|
||||||
repaintWideDropLocation( tree.getDropLocation() );
|
repaintWideDropLocation( tree.getDropLocation() );
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void repaintWideDropLocation(JTree.DropLocation loc) {
|
private void repaintWideDropLocation(JTree.DropLocation loc) {
|
||||||
@@ -215,8 +229,6 @@ public class FlatTreeUI
|
|||||||
if( r != null )
|
if( r != null )
|
||||||
tree.repaint( 0, r.y, tree.getWidth(), r.height );
|
tree.repaint( 0, r.y, tree.getWidth(), r.height );
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as super.paintRow(), but supports wide selection and uses
|
* Same as super.paintRow(), but supports wide selection and uses
|
||||||
@@ -227,34 +239,22 @@ public class FlatTreeUI
|
|||||||
TreePath path, int row, boolean isExpanded, boolean hasBeenExpanded, boolean isLeaf )
|
TreePath path, int row, boolean isExpanded, boolean hasBeenExpanded, boolean isLeaf )
|
||||||
{
|
{
|
||||||
boolean isEditing = (editingComponent != null && editingRow == row);
|
boolean isEditing = (editingComponent != null && editingRow == row);
|
||||||
boolean hasFocus = FlatUIUtils.isPermanentFocusOwner( tree );
|
|
||||||
boolean cellHasFocus = hasFocus && (row == getLeadSelectionRow());
|
|
||||||
boolean isSelected = tree.isRowSelected( row );
|
boolean isSelected = tree.isRowSelected( row );
|
||||||
boolean isDropRow = isDropRow( row );
|
boolean isDropRow = isDropRow( row );
|
||||||
|
boolean needsSelectionPainting = (isSelected || isDropRow) && isPaintSelection();
|
||||||
|
|
||||||
|
// do not paint row if editing, except if selection needs painted
|
||||||
|
if( isEditing && !needsSelectionPainting )
|
||||||
|
return;
|
||||||
|
|
||||||
|
boolean hasFocus = FlatUIUtils.isPermanentFocusOwner( tree );
|
||||||
|
boolean cellHasFocus = hasFocus && (row == getLeadSelectionRow());
|
||||||
|
|
||||||
// if tree is used as cell renderer in another component (e.g. in Rhino JavaScript debugger),
|
// if tree is used as cell renderer in another component (e.g. in Rhino JavaScript debugger),
|
||||||
// check whether that component is focused to get correct selection colors
|
// check whether that component is focused to get correct selection colors
|
||||||
if( !hasFocus && isSelected && tree.getParent() instanceof CellRendererPane )
|
if( !hasFocus && isSelected && tree.getParent() instanceof CellRendererPane )
|
||||||
hasFocus = FlatUIUtils.isPermanentFocusOwner( tree.getParent().getParent() );
|
hasFocus = FlatUIUtils.isPermanentFocusOwner( tree.getParent().getParent() );
|
||||||
|
|
||||||
// wide selection background
|
|
||||||
if( wideSelection && (isSelected || isDropRow) ) {
|
|
||||||
// fill background
|
|
||||||
g.setColor( isDropRow
|
|
||||||
? UIManager.getColor( "Tree.dropCellBackground" )
|
|
||||||
: (hasFocus ? selectionBackground : selectionInactiveBackground) );
|
|
||||||
g.fillRect( 0, bounds.y, tree.getWidth(), bounds.height );
|
|
||||||
|
|
||||||
// paint expand/collapse icon
|
|
||||||
if( shouldPaintExpandControl( path, row, isExpanded, hasBeenExpanded, isLeaf ) ) {
|
|
||||||
paintExpandControl( g, clipBounds, insets, bounds,
|
|
||||||
path, row, isExpanded, hasBeenExpanded, isLeaf );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( isEditing )
|
|
||||||
return;
|
|
||||||
|
|
||||||
// get renderer component
|
// get renderer component
|
||||||
Component rendererComponent = currentCellRenderer.getTreeCellRendererComponent( tree,
|
Component rendererComponent = currentCellRenderer.getTreeCellRendererComponent( tree,
|
||||||
path.getLastPathComponent(), isSelected, isExpanded, isLeaf, row, cellHasFocus );
|
path.getLastPathComponent(), isSelected, isExpanded, isLeaf, row, cellHasFocus );
|
||||||
@@ -290,7 +290,50 @@ public class FlatTreeUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// paint selection background
|
||||||
|
if( needsSelectionPainting ) {
|
||||||
|
// set selection color
|
||||||
|
Color oldColor = g.getColor();
|
||||||
|
g.setColor( isDropRow
|
||||||
|
? UIManager.getColor( "Tree.dropCellBackground" )
|
||||||
|
: (rendererComponent instanceof DefaultTreeCellRenderer
|
||||||
|
? ((DefaultTreeCellRenderer)rendererComponent).getBackgroundSelectionColor()
|
||||||
|
: (hasFocus ? selectionBackground : selectionInactiveBackground)) );
|
||||||
|
|
||||||
|
if( isWideSelection() ) {
|
||||||
|
// wide selection
|
||||||
|
g.fillRect( 0, bounds.y, tree.getWidth(), bounds.height );
|
||||||
|
|
||||||
|
// paint expand/collapse icon
|
||||||
|
// (was already painted before, but painted over with wide selection)
|
||||||
|
if( shouldPaintExpandControl( path, row, isExpanded, hasBeenExpanded, isLeaf ) ) {
|
||||||
|
paintExpandControl( g, clipBounds, insets, bounds,
|
||||||
|
path, row, isExpanded, hasBeenExpanded, isLeaf );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// non-wide selection
|
||||||
|
paintCellBackground( g, rendererComponent, bounds );
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is actually not necessary because renderer should always set color
|
||||||
|
// before painting, but doing anyway to avoid any side effect (in bad renderers)
|
||||||
|
g.setColor( oldColor );
|
||||||
|
} else {
|
||||||
|
// paint cell background if DefaultTreeCellRenderer.getBackgroundNonSelectionColor() is set
|
||||||
|
if( rendererComponent instanceof DefaultTreeCellRenderer ) {
|
||||||
|
DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) rendererComponent;
|
||||||
|
Color bg = renderer.getBackgroundNonSelectionColor();
|
||||||
|
if( bg != null && !bg.equals( defaultCellNonSelectionBackground ) ) {
|
||||||
|
Color oldColor = g.getColor();
|
||||||
|
g.setColor( bg );
|
||||||
|
paintCellBackground( g, rendererComponent, bounds );
|
||||||
|
g.setColor( oldColor );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// paint renderer
|
// paint renderer
|
||||||
|
if( !isEditing )
|
||||||
rendererPane.paintComponent( g, rendererComponent, tree, bounds.x, bounds.y, bounds.width, bounds.height, true );
|
rendererPane.paintComponent( g, rendererComponent, tree, bounds.x, bounds.y, bounds.width, bounds.height, true );
|
||||||
|
|
||||||
// restore background selection color and border selection color
|
// restore background selection color and border selection color
|
||||||
@@ -300,6 +343,22 @@ public class FlatTreeUI
|
|||||||
((DefaultTreeCellRenderer)rendererComponent).setBorderSelectionColor( oldBorderSelectionColor );
|
((DefaultTreeCellRenderer)rendererComponent).setBorderSelectionColor( oldBorderSelectionColor );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void paintCellBackground( Graphics g, Component rendererComponent, Rectangle bounds ) {
|
||||||
|
int xOffset = 0;
|
||||||
|
int imageOffset = 0;
|
||||||
|
|
||||||
|
if( rendererComponent instanceof JLabel ) {
|
||||||
|
JLabel label = (JLabel) rendererComponent;
|
||||||
|
Icon icon = label.getIcon();
|
||||||
|
imageOffset = (icon != null && label.getText() != null)
|
||||||
|
? icon.getIconWidth() + Math.max( label.getIconTextGap() - 1, 0 )
|
||||||
|
: 0;
|
||||||
|
xOffset = label.getComponentOrientation().isLeftToRight() ? imageOffset : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
g.fillRect( bounds.x + xOffset, bounds.y, bounds.width - imageOffset, bounds.height );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether dropping on a row.
|
* Checks whether dropping on a row.
|
||||||
* See DefaultTreeCellRenderer.getTreeCellRendererComponent().
|
* See DefaultTreeCellRenderer.getTreeCellRendererComponent().
|
||||||
@@ -314,6 +373,14 @@ public class FlatTreeUI
|
|||||||
@Override
|
@Override
|
||||||
protected Rectangle getDropLineRect( DropLocation loc ) {
|
protected Rectangle getDropLineRect( DropLocation loc ) {
|
||||||
Rectangle r = super.getDropLineRect( loc );
|
Rectangle r = super.getDropLineRect( loc );
|
||||||
return wideSelection ? new Rectangle( 0, r.y, tree.getWidth(), r.height ) : r;
|
return isWideSelection() ? new Rectangle( 0, r.y, tree.getWidth(), r.height ) : r;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isWideSelection() {
|
||||||
|
return clientPropertyBoolean( tree, TREE_WIDE_SELECTION, wideSelection );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isPaintSelection() {
|
||||||
|
return clientPropertyBoolean( tree, TREE_PAINT_SELECTION, true );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.BasicStroke;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
@@ -23,28 +24,37 @@ import java.awt.Dimension;
|
|||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.GraphicsConfiguration;
|
||||||
|
import java.awt.GraphicsDevice;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.KeyboardFocusManager;
|
import java.awt.KeyboardFocusManager;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.RenderingHints;
|
import java.awt.RenderingHints;
|
||||||
import java.awt.Shape;
|
import java.awt.Shape;
|
||||||
|
import java.awt.Stroke;
|
||||||
|
import java.awt.Window;
|
||||||
import java.awt.event.FocusEvent;
|
import java.awt.event.FocusEvent;
|
||||||
import java.awt.event.FocusListener;
|
import java.awt.event.FocusListener;
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.geom.Ellipse2D;
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.awt.geom.RoundRectangle2D;
|
import java.awt.geom.RoundRectangle2D;
|
||||||
import java.util.function.Consumer;
|
import java.util.IdentityHashMap;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.function.Supplier;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
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.border.Border;
|
import javax.swing.border.Border;
|
||||||
import javax.swing.border.CompoundBorder;
|
import javax.swing.border.CompoundBorder;
|
||||||
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.FlatSystemProperties;
|
||||||
import com.formdev.flatlaf.util.DerivedColor;
|
import com.formdev.flatlaf.util.DerivedColor;
|
||||||
import com.formdev.flatlaf.util.Graphics2DProxy;
|
import com.formdev.flatlaf.util.Graphics2DProxy;
|
||||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
@@ -59,6 +69,8 @@ public class FlatUIUtils
|
|||||||
{
|
{
|
||||||
public static final boolean MAC_USE_QUARTZ = Boolean.getBoolean( "apple.awt.graphics.UseQuartz" );
|
public static final boolean MAC_USE_QUARTZ = Boolean.getBoolean( "apple.awt.graphics.UseQuartz" );
|
||||||
|
|
||||||
|
private static WeakHashMap<LookAndFeel, IdentityHashMap<Object, ComponentUI>> sharedUIinstances = new WeakHashMap<>();
|
||||||
|
|
||||||
public static Rectangle addInsets( Rectangle r, Insets insets ) {
|
public static Rectangle addInsets( Rectangle r, Insets insets ) {
|
||||||
return new Rectangle(
|
return new Rectangle(
|
||||||
r.x - insets.left,
|
r.x - insets.left,
|
||||||
@@ -111,6 +123,14 @@ public class FlatUIUtils
|
|||||||
return (color != null) ? color : UIManager.getColor( defaultKey );
|
return (color != null) ? color : UIManager.getColor( defaultKey );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public static boolean getUIBoolean( String key, boolean defaultValue ) {
|
||||||
|
Object value = UIManager.get( key );
|
||||||
|
return (value instanceof Boolean) ? (Boolean) value : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
public static int getUIInt( String key, int defaultValue ) {
|
public static int getUIInt( String key, int defaultValue ) {
|
||||||
Object value = UIManager.get( key );
|
Object value = UIManager.get( key );
|
||||||
return (value instanceof Integer) ? (Integer) value : defaultValue;
|
return (value instanceof Integer) ? (Integer) value : defaultValue;
|
||||||
@@ -121,6 +141,29 @@ public class FlatUIUtils
|
|||||||
return (value instanceof Number) ? ((Number)value).floatValue() : defaultValue;
|
return (value instanceof Number) ? ((Number)value).floatValue() : defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1.2
|
||||||
|
*/
|
||||||
|
public static boolean getBoolean( JComponent c, String systemPropertyKey,
|
||||||
|
String clientPropertyKey, String uiKey, boolean defaultValue )
|
||||||
|
{
|
||||||
|
// check whether forced to true/false via system property
|
||||||
|
Boolean value = FlatSystemProperties.getBooleanStrict( systemPropertyKey, null );
|
||||||
|
if( value != null )
|
||||||
|
return value;
|
||||||
|
|
||||||
|
// check whether forced to true/false via client property
|
||||||
|
value = FlatClientProperties.clientPropertyBooleanStrict( c, clientPropertyKey, null );
|
||||||
|
if( value != null )
|
||||||
|
return value;
|
||||||
|
|
||||||
|
return getUIBoolean( uiKey, defaultValue );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isChevron( String arrowType ) {
|
||||||
|
return !"triangle".equals( arrowType );
|
||||||
|
}
|
||||||
|
|
||||||
public static Color nonUIResource( Color c ) {
|
public static Color nonUIResource( Color c ) {
|
||||||
return (c instanceof UIResource) ? new Color( c.getRGB(), true ) : c;
|
return (c instanceof UIResource) ? new Color( c.getRGB(), true ) : c;
|
||||||
}
|
}
|
||||||
@@ -162,12 +205,39 @@ public class FlatUIUtils
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the given component is the permanent focus owner and
|
* Returns whether the given component is the permanent focus owner and
|
||||||
* is in the active window. Used to paint focus indicators.
|
* is in the active window or in a popup window owned by the active window.
|
||||||
|
* Used to paint focus indicators.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings( "unchecked" )
|
||||||
public static boolean isPermanentFocusOwner( Component c ) {
|
public static boolean isPermanentFocusOwner( Component c ) {
|
||||||
KeyboardFocusManager keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
KeyboardFocusManager keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||||
|
|
||||||
|
if( c instanceof JComponent ) {
|
||||||
|
Object value = ((JComponent)c).getClientProperty( FlatClientProperties.COMPONENT_FOCUS_OWNER );
|
||||||
|
if( value instanceof Predicate ) {
|
||||||
|
return ((Predicate<JComponent>)value).test( (JComponent) c ) &&
|
||||||
|
isInActiveWindow( c, keyboardFocusManager.getActiveWindow() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return keyboardFocusManager.getPermanentFocusOwner() == c &&
|
return keyboardFocusManager.getPermanentFocusOwner() == c &&
|
||||||
keyboardFocusManager.getActiveWindow() == SwingUtilities.windowForComponent( c );
|
isInActiveWindow( c, keyboardFocusManager.getActiveWindow() );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isInActiveWindow( Component c, Window activeWindow ) {
|
||||||
|
Window window = SwingUtilities.windowForComponent( c );
|
||||||
|
return window == activeWindow ||
|
||||||
|
(window != null && window.getType() == Window.Type.POPUP && window.getOwner() == activeWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the given component is in a window that is in full-screen mode.
|
||||||
|
*/
|
||||||
|
public static boolean isFullScreen( Component c ) {
|
||||||
|
GraphicsConfiguration gc = c.getGraphicsConfiguration();
|
||||||
|
GraphicsDevice gd = (gc != null) ? gc.getDevice() : null;
|
||||||
|
Window fullScreenWindow = (gd != null) ? gd.getFullScreenWindow() : null;
|
||||||
|
return (fullScreenWindow != null && fullScreenWindow == SwingUtilities.windowForComponent( c ));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Boolean isRoundRect( Component c ) {
|
public static Boolean isRoundRect( Component c ) {
|
||||||
@@ -216,10 +286,57 @@ public class FlatUIUtils
|
|||||||
/**
|
/**
|
||||||
* Sets rendering hints used for painting.
|
* Sets rendering hints used for painting.
|
||||||
*/
|
*/
|
||||||
public static void setRenderingHints( Graphics2D g ) {
|
public static Object[] setRenderingHints( Graphics g ) {
|
||||||
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
|
Graphics2D g2 = (Graphics2D) g;
|
||||||
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL,
|
Object[] oldRenderingHints = new Object[] {
|
||||||
|
g2.getRenderingHint( RenderingHints.KEY_ANTIALIASING ),
|
||||||
|
g2.getRenderingHint( RenderingHints.KEY_STROKE_CONTROL ),
|
||||||
|
};
|
||||||
|
|
||||||
|
g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
|
||||||
|
g2.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL,
|
||||||
MAC_USE_QUARTZ ? RenderingHints.VALUE_STROKE_PURE : RenderingHints.VALUE_STROKE_NORMALIZE );
|
MAC_USE_QUARTZ ? RenderingHints.VALUE_STROKE_PURE : RenderingHints.VALUE_STROKE_NORMALIZE );
|
||||||
|
|
||||||
|
return oldRenderingHints;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets rendering hints previously set with {@link #setRenderingHints}.
|
||||||
|
*/
|
||||||
|
public static void resetRenderingHints( Graphics g, Object[] oldRenderingHints ) {
|
||||||
|
Graphics2D g2 = (Graphics2D) g;
|
||||||
|
g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING, oldRenderingHints[0] );
|
||||||
|
g2.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, oldRenderingHints[1] );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Temporary resets rendering hints set with {@link #setRenderingHints}
|
||||||
|
* and runs the given runnable.
|
||||||
|
* <p>
|
||||||
|
* This is intended for painting text while rendering hints are set.
|
||||||
|
* <p>
|
||||||
|
* If text antialiasing is disabled (in OS system settings or via
|
||||||
|
* {@code -Dawt.useSystemAAFontSettings=off}), but general antialiasing is enabled,
|
||||||
|
* then text is still painted using some kind of "grayscale" antialiasing,
|
||||||
|
* which may make the text look bold (depends on font and font size).
|
||||||
|
* To avoid this, temporary disable general antialiasing.
|
||||||
|
* This does not affect text rendering if text antialiasing is enabled (usually the default).
|
||||||
|
*/
|
||||||
|
public static void runWithoutRenderingHints( Graphics g, Object[] oldRenderingHints, Runnable runnable ) {
|
||||||
|
if( oldRenderingHints == null ) {
|
||||||
|
runnable.run();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Graphics2D g2 = (Graphics2D) g;
|
||||||
|
Object[] oldRenderingHints2 = new Object[] {
|
||||||
|
g2.getRenderingHint( RenderingHints.KEY_ANTIALIASING ),
|
||||||
|
g2.getRenderingHint( RenderingHints.KEY_STROKE_CONTROL ),
|
||||||
|
};
|
||||||
|
|
||||||
|
resetRenderingHints( g2, oldRenderingHints );
|
||||||
|
runnable.run();
|
||||||
|
resetRenderingHints( g2, oldRenderingHints2 );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Color deriveColor( Color color, Color baseColor ) {
|
public static Color deriveColor( Color color, Color baseColor ) {
|
||||||
@@ -266,7 +383,7 @@ public class FlatUIUtils
|
|||||||
float innerArc = arc - (lineWidth * 2);
|
float innerArc = arc - (lineWidth * 2);
|
||||||
|
|
||||||
// reduce outer arc slightly for small arcs to make the curve slightly wider
|
// reduce outer arc slightly for small arcs to make the curve slightly wider
|
||||||
if( arc > 0 && arc < UIScale.scale( 10 ) )
|
if( focusWidth > 0 && arc > 0 && arc < UIScale.scale( 10 ) )
|
||||||
outerArc -= UIScale.scale( 2f );
|
outerArc -= UIScale.scale( 2f );
|
||||||
|
|
||||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||||
@@ -373,6 +490,52 @@ public class FlatUIUtils
|
|||||||
return new RoundRectangle2D.Float( x, y, w, h, arc, arc );
|
return new RoundRectangle2D.Float( x, y, w, h, arc, arc );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void paintFilledRectangle( Graphics g, Color color, float x, float y, float w, float h ) {
|
||||||
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
|
try {
|
||||||
|
FlatUIUtils.setRenderingHints( g2 );
|
||||||
|
g2.setColor( color );
|
||||||
|
g2.fill( new Rectangle2D.Float( x, y, w, h ) );
|
||||||
|
} finally {
|
||||||
|
g2.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void paintGrip( Graphics g, int x, int y, int width, int height,
|
||||||
|
boolean horizontal, int dotCount, int dotSize, int gap, boolean centerPrecise )
|
||||||
|
{
|
||||||
|
dotSize = UIScale.scale( dotSize );
|
||||||
|
gap = UIScale.scale( gap );
|
||||||
|
int gripSize = (dotSize * dotCount) + ((gap * (dotCount - 1)));
|
||||||
|
|
||||||
|
// calculate grip position
|
||||||
|
float gx;
|
||||||
|
float gy;
|
||||||
|
if( horizontal ) {
|
||||||
|
gx = x + Math.round( (width - gripSize) / 2f );
|
||||||
|
gy = y + ((height - dotSize) / 2f);
|
||||||
|
|
||||||
|
if( !centerPrecise )
|
||||||
|
gy = Math.round( gy );
|
||||||
|
} else {
|
||||||
|
// vertical
|
||||||
|
gx = x + ((width - dotSize) / 2f);
|
||||||
|
gy = y + Math.round( (height - gripSize) / 2f );
|
||||||
|
|
||||||
|
if( !centerPrecise )
|
||||||
|
gx = Math.round( gx );
|
||||||
|
}
|
||||||
|
|
||||||
|
// paint dots
|
||||||
|
for( int i = 0; i < dotCount; i++ ) {
|
||||||
|
((Graphics2D)g).fill( new Ellipse2D.Float( gx, gy, dotSize, dotSize ) );
|
||||||
|
if( horizontal )
|
||||||
|
gx += dotSize + gap;
|
||||||
|
else
|
||||||
|
gy += dotSize + gap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fill background with parent's background color because the visible component
|
* Fill background with parent's background color because the visible component
|
||||||
* is smaller than its bounds (for the focus decoration).
|
* is smaller than its bounds (for the focus decoration).
|
||||||
@@ -449,20 +612,143 @@ public class FlatUIUtils
|
|||||||
float x2 = x + width;
|
float x2 = x + width;
|
||||||
float y2 = y + height;
|
float y2 = y + height;
|
||||||
|
|
||||||
|
// same constant as in java.awt.geom.EllipseIterator.CtrlVal used to paint circles
|
||||||
|
double c = 0.5522847498307933;
|
||||||
|
double ci = 1. - c;
|
||||||
|
double ciTopLeft = arcTopLeft * ci;
|
||||||
|
double ciTopRight = arcTopRight * ci;
|
||||||
|
double ciBottomLeft = arcBottomLeft * ci;
|
||||||
|
double ciBottomRight = arcBottomRight * ci;
|
||||||
|
|
||||||
Path2D rect = new Path2D.Float();
|
Path2D rect = new Path2D.Float();
|
||||||
rect.moveTo( x2 - arcTopRight, y );
|
rect.moveTo( x2 - arcTopRight, y );
|
||||||
rect.quadTo( x2, y, x2, y + arcTopRight );
|
rect.curveTo( x2 - ciTopRight, y,
|
||||||
|
x2, y + ciTopRight,
|
||||||
|
x2, y + arcTopRight );
|
||||||
rect.lineTo( x2, y2 - arcBottomRight );
|
rect.lineTo( x2, y2 - arcBottomRight );
|
||||||
rect.quadTo( x2, y2, x2 - arcBottomRight, y2 );
|
rect.curveTo( x2, y2 - ciBottomRight,
|
||||||
|
x2 - ciBottomRight, y2,
|
||||||
|
x2 - arcBottomRight, y2 );
|
||||||
rect.lineTo( x + arcBottomLeft, y2 );
|
rect.lineTo( x + arcBottomLeft, y2 );
|
||||||
rect.quadTo( x, y2, x, y2 - arcBottomLeft );
|
rect.curveTo( x + ciBottomLeft, y2,
|
||||||
|
x, y2 - ciBottomLeft,
|
||||||
|
x, y2 - arcBottomLeft );
|
||||||
rect.lineTo( x, y + arcTopLeft );
|
rect.lineTo( x, y + arcTopLeft );
|
||||||
rect.quadTo( x, y, x + arcTopLeft, y );
|
rect.curveTo( x, y + ciTopLeft,
|
||||||
|
x + ciTopLeft, y,
|
||||||
|
x + arcTopLeft, y );
|
||||||
rect.closePath();
|
rect.closePath();
|
||||||
|
|
||||||
return rect;
|
return rect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paints a chevron or triangle arrow in the center of the given rectangle.
|
||||||
|
*
|
||||||
|
* @param g the graphics context used for painting
|
||||||
|
* @param x the x coordinate of the rectangle
|
||||||
|
* @param y the y coordinate of the rectangle
|
||||||
|
* @param width the width of the rectangle
|
||||||
|
* @param height the height of the rectangle
|
||||||
|
* @param direction the arrow direction ({@link SwingConstants#NORTH}, {@link SwingConstants#SOUTH}
|
||||||
|
* {@link SwingConstants#WEST} or {@link SwingConstants#EAST})
|
||||||
|
* @param chevron {@code true} for chevron arrow, {@code false} for triangle arrow
|
||||||
|
* @param arrowSize the width of the painted arrow (for vertical direction) (will be scaled)
|
||||||
|
* @param xOffset a offset added to the x coordinate of the arrow to paint it out-of-center. Usually zero. (will be scaled)
|
||||||
|
* @param yOffset a offset added to the y coordinate of the arrow to paint it out-of-center. Usually zero. (will be scaled)
|
||||||
|
*
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public static void paintArrow( Graphics2D g, int x, int y, int width, int height,
|
||||||
|
int direction, boolean chevron, int arrowSize, float xOffset, float yOffset )
|
||||||
|
{
|
||||||
|
// compute arrow width/height
|
||||||
|
int aw = UIScale.scale( arrowSize + (chevron ? 0 : 1) );
|
||||||
|
int ah = UIScale.scale( (arrowSize / 2) + (chevron ? 0 : 1) );
|
||||||
|
|
||||||
|
// rotate arrow width/height for horizontal directions
|
||||||
|
boolean vert = (direction == SwingConstants.NORTH || direction == SwingConstants.SOUTH);
|
||||||
|
if( !vert ) {
|
||||||
|
int temp = aw;
|
||||||
|
aw = ah;
|
||||||
|
ah = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// chevron lines end 1px outside of width/height
|
||||||
|
// --> add 1px to arrow width/height for position calculation
|
||||||
|
int extra = chevron ? 1 : 0;
|
||||||
|
|
||||||
|
// compute arrow location
|
||||||
|
float ox = ((width - (aw + extra)) / 2f) + UIScale.scale( xOffset );
|
||||||
|
float oy = ((height - (ah + extra)) / 2f) + UIScale.scale( yOffset );
|
||||||
|
int ax = x + ((direction == SwingConstants.WEST) ? -Math.round( -ox ) : Math.round( ox ));
|
||||||
|
int ay = y + ((direction == SwingConstants.NORTH) ? -Math.round( -oy ) : Math.round( oy ));
|
||||||
|
|
||||||
|
// paint arrow
|
||||||
|
g.translate( ax, ay );
|
||||||
|
/*debug
|
||||||
|
debugPaintArrow( g, Color.red, vert, aw + extra, ah + extra );
|
||||||
|
debug*/
|
||||||
|
Shape arrowShape = createArrowShape( direction, chevron, aw, ah );
|
||||||
|
if( chevron ) {
|
||||||
|
Stroke oldStroke = g.getStroke();
|
||||||
|
g.setStroke( new BasicStroke( UIScale.scale( 1f ) ) );
|
||||||
|
g.draw( arrowShape );
|
||||||
|
g.setStroke( oldStroke );
|
||||||
|
} else {
|
||||||
|
// triangle
|
||||||
|
g.fill( arrowShape );
|
||||||
|
}
|
||||||
|
g.translate( -ax, -ay );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a chevron or triangle arrow shape for the given direction and size.
|
||||||
|
* <p>
|
||||||
|
* The chevron shape is a open path that can be painted with {@link Graphics2D#draw(Shape)}.
|
||||||
|
* The triangle shape is a close path that can be painted with {@link Graphics2D#fill(Shape)}.
|
||||||
|
*
|
||||||
|
* @param direction the arrow direction ({@link SwingConstants#NORTH}, {@link SwingConstants#SOUTH}
|
||||||
|
* {@link SwingConstants#WEST} or {@link SwingConstants#EAST})
|
||||||
|
* @param chevron {@code true} for chevron arrow, {@code false} for triangle arrow
|
||||||
|
* @param w the width of the returned shape
|
||||||
|
* @param h the height of the returned shape
|
||||||
|
*
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public static Shape createArrowShape( int direction, boolean chevron, float w, float h ) {
|
||||||
|
switch( direction ) {
|
||||||
|
case SwingConstants.NORTH: return createPath( !chevron, 0,h, (w / 2f),0, w,h );
|
||||||
|
case SwingConstants.SOUTH: return createPath( !chevron, 0,0, (w / 2f),h, w,0 );
|
||||||
|
case SwingConstants.WEST: return createPath( !chevron, w,0, 0,(h / 2f), w,h );
|
||||||
|
case SwingConstants.EAST: return createPath( !chevron, 0,0, w,(h / 2f), 0,h );
|
||||||
|
default: return new Path2D.Float();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*debug
|
||||||
|
private static void debugPaintArrow( Graphics2D g, Color color, boolean vert, int w, int h ) {
|
||||||
|
Color oldColor = g.getColor();
|
||||||
|
g.setColor( color );
|
||||||
|
g.fill( createRectangle( 0, 0, w, h, 1 ) );
|
||||||
|
|
||||||
|
int xy1 = -2;
|
||||||
|
int x2 = w + 1;
|
||||||
|
int y2 = h + 1;
|
||||||
|
for( int i = 0; i < 20; i++ ) {
|
||||||
|
g.fillRect( 0, xy1, 1, 1 );
|
||||||
|
g.fillRect( 0, y2, 1, 1 );
|
||||||
|
g.fillRect( xy1, 0, 1, 1 );
|
||||||
|
g.fillRect( x2, 0, 1, 1 );
|
||||||
|
xy1 -= 2;
|
||||||
|
x2 += 2;
|
||||||
|
y2 += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
g.setColor( oldColor );
|
||||||
|
}
|
||||||
|
debug*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a closed path for the given points.
|
* Creates a closed path for the given points.
|
||||||
*/
|
*/
|
||||||
@@ -534,35 +820,17 @@ public class FlatUIUtils
|
|||||||
return explicitlySet;
|
return explicitlySet;
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class HoverListener ------------------------------------------------
|
/**
|
||||||
|
* Creates a shared component UI for the given key and the current Laf.
|
||||||
public static class HoverListener
|
* Each Laf instance has its own shared component UI instance.
|
||||||
extends MouseAdapter
|
* <p>
|
||||||
{
|
* This is for GUI builders that support Laf switching and
|
||||||
private final Component repaintComponent;
|
* may use multiple Laf instances at the same time.
|
||||||
private final Consumer<Boolean> hoverChanged;
|
*/
|
||||||
|
public static ComponentUI createSharedUI( Object key, Supplier<ComponentUI> newInstanceSupplier ) {
|
||||||
public HoverListener( Component repaintComponent, Consumer<Boolean> hoverChanged ) {
|
return sharedUIinstances
|
||||||
this.repaintComponent = repaintComponent;
|
.computeIfAbsent( UIManager.getLookAndFeel(), k -> new IdentityHashMap<>() )
|
||||||
this.hoverChanged = hoverChanged;
|
.computeIfAbsent( key, k -> newInstanceSupplier.get() );
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseEntered( MouseEvent e ) {
|
|
||||||
hoverChanged.accept( true );
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseExited( MouseEvent e ) {
|
|
||||||
hoverChanged.accept( false );
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void repaint() {
|
|
||||||
if( repaintComponent != null && repaintComponent.isEnabled() )
|
|
||||||
repaintComponent.repaint();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class RepaintFocusListener -----------------------------------------
|
//---- class RepaintFocusListener -----------------------------------------
|
||||||
@@ -571,18 +839,22 @@ public class FlatUIUtils
|
|||||||
implements FocusListener
|
implements FocusListener
|
||||||
{
|
{
|
||||||
private final Component repaintComponent;
|
private final Component repaintComponent;
|
||||||
|
private final Predicate<Component> repaintCondition;
|
||||||
|
|
||||||
public RepaintFocusListener( Component repaintComponent ) {
|
public RepaintFocusListener( Component repaintComponent, Predicate<Component> repaintCondition ) {
|
||||||
this.repaintComponent = repaintComponent;
|
this.repaintComponent = repaintComponent;
|
||||||
|
this.repaintCondition = repaintCondition;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void focusGained( FocusEvent e ) {
|
public void focusGained( FocusEvent e ) {
|
||||||
|
if( repaintCondition == null || repaintCondition.test( repaintComponent ) )
|
||||||
repaintComponent.repaint();
|
repaintComponent.repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void focusLost( FocusEvent e ) {
|
public void focusLost( FocusEvent e ) {
|
||||||
|
if( repaintCondition == null || repaintCondition.test( repaintComponent ) )
|
||||||
repaintComponent.repaint();
|
repaintComponent.repaint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,12 +38,8 @@ import javax.swing.plaf.basic.BasicViewportUI;
|
|||||||
public class FlatViewportUI
|
public class FlatViewportUI
|
||||||
extends BasicViewportUI
|
extends BasicViewportUI
|
||||||
{
|
{
|
||||||
private static ComponentUI instance;
|
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
if( instance == null )
|
return FlatUIUtils.createSharedUI( FlatViewportUI.class, FlatViewportUI::new );
|
||||||
instance = new FlatViewportUI();
|
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -17,11 +17,13 @@
|
|||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import static java.awt.Cursor.*;
|
import static java.awt.Cursor.*;
|
||||||
|
import static javax.swing.SwingConstants.*;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.Dialog;
|
import java.awt.Dialog;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Frame;
|
import java.awt.Frame;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Insets;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.Toolkit;
|
import java.awt.Toolkit;
|
||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
@@ -34,54 +36,59 @@ import java.awt.event.WindowEvent;
|
|||||||
import java.awt.event.WindowStateListener;
|
import java.awt.event.WindowStateListener;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import javax.swing.DesktopManager;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JInternalFrame;
|
||||||
import javax.swing.JLayeredPane;
|
import javax.swing.JLayeredPane;
|
||||||
import javax.swing.JRootPane;
|
import javax.swing.JRootPane;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resizes frames and dialogs.
|
* Resizes frames, dialogs or internal frames.
|
||||||
|
* <p>
|
||||||
|
* Could also be used to implement resize support for any Swing component
|
||||||
|
* by creating a new subclass.
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatWindowResizer
|
public abstract class FlatWindowResizer
|
||||||
implements PropertyChangeListener, WindowStateListener, ComponentListener
|
implements PropertyChangeListener, ComponentListener
|
||||||
{
|
{
|
||||||
protected final static Integer WINDOW_RESIZER_LAYER = JLayeredPane.DRAG_LAYER + 1;
|
protected final static Integer WINDOW_RESIZER_LAYER = JLayeredPane.DRAG_LAYER + 1;
|
||||||
|
|
||||||
protected final JRootPane rootPane;
|
protected final JComponent resizeComp;
|
||||||
|
|
||||||
protected final int borderDragThickness = FlatUIUtils.getUIInt( "RootPane.borderDragThickness", 5 );
|
protected final int borderDragThickness = FlatUIUtils.getUIInt( "RootPane.borderDragThickness", 5 );
|
||||||
protected final int cornerDragWidth = FlatUIUtils.getUIInt( "RootPane.cornerDragWidth", 16 );
|
protected final int cornerDragWidth = FlatUIUtils.getUIInt( "RootPane.cornerDragWidth", 16 );
|
||||||
protected final boolean honorFrameMinimumSizeOnResize = UIManager.getBoolean( "RootPane.honorFrameMinimumSizeOnResize" );
|
protected final boolean honorFrameMinimumSizeOnResize = UIManager.getBoolean( "RootPane.honorFrameMinimumSizeOnResize" );
|
||||||
protected final boolean honorDialogMinimumSizeOnResize = UIManager.getBoolean( "RootPane.honorDialogMinimumSizeOnResize" );
|
protected final boolean honorDialogMinimumSizeOnResize = UIManager.getBoolean( "RootPane.honorDialogMinimumSizeOnResize" );
|
||||||
|
|
||||||
protected final JComponent north;
|
protected final DragBorderComponent topDragComp;
|
||||||
protected final JComponent south;
|
protected final DragBorderComponent bottomDragComp;
|
||||||
protected final JComponent west;
|
protected final DragBorderComponent leftDragComp;
|
||||||
protected final JComponent east;
|
protected final DragBorderComponent rightDragComp;
|
||||||
|
|
||||||
protected Window window;
|
protected FlatWindowResizer( JComponent resizeComp ) {
|
||||||
|
this.resizeComp = resizeComp;
|
||||||
|
|
||||||
public FlatWindowResizer( JRootPane rootPane ) {
|
topDragComp = createDragBorderComponent( NW_RESIZE_CURSOR, N_RESIZE_CURSOR, NE_RESIZE_CURSOR );
|
||||||
this.rootPane = rootPane;
|
bottomDragComp = createDragBorderComponent( SW_RESIZE_CURSOR, S_RESIZE_CURSOR, SE_RESIZE_CURSOR );
|
||||||
|
leftDragComp = createDragBorderComponent( NW_RESIZE_CURSOR, W_RESIZE_CURSOR, SW_RESIZE_CURSOR );
|
||||||
|
rightDragComp = createDragBorderComponent( NE_RESIZE_CURSOR, E_RESIZE_CURSOR, SE_RESIZE_CURSOR );
|
||||||
|
|
||||||
north = createDragBorderComponent( NW_RESIZE_CURSOR, N_RESIZE_CURSOR, NE_RESIZE_CURSOR );
|
Container cont = (resizeComp instanceof JRootPane) ? ((JRootPane)resizeComp).getLayeredPane() : resizeComp;
|
||||||
south = createDragBorderComponent( SW_RESIZE_CURSOR, S_RESIZE_CURSOR, SE_RESIZE_CURSOR );
|
Object cons = (cont instanceof JLayeredPane) ? WINDOW_RESIZER_LAYER : null;
|
||||||
west = createDragBorderComponent( NW_RESIZE_CURSOR, W_RESIZE_CURSOR, SW_RESIZE_CURSOR );
|
cont.add( topDragComp, cons, 0 );
|
||||||
east = createDragBorderComponent( NE_RESIZE_CURSOR, E_RESIZE_CURSOR, SE_RESIZE_CURSOR );
|
cont.add( bottomDragComp, cons, 1 );
|
||||||
|
cont.add( leftDragComp, cons, 2 );
|
||||||
|
cont.add( rightDragComp, cons, 3 );
|
||||||
|
|
||||||
JLayeredPane layeredPane = rootPane.getLayeredPane();
|
resizeComp.addComponentListener( this );
|
||||||
layeredPane.add( north, WINDOW_RESIZER_LAYER );
|
resizeComp.addPropertyChangeListener( "ancestor", this );
|
||||||
layeredPane.add( south, WINDOW_RESIZER_LAYER );
|
|
||||||
layeredPane.add( west, WINDOW_RESIZER_LAYER );
|
|
||||||
layeredPane.add( east, WINDOW_RESIZER_LAYER );
|
|
||||||
|
|
||||||
rootPane.addComponentListener( this );
|
if( resizeComp.isDisplayable() )
|
||||||
rootPane.addPropertyChangeListener( "ancestor", this );
|
|
||||||
|
|
||||||
if( rootPane.isDisplayable() )
|
|
||||||
addNotify();
|
addNotify();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,85 +99,96 @@ public class FlatWindowResizer
|
|||||||
public void uninstall() {
|
public void uninstall() {
|
||||||
removeNotify();
|
removeNotify();
|
||||||
|
|
||||||
rootPane.removeComponentListener( this );
|
resizeComp.removeComponentListener( this );
|
||||||
rootPane.removePropertyChangeListener( "ancestor", this );
|
resizeComp.removePropertyChangeListener( "ancestor", this );
|
||||||
|
|
||||||
JLayeredPane layeredPane = rootPane.getLayeredPane();
|
Container cont = topDragComp.getParent();
|
||||||
layeredPane.remove( north );
|
cont.remove( topDragComp );
|
||||||
layeredPane.remove( south );
|
cont.remove( bottomDragComp );
|
||||||
layeredPane.remove( west );
|
cont.remove( leftDragComp );
|
||||||
layeredPane.remove( east );
|
cont.remove( rightDragComp );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void doLayout() {
|
public void doLayout() {
|
||||||
if( !north.isVisible() )
|
if( !topDragComp.isVisible() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int x = 0;
|
int x = 0;
|
||||||
int y = 0;
|
int y = 0;
|
||||||
int width = rootPane.getWidth();
|
int width = resizeComp.getWidth();
|
||||||
int height = rootPane.getHeight();
|
int height = resizeComp.getHeight();
|
||||||
if( width == 0 || height == 0 )
|
if( width == 0 || height == 0 )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
Insets resizeInsets = getResizeInsets();
|
||||||
int thickness = UIScale.scale( borderDragThickness );
|
int thickness = UIScale.scale( borderDragThickness );
|
||||||
int y2 = y + thickness;
|
int topThickness = Math.max( resizeInsets.top, thickness );
|
||||||
int height2 = height - (thickness * 2);
|
int bottomThickness = Math.max( resizeInsets.bottom, thickness );
|
||||||
|
int leftThickness = Math.max( resizeInsets.left, thickness );
|
||||||
|
int rightThickness = Math.max( resizeInsets.right, thickness );
|
||||||
|
int y2 = y + topThickness;
|
||||||
|
int height2 = height - topThickness - bottomThickness;
|
||||||
|
|
||||||
north.setBounds( x, y, width, thickness );
|
// set bounds of drag components
|
||||||
south.setBounds( x, y + height - thickness, width, thickness );
|
topDragComp.setBounds( x, y, width, topThickness );
|
||||||
west.setBounds( x, y2, thickness, height2 );
|
bottomDragComp.setBounds( x, y + height - bottomThickness, width, bottomThickness );
|
||||||
east.setBounds( x + width - thickness, y2, thickness, height2 );
|
leftDragComp.setBounds( x, y2, leftThickness, height2 );
|
||||||
|
rightDragComp.setBounds( x + width - rightThickness, y2, rightThickness, height2 );
|
||||||
|
|
||||||
|
// set corner drag widths
|
||||||
|
int cornerDelta = UIScale.scale( cornerDragWidth - borderDragThickness );
|
||||||
|
topDragComp.setCornerDragWidths( leftThickness + cornerDelta, rightThickness + cornerDelta );
|
||||||
|
bottomDragComp.setCornerDragWidths( leftThickness + cornerDelta, rightThickness + cornerDelta );
|
||||||
|
leftDragComp.setCornerDragWidths( cornerDelta, cornerDelta );
|
||||||
|
rightDragComp.setCornerDragWidths( cornerDelta, cornerDelta );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Insets getResizeInsets() {
|
||||||
|
return new Insets( 0, 0, 0, 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addNotify() {
|
protected void addNotify() {
|
||||||
Container parent = rootPane.getParent();
|
|
||||||
window = (parent instanceof Window) ? (Window) parent : null;
|
|
||||||
if( window instanceof Frame ) {
|
|
||||||
window.addPropertyChangeListener( "resizable", this );
|
|
||||||
window.addWindowStateListener( this );
|
|
||||||
}
|
|
||||||
|
|
||||||
updateVisibility();
|
updateVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void removeNotify() {
|
protected void removeNotify() {
|
||||||
if( window instanceof Frame ) {
|
|
||||||
window.removePropertyChangeListener( "resizable", this );
|
|
||||||
window.removeWindowStateListener( this );
|
|
||||||
}
|
|
||||||
window = null;
|
|
||||||
|
|
||||||
updateVisibility();
|
updateVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void updateVisibility() {
|
protected void updateVisibility() {
|
||||||
boolean visible = isWindowResizable();
|
boolean visible = isWindowResizable();
|
||||||
if( visible == north.isVisible() )
|
if( visible == topDragComp.isVisible() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
north.setVisible( visible );
|
topDragComp.setVisible( visible );
|
||||||
south.setVisible( visible );
|
bottomDragComp.setVisible( visible );
|
||||||
west.setVisible( visible );
|
leftDragComp.setVisible( visible );
|
||||||
|
|
||||||
// The east component is not hidden, instead its bounds are set to 0,0,1,1 and
|
// The east component is not hidden, instead its bounds are set to 0,0,1,1 and
|
||||||
// it is disabled. This is necessary so that DragBorderComponent.paintComponent() is invoked.
|
// it is disabled. This is necessary so that DragBorderComponent.paintComponent() is invoked.
|
||||||
east.setEnabled( visible );
|
rightDragComp.setEnabled( visible );
|
||||||
if( visible ) {
|
if( visible ) {
|
||||||
east.setVisible( true ); // necessary because it is initially invisible
|
rightDragComp.setVisible( true ); // necessary because it is initially invisible
|
||||||
doLayout();
|
doLayout();
|
||||||
} else
|
} else
|
||||||
east.setBounds( 0, 0, 1, 1 );
|
rightDragComp.setBounds( 0, 0, 1, 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isWindowResizable() {
|
boolean isDialog() {
|
||||||
if( window instanceof Frame )
|
|
||||||
return ((Frame)window).isResizable() && (((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) == 0;
|
|
||||||
if( window instanceof Dialog )
|
|
||||||
return ((Dialog)window).isResizable();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract boolean isWindowResizable();
|
||||||
|
protected abstract Rectangle getWindowBounds();
|
||||||
|
protected abstract void setWindowBounds( Rectangle r );
|
||||||
|
protected abstract boolean honorMinimumSizeOnResize();
|
||||||
|
protected abstract Dimension getWindowMinimumSize();
|
||||||
|
|
||||||
|
protected void beginResizing( int direction ) {}
|
||||||
|
protected void endResizing() {}
|
||||||
|
|
||||||
|
//---- interface PropertyChangeListener ----
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void propertyChange( PropertyChangeEvent e ) {
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
@@ -187,10 +205,7 @@ public class FlatWindowResizer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
//---- interface ComponentListener ----
|
||||||
public void windowStateChanged( WindowEvent e ) {
|
|
||||||
updateVisibility();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void componentResized( ComponentEvent e ) {
|
public void componentResized( ComponentEvent e ) {
|
||||||
@@ -201,6 +216,165 @@ public class FlatWindowResizer
|
|||||||
@Override public void componentShown( ComponentEvent e ) {}
|
@Override public void componentShown( ComponentEvent e ) {}
|
||||||
@Override public void componentHidden( ComponentEvent e ) {}
|
@Override public void componentHidden( ComponentEvent e ) {}
|
||||||
|
|
||||||
|
//---- class WindowResizer ------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resizes frames and dialogs.
|
||||||
|
*/
|
||||||
|
public static class WindowResizer
|
||||||
|
extends FlatWindowResizer
|
||||||
|
implements WindowStateListener
|
||||||
|
{
|
||||||
|
protected Window window;
|
||||||
|
|
||||||
|
public WindowResizer( JRootPane rootPane ) {
|
||||||
|
super( rootPane );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addNotify() {
|
||||||
|
Container parent = resizeComp.getParent();
|
||||||
|
window = (parent instanceof Window) ? (Window) parent : null;
|
||||||
|
if( window instanceof Frame ) {
|
||||||
|
window.addPropertyChangeListener( "resizable", this );
|
||||||
|
window.addWindowStateListener( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
super.addNotify();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void removeNotify() {
|
||||||
|
if( window instanceof Frame ) {
|
||||||
|
window.removePropertyChangeListener( "resizable", this );
|
||||||
|
window.removeWindowStateListener( this );
|
||||||
|
}
|
||||||
|
window = null;
|
||||||
|
|
||||||
|
super.removeNotify();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isWindowResizable() {
|
||||||
|
if( FlatUIUtils.isFullScreen( resizeComp ) )
|
||||||
|
return false;
|
||||||
|
if( window instanceof Frame )
|
||||||
|
return ((Frame)window).isResizable() && (((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) == 0;
|
||||||
|
if( window instanceof Dialog )
|
||||||
|
return ((Dialog)window).isResizable();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Rectangle getWindowBounds() {
|
||||||
|
return window.getBounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setWindowBounds( Rectangle r ) {
|
||||||
|
window.setBounds( r );
|
||||||
|
|
||||||
|
// immediately layout drag border components
|
||||||
|
doLayout();
|
||||||
|
|
||||||
|
if( Toolkit.getDefaultToolkit().isDynamicLayoutActive() ) {
|
||||||
|
window.validate();
|
||||||
|
resizeComp.repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean honorMinimumSizeOnResize() {
|
||||||
|
return
|
||||||
|
(honorFrameMinimumSizeOnResize && window instanceof Frame) ||
|
||||||
|
(honorDialogMinimumSizeOnResize && window instanceof Dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Dimension getWindowMinimumSize() {
|
||||||
|
return window.getMinimumSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean isDialog() {
|
||||||
|
return window instanceof Dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void windowStateChanged( WindowEvent e ) {
|
||||||
|
updateVisibility();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class InternalFrameResizer -----------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resizes internal frames.
|
||||||
|
*/
|
||||||
|
public static class InternalFrameResizer
|
||||||
|
extends FlatWindowResizer
|
||||||
|
{
|
||||||
|
protected final Supplier<DesktopManager> desktopManager;
|
||||||
|
|
||||||
|
public InternalFrameResizer( JInternalFrame frame, Supplier<DesktopManager> desktopManager ) {
|
||||||
|
super( frame );
|
||||||
|
this.desktopManager = desktopManager;
|
||||||
|
|
||||||
|
frame.addPropertyChangeListener( "resizable", this );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uninstall() {
|
||||||
|
getFrame().removePropertyChangeListener( "resizable", this );
|
||||||
|
|
||||||
|
super.uninstall();
|
||||||
|
}
|
||||||
|
|
||||||
|
private JInternalFrame getFrame() {
|
||||||
|
return (JInternalFrame) resizeComp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Insets getResizeInsets() {
|
||||||
|
return getFrame().getInsets();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isWindowResizable() {
|
||||||
|
return getFrame().isResizable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Rectangle getWindowBounds() {
|
||||||
|
return getFrame().getBounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setWindowBounds( Rectangle r ) {
|
||||||
|
desktopManager.get().resizeFrame( getFrame(), r.x, r.y, r.width, r.height );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean honorMinimumSizeOnResize() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Dimension getWindowMinimumSize() {
|
||||||
|
return getFrame().getMinimumSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void beginResizing( int direction ) {
|
||||||
|
desktopManager.get().beginResizingFrame( getFrame(), direction );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void endResizing() {
|
||||||
|
desktopManager.get().endResizingFrame( getFrame() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//---- class DragBorderComponent ------------------------------------------
|
//---- class DragBorderComponent ------------------------------------------
|
||||||
|
|
||||||
protected class DragBorderComponent
|
protected class DragBorderComponent
|
||||||
@@ -212,9 +386,15 @@ public class FlatWindowResizer
|
|||||||
private final int trailingResizeDir;
|
private final int trailingResizeDir;
|
||||||
|
|
||||||
private int resizeDir = -1;
|
private int resizeDir = -1;
|
||||||
private int dragStartMouseX;
|
|
||||||
private int dragStartMouseY;
|
private int leadingCornerDragWidth;
|
||||||
private Rectangle dragStartWindowBounds;
|
private int trailingCornerDragWidth;
|
||||||
|
|
||||||
|
// offsets of mouse position to window edges
|
||||||
|
private int dragLeftOffset;
|
||||||
|
private int dragRightOffset;
|
||||||
|
private int dragTopOffset;
|
||||||
|
private int dragBottomOffset;
|
||||||
|
|
||||||
protected DragBorderComponent( int leadingResizeDir, int centerResizeDir, int trailingResizeDir ) {
|
protected DragBorderComponent( int leadingResizeDir, int centerResizeDir, int trailingResizeDir ) {
|
||||||
this.leadingResizeDir = leadingResizeDir;
|
this.leadingResizeDir = leadingResizeDir;
|
||||||
@@ -228,6 +408,11 @@ public class FlatWindowResizer
|
|||||||
addMouseMotionListener( this );
|
addMouseMotionListener( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setCornerDragWidths( int leading, int trailing ) {
|
||||||
|
leadingCornerDragWidth = leading;
|
||||||
|
trailingCornerDragWidth = trailing;
|
||||||
|
}
|
||||||
|
|
||||||
protected void setResizeDir( int resizeDir ) {
|
protected void setResizeDir( int resizeDir ) {
|
||||||
if( this.resizeDir == resizeDir )
|
if( this.resizeDir == resizeDir )
|
||||||
return;
|
return;
|
||||||
@@ -246,13 +431,26 @@ public class FlatWindowResizer
|
|||||||
protected void paintComponent( Graphics g ) {
|
protected void paintComponent( Graphics g ) {
|
||||||
super.paintChildren( g );
|
super.paintChildren( g );
|
||||||
|
|
||||||
// this is necessary because Dialog.setResizable() does not fire events
|
// for dialogs: necessary because Dialog.setResizable() does not fire events
|
||||||
if( window instanceof Dialog )
|
// for frames: necessary because GraphicsDevice.setFullScreenWindow() does not fire events
|
||||||
updateVisibility();
|
updateVisibility();
|
||||||
|
|
||||||
/*debug
|
/*debug
|
||||||
|
int width = getWidth();
|
||||||
|
int height = getHeight();
|
||||||
|
|
||||||
|
g.setColor( java.awt.Color.blue );
|
||||||
|
boolean topOrBottom = (centerResizeDir == N_RESIZE_CURSOR || centerResizeDir == S_RESIZE_CURSOR);
|
||||||
|
if( topOrBottom ) {
|
||||||
|
g.drawLine( leadingCornerDragWidth, 0, leadingCornerDragWidth, height );
|
||||||
|
g.drawLine( width - trailingCornerDragWidth, 0, width - trailingCornerDragWidth, height );
|
||||||
|
} else {
|
||||||
|
g.drawLine( 0, leadingCornerDragWidth, width, leadingCornerDragWidth );
|
||||||
|
g.drawLine( 0, height - trailingCornerDragWidth, width, height - trailingCornerDragWidth );
|
||||||
|
}
|
||||||
|
|
||||||
g.setColor( java.awt.Color.red );
|
g.setColor( java.awt.Color.red );
|
||||||
g.drawRect( 0, 0, getWidth() - 1, getHeight() - 1 );
|
g.drawRect( 0, 0, width - 1, height - 1 );
|
||||||
debug*/
|
debug*/
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,114 +460,116 @@ debug*/
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mousePressed( MouseEvent e ) {
|
public void mousePressed( MouseEvent e ) {
|
||||||
if( window == null )
|
if( !isWindowResizable() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
dragStartMouseX = e.getXOnScreen();
|
int xOnScreen = e.getXOnScreen();
|
||||||
dragStartMouseY = e.getYOnScreen();
|
int yOnScreen = e.getYOnScreen();
|
||||||
dragStartWindowBounds = window.getBounds();
|
Rectangle windowBounds = getWindowBounds();
|
||||||
|
|
||||||
|
// compute offsets of mouse position to window edges
|
||||||
|
dragLeftOffset = xOnScreen - windowBounds.x;
|
||||||
|
dragTopOffset = yOnScreen - windowBounds.y;
|
||||||
|
dragRightOffset = windowBounds.x + windowBounds.width - xOnScreen;
|
||||||
|
dragBottomOffset = windowBounds.y + windowBounds.height - yOnScreen;
|
||||||
|
|
||||||
|
int direction = 0;
|
||||||
|
switch( resizeDir ) {
|
||||||
|
case N_RESIZE_CURSOR: direction = NORTH; break;
|
||||||
|
case S_RESIZE_CURSOR: direction = SOUTH; break;
|
||||||
|
case W_RESIZE_CURSOR: direction = WEST; break;
|
||||||
|
case E_RESIZE_CURSOR: direction = EAST; break;
|
||||||
|
case NW_RESIZE_CURSOR: direction = NORTH_WEST; break;
|
||||||
|
case NE_RESIZE_CURSOR: direction = NORTH_EAST; break;
|
||||||
|
case SW_RESIZE_CURSOR: direction = SOUTH_WEST; break;
|
||||||
|
case SE_RESIZE_CURSOR: direction = SOUTH_EAST; break;
|
||||||
|
}
|
||||||
|
beginResizing( direction );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseReleased( MouseEvent e ) {
|
public void mouseReleased( MouseEvent e ) {
|
||||||
dragStartWindowBounds = null;
|
if( !isWindowResizable() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
dragLeftOffset = dragRightOffset = dragTopOffset = dragBottomOffset = 0;
|
||||||
|
|
||||||
|
endResizing();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override public void mouseEntered( MouseEvent e ) {}
|
||||||
public void mouseEntered( MouseEvent e ) {
|
@Override public void mouseExited( MouseEvent e ) {}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseExited( MouseEvent e ) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseMoved( MouseEvent e ) {
|
public void mouseMoved( MouseEvent e ) {
|
||||||
boolean topBottom = (centerResizeDir == N_RESIZE_CURSOR || centerResizeDir == S_RESIZE_CURSOR);
|
boolean topOrBottom = (centerResizeDir == N_RESIZE_CURSOR || centerResizeDir == S_RESIZE_CURSOR);
|
||||||
int xy = topBottom ? e.getX() : e.getY();
|
int xy = topOrBottom ? e.getX() : e.getY();
|
||||||
int wh = topBottom ? getWidth() : getHeight();
|
int wh = topOrBottom ? getWidth() : getHeight();
|
||||||
int cornerWH = UIScale.scale( cornerDragWidth - (topBottom ? 0 : borderDragThickness) );
|
|
||||||
|
|
||||||
setResizeDir( xy <= cornerWH
|
setResizeDir( xy <= leadingCornerDragWidth
|
||||||
? leadingResizeDir
|
? leadingResizeDir
|
||||||
: (xy >= wh - cornerWH
|
: (xy >= wh - trailingCornerDragWidth
|
||||||
? trailingResizeDir
|
? trailingResizeDir
|
||||||
: centerResizeDir) );
|
: centerResizeDir) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseDragged( MouseEvent e ) {
|
public void mouseDragged( MouseEvent e ) {
|
||||||
if( dragStartWindowBounds == null )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if( !isWindowResizable() )
|
if( !isWindowResizable() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int mouseDeltaX = e.getXOnScreen() - dragStartMouseX;
|
int xOnScreen = e.getXOnScreen();
|
||||||
int mouseDeltaY = e.getYOnScreen() - dragStartMouseY;
|
int yOnScreen = e.getYOnScreen();
|
||||||
|
|
||||||
int deltaX = 0;
|
// Get current window bounds and compute new bounds based them.
|
||||||
int deltaY = 0;
|
// This is necessary because window manager may alter window bounds while resizing.
|
||||||
int deltaWidth = 0;
|
// E.g. when having two monitors with different scale factors and resizing
|
||||||
int deltaHeight = 0;
|
// a window on first screen to the second screen, then the window manager may
|
||||||
|
// decide at some point that the window should be only on second screen
|
||||||
// north
|
// and adjusts its bounds.
|
||||||
if( resizeDir == N_RESIZE_CURSOR || resizeDir == NW_RESIZE_CURSOR || resizeDir == NE_RESIZE_CURSOR ) {
|
Rectangle oldBounds = getWindowBounds();
|
||||||
deltaY = mouseDeltaY;
|
Rectangle newBounds = new Rectangle( oldBounds );
|
||||||
deltaHeight = -mouseDeltaY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// south
|
|
||||||
if( resizeDir == S_RESIZE_CURSOR || resizeDir == SW_RESIZE_CURSOR || resizeDir == SE_RESIZE_CURSOR )
|
|
||||||
deltaHeight = mouseDeltaY;
|
|
||||||
|
|
||||||
// west
|
|
||||||
if( resizeDir == W_RESIZE_CURSOR || resizeDir == NW_RESIZE_CURSOR || resizeDir == SW_RESIZE_CURSOR ) {
|
|
||||||
deltaX = mouseDeltaX;
|
|
||||||
deltaWidth = -mouseDeltaX;
|
|
||||||
}
|
|
||||||
|
|
||||||
// east
|
|
||||||
if( resizeDir == E_RESIZE_CURSOR || resizeDir == NE_RESIZE_CURSOR || resizeDir == SE_RESIZE_CURSOR )
|
|
||||||
deltaWidth = mouseDeltaX;
|
|
||||||
|
|
||||||
// compute new window bounds
|
// compute new window bounds
|
||||||
Rectangle newBounds = new Rectangle( dragStartWindowBounds );
|
|
||||||
newBounds.x += deltaX;
|
// top
|
||||||
newBounds.y += deltaY;
|
if( resizeDir == N_RESIZE_CURSOR || resizeDir == NW_RESIZE_CURSOR || resizeDir == NE_RESIZE_CURSOR ) {
|
||||||
newBounds.width += deltaWidth;
|
newBounds.y = yOnScreen - dragTopOffset;
|
||||||
newBounds.height += deltaHeight;
|
newBounds.height += (oldBounds.y - newBounds.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// bottom
|
||||||
|
if( resizeDir == S_RESIZE_CURSOR || resizeDir == SW_RESIZE_CURSOR || resizeDir == SE_RESIZE_CURSOR )
|
||||||
|
newBounds.height = (yOnScreen + dragBottomOffset) - newBounds.y;
|
||||||
|
|
||||||
|
// left
|
||||||
|
if( resizeDir == W_RESIZE_CURSOR || resizeDir == NW_RESIZE_CURSOR || resizeDir == SW_RESIZE_CURSOR ) {
|
||||||
|
newBounds.x = xOnScreen - dragLeftOffset;
|
||||||
|
newBounds.width += (oldBounds.x - newBounds.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// right
|
||||||
|
if( resizeDir == E_RESIZE_CURSOR || resizeDir == NE_RESIZE_CURSOR || resizeDir == SE_RESIZE_CURSOR )
|
||||||
|
newBounds.width = (xOnScreen + dragRightOffset) - newBounds.x;
|
||||||
|
|
||||||
// apply minimum window size
|
// apply minimum window size
|
||||||
boolean honorMinimumSizeOnResize =
|
Dimension minimumSize = honorMinimumSizeOnResize() ? getWindowMinimumSize() : null;
|
||||||
(honorFrameMinimumSizeOnResize && window instanceof Frame) ||
|
|
||||||
(honorDialogMinimumSizeOnResize && window instanceof Dialog);
|
|
||||||
Dimension minimumSize = honorMinimumSizeOnResize ? window.getMinimumSize() : null;
|
|
||||||
if( minimumSize == null )
|
if( minimumSize == null )
|
||||||
minimumSize = UIScale.scale( new Dimension( 150, 50 ) );
|
minimumSize = UIScale.scale( new Dimension( 150, 50 ) );
|
||||||
if( newBounds.width < minimumSize.width ) {
|
if( newBounds.width < minimumSize.width ) {
|
||||||
if( deltaX != 0 )
|
if( newBounds.x != oldBounds.x )
|
||||||
newBounds.x -= (minimumSize.width - newBounds.width);
|
newBounds.x -= (minimumSize.width - newBounds.width);
|
||||||
newBounds.width = minimumSize.width;
|
newBounds.width = minimumSize.width;
|
||||||
}
|
}
|
||||||
if( newBounds.height < minimumSize.height ) {
|
if( newBounds.height < minimumSize.height ) {
|
||||||
if( deltaY != 0 )
|
if( newBounds.y != oldBounds.y )
|
||||||
newBounds.y -= (minimumSize.height - newBounds.height);
|
newBounds.y -= (minimumSize.height - newBounds.height);
|
||||||
newBounds.height = minimumSize.height;
|
newBounds.height = minimumSize.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set window bounds
|
// set window bounds
|
||||||
if( !newBounds.equals( dragStartWindowBounds ) ) {
|
if( !newBounds.equals( oldBounds ) )
|
||||||
window.setBounds( newBounds );
|
setWindowBounds( newBounds );
|
||||||
|
|
||||||
// immediately layout drag border components
|
|
||||||
FlatWindowResizer.this.doLayout();
|
|
||||||
|
|
||||||
if( Toolkit.getDefaultToolkit().isDynamicLayoutActive() ) {
|
|
||||||
window.validate();
|
|
||||||
rootPane.repaint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,398 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Dialog;
|
||||||
|
import java.awt.EventQueue;
|
||||||
|
import java.awt.Frame;
|
||||||
|
import java.awt.GraphicsConfiguration;
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.Window;
|
||||||
|
import java.awt.geom.AffineTransform;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.IdentityHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.swing.JDialog;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.Timer;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
import javax.swing.event.EventListenerList;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
import com.formdev.flatlaf.util.NativeLibrary;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Interesting resources:
|
||||||
|
// https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/dwm/customframe
|
||||||
|
// https://github.com/JetBrains/JetBrainsRuntime/blob/master/src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp
|
||||||
|
// https://github.com/JetBrains/JetBrainsRuntime/commit/d2820524a1aa211b1c49b30f659b9b4d07a6f96e
|
||||||
|
// https://github.com/JetBrains/JetBrainsRuntime/pull/18
|
||||||
|
// https://medium.com/swlh/customizing-the-title-bar-of-an-application-window-50a4ac3ed27e
|
||||||
|
// https://github.com/kalbetredev/CustomDecoratedJFrame
|
||||||
|
// https://github.com/Guerra24/NanoUI-win32
|
||||||
|
// https://github.com/oberth/custom-chrome
|
||||||
|
// https://github.com/rossy/borderless-window
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Native window border support for Windows 10 when using custom decorations.
|
||||||
|
* <p>
|
||||||
|
* If the application wants to use custom decorations, the Windows 10 title bar is hidden
|
||||||
|
* (including minimize, maximize and close buttons), but not the resize borders (including drop shadow).
|
||||||
|
* Windows 10 window snapping functionality will remain unaffected:
|
||||||
|
* https://support.microsoft.com/en-us/windows/snap-your-windows-885a9b1e-a983-a3b1-16cd-c531795e6241
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
class FlatWindowsNativeWindowBorder
|
||||||
|
implements FlatNativeWindowBorder.Provider
|
||||||
|
{
|
||||||
|
private final Map<Window, WndProc> windowsMap = Collections.synchronizedMap( new IdentityHashMap<>() );
|
||||||
|
private final EventListenerList listenerList = new EventListenerList();
|
||||||
|
private Timer fireStateChangedTimer;
|
||||||
|
|
||||||
|
private boolean colorizationUpToDate;
|
||||||
|
private boolean colorizationColorAffectsBorders;
|
||||||
|
private Color colorizationColor;
|
||||||
|
private int colorizationColorBalance;
|
||||||
|
|
||||||
|
private static NativeLibrary nativeLibrary;
|
||||||
|
private static FlatWindowsNativeWindowBorder instance;
|
||||||
|
|
||||||
|
static FlatNativeWindowBorder.Provider getInstance() {
|
||||||
|
// requires Windows 10
|
||||||
|
if( !SystemInfo.isWindows_10_orLater )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// load native library
|
||||||
|
if( nativeLibrary == null ) {
|
||||||
|
if( !SystemInfo.isJava_9_orLater ) {
|
||||||
|
// In Java 8, load jawt.dll (part of JRE) explicitly because it
|
||||||
|
// is not found when running application with <jdk>/bin/java.exe.
|
||||||
|
// When using <jdk>/jre/bin/java.exe, it is found.
|
||||||
|
// jawt.dll is located in <jdk>/jre/bin/.
|
||||||
|
// Java 9 and later does not have this problem.
|
||||||
|
try {
|
||||||
|
System.loadLibrary( "jawt" );
|
||||||
|
} catch( Exception ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String libraryName = "com/formdev/flatlaf/natives/flatlaf-windows-x86";
|
||||||
|
if( SystemInfo.isX86_64 )
|
||||||
|
libraryName += "_64";
|
||||||
|
|
||||||
|
nativeLibrary = new NativeLibrary( libraryName, null, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
// check whether native library was successfully loaded
|
||||||
|
if( !nativeLibrary.isLoaded() )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// create new instance
|
||||||
|
if( instance == null )
|
||||||
|
instance = new FlatWindowsNativeWindowBorder();
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FlatWindowsNativeWindowBorder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasCustomDecoration( Window window ) {
|
||||||
|
return windowsMap.containsKey( window );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tell the window whether the application wants use custom decorations.
|
||||||
|
* If {@code true}, the Windows 10 title bar is hidden (including minimize,
|
||||||
|
* maximize and close buttons), but not the resize borders (including drop shadow).
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setHasCustomDecoration( Window window, boolean hasCustomDecoration ) {
|
||||||
|
if( hasCustomDecoration )
|
||||||
|
install( window );
|
||||||
|
else
|
||||||
|
uninstall( window );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void install( Window window ) {
|
||||||
|
// requires Windows 10
|
||||||
|
if( !SystemInfo.isWindows_10_orLater )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// only JFrame and JDialog are supported
|
||||||
|
if( !(window instanceof JFrame) && !(window instanceof JDialog) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// not supported if frame/dialog is undecorated
|
||||||
|
if( (window instanceof Frame && ((Frame)window).isUndecorated()) ||
|
||||||
|
(window instanceof Dialog && ((Dialog)window).isUndecorated()) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// check whether already installed
|
||||||
|
if( windowsMap.containsKey( window ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// install
|
||||||
|
WndProc wndProc = new WndProc( window );
|
||||||
|
if( wndProc.hwnd == 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
windowsMap.put( window, wndProc );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void uninstall( Window window ) {
|
||||||
|
WndProc wndProc = windowsMap.remove( window );
|
||||||
|
if( wndProc != null )
|
||||||
|
wndProc.uninstall();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTitleBarHeight( Window window, int titleBarHeight ) {
|
||||||
|
WndProc wndProc = windowsMap.get( window );
|
||||||
|
if( wndProc == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
wndProc.titleBarHeight = titleBarHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTitleBarHitTestSpots( Window window, List<Rectangle> hitTestSpots ) {
|
||||||
|
WndProc wndProc = windowsMap.get( window );
|
||||||
|
if( wndProc == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
wndProc.hitTestSpots = hitTestSpots.toArray( new Rectangle[hitTestSpots.size()] );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTitleBarAppIconBounds( Window window, Rectangle appIconBounds ) {
|
||||||
|
WndProc wndProc = windowsMap.get( window );
|
||||||
|
if( wndProc == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
wndProc.appIconBounds = (appIconBounds != null) ? new Rectangle( appIconBounds ) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean showWindow( Window window, int cmd ) {
|
||||||
|
WndProc wndProc = windowsMap.get( window );
|
||||||
|
if( wndProc == null )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
wndProc.showWindow( wndProc.hwnd, cmd );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isColorizationColorAffectsBorders() {
|
||||||
|
updateColorization();
|
||||||
|
return colorizationColorAffectsBorders;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color getColorizationColor() {
|
||||||
|
updateColorization();
|
||||||
|
return colorizationColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColorizationColorBalance() {
|
||||||
|
updateColorization();
|
||||||
|
return colorizationColorBalance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateColorization() {
|
||||||
|
if( colorizationUpToDate )
|
||||||
|
return;
|
||||||
|
colorizationUpToDate = true;
|
||||||
|
|
||||||
|
String subKey = "SOFTWARE\\Microsoft\\Windows\\DWM";
|
||||||
|
|
||||||
|
int value = registryGetIntValue( subKey, "ColorPrevalence", -1 );
|
||||||
|
colorizationColorAffectsBorders = (value > 0);
|
||||||
|
|
||||||
|
value = registryGetIntValue( subKey, "ColorizationColor", -1 );
|
||||||
|
colorizationColor = (value != -1) ? new Color( value ) : null;
|
||||||
|
|
||||||
|
colorizationColorBalance = registryGetIntValue( subKey, "ColorizationColorBalance", -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
private native static int registryGetIntValue( String key, String valueName, int defaultValue );
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addChangeListener( ChangeListener l ) {
|
||||||
|
listenerList.add( ChangeListener.class, l );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeChangeListener( ChangeListener l ) {
|
||||||
|
listenerList.remove( ChangeListener.class, l );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fireStateChanged() {
|
||||||
|
Object[] listeners = listenerList.getListenerList();
|
||||||
|
if( listeners.length == 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
ChangeEvent e = new ChangeEvent( this );
|
||||||
|
for( int i = 0; i < listeners.length; i += 2 ) {
|
||||||
|
if( listeners[i] == ChangeListener.class )
|
||||||
|
((ChangeListener)listeners[i+1]).stateChanged( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Because there may be sent many WM_DWMCOLORIZATIONCOLORCHANGED messages,
|
||||||
|
* slightly delay event firing and fire it only once (on the AWT thread).
|
||||||
|
*/
|
||||||
|
void fireStateChangedLaterOnce() {
|
||||||
|
EventQueue.invokeLater( () -> {
|
||||||
|
if( fireStateChangedTimer != null ) {
|
||||||
|
fireStateChangedTimer.restart();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fireStateChangedTimer = new Timer( 300, e -> {
|
||||||
|
fireStateChangedTimer = null;
|
||||||
|
colorizationUpToDate = false;
|
||||||
|
|
||||||
|
fireStateChanged();
|
||||||
|
} );
|
||||||
|
fireStateChangedTimer.setRepeats( false );
|
||||||
|
fireStateChangedTimer.start();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class WndProc ------------------------------------------------------
|
||||||
|
|
||||||
|
private class WndProc
|
||||||
|
{
|
||||||
|
// WM_NCHITTEST mouse position codes
|
||||||
|
private static final int
|
||||||
|
HTCLIENT = 1,
|
||||||
|
HTCAPTION = 2,
|
||||||
|
HTSYSMENU = 3,
|
||||||
|
HTTOP = 12;
|
||||||
|
|
||||||
|
private Window window;
|
||||||
|
private final long hwnd;
|
||||||
|
|
||||||
|
private int titleBarHeight;
|
||||||
|
private Rectangle[] hitTestSpots;
|
||||||
|
private Rectangle appIconBounds;
|
||||||
|
|
||||||
|
WndProc( Window window ) {
|
||||||
|
this.window = window;
|
||||||
|
|
||||||
|
hwnd = installImpl( window );
|
||||||
|
if( hwnd == 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// remove the OS window title bar
|
||||||
|
updateFrame( hwnd, (window instanceof JFrame) ? ((JFrame)window).getExtendedState() : 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
void uninstall() {
|
||||||
|
uninstallImpl( hwnd );
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
window = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private native long installImpl( Window window );
|
||||||
|
private native void uninstallImpl( long hwnd );
|
||||||
|
private native void updateFrame( long hwnd, int state );
|
||||||
|
private native void showWindow( long hwnd, int cmd );
|
||||||
|
|
||||||
|
// invoked from native code
|
||||||
|
private int onNcHitTest( int x, int y, boolean isOnResizeBorder ) {
|
||||||
|
// scale-down mouse x/y
|
||||||
|
Point pt = scaleDown( x, y );
|
||||||
|
int sx = pt.x;
|
||||||
|
int sy = pt.y;
|
||||||
|
|
||||||
|
// return HTSYSMENU if mouse is over application icon
|
||||||
|
// - left-click on HTSYSMENU area shows system menu
|
||||||
|
// - double-left-click sends WM_CLOSE
|
||||||
|
if( appIconBounds != null && appIconBounds.contains( sx, sy ) )
|
||||||
|
return HTSYSMENU;
|
||||||
|
|
||||||
|
boolean isOnTitleBar = (sy < titleBarHeight);
|
||||||
|
|
||||||
|
if( isOnTitleBar ) {
|
||||||
|
// use a second reference to the array to avoid that it can be changed
|
||||||
|
// in another thread while processing the array
|
||||||
|
Rectangle[] hitTestSpots2 = hitTestSpots;
|
||||||
|
for( Rectangle spot : hitTestSpots2 ) {
|
||||||
|
if( spot.contains( sx, sy ) )
|
||||||
|
return HTCLIENT;
|
||||||
|
}
|
||||||
|
return isOnResizeBorder ? HTTOP : HTCAPTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isOnResizeBorder ? HTTOP : HTCLIENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scales down in the same way as AWT.
|
||||||
|
* See AwtWin32GraphicsDevice::ScaleDownX() and ::ScaleDownY()
|
||||||
|
*/
|
||||||
|
private Point scaleDown( int x, int y ) {
|
||||||
|
GraphicsConfiguration gc = window.getGraphicsConfiguration();
|
||||||
|
if( gc == null )
|
||||||
|
return new Point( x, y );
|
||||||
|
|
||||||
|
AffineTransform t = gc.getDefaultTransform();
|
||||||
|
return new Point( clipRound( x / t.getScaleX() ), clipRound( y / t.getScaleY() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rounds in the same way as AWT.
|
||||||
|
* See AwtWin32GraphicsDevice::ClipRound()
|
||||||
|
*/
|
||||||
|
private int clipRound( double value ) {
|
||||||
|
value -= 0.5;
|
||||||
|
if( value < Integer.MIN_VALUE )
|
||||||
|
return Integer.MIN_VALUE;
|
||||||
|
if( value > Integer.MAX_VALUE )
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
return (int) Math.ceil( value );
|
||||||
|
}
|
||||||
|
|
||||||
|
// invoked from native code
|
||||||
|
private boolean isFullscreen() {
|
||||||
|
GraphicsConfiguration gc = window.getGraphicsConfiguration();
|
||||||
|
if( gc == null )
|
||||||
|
return false;
|
||||||
|
return gc.getDevice().getFullScreenWindow() == window;
|
||||||
|
}
|
||||||
|
|
||||||
|
// invoked from native code
|
||||||
|
private void fireStateChangedLaterOnce() {
|
||||||
|
FlatWindowsNativeWindowBorder.this.fireStateChangedLaterOnce();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,17 +29,14 @@ import java.awt.event.HierarchyEvent;
|
|||||||
import java.awt.event.HierarchyListener;
|
import java.awt.event.HierarchyListener;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
import javax.swing.JDialog;
|
|
||||||
import javax.swing.JFrame;
|
|
||||||
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.plaf.BorderUIResource;
|
import javax.swing.plaf.BorderUIResource;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
import com.formdev.flatlaf.FlatSystemProperties;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
@@ -55,26 +52,29 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
*/
|
*/
|
||||||
public class JBRCustomDecorations
|
public class JBRCustomDecorations
|
||||||
{
|
{
|
||||||
private static boolean initialized;
|
private static Boolean supported;
|
||||||
private static Method Window_hasCustomDecoration;
|
private static Method Window_hasCustomDecoration;
|
||||||
private static Method Window_setHasCustomDecoration;
|
private static Method Window_setHasCustomDecoration;
|
||||||
private static Method WWindowPeer_setCustomDecorationHitTestSpots;
|
|
||||||
private static Method WWindowPeer_setCustomDecorationTitleBarHeight;
|
private static Method WWindowPeer_setCustomDecorationTitleBarHeight;
|
||||||
|
private static Method WWindowPeer_setCustomDecorationHitTestSpots;
|
||||||
private static Method AWTAccessor_getComponentAccessor;
|
private static Method AWTAccessor_getComponentAccessor;
|
||||||
private static Method AWTAccessor_ComponentAccessor_getPeer;
|
private static Method AWTAccessor_ComponentAccessor_getPeer;
|
||||||
|
|
||||||
public static boolean isSupported() {
|
public static boolean isSupported() {
|
||||||
initialize();
|
initialize();
|
||||||
return Window_setHasCustomDecoration != null;
|
return supported;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void install( JRootPane rootPane ) {
|
static Object install( JRootPane rootPane ) {
|
||||||
if( !isSupported() )
|
if( !isSupported() )
|
||||||
return;
|
return null;
|
||||||
|
|
||||||
// check whether root pane already has a parent, which is the case when switching LaF
|
// check whether root pane already has a parent, which is the case when switching LaF
|
||||||
if( rootPane.getParent() != null )
|
Window window = SwingUtilities.windowForComponent( rootPane );
|
||||||
return;
|
if( window != null ) {
|
||||||
|
FlatNativeWindowBorder.install( window );
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// Use hierarchy listener to wait until the root pane is added to a window.
|
// Use hierarchy listener to wait until the root pane is added to a window.
|
||||||
// Enabling JBR decorations must be done very early, probably before
|
// Enabling JBR decorations must be done very early, probably before
|
||||||
@@ -88,8 +88,9 @@ public class JBRCustomDecorations
|
|||||||
|
|
||||||
Container parent = e.getChangedParent();
|
Container parent = e.getChangedParent();
|
||||||
if( parent instanceof Window )
|
if( parent instanceof Window )
|
||||||
install( (Window) parent );
|
FlatNativeWindowBorder.install( (Window) parent );
|
||||||
|
|
||||||
|
// remove listener since it is actually not possible to uninstall JBR decorations
|
||||||
// use invokeLater to remove listener to avoid that listener
|
// use invokeLater to remove listener to avoid that listener
|
||||||
// is removed while listener queue is processed
|
// is removed while listener queue is processed
|
||||||
EventQueue.invokeLater( () -> {
|
EventQueue.invokeLater( () -> {
|
||||||
@@ -98,54 +99,20 @@ public class JBRCustomDecorations
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
rootPane.addHierarchyListener( addListener );
|
rootPane.addHierarchyListener( addListener );
|
||||||
|
return addListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void install( Window window ) {
|
static void uninstall( JRootPane rootPane, Object data ) {
|
||||||
if( !isSupported() )
|
// remove listener (if not yet done)
|
||||||
return;
|
if( data instanceof HierarchyListener )
|
||||||
|
rootPane.removeHierarchyListener( (HierarchyListener) data );
|
||||||
|
|
||||||
// do not enable JBR decorations if LaF provides decorations
|
// since it is actually not possible to uninstall JBR decorations,
|
||||||
if( UIManager.getLookAndFeel().getSupportsWindowDecorations() )
|
// simply reduce titleBarHeight so that it is still possible to resize window
|
||||||
return;
|
// and remove hitTestSpots
|
||||||
|
Window window = SwingUtilities.windowForComponent( rootPane );
|
||||||
if( window instanceof JFrame ) {
|
if( window != null )
|
||||||
JFrame frame = (JFrame) window;
|
setHasCustomDecoration( window, false );
|
||||||
|
|
||||||
// do not enable JBR decorations if JFrame should use system window decorations
|
|
||||||
// and if not forced to use JBR decorations
|
|
||||||
if( !JFrame.isDefaultLookAndFeelDecorated() &&
|
|
||||||
!FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, false ))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// do not enable JBR decorations if frame is undecorated
|
|
||||||
if( frame.isUndecorated() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
// enable JBR custom window decoration for window
|
|
||||||
setHasCustomDecoration( frame );
|
|
||||||
|
|
||||||
// enable Swing window decoration
|
|
||||||
frame.getRootPane().setWindowDecorationStyle( JRootPane.FRAME );
|
|
||||||
|
|
||||||
} else if( window instanceof JDialog ) {
|
|
||||||
JDialog dialog = (JDialog) window;
|
|
||||||
|
|
||||||
// do not enable JBR decorations if JDialog should use system window decorations
|
|
||||||
// and if not forced to use JBR decorations
|
|
||||||
if( !JDialog.isDefaultLookAndFeelDecorated() &&
|
|
||||||
!FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, false ))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// do not enable JBR decorations if dialog is undecorated
|
|
||||||
if( dialog.isUndecorated() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
// enable JBR custom window decoration for window
|
|
||||||
setHasCustomDecoration( dialog );
|
|
||||||
|
|
||||||
// enable Swing window decoration
|
|
||||||
dialog.getRootPane().setWindowDecorationStyle( JRootPane.PLAIN_DIALOG );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean hasCustomDecoration( Window window ) {
|
static boolean hasCustomDecoration( Window window ) {
|
||||||
@@ -155,48 +122,48 @@ public class JBRCustomDecorations
|
|||||||
try {
|
try {
|
||||||
return (Boolean) Window_hasCustomDecoration.invoke( window );
|
return (Boolean) Window_hasCustomDecoration.invoke( window );
|
||||||
} catch( Exception ex ) {
|
} catch( Exception ex ) {
|
||||||
Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, null, ex );
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setHasCustomDecoration( Window window ) {
|
static void setHasCustomDecoration( Window window, boolean hasCustomDecoration ) {
|
||||||
if( !isSupported() )
|
if( !isSupported() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if( hasCustomDecoration )
|
||||||
Window_setHasCustomDecoration.invoke( window );
|
Window_setHasCustomDecoration.invoke( window );
|
||||||
|
else
|
||||||
|
setTitleBarHeightAndHitTestSpots( window, 4, Collections.emptyList() );
|
||||||
} catch( Exception ex ) {
|
} catch( Exception ex ) {
|
||||||
Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, null, ex );
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setHitTestSpotsAndTitleBarHeight( Window window, List<Rectangle> hitTestSpots, int titleBarHeight ) {
|
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight, List<Rectangle> hitTestSpots ) {
|
||||||
if( !isSupported() )
|
if( !isSupported() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Object compAccessor = AWTAccessor_getComponentAccessor.invoke( null );
|
Object compAccessor = AWTAccessor_getComponentAccessor.invoke( null );
|
||||||
Object peer = AWTAccessor_ComponentAccessor_getPeer.invoke( compAccessor, window );
|
Object peer = AWTAccessor_ComponentAccessor_getPeer.invoke( compAccessor, window );
|
||||||
WWindowPeer_setCustomDecorationHitTestSpots.invoke( peer, hitTestSpots );
|
|
||||||
WWindowPeer_setCustomDecorationTitleBarHeight.invoke( peer, titleBarHeight );
|
WWindowPeer_setCustomDecorationTitleBarHeight.invoke( peer, titleBarHeight );
|
||||||
|
WWindowPeer_setCustomDecorationHitTestSpots.invoke( peer, hitTestSpots );
|
||||||
} catch( Exception ex ) {
|
} catch( Exception ex ) {
|
||||||
Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, null, ex );
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void initialize() {
|
private static void initialize() {
|
||||||
if( initialized )
|
if( supported != null )
|
||||||
return;
|
return;
|
||||||
initialized = true;
|
supported = false;
|
||||||
|
|
||||||
// requires JetBrains Runtime 11 and Windows 10
|
// requires JetBrains Runtime 11 and Windows 10
|
||||||
if( !SystemInfo.isJetBrainsJVM_11_orLater || !SystemInfo.isWindows_10_orLater )
|
if( !SystemInfo.isJetBrainsJVM_11_orLater || !SystemInfo.isWindows_10_orLater )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if( !FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, true ) )
|
|
||||||
return;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Class<?> awtAcessorClass = Class.forName( "sun.awt.AWTAccessor" );
|
Class<?> awtAcessorClass = Class.forName( "sun.awt.AWTAccessor" );
|
||||||
Class<?> compAccessorClass = Class.forName( "sun.awt.AWTAccessor$ComponentAccessor" );
|
Class<?> compAccessorClass = Class.forName( "sun.awt.AWTAccessor$ComponentAccessor" );
|
||||||
@@ -204,15 +171,17 @@ public class JBRCustomDecorations
|
|||||||
AWTAccessor_ComponentAccessor_getPeer = compAccessorClass.getDeclaredMethod( "getPeer", Component.class );
|
AWTAccessor_ComponentAccessor_getPeer = compAccessorClass.getDeclaredMethod( "getPeer", Component.class );
|
||||||
|
|
||||||
Class<?> peerClass = Class.forName( "sun.awt.windows.WWindowPeer" );
|
Class<?> peerClass = Class.forName( "sun.awt.windows.WWindowPeer" );
|
||||||
WWindowPeer_setCustomDecorationHitTestSpots = peerClass.getDeclaredMethod( "setCustomDecorationHitTestSpots", List.class );
|
|
||||||
WWindowPeer_setCustomDecorationTitleBarHeight = peerClass.getDeclaredMethod( "setCustomDecorationTitleBarHeight", int.class );
|
WWindowPeer_setCustomDecorationTitleBarHeight = peerClass.getDeclaredMethod( "setCustomDecorationTitleBarHeight", int.class );
|
||||||
WWindowPeer_setCustomDecorationHitTestSpots.setAccessible( true );
|
WWindowPeer_setCustomDecorationHitTestSpots = peerClass.getDeclaredMethod( "setCustomDecorationHitTestSpots", List.class );
|
||||||
WWindowPeer_setCustomDecorationTitleBarHeight.setAccessible( true );
|
WWindowPeer_setCustomDecorationTitleBarHeight.setAccessible( true );
|
||||||
|
WWindowPeer_setCustomDecorationHitTestSpots.setAccessible( true );
|
||||||
|
|
||||||
Window_hasCustomDecoration = Window.class.getDeclaredMethod( "hasCustomDecoration" );
|
Window_hasCustomDecoration = Window.class.getDeclaredMethod( "hasCustomDecoration" );
|
||||||
Window_setHasCustomDecoration = Window.class.getDeclaredMethod( "setHasCustomDecoration" );
|
Window_setHasCustomDecoration = Window.class.getDeclaredMethod( "setHasCustomDecoration" );
|
||||||
Window_hasCustomDecoration.setAccessible( true );
|
Window_hasCustomDecoration.setAccessible( true );
|
||||||
Window_setHasCustomDecoration.setAccessible( true );
|
Window_setHasCustomDecoration.setAccessible( true );
|
||||||
|
|
||||||
|
supported = true;
|
||||||
} catch( Exception ex ) {
|
} catch( Exception ex ) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
@@ -227,7 +196,6 @@ public class JBRCustomDecorations
|
|||||||
|
|
||||||
private final Color defaultActiveBorder = new Color( 0x707070 );
|
private final Color defaultActiveBorder = new Color( 0x707070 );
|
||||||
private final Color inactiveLightColor = new Color( 0xaaaaaa );
|
private final Color inactiveLightColor = new Color( 0xaaaaaa );
|
||||||
private final Color inactiveDarkColor = new Color( 0x3f3f3f );
|
|
||||||
|
|
||||||
private boolean colorizationAffectsBorders;
|
private boolean colorizationAffectsBorders;
|
||||||
private Color activeColor = defaultActiveBorder;
|
private Color activeColor = defaultActiveBorder;
|
||||||
@@ -238,15 +206,22 @@ public class JBRCustomDecorations
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private JBRWindowTopBorder() {
|
JBRWindowTopBorder() {
|
||||||
super( 1, 0, 0, 0 );
|
super( 1, 0, 0, 0 );
|
||||||
|
|
||||||
colorizationAffectsBorders = calculateAffectsBorders();
|
update();
|
||||||
activeColor = calculateActiveBorderColor();
|
installListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void update() {
|
||||||
|
colorizationAffectsBorders = isColorizationColorAffectsBorders();
|
||||||
|
activeColor = calculateActiveBorderColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void installListeners() {
|
||||||
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
||||||
toolkit.addPropertyChangeListener( "win.dwm.colorizationColor.affects.borders", e -> {
|
toolkit.addPropertyChangeListener( "win.dwm.colorizationColor.affects.borders", e -> {
|
||||||
colorizationAffectsBorders = calculateAffectsBorders();
|
colorizationAffectsBorders = isColorizationColorAffectsBorders();
|
||||||
activeColor = calculateActiveBorderColor();
|
activeColor = calculateActiveBorderColor();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
@@ -258,22 +233,28 @@ public class JBRCustomDecorations
|
|||||||
toolkit.addPropertyChangeListener( "win.frame.activeBorderColor", l );
|
toolkit.addPropertyChangeListener( "win.frame.activeBorderColor", l );
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean calculateAffectsBorders() {
|
boolean isColorizationColorAffectsBorders() {
|
||||||
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "win.dwm.colorizationColor.affects.borders" );
|
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "win.dwm.colorizationColor.affects.borders" );
|
||||||
return (value instanceof Boolean) ? (Boolean) value : true;
|
return (value instanceof Boolean) ? (Boolean) value : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Color getColorizationColor() {
|
||||||
|
return (Color) Toolkit.getDefaultToolkit().getDesktopProperty( "win.dwm.colorizationColor" );
|
||||||
|
}
|
||||||
|
|
||||||
|
int getColorizationColorBalance() {
|
||||||
|
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "win.dwm.colorizationColorBalance" );
|
||||||
|
return (value instanceof Integer) ? (Integer) value : -1;
|
||||||
|
}
|
||||||
|
|
||||||
private Color calculateActiveBorderColor() {
|
private Color calculateActiveBorderColor() {
|
||||||
if( !colorizationAffectsBorders )
|
if( !colorizationAffectsBorders )
|
||||||
return defaultActiveBorder;
|
return defaultActiveBorder;
|
||||||
|
|
||||||
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
Color colorizationColor = getColorizationColor();
|
||||||
Color colorizationColor = (Color) toolkit.getDesktopProperty( "win.dwm.colorizationColor" );
|
|
||||||
if( colorizationColor != null ) {
|
if( colorizationColor != null ) {
|
||||||
Object colorizationColorBalanceObj = toolkit.getDesktopProperty( "win.dwm.colorizationColorBalance" );
|
int colorizationColorBalance = getColorizationColorBalance();
|
||||||
if( colorizationColorBalanceObj instanceof Integer ) {
|
if( colorizationColorBalance < 0 || colorizationColorBalance > 100 )
|
||||||
int colorizationColorBalance = (Integer) colorizationColorBalanceObj;
|
|
||||||
if( colorizationColorBalance < 0 )
|
|
||||||
colorizationColorBalance = 100;
|
colorizationColorBalance = 100;
|
||||||
|
|
||||||
if( colorizationColorBalance == 0 )
|
if( colorizationColorBalance == 0 )
|
||||||
@@ -283,15 +264,19 @@ public class JBRCustomDecorations
|
|||||||
|
|
||||||
float alpha = colorizationColorBalance / 100.0f;
|
float alpha = colorizationColorBalance / 100.0f;
|
||||||
float remainder = 1 - alpha;
|
float remainder = 1 - alpha;
|
||||||
int r = Math.round( (colorizationColor.getRed() * alpha + 0xD9 * remainder) );
|
int r = Math.round( colorizationColor.getRed() * alpha + 0xD9 * remainder );
|
||||||
int g = Math.round( (colorizationColor.getGreen() * alpha + 0xD9 * remainder) );
|
int g = Math.round( colorizationColor.getGreen() * alpha + 0xD9 * remainder );
|
||||||
int b = Math.round( (colorizationColor.getBlue() * 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 );
|
return new Color( r, g, b );
|
||||||
}
|
}
|
||||||
return colorizationColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
Color activeBorderColor = (Color) toolkit.getDesktopProperty( "win.frame.activeBorderColor" );
|
Color activeBorderColor = (Color) Toolkit.getDefaultToolkit().getDesktopProperty( "win.frame.activeBorderColor" );
|
||||||
return (activeBorderColor != null) ? activeBorderColor : UIManager.getColor( "MenuBar.borderColor" );
|
return (activeBorderColor != null) ? activeBorderColor : UIManager.getColor( "MenuBar.borderColor" );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,7 +285,14 @@ public class JBRCustomDecorations
|
|||||||
Window window = SwingUtilities.windowForComponent( c );
|
Window window = SwingUtilities.windowForComponent( c );
|
||||||
boolean active = (window != null) ? window.isActive() : false;
|
boolean active = (window != null) ? window.isActive() : false;
|
||||||
|
|
||||||
g.setColor( active ? activeColor : (FlatLaf.isLafDark() ? inactiveDarkColor : inactiveLightColor) );
|
// paint top border
|
||||||
|
// - in light themes
|
||||||
|
// - in dark themes only for active windows if colorization affects borders
|
||||||
|
boolean paintTopBorder = !FlatLaf.isLafDark() || (active && colorizationAffectsBorders);
|
||||||
|
if( !paintTopBorder )
|
||||||
|
return;
|
||||||
|
|
||||||
|
g.setColor( active ? activeColor : inactiveLightColor );
|
||||||
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
|
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,249 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.util;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import javax.swing.Icon;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import com.formdev.flatlaf.util.Animator.Interpolator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Icon that automatically animates painting on component value changes.
|
||||||
|
* <p>
|
||||||
|
* {@link #getValue(Component)} returns the value of the component.
|
||||||
|
* If the value changes, then {@link #paintIconAnimated(Component, Graphics, int, int, float)}
|
||||||
|
* is invoked multiple times with animated value (from old value to new value).
|
||||||
|
* <p>
|
||||||
|
* Example for an animated icon:
|
||||||
|
* <pre>
|
||||||
|
* private class AnimatedMinimalTestIcon
|
||||||
|
* implements AnimatedIcon
|
||||||
|
* {
|
||||||
|
* @Override public int getIconWidth() { return 100; }
|
||||||
|
* @Override public int getIconHeight() { return 20; }
|
||||||
|
*
|
||||||
|
* @Override
|
||||||
|
* public void paintIconAnimated( Component c, Graphics g, int x, int y, float animatedValue ) {
|
||||||
|
* int w = getIconWidth();
|
||||||
|
* int h = getIconHeight();
|
||||||
|
*
|
||||||
|
* g.setColor( Color.red );
|
||||||
|
* g.drawRect( x, y, w - 1, h - 1 );
|
||||||
|
* g.fillRect( x, y, Math.round( w * animatedValue ), h );
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @Override
|
||||||
|
* public float getValue( Component c ) {
|
||||||
|
* return ((AbstractButton)c).isSelected() ? 1 : 0;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // sample usage
|
||||||
|
* JCheckBox checkBox = new JCheckBox( "test" );
|
||||||
|
* checkBox.setIcon( new AnimatedMinimalTestIcon() );
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Animation works only if the component passed to {@link #paintIcon(Component, Graphics, int, int)}
|
||||||
|
* is a instance of {@link JComponent}.
|
||||||
|
* A client property is set on the component to store the animation state.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public interface AnimatedIcon
|
||||||
|
extends Icon
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public default void paintIcon( Component c, Graphics g, int x, int y ) {
|
||||||
|
AnimationSupport.paintIcon( this, c, g, x, y );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paints the icon for the given animated value.
|
||||||
|
*
|
||||||
|
* @param c the component that this icon belongs to
|
||||||
|
* @param g the graphics context
|
||||||
|
* @param x the x coordinate of the icon
|
||||||
|
* @param y the y coordinate of the icon
|
||||||
|
* @param animatedValue the animated value, which is either equal to what {@link #getValue(Component)}
|
||||||
|
* returned, or somewhere between the previous value and the latest value
|
||||||
|
* that {@link #getValue(Component)} returned
|
||||||
|
*/
|
||||||
|
void paintIconAnimated( Component c, Graphics g, int x, int y, float animatedValue );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the component.
|
||||||
|
* <p>
|
||||||
|
* This can be any value and depends on the component.
|
||||||
|
* If the value changes, then this class animates from the old value to the new one.
|
||||||
|
* <p>
|
||||||
|
* For a toggle button this could be {@code 0} for off and {@code 1} for on.
|
||||||
|
*/
|
||||||
|
float getValue( Component c );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether animation is enabled for this icon (default is {@code true}).
|
||||||
|
*/
|
||||||
|
default boolean isAnimationEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the duration of the animation in milliseconds (default is 150).
|
||||||
|
*/
|
||||||
|
default int getAnimationDuration() {
|
||||||
|
return 150;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the resolution of the animation in milliseconds (default is 10).
|
||||||
|
* Resolution is the amount of time between timing events.
|
||||||
|
*/
|
||||||
|
default int getAnimationResolution() {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the interpolator for the animation.
|
||||||
|
* Default is {@link CubicBezierEasing#STANDARD_EASING}.
|
||||||
|
*/
|
||||||
|
default Interpolator getAnimationInterpolator() {
|
||||||
|
return CubicBezierEasing.STANDARD_EASING;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the client property key used to store the animation support.
|
||||||
|
*/
|
||||||
|
default Object getClientPropertyKey() {
|
||||||
|
return getClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class AnimationSupport ---------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animation support class that stores the animation state and implements the animation.
|
||||||
|
*/
|
||||||
|
class AnimationSupport
|
||||||
|
{
|
||||||
|
private float startValue;
|
||||||
|
private float targetValue;
|
||||||
|
private float animatedValue;
|
||||||
|
private float fraction;
|
||||||
|
|
||||||
|
private Animator animator;
|
||||||
|
|
||||||
|
// last x,y coordinates of the icon needed to repaint while animating
|
||||||
|
private int x;
|
||||||
|
private int y;
|
||||||
|
|
||||||
|
public static void paintIcon( AnimatedIcon icon, Component c, Graphics g, int x, int y ) {
|
||||||
|
if( !isAnimationEnabled( icon, c ) ) {
|
||||||
|
// paint without animation if animation is disabled or
|
||||||
|
// component is not a JComponent and therefore does not support
|
||||||
|
// client properties, which are required to keep animation state
|
||||||
|
paintIconImpl( icon, c, g, x, y, null );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JComponent jc = (JComponent) c;
|
||||||
|
Object key = icon.getClientPropertyKey();
|
||||||
|
AnimationSupport as = (AnimationSupport) jc.getClientProperty( key );
|
||||||
|
if( as == null ) {
|
||||||
|
// painted first time --> do not animate, but remember current component value
|
||||||
|
as = new AnimationSupport();
|
||||||
|
as.startValue = as.targetValue = as.animatedValue = icon.getValue( c );
|
||||||
|
as.x = x;
|
||||||
|
as.y = y;
|
||||||
|
jc.putClientProperty( key, as );
|
||||||
|
} else {
|
||||||
|
// get component value
|
||||||
|
float value = icon.getValue( c );
|
||||||
|
|
||||||
|
if( value != as.targetValue ) {
|
||||||
|
// value changed --> (re)start animation
|
||||||
|
|
||||||
|
if( as.animator == null ) {
|
||||||
|
// create animator
|
||||||
|
AnimationSupport as2 = as;
|
||||||
|
as.animator = new Animator( icon.getAnimationDuration(), fraction -> {
|
||||||
|
// check whether component was removed while animation is running
|
||||||
|
if( !c.isDisplayable() ) {
|
||||||
|
as2.animator.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute animated value
|
||||||
|
as2.animatedValue = as2.startValue + ((as2.targetValue - as2.startValue) * fraction);
|
||||||
|
as2.fraction = fraction;
|
||||||
|
|
||||||
|
// repaint icon
|
||||||
|
c.repaint( as2.x, as2.y, icon.getIconWidth(), icon.getIconHeight() );
|
||||||
|
}, () -> {
|
||||||
|
as2.startValue = as2.animatedValue = as2.targetValue;
|
||||||
|
as2.animator = null;
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( as.animator.isRunning() ) {
|
||||||
|
// if animation is still running, restart it from the current
|
||||||
|
// animated value to the new target value with reduced duration
|
||||||
|
as.animator.cancel();
|
||||||
|
int duration2 = (int) (icon.getAnimationDuration() * as.fraction);
|
||||||
|
if( duration2 > 0 )
|
||||||
|
as.animator.setDuration( duration2 );
|
||||||
|
as.startValue = as.animatedValue;
|
||||||
|
} else {
|
||||||
|
// new animation
|
||||||
|
as.animator.setDuration( icon.getAnimationDuration() );
|
||||||
|
as.animator.setResolution( icon.getAnimationResolution() );
|
||||||
|
as.animator.setInterpolator( icon.getAnimationInterpolator() );
|
||||||
|
|
||||||
|
as.animatedValue = as.startValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
as.targetValue = value;
|
||||||
|
as.animator.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
as.x = x;
|
||||||
|
as.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
paintIconImpl( icon, c, g, x, y, as );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void paintIconImpl( AnimatedIcon icon, Component c, Graphics g, int x, int y, AnimationSupport as ) {
|
||||||
|
float value = (as != null) ? as.animatedValue : icon.getValue( c );
|
||||||
|
icon.paintIconAnimated( c, g, x, y, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isAnimationEnabled( AnimatedIcon icon, Component c ) {
|
||||||
|
return Animator.useAnimation() && icon.isAnimationEnabled() && c instanceof JComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void saveIconLocation( AnimatedIcon icon, Component c, int x, int y ) {
|
||||||
|
if( !isAnimationEnabled( icon, c ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
AnimationSupport as = (AnimationSupport) ((JComponent)c).getClientProperty( icon.getClientPropertyKey() );
|
||||||
|
if( as != null ) {
|
||||||
|
as.x = x;
|
||||||
|
as.y = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ package com.formdev.flatlaf.util;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import javax.swing.Timer;
|
import javax.swing.Timer;
|
||||||
|
import com.formdev.flatlaf.FlatSystemProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple animator based on ideas and concepts from "Filthy Rich Clients" book
|
* Simple animator based on ideas and concepts from "Filthy Rich Clients" book
|
||||||
@@ -39,6 +40,15 @@ public class Animator
|
|||||||
private long startTime;
|
private long startTime;
|
||||||
private Timer timer;
|
private Timer timer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether animations are enabled (the default) or disabled via
|
||||||
|
* system property {@code flatlaf.animation} set to {@code false}.
|
||||||
|
* This allows disabling all animations at command line with {@code -Dflatlaf.animation=false}.
|
||||||
|
*/
|
||||||
|
public static boolean useAnimation() {
|
||||||
|
return FlatSystemProperties.getBoolean( FlatSystemProperties.ANIMATION, true );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an animation that runs duration milliseconds.
|
* Creates an animation that runs duration milliseconds.
|
||||||
* Use {@link #addTarget(TimingTarget)} to receive timing events
|
* Use {@link #addTarget(TimingTarget)} to receive timing events
|
||||||
@@ -86,7 +96,7 @@ public class Animator
|
|||||||
* Sets the duration of the animation in milliseconds.
|
* Sets the duration of the animation in milliseconds.
|
||||||
*
|
*
|
||||||
* @throws IllegalStateException if animation is running
|
* @throws IllegalStateException if animation is running
|
||||||
* @throws IllegalArgumentException if duration is <= zero
|
* @throws IllegalArgumentException if duration is <= zero
|
||||||
*/
|
*/
|
||||||
public void setDuration( int duration ) {
|
public void setDuration( int duration ) {
|
||||||
throwExceptionIfRunning();
|
throwExceptionIfRunning();
|
||||||
@@ -108,7 +118,7 @@ public class Animator
|
|||||||
*
|
*
|
||||||
* @param resolution the resolution of the animation in milliseconds
|
* @param resolution the resolution of the animation in milliseconds
|
||||||
* @throws IllegalStateException if animation is running
|
* @throws IllegalStateException if animation is running
|
||||||
* @throws IllegalArgumentException if resolution is <= zero
|
* @throws IllegalArgumentException if resolution is <= zero
|
||||||
*/
|
*/
|
||||||
public void setResolution( int resolution ) {
|
public void setResolution( int resolution ) {
|
||||||
throwExceptionIfRunning();
|
throwExceptionIfRunning();
|
||||||
@@ -174,6 +184,7 @@ public class Animator
|
|||||||
timeToStop = false;
|
timeToStop = false;
|
||||||
startTime = System.nanoTime() / 1000000;
|
startTime = System.nanoTime() / 1000000;
|
||||||
|
|
||||||
|
if( timer == null ) {
|
||||||
timer = new Timer( resolution, e -> {
|
timer = new Timer( resolution, e -> {
|
||||||
if( !hasBegun ) {
|
if( !hasBegun ) {
|
||||||
begin();
|
begin();
|
||||||
@@ -182,6 +193,8 @@ public class Animator
|
|||||||
|
|
||||||
timingEvent( getTimingFraction() );
|
timingEvent( getTimingFraction() );
|
||||||
} );
|
} );
|
||||||
|
} else
|
||||||
|
timer.setDelay( resolution );
|
||||||
timer.setInitialDelay( 0 );
|
timer.setInitialDelay( 0 );
|
||||||
timer.start();
|
timer.start();
|
||||||
}
|
}
|
||||||
@@ -203,10 +216,11 @@ public class Animator
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void stop( boolean cancel ) {
|
private void stop( boolean cancel ) {
|
||||||
if( timer != null ) {
|
if( !running )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( timer != null )
|
||||||
timer.stop();
|
timer.stop();
|
||||||
timer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !cancel )
|
if( !cancel )
|
||||||
end();
|
end();
|
||||||
@@ -215,6 +229,15 @@ public class Animator
|
|||||||
timeToStop = false;
|
timeToStop = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restarts the animation.
|
||||||
|
* Invokes {@link #cancel()} and {@link #start()}.
|
||||||
|
*/
|
||||||
|
public void restart() {
|
||||||
|
cancel();
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether this animation is running.
|
* Returns whether this animation is running.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -28,11 +28,12 @@ public class ColorFunctions
|
|||||||
public static Color applyFunctions( Color color, ColorFunction... functions ) {
|
public static Color applyFunctions( Color color, ColorFunction... functions ) {
|
||||||
float[] hsl = HSLColor.fromRGB( color );
|
float[] hsl = HSLColor.fromRGB( color );
|
||||||
float alpha = color.getAlpha() / 255f;
|
float alpha = color.getAlpha() / 255f;
|
||||||
|
float[] hsla = { hsl[0], hsl[1], hsl[2], alpha * 100 };
|
||||||
|
|
||||||
for( ColorFunction function : functions )
|
for( ColorFunction function : functions )
|
||||||
function.apply( hsl );
|
function.apply( hsla );
|
||||||
|
|
||||||
return HSLColor.toRGB( hsl, alpha );
|
return HSLColor.toRGB( hsla[0], hsla[1], hsla[2], hsla[3] / 100 );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float clamp( float value ) {
|
public static float clamp( float value ) {
|
||||||
@@ -43,16 +44,48 @@ public class ColorFunctions
|
|||||||
: value);
|
: value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a color that is a mixture of two colors.
|
||||||
|
*
|
||||||
|
* @param color1 first color
|
||||||
|
* @param color2 second color
|
||||||
|
* @param weight the weight (in range 0-1) to mix the two colors.
|
||||||
|
* Larger weight uses more of first color, smaller weight more of second color.
|
||||||
|
* @return mixture of colors
|
||||||
|
*/
|
||||||
|
public static Color mix( Color color1, Color color2, float weight ) {
|
||||||
|
if( weight >= 1 )
|
||||||
|
return color1;
|
||||||
|
if( weight <= 0 )
|
||||||
|
return color2;
|
||||||
|
|
||||||
|
int r1 = color1.getRed();
|
||||||
|
int g1 = color1.getGreen();
|
||||||
|
int b1 = color1.getBlue();
|
||||||
|
int a1 = color1.getAlpha();
|
||||||
|
|
||||||
|
int r2 = color2.getRed();
|
||||||
|
int g2 = color2.getGreen();
|
||||||
|
int b2 = color2.getBlue();
|
||||||
|
int a2 = color2.getAlpha();
|
||||||
|
|
||||||
|
return new Color(
|
||||||
|
Math.round( r2 + ((r1 - r2) * weight) ),
|
||||||
|
Math.round( g2 + ((g1 - g2) * weight) ),
|
||||||
|
Math.round( b2 + ((b1 - b2) * weight) ),
|
||||||
|
Math.round( a2 + ((a1 - a2) * weight) ) );
|
||||||
|
}
|
||||||
|
|
||||||
//---- interface ColorFunction --------------------------------------------
|
//---- interface ColorFunction --------------------------------------------
|
||||||
|
|
||||||
public interface ColorFunction {
|
public interface ColorFunction {
|
||||||
void apply( float[] hsl );
|
void apply( float[] hsla );
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class HSLIncreaseDecrease ------------------------------------------
|
//---- class HSLIncreaseDecrease ------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Increase or decrease hue, saturation or luminance of a color in the HSL color space
|
* Increase or decrease hue, saturation, luminance or alpha of a color in the HSL color space
|
||||||
* by an absolute or relative amount.
|
* by an absolute or relative amount.
|
||||||
*/
|
*/
|
||||||
public static class HSLIncreaseDecrease
|
public static class HSLIncreaseDecrease
|
||||||
@@ -75,18 +108,65 @@ public class ColorFunctions
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply( float[] hsl ) {
|
public void apply( float[] hsla ) {
|
||||||
float amount2 = increase ? amount : -amount;
|
float amount2 = increase ? amount : -amount;
|
||||||
amount2 = autoInverse && shouldInverse( hsl ) ? -amount2 : amount2;
|
|
||||||
hsl[hslIndex] = clamp( relative
|
if( hslIndex == 0 ) {
|
||||||
? (hsl[hslIndex] * ((100 + amount2) / 100))
|
// hue is range 0-360
|
||||||
: (hsl[hslIndex] + amount2) );
|
hsla[0] = (hsla[0] + amount2) % 360;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean shouldInverse( float[] hsl ) {
|
amount2 = autoInverse && shouldInverse( hsla ) ? -amount2 : amount2;
|
||||||
|
hsla[hslIndex] = clamp( relative
|
||||||
|
? (hsla[hslIndex] * ((100 + amount2) / 100))
|
||||||
|
: (hsla[hslIndex] + amount2) );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean shouldInverse( float[] hsla ) {
|
||||||
return increase
|
return increase
|
||||||
? hsl[hslIndex] >= 50
|
? hsla[hslIndex] > 65
|
||||||
: hsl[hslIndex] < 50;
|
: hsla[hslIndex] < 35;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
String name;
|
||||||
|
switch( hslIndex ) {
|
||||||
|
case 0: name = "spin"; break;
|
||||||
|
case 1: name = increase ? "saturate" : "desaturate"; break;
|
||||||
|
case 2: name = increase ? "lighten" : "darken"; break;
|
||||||
|
case 3: name = increase ? "fadein" : "fadeout"; break;
|
||||||
|
default: throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
return String.format( "%s(%.0f%%%s%s)", name, amount,
|
||||||
|
(relative ? " relative" : ""),
|
||||||
|
(autoInverse ? " autoInverse" : "") );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class HSLIncreaseDecrease ------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the alpha of a color.
|
||||||
|
*/
|
||||||
|
public static class Fade
|
||||||
|
implements ColorFunction
|
||||||
|
{
|
||||||
|
public final float amount;
|
||||||
|
|
||||||
|
public Fade( float amount ) {
|
||||||
|
this.amount = amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply( float[] hsla ) {
|
||||||
|
hsla[3] = clamp( amount );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format( "fade(%.0f%%)", amount );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,13 @@ package com.formdev.flatlaf.util;
|
|||||||
public class CubicBezierEasing
|
public class CubicBezierEasing
|
||||||
implements Animator.Interpolator
|
implements Animator.Interpolator
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Standard easing as specified in Material design (0.4, 0, 0.2, 1).
|
||||||
|
*
|
||||||
|
* @see <a href="https://material.io/design/motion/speed.html#easing">https://material.io/design/motion/speed.html#easing</a>
|
||||||
|
*/
|
||||||
|
public static final CubicBezierEasing STANDARD_EASING = new CubicBezierEasing( 0.4f, 0f, 0.2f, 1f );
|
||||||
|
|
||||||
// common cubic-bezier easing functions (same as in CSS)
|
// common cubic-bezier easing functions (same as in CSS)
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function
|
// https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function
|
||||||
public static final CubicBezierEasing EASE = new CubicBezierEasing( 0.25f, 0.1f, 0.25f, 1f );
|
public static final CubicBezierEasing EASE = new CubicBezierEasing( 0.25f, 0.1f, 0.25f, 1f );
|
||||||
|
|||||||
@@ -59,4 +59,17 @@ public class DerivedColor
|
|||||||
public ColorFunction[] getFunctions() {
|
public ColorFunction[] getFunctions() {
|
||||||
return functions;
|
return functions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder buf = new StringBuilder();
|
||||||
|
buf.append( super.toString() );
|
||||||
|
|
||||||
|
for( ColorFunction function : functions ) {
|
||||||
|
buf.append( '\n' );
|
||||||
|
buf.append( function.toString() );
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -256,11 +256,6 @@ public class Graphics2DProxy
|
|||||||
delegate.dispose();
|
delegate.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void finalize() {
|
|
||||||
delegate.finalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return delegate.toString();
|
return delegate.toString();
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ public class HiDPIUtils
|
|||||||
// - fractional scale factors result in fractional component Y device coordinates
|
// - fractional scale factors result in fractional component Y device coordinates
|
||||||
// - fractional text Y device coordinates are rounded for horizontal lines of characters
|
// - fractional text Y device coordinates are rounded for horizontal lines of characters
|
||||||
// - maybe different rounding methods for drawing primitives (e.g. rectangle) and text
|
// - maybe different rounding methods for drawing primitives (e.g. rectangle) and text
|
||||||
// - Java adds 0.5 to X/Y positions in before drawing string in BufferedTextPipe.enqueueGlyphList()
|
// - Java adds 0.5 to X/Y positions before drawing string in BufferedTextPipe.enqueueGlyphList()
|
||||||
|
|
||||||
// this is not the optimal solution, but works very good in most cases
|
// this is not the optimal solution, but works very good in most cases
|
||||||
// (tested with class FlatPaintingStringTest on Windows 10 with font "Segoe UI")
|
// (tested with class FlatPaintingStringTest on Windows 10 with font "Segoe UI")
|
||||||
|
|||||||
@@ -16,18 +16,16 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.util;
|
package com.formdev.flatlaf.util;
|
||||||
|
|
||||||
|
import java.awt.FontMetrics;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides Java version compatibility methods.
|
* Provides Java version compatibility methods.
|
||||||
*
|
* <p>
|
||||||
* WARNING: This is private API and may change.
|
* WARNING: This is private API and may change.
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
@@ -35,10 +33,12 @@ import com.formdev.flatlaf.FlatLaf;
|
|||||||
public class JavaCompatibility
|
public class JavaCompatibility
|
||||||
{
|
{
|
||||||
private static Method drawStringUnderlineCharAtMethod;
|
private static Method drawStringUnderlineCharAtMethod;
|
||||||
|
private static Method getClippedStringMethod;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java 8: sun.swing.SwingUtilities2.drawStringUnderlineCharAt( JComponent c,
|
* Java 8: sun.swing.SwingUtilities2.drawStringUnderlineCharAt( JComponent c,
|
||||||
* Graphics g, String text, int underlinedIndex, int x, int y )
|
* Graphics g, String text, int underlinedIndex, int x, int y )
|
||||||
|
* <br>
|
||||||
* Java 9: javax.swing.plaf.basic.BasicGraphicsUtils.drawStringUnderlineCharAt( JComponent c,
|
* Java 9: javax.swing.plaf.basic.BasicGraphicsUtils.drawStringUnderlineCharAt( JComponent c,
|
||||||
* Graphics2D g, String string, int underlinedIndex, float x, float y )
|
* Graphics2D g, String string, int underlinedIndex, float x, float y )
|
||||||
*/
|
*/
|
||||||
@@ -55,7 +55,7 @@ public class JavaCompatibility
|
|||||||
? new Class[] { JComponent.class, Graphics2D.class, String.class, int.class, float.class, float.class }
|
? new Class[] { JComponent.class, Graphics2D.class, String.class, int.class, float.class, float.class }
|
||||||
: new Class[] { JComponent.class, Graphics.class, String.class, int.class, int.class, int.class } );
|
: new Class[] { JComponent.class, Graphics.class, String.class, int.class, int.class, int.class } );
|
||||||
} catch( Exception ex ) {
|
} catch( Exception ex ) {
|
||||||
Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, null, ex );
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
throw new RuntimeException( ex );
|
throw new RuntimeException( ex );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,7 +67,40 @@ public class JavaCompatibility
|
|||||||
else
|
else
|
||||||
drawStringUnderlineCharAtMethod.invoke( null, c, g, text, underlinedIndex, x, y );
|
drawStringUnderlineCharAtMethod.invoke( null, c, g, text, underlinedIndex, x, y );
|
||||||
} catch( IllegalAccessException | IllegalArgumentException | InvocationTargetException ex ) {
|
} catch( IllegalAccessException | IllegalArgumentException | InvocationTargetException ex ) {
|
||||||
Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, null, ex );
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
throw new RuntimeException( ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 8: sun.swing.SwingUtilities2.clipStringIfNecessary( JComponent c,
|
||||||
|
* FontMetrics fm, String string, int availTextWidth )
|
||||||
|
* <br>
|
||||||
|
* Java 9: javax.swing.plaf.basic.BasicGraphicsUtils.getClippedString( JComponent c,
|
||||||
|
* FontMetrics fm, String string, int availTextWidth )
|
||||||
|
*/
|
||||||
|
public static String getClippedString( JComponent c, FontMetrics fm, String string, int availTextWidth ) {
|
||||||
|
synchronized( JavaCompatibility.class ) {
|
||||||
|
if( getClippedStringMethod == null ) {
|
||||||
|
try {
|
||||||
|
Class<?> cls = Class.forName( SystemInfo.isJava_9_orLater
|
||||||
|
? "javax.swing.plaf.basic.BasicGraphicsUtils"
|
||||||
|
: "sun.swing.SwingUtilities2" );
|
||||||
|
getClippedStringMethod = cls.getMethod( SystemInfo.isJava_9_orLater
|
||||||
|
? "getClippedString"
|
||||||
|
: "clipStringIfNecessary",
|
||||||
|
new Class[] { JComponent.class, FontMetrics.class, String.class, int.class } );
|
||||||
|
} catch( Exception ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
throw new RuntimeException( ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return (String) getClippedStringMethod.invoke( null, c, fm, string, availTextWidth );
|
||||||
|
} catch( IllegalAccessException | IllegalArgumentException | InvocationTargetException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
throw new RuntimeException( ex );
|
throw new RuntimeException( ex );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public interface LoggingFacade
|
||||||
|
{
|
||||||
|
LoggingFacade INSTANCE = new LoggingFacadeImpl();
|
||||||
|
|
||||||
|
void logSevere( String message, Throwable t );
|
||||||
|
void logConfig( String message, Throwable t );
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.util;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
class LoggingFacadeImpl
|
||||||
|
implements LoggingFacade
|
||||||
|
{
|
||||||
|
private static final Logger LOG = Logger.getLogger( FlatLaf.class.getName() );
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logSevere( String message, Throwable t ) {
|
||||||
|
LOG.log( Level.SEVERE, message, t );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logConfig( String message, Throwable t ) {
|
||||||
|
LOG.log( Level.CONFIG, message, t );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,11 +16,20 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.util;
|
package com.formdev.flatlaf.util;
|
||||||
|
|
||||||
|
import java.awt.Dimension;
|
||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
//
|
||||||
|
// NOTE:
|
||||||
|
// This implementation is for Java 8 only.
|
||||||
|
// There is also a variant for Java 9 and later.
|
||||||
|
//
|
||||||
|
// Make sure that the API is in sync.
|
||||||
|
//
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Support for multi-resolution images available since Java 9.
|
* Support for multi-resolution images available since Java 9.
|
||||||
*
|
*
|
||||||
@@ -28,26 +37,86 @@ import java.util.function.Function;
|
|||||||
*/
|
*/
|
||||||
public class MultiResolutionImageSupport
|
public class MultiResolutionImageSupport
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Checks whether multi-resolution image support is available.
|
||||||
|
*
|
||||||
|
* @return {@code true} when running on Java 9 or later; {@code false} on Java 8
|
||||||
|
*/
|
||||||
public static boolean isAvailable() {
|
public static boolean isAvailable() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given image is a multi-resolution image that implements
|
||||||
|
* the interface {@code java.awt.image.MultiResolutionImage}.
|
||||||
|
*/
|
||||||
public static boolean isMultiResolutionImage( Image image ) {
|
public static boolean isMultiResolutionImage( Image image ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a multi-resolution image from the given resolution variants.
|
||||||
|
*
|
||||||
|
* @param baseImageIndex index of the base image in the resolution variants array
|
||||||
|
* @param resolutionVariants image resolution variants (sorted by size; smallest first)
|
||||||
|
* @return a multi-resolution image on Java 9 or later; the base image on Java 8
|
||||||
|
*/
|
||||||
public static Image create( int baseImageIndex, Image... resolutionVariants ) {
|
public static Image create( int baseImageIndex, Image... resolutionVariants ) {
|
||||||
return resolutionVariants[baseImageIndex];
|
return resolutionVariants[baseImageIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a multi-resolution image for the given dimensions.
|
||||||
|
* Initially the image does not contain any image data.
|
||||||
|
* The real images are created (and cached) on demand by invoking the given producer function.
|
||||||
|
* <p>
|
||||||
|
* The given dimensions array is only used for {@link #getResolutionVariants(Image)}.
|
||||||
|
* The producer function may be invoked with any dimension (that is not contained in
|
||||||
|
* dimensions array) and is expected to produce a image for the passed in dimension.
|
||||||
|
*
|
||||||
|
* @param baseImageIndex index of the base image in the dimensions array
|
||||||
|
* @param dimensions dimensions of resolution variants (sorted by size; smallest first)
|
||||||
|
* @param producer producer function that creates a real image for the requested size
|
||||||
|
* @return a multi-resolution image on Java 9 or later; the base image on Java 8
|
||||||
|
*/
|
||||||
|
public static Image create( int baseImageIndex, Dimension[] dimensions, Function<Dimension, Image> producer ) {
|
||||||
|
return producer.apply( dimensions[baseImageIndex] );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a multi-resolution image that maps images from another multi-resolution image
|
||||||
|
* using the given mapper function.
|
||||||
|
* <p>
|
||||||
|
* Can be used to apply filter to multi-resolution images on demand.
|
||||||
|
* E.g. passed in image is for "enabled" state and mapper function creates images
|
||||||
|
* for "disabled" state.
|
||||||
|
*
|
||||||
|
* @param image a multi-resolution image that is mapped using the given mapper function
|
||||||
|
* @param mapper mapper function that maps a single resolution variant to a new image (e.g. applying an filter)
|
||||||
|
* @return a multi-resolution image on Java 9 or later; a mapped image on Java 8
|
||||||
|
*/
|
||||||
public static Image map( Image image, Function<Image, Image> mapper ) {
|
public static Image map( Image image, Function<Image, Image> mapper ) {
|
||||||
return mapper.apply( image );
|
return mapper.apply( image );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the image variant that best matches the given width and height.
|
||||||
|
* <p>
|
||||||
|
* If the given image is a multi-resolution image then invokes
|
||||||
|
* {@code java.awt.image.MultiResolutionImage.getResolutionVariant(destImageWidth, destImageHeight)}.
|
||||||
|
* Otherwise returns the given image.
|
||||||
|
*/
|
||||||
public static Image getResolutionVariant( Image image, int destImageWidth, int destImageHeight ) {
|
public static Image getResolutionVariant( Image image, int destImageWidth, int destImageHeight ) {
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of all resolution variants.
|
||||||
|
* <p>
|
||||||
|
* If the given image is a multi-resolution image then invokes
|
||||||
|
* {@code java.awt.image.MultiResolutionImage.getResolutionVariants()}.
|
||||||
|
* Otherwise returns a list containing only the given image.
|
||||||
|
*/
|
||||||
public static List<Image> getResolutionVariants( Image image ) {
|
public static List<Image> getResolutionVariants( Image image ) {
|
||||||
return Collections.singletonList( image );
|
return Collections.singletonList( image );
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user