mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2026-02-11 06:27:13 -06:00
Compare commits
460 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0997fb5d2 | ||
|
|
37dab9fb22 | ||
|
|
fb44c8fbe4 | ||
|
|
94375b7d36 | ||
|
|
8b585deb78 | ||
|
|
4d8b544aed | ||
|
|
548d651d29 | ||
|
|
0b342acec9 | ||
|
|
cc6d3c1b1a | ||
|
|
74a748d92e | ||
|
|
1de81d0af5 | ||
|
|
ff9ef21f67 | ||
|
|
266a546478 | ||
|
|
87407ca832 | ||
|
|
90282d4436 | ||
|
|
abbe6d6c1f | ||
|
|
a28f701e6f | ||
|
|
4cdc995a7f | ||
|
|
c708205593 | ||
|
|
a22c6c8013 | ||
|
|
b576f473e5 | ||
|
|
0b127caa83 | ||
|
|
4507ce359d | ||
|
|
3e14f28dc2 | ||
|
|
a9dcf09d13 | ||
|
|
c8998c2bcf | ||
|
|
10bf1295bc | ||
|
|
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 | ||
|
|
f57dbf94c8 | ||
|
|
c0f15d2e6f | ||
|
|
cb525fafb6 | ||
|
|
5cae3a8141 |
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/
|
||||||
|
|||||||
40
.travis.yml
40
.travis.yml
@@ -1,40 +0,0 @@
|
|||||||
language: java
|
|
||||||
sudo: false
|
|
||||||
|
|
||||||
jdk:
|
|
||||||
- openjdk8
|
|
||||||
- openjdk9
|
|
||||||
- openjdk11
|
|
||||||
- openjdk14
|
|
||||||
- openjdk15
|
|
||||||
|
|
||||||
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
|
|
||||||
431
CHANGELOG.md
431
CHANGELOG.md
@@ -1,6 +1,437 @@
|
|||||||
FlatLaf Change Log
|
FlatLaf Change Log
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
## 1.4
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- TextField, FormattedTextField and PasswordField: Support adding extra padding.
|
||||||
|
(set client property `JTextField.padding` to `Insets`).
|
||||||
|
- PasswordField: UI delegate `FlatPasswordFieldUI` now extends `FlatTextFieldUI`
|
||||||
|
(instead of `BasicPasswordFieldUI`) to avoid duplicate code and for easier
|
||||||
|
extensibility.
|
||||||
|
- Table and PopupFactory: Use `StackWalker` in Java 9+ for better performance.
|
||||||
|
(issue #334)
|
||||||
|
- ToolBar: Paint focus indicator for focused button in toolbar. (issue #346)
|
||||||
|
- ToolBar: Support focusable buttons in toolbar (set UI values
|
||||||
|
`ToolBar.focusableButtons` to `true`). (issue #346)
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- ComboBox (editable) and Spinner: Increased size of internal text field to the
|
||||||
|
component border so that it behaves like plain text field (mouse click to left
|
||||||
|
of text now positions caret to first character instead of opening ComboBox
|
||||||
|
popup; mouse cursor is now of type "text" within the whole component, except
|
||||||
|
for arrow buttons). (issue #330)
|
||||||
|
- ComboBox (not editable): Increased size of internal renderer pane to the
|
||||||
|
component border so that it can paint within the whole component. Also
|
||||||
|
increase combo box size if a custom renderer uses a border with insets that
|
||||||
|
are larger than the default combo box padding (`2,6,2,6`).
|
||||||
|
- Fixed component heights at `1.25x`, `1.75x` and `2.25x` scaling factors (Java
|
||||||
|
8 only) so that Button, ComboBox, Spinner and TextField components (including
|
||||||
|
subclasses) have same heights. This increases heights of Button and TextField
|
||||||
|
components by:
|
||||||
|
- `2px` at `1.75x` in **Light** and **Dark** themes
|
||||||
|
- `2px` at `1.25x` and `2.25x` in **IntelliJ** and **Darcula** themes
|
||||||
|
- OptionPane: Do not make child components, which are derived from `JPanel`,
|
||||||
|
non-opaque. (issue #349)
|
||||||
|
- OptionPane: Align wrapped lines to the right if component orientation is
|
||||||
|
right-to-left. (issue #350)
|
||||||
|
- PasswordField: Caps lock icon no longer painted over long text. (issue #172)
|
||||||
|
- PasswordField: Paint caps lock icon on left side in right-to-left component
|
||||||
|
orientation.
|
||||||
|
- Window decorations: Window title bar width is no longer considered when
|
||||||
|
calculating preferred/minimum width of window. (issue #351)
|
||||||
|
|
||||||
|
|
||||||
|
## 1.3
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- TextComponents, ComboBox and Spinner: Support different background color when
|
||||||
|
component is focused (use UI values `TextField.focusedBackground`,
|
||||||
|
`PasswordField.focusedBackground`, `FormattedTextField.focusedBackground`,
|
||||||
|
`TextArea.focusedBackground`, `TextPane.focusedBackground`,
|
||||||
|
`EditorPane.focusedBackground`, `ComboBox.focusedBackground`,
|
||||||
|
`ComboBox.buttonFocusedBackground`, `ComboBox.popupBackground` and
|
||||||
|
`Spinner.focusedBackground`). (issue #335)
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Fixed white lines at bottom and right side of window (in dark themes on HiDPI
|
||||||
|
screens with scaling enabled).
|
||||||
|
- ScrollBar: Fixed left/top arrow icon location (if visible). (issue #329)
|
||||||
|
- Spinner: Fixed up/down arrow icon location.
|
||||||
|
- ToolTip: Fixed positioning of huge tooltips. (issue #333)
|
||||||
|
|
||||||
|
|
||||||
|
## 1.2
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Renamed `Flat*Laf.install()` methods to `Flat*Laf.setup()` to avoid confusion
|
||||||
|
with `UIManager.installLookAndFeel(LookAndFeelInfo info)`. The old
|
||||||
|
`Flat*Laf.install()` methods are still there, but marked as deprecated. They
|
||||||
|
will be removed in a future version.
|
||||||
|
- Button and ToggleButton: Support borderless button style (set client property
|
||||||
|
`JButton.buttonType` to `borderless`). (PR #276)
|
||||||
|
- ComboBox: Support using as cell renderer (e.g. in `JTable`).
|
||||||
|
- DesktopPane: Improved layout of iconified internal frames in dock:
|
||||||
|
- Always placed at bottom-left in desktop pane.
|
||||||
|
- Newly iconified frames are added to the right side of the dock.
|
||||||
|
- If frame is deiconified, dock is compacted (icons move to the left).
|
||||||
|
- If dock is wider than desktop width, additional rows are used.
|
||||||
|
- If desktop pane is resized, layout of dock is updated.
|
||||||
|
- TableHeader: Moved table header column border painting from
|
||||||
|
`FlatTableHeaderUI` to new border `FlatTableHeaderBorder` to improve
|
||||||
|
compatibility with custom table header implementations. (issue #228)
|
||||||
|
- Linux: Enable text anti-aliasing if no Gnome or KDE Desktop properties are
|
||||||
|
available. (issue #218)
|
||||||
|
- IntelliJ Themes: Added "Material Theme UI Lite / GitHub Dark" theme.
|
||||||
|
- JIDE Common Layer: Improved support for `JideTabbedPane`. (PR #306)
|
||||||
|
- Extras: `FlatSVGIcon` improvements:
|
||||||
|
- Each icon can now have its own color filter. (PR #303)
|
||||||
|
- Use mapper function in color filter to dynamically map colors. (PR #303)
|
||||||
|
- Color filter supports light and dark themes.
|
||||||
|
- Getters for icon name, classloader, etc.
|
||||||
|
- Extras: UI Inspector: Show class hierarchies when pressing <kbd>Alt</kbd> key
|
||||||
|
and prettified class names (dimmed package name).
|
||||||
|
- Extras: `FlatSVGUtils.createWindowIconImages()` now returns a single
|
||||||
|
multi-resolution image that creates requested image sizes on demand from SVG
|
||||||
|
(only on Windows with Java 9+).
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- CheckBox and RadioButton: Do not fill background if used as cell renderer,
|
||||||
|
except if cell is selected or has different background color. (issue #311)
|
||||||
|
- DesktopPane:
|
||||||
|
- Fixed missing preview of iconified internal frames in dock when using a
|
||||||
|
custom desktop manager. (PR #294)
|
||||||
|
- Fixed incomplete preview of iconified internal frames in dock when switching
|
||||||
|
LaF.
|
||||||
|
- On HiDPI screens, use high-resolution images for preview of iconified
|
||||||
|
internal frames in dock.
|
||||||
|
- PopupFactory: Fixed occasional `NullPointerException` in
|
||||||
|
`FlatPopupFactory.fixToolTipLocation()`. (issue #305)
|
||||||
|
- Tree: Fill cell background if
|
||||||
|
`DefaultTreeCellRenderer.setBackgroundNonSelectionColor(Color)` was used.
|
||||||
|
(issue #322)
|
||||||
|
- IntelliJ Themes: Fixed background colors of DesktopPane and DesktopIcon in all
|
||||||
|
themes.
|
||||||
|
- Native window decorations:
|
||||||
|
- Fixed slow application startup under particular conditions. (e.g. incomplete
|
||||||
|
custom JRE) (issue #319)
|
||||||
|
- Fixed occasional double window title bar when creating many frames or
|
||||||
|
dialogs. (issue #315)
|
||||||
|
- Fixed broken maximizing window (under special conditions) when restoring
|
||||||
|
frame state at startup.
|
||||||
|
- Title icon: For multi-resolution images now use `getResolutionVariant(width,
|
||||||
|
height)` (instead of `getResolutionVariants()`) to allow creation of
|
||||||
|
requested size on demand. This also avoids creation of all resolution
|
||||||
|
variants.
|
||||||
|
- Double-click at upper-left corner of maximized frame did not close window.
|
||||||
|
(issue #326)
|
||||||
|
- Linux: Fixed/improved detection of user font settings. (issue #309)
|
||||||
|
|
||||||
|
|
||||||
|
## 1.1.2
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Native window decorations: Added API to check whether current platform
|
||||||
|
supports window decorations (`FlatLaf.supportsNativeWindowDecorations()`) and
|
||||||
|
to toggle window decorations of all windows
|
||||||
|
(`FlatLaf.setUseNativeWindowDecorations(boolean)`).
|
||||||
|
- Native window decorations: Support changing title bar background and
|
||||||
|
foreground colors per window. (set client properties
|
||||||
|
`JRootPane.titleBarBackground` and `JRootPane.titleBarForeground` on root pane
|
||||||
|
to a `java.awt.Color`).
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Native window decorations: Fixed loading of native library when using Java
|
||||||
|
Platform Module System (JPMS) for application. (issue #289)
|
||||||
|
- Native window decorations: Removed superfluous pixel-line at top of screen
|
||||||
|
when window is maximized. (issue #296)
|
||||||
|
- Window decorations: Fixed random window title bar background in cases were
|
||||||
|
background is not filled by custom window or root pane components and unified
|
||||||
|
background is enabled.
|
||||||
|
- IntelliJ Themes: Fixed window title bar background if unified background is
|
||||||
|
enabled.
|
||||||
|
- IntelliJ Themes: Fixed system colors.
|
||||||
|
- Button and ToggleButton: Do not paint background of disabled (and unselected)
|
||||||
|
toolBar buttons. (issue #292; regression since fixing #112)
|
||||||
|
- ComboBox and Spinner: Fixed too wide arrow button if component is higher than
|
||||||
|
preferred. (issue #302)
|
||||||
|
- SplitPane: `JSplitPane.setContinuousLayout(false)` did not work. (issue #301)
|
||||||
|
- TabbedPane: Fixed NPE when creating/modifying in another thread. (issue #299)
|
||||||
|
- Fixed crash when running in Webswing. (issue #290)
|
||||||
|
|
||||||
|
|
||||||
|
## 1.1.1
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Native window decorations: Support disabling native window decorations per
|
||||||
|
window. (set client property `JRootPane.useWindowDecorations` on root pane to
|
||||||
|
`false`).
|
||||||
|
- Support running on WinPE. (issue #279)
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Native window decorations: Fixed missing animations when minimizing,
|
||||||
|
maximizing or restoring a window using window title bar buttons. (issue #282)
|
||||||
|
- Native window decorations: Fixed broken maximizing window when restoring frame
|
||||||
|
state at startup. (issue #283)
|
||||||
|
- Native window decorations: Fixed double window title bar when first disposing
|
||||||
|
a window with `frame.dispose()` and then showing it again with
|
||||||
|
`frame.setVisible(true)`. (issue #277)
|
||||||
|
- Custom window decorations: Fixed NPE in `FlatTitlePane.findHorizontalGlue()`.
|
||||||
|
(issue #275)
|
||||||
|
- Custom window decorations: Fixed right aligned progress bar in embedded menu
|
||||||
|
bar was overlapping window title. (issue #272)
|
||||||
|
- Fixed missing focus indicators in heavy-weight popups. (issue #273)
|
||||||
|
- InternalFrame: Fixed translucent internal frame menu bar background if
|
||||||
|
`TitlePane.unifiedBackground` is `true`. (issue #274)
|
||||||
|
- Extras: UI Inspector: Fixed `InaccessibleObjectException` when running in Java 16.
|
||||||
|
|
||||||
|
|
||||||
|
## 1.1
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Windows 10 only:
|
||||||
|
- Native window decorations for Windows 10 enables dark frame/dialog title bar
|
||||||
|
and embedded menu bar with all JREs, while still having native Windows 10
|
||||||
|
border drop shadows, resize behavior, window snapping and system window
|
||||||
|
menu. (PR #267)
|
||||||
|
- Custom window decorations: Support right aligned components in `JFrame`
|
||||||
|
title bar with embedded menu bar (using `Box.createHorizontalGlue()`). (PR
|
||||||
|
#268)
|
||||||
|
- Custom window decorations: Improved centering of window title with embedded
|
||||||
|
menu bar. (PR #268; issue #252)
|
||||||
|
- Custom window decorations: Support unified backgrounds for window title bar,
|
||||||
|
menu bar and main content. If enabled with `UIManager.put(
|
||||||
|
"TitlePane.unifiedBackground", true );` then window title bar and menu bar
|
||||||
|
use same background color as main content. (PR #268; issue #254)
|
||||||
|
- JIDE Common Layer: Support `JideButton`, `JideLabel`, `JideSplitButton`,
|
||||||
|
`JideToggleButton` and `JideToggleSplitButton`.
|
||||||
|
- JIDE Common Layer: The library on Maven Central no longer depends on
|
||||||
|
`com.jidesoft:jide-oss:3.6.18` to avoid problems when another JIDE library
|
||||||
|
should be used. (issue #270)
|
||||||
|
- SwingX: The library on Maven Central no longer depends on
|
||||||
|
`org.swinglabs.swingx:swingx-all:1.6.5-1` to avoid problems when another
|
||||||
|
SwingX library should be used.
|
||||||
|
- Support running in [JetBrains Projector](https://jetbrains.com/projector/).
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- IntelliJ Themes: Fixed text color of CheckBoxMenuItem and RadioButtonMenuItem
|
||||||
|
in all "Arc" themes. (issue #259)
|
||||||
|
|
||||||
|
|
||||||
|
## 1.0
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Extras: UI Inspector: Tooltip is no longer limited to window bounds.
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- TabbedPane: Custom `TabbedPane.selectedForeground` color did not work when
|
||||||
|
`TabbedPane.foreground` has also custom color. (issue #257)
|
||||||
|
- FileChooser: Fixed display of date in details view if current user is selected
|
||||||
|
in "Look in" combobox. (Windows 10 only; issue #249)
|
||||||
|
- Table: Fixed wrong grid line thickness in dragged column on HiDPI screens on
|
||||||
|
Java 9+. (issue #236)
|
||||||
|
- PopupFactory: Fixed `NullPointerException` when `PopupFactory.getPopup()` is
|
||||||
|
invoked with parameter `owner` set to `null`.
|
||||||
|
|
||||||
|
|
||||||
|
## 1.0-rc3
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Extras:
|
||||||
|
- UI Inspector: Use HTML in tooltip. Display color value in same format as
|
||||||
|
used in FlatLaf properties files. Added color preview.
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Label and ToolTip: Fixed font sizes for `<code>`, `<kbd>`, `<big>`, `<small>`
|
||||||
|
and `<samp>` tags in HTML text.
|
||||||
|
- Fixed color of `<address>` tag in HTML text.
|
||||||
|
- IntelliJ Themes: Fixed table header background when dragging column in "Dark
|
||||||
|
Flat" and "Light Flat" themes.
|
||||||
|
- CheckBox: Fixed background of check boxes in JIDE `CheckBoxTree`. (regression
|
||||||
|
in 1.0-rc2)
|
||||||
|
|
||||||
|
|
||||||
|
## 1.0-rc2
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Button:
|
||||||
|
- In "Flat Light" theme, use a slightly thinner border for focused buttons
|
||||||
|
(because they already have light blue background).
|
||||||
|
- In "Flat Dark" theme, use slightly wider border for focused buttons.
|
||||||
|
- CheckBox and RadioButton: In "Flat Dark" theme, use blueish background for
|
||||||
|
focused components.
|
||||||
|
- Tree: Support disabling wide selection per component. (set client property
|
||||||
|
`JTree.wideSelection` to `false`). (PR #245)
|
||||||
|
- Tree: Support disabling selection painting per component. Then the tree cell
|
||||||
|
renderer is responsible for selection painting. (set client property
|
||||||
|
`JTree.paintSelection` to `false`).
|
||||||
|
- JIDE Common Layer: Support `JidePopupMenu`.
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Button: Fixed behavior of <kbd>Enter</kbd> key on focused button on Windows
|
||||||
|
and Linux, which now clicks the focused button (instead of the default
|
||||||
|
button).
|
||||||
|
- On Windows, this is a regression in 1.0-rc1.
|
||||||
|
- On macOS, the <kbd>Enter</kbd> key always clicks the default button, which
|
||||||
|
is the platform behavior.
|
||||||
|
- On all platforms, the default button can be always clicked with
|
||||||
|
<kbd>Ctrl+Enter</kbd> keys, even if another button is focused.
|
||||||
|
- CheckBox and RadioButton: Fill component background as soon as background
|
||||||
|
color is different to default background color, even if component is not
|
||||||
|
opaque (which is the default). This paints selection if using the component as
|
||||||
|
cell renderer in Table, Tree or List.
|
||||||
|
- TextComponents: Border of focused non-editable text components had wrong
|
||||||
|
color.
|
||||||
|
- Custom window decorations: Fixed top window border in dark themes when running
|
||||||
|
in JetBrains Runtime.
|
||||||
|
|
||||||
|
|
||||||
|
## 1.0-rc1
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Button: Disabled `Button.defaultButtonFollowsFocus` on Windows (as on other
|
||||||
|
platforms). If you like to keep the old behavior in your application, use:
|
||||||
|
`if(SystemInfo.isWindows)
|
||||||
|
UIManager.put("Button.defaultButtonFollowsFocus",true);`.
|
||||||
|
- ComboBox, Spinner and SplitPaneDivider: Added pressed feedback to arrow
|
||||||
|
buttons.
|
||||||
|
- Slider: Support per component custom thumb and track colors via
|
||||||
|
`JSlider.setForeground(Color)` and `JSlider.setBackground(Color)`.
|
||||||
|
- Slider: Improved thumb hover and pressed colors.
|
||||||
|
- TextComponent: Clip placeholder text if it does not fit into visible area. (PR
|
||||||
|
#229)
|
||||||
|
- macOS: Improved font rendering on macOS when using JetBrains Runtime. (PRs
|
||||||
|
#237, #239 and #241)
|
||||||
|
- Extras: UI defaults inspector:
|
||||||
|
- Support embedding UI defaults inspector panel into any window. See
|
||||||
|
`FlatUIDefaultsInspector.createInspectorPanel()`.
|
||||||
|
- Copy selected keys and values into clipboard via context menu.
|
||||||
|
- Support wildcard matching in filter (`*` matches any number of characters,
|
||||||
|
`?` matches a single character, `^` beginning of line, `$` end of line).
|
||||||
|
- IntelliJ Themes:
|
||||||
|
- Added hover and pressed feedback to Button, CheckBox, RadioButton and
|
||||||
|
ToggleButton. (issue #176)
|
||||||
|
- Added "Material Theme UI Lite / Moonlight" theme.
|
||||||
|
- Updated "Dracula", "Gradianto" and "Material Theme UI Lite" themes.
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Button and ToggleButton: Threat Unicode surrogate character pair as single
|
||||||
|
character and make button square. (issue #234)
|
||||||
|
- Button and ToggleButton: ToolBar buttons now respect explicitly set background
|
||||||
|
color. If no background color is set, then the button background is not
|
||||||
|
painted anymore. (issue #191)
|
||||||
|
- ToggleButton: Tab style buttons (client property `JButton.buttonType` is
|
||||||
|
`tab`) now respect explicitly set background color.
|
||||||
|
- TabbedPane: Fixed `IndexOutOfBoundsException` when using tooltip text on close
|
||||||
|
buttons and closing last/rightmost tab. (issue #235)
|
||||||
|
- TabbedPane: Fixed scrolling tabs with touchpads and high-resolution mouse
|
||||||
|
wheels.
|
||||||
|
- Extras: Added missing export of package
|
||||||
|
`com.formdev.flatlaf.extras.components` to Java 9 module descriptor.
|
||||||
|
- JIDE Common Layer:
|
||||||
|
- Invoke `LookAndFeelFactory.installJideExtension()` when using FlatLaf UI
|
||||||
|
delegates. (issue #230)
|
||||||
|
- RangeSlider: Fixed slider focused colors in IntelliJ themes.
|
||||||
|
- IntelliJ Themes:
|
||||||
|
- Fixed menu item check colors.
|
||||||
|
- Fixed `MenuItem.underlineSelectionColor`.
|
||||||
|
- Fixed List, Tree and Table `selectionInactiveForeground` in light Arc
|
||||||
|
themes.
|
||||||
|
- Fixed List and Table background colors in Material UI Lite themes.
|
||||||
|
- Fixed menu accelerator colors in Monocai theme. (issue #243)
|
||||||
|
|
||||||
|
|
||||||
|
## 0.46
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Slider and JIDE RangeSlider: Clicking on track now immediately moves the thumb
|
||||||
|
to mouse location and starts dragging the thumb. Use `UIManager.put(
|
||||||
|
"Slider.scrollOnTrackClick", true )` to enable old behavior that scrolls the
|
||||||
|
thumb when clicking on track.
|
||||||
|
- Slider: Snap to ticks is now done while dragging the thumb. Use
|
||||||
|
`UIManager.put( "Slider.snapToTicksOnReleased", true )` to enable old behavior
|
||||||
|
that snaps to ticks on mouse released.
|
||||||
|
- Extras: Added standard component extension classes that provides easy access
|
||||||
|
to FlatLaf specific client properties (see package
|
||||||
|
`com.formdev.flatlaf.extras.components`).
|
||||||
|
- Extras: Renamed tri-state check box class from
|
||||||
|
`com.formdev.flatlaf.extras.TriStateCheckBox` to
|
||||||
|
`com.formdev.flatlaf.extras.components.FlatTriStateCheckBox`. Also
|
||||||
|
changed/improved API and added javadoc.
|
||||||
|
- Extras: Renamed SVG utility class from `com.formdev.flatlaf.extras.SVGUtils`
|
||||||
|
to `com.formdev.flatlaf.extras.FlatSVGUtils`.
|
||||||
|
- IntelliJ Themes: Added flag whether a theme is dark to
|
||||||
|
`FlatAllIJThemes.INFOS`. (issue #221)
|
||||||
|
- JIDE Common Layer: Support `TristateCheckBox`.
|
||||||
|
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Slider: Fixed painting of colored track if `JSlider.inverted` is `true`.
|
||||||
|
- Table and TableHeader: Fixed missing right vertical grid line if using table
|
||||||
|
as row header in scroll pane. (issues #152 and #46)
|
||||||
|
- TableHeader: Fixed position of column separators in right-to-left component
|
||||||
|
orientation.
|
||||||
|
- ToolTip: Fixed drop shadow for wide tooltips on Windows and Java 9+. (issue
|
||||||
|
#224)
|
||||||
|
- SwingX: Fixed striping background highlighting color (e.g. alternating table
|
||||||
|
rows) in dark themes.
|
||||||
|
- Fixed: If text antialiasing is disabled (in OS system settings or via
|
||||||
|
`-Dawt.useSystemAAFontSettings=off`), then some components still did use
|
||||||
|
antialiasing to render text (not-editable ComboBox, ProgressBar, Slider,
|
||||||
|
TabbedPane and multiline ToolTip). (issue #227)
|
||||||
|
|
||||||
|
|
||||||
|
## 0.45
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Slider: New design, added hover and pressed feedback and improved customizing.
|
||||||
|
(PR #214)
|
||||||
|
- JIDE Common Layer: Support `RangeSlider`. (PR #209)
|
||||||
|
- IntelliJ Themes:
|
||||||
|
- Added "Gradianto Nature Green" theme.
|
||||||
|
- Updated "Arc Dark", "Cyan", "Dark purple", "Gradianto", "Gray", "Gruvbox"
|
||||||
|
and "One Dark" themes.
|
||||||
|
- TabbedPane: Support hiding tab area if it contains only one tab. (set client
|
||||||
|
property `JTabbedPane.hideTabAreaWithOneTab` to `true`)
|
||||||
|
- MenuBar: Support different underline menu selection style UI defaults for
|
||||||
|
`MenuBar` and `MenuItem`. (PR #217; issue #216)
|
||||||
|
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Table: Do not paint last vertical grid line if auto-resize mode is not off.
|
||||||
|
(issue #46)
|
||||||
|
- Table: Fixed unstable grid line thickness when scaled on HiDPI screens. (issue
|
||||||
|
#152)
|
||||||
|
- TabbedPane: No longer add (internal) tab close button component as child to
|
||||||
|
`JTabbedPane`. (issue #219)
|
||||||
|
- Custom window decorations: Title bar was not hidden if window is in
|
||||||
|
full-screen mode. (issue #212)
|
||||||
|
|
||||||
|
|
||||||
## 0.44
|
## 0.44
|
||||||
|
|
||||||
#### 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.44"
|
val releaseVersion = "1.4"
|
||||||
val developmentVersion = "0.45-SNAPSHOT"
|
val developmentVersion = "1.5-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" )
|
|
||||||
} )
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,28 @@ plugins {
|
|||||||
`flatlaf-publish`
|
`flatlaf-publish`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testImplementation( "org.junit.jupiter:junit-jupiter-api:5.7.2" )
|
||||||
|
testImplementation( "org.junit.jupiter:junit-jupiter-params" )
|
||||||
|
testRuntimeOnly( "org.junit.jupiter:junit-jupiter-engine" )
|
||||||
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
withSourcesJar()
|
withSourcesJar()
|
||||||
withJavadocJar()
|
withJavadocJar()
|
||||||
}
|
}
|
||||||
|
|
||||||
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,23 +51,18 @@ 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" )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flatlafPublish {
|
flatlafPublish {
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import javax.swing.JComponent;
|
|||||||
import javax.swing.SwingConstants;
|
import javax.swing.SwingConstants;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Defines/documents own client properties used in FlatLaf.
|
||||||
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public interface FlatClientProperties
|
public interface FlatClientProperties
|
||||||
@@ -37,8 +39,9 @@ public interface FlatClientProperties
|
|||||||
* {@link #BUTTON_TYPE_SQUARE},
|
* {@link #BUTTON_TYPE_SQUARE},
|
||||||
* {@link #BUTTON_TYPE_ROUND_RECT},
|
* {@link #BUTTON_TYPE_ROUND_RECT},
|
||||||
* {@link #BUTTON_TYPE_TAB},
|
* {@link #BUTTON_TYPE_TAB},
|
||||||
* {@link #BUTTON_TYPE_HELP} or
|
* {@link #BUTTON_TYPE_HELP},
|
||||||
* {@link BUTTON_TYPE_TOOLBAR_BUTTON}
|
* {@link #BUTTON_TYPE_TOOLBAR_BUTTON} or
|
||||||
|
* {@link #BUTTON_TYPE_BORDERLESS}
|
||||||
*/
|
*/
|
||||||
String BUTTON_TYPE = "JButton.buttonType";
|
String BUTTON_TYPE = "JButton.buttonType";
|
||||||
|
|
||||||
@@ -87,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>
|
||||||
@@ -130,6 +143,15 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
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>
|
||||||
@@ -162,13 +184,23 @@ 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}<br>
|
* <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 --------------------------------------------------------------
|
//---- Popup --------------------------------------------------------------
|
||||||
|
|
||||||
@@ -181,6 +213,15 @@ 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 -------------------------------------------------------
|
//---- JProgressBar -------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -202,15 +243,68 @@ public interface FlatClientProperties
|
|||||||
//---- JRootPane ----------------------------------------------------------
|
//---- 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";
|
||||||
|
|
||||||
//---- JScrollBar ---------------------------------------------------------
|
/**
|
||||||
|
* 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.
|
||||||
@@ -223,7 +317,7 @@ 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";
|
||||||
@@ -254,6 +348,14 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String TABBED_PANE_HAS_FULL_BORDER = "JTabbedPane.hasFullBorder";
|
String TABBED_PANE_HAS_FULL_BORDER = "JTabbedPane.hasFullBorder";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* Specifies the minimum width of a tab.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -276,10 +378,12 @@ public interface FlatClientProperties
|
|||||||
String TABBED_PANE_MAXIMUM_TAB_WIDTH = "JTabbedPane.maximumTabWidth";
|
String TABBED_PANE_MAXIMUM_TAB_WIDTH = "JTabbedPane.maximumTabWidth";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies the height of a tab.
|
* 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";
|
||||||
|
|
||||||
@@ -289,6 +393,8 @@ public interface FlatClientProperties
|
|||||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
|
||||||
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
|
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
|
||||||
* <strong>Value type</strong> {@link java.awt.Insets}
|
* <strong>Value type</strong> {@link java.awt.Insets}
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TAB_HEIGHT
|
||||||
*/
|
*/
|
||||||
String TABBED_PANE_TAB_INSETS = "JTabbedPane.tabInsets";
|
String TABBED_PANE_TAB_INSETS = "JTabbedPane.tabInsets";
|
||||||
|
|
||||||
@@ -332,7 +438,7 @@ public interface FlatClientProperties
|
|||||||
* Specifies the callback that is invoked when a tab close button is clicked.
|
* Specifies the callback that is invoked when a tab close button is clicked.
|
||||||
* The callback is responsible for closing the tab.
|
* The callback is responsible for closing the tab.
|
||||||
* <p>
|
* <p>
|
||||||
* Either use a {@link java.util.function.IntConsumer} that received the tab index as parameter:
|
* Either use a {@link java.util.function.IntConsumer} that receives the tab index as parameter:
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* myTabbedPane.putClientProperty( "JTabbedPane.tabCloseCallback",
|
* myTabbedPane.putClientProperty( "JTabbedPane.tabCloseCallback",
|
||||||
* (IntConsumer) tabIndex -> {
|
* (IntConsumer) tabIndex -> {
|
||||||
@@ -340,7 +446,7 @@ public interface FlatClientProperties
|
|||||||
* } );
|
* } );
|
||||||
* }</pre>
|
* }</pre>
|
||||||
* Or use a {@link java.util.function.BiConsumer}<javax.swing.JTabbedPane, Integer>
|
* Or use a {@link java.util.function.BiConsumer}<javax.swing.JTabbedPane, Integer>
|
||||||
* that received the tabbed pane and the tab index as parameters:
|
* that receives the tabbed pane and the tab index as parameters:
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* myTabbedPane.putClientProperty( "JTabbedPane.tabCloseCallback",
|
* myTabbedPane.putClientProperty( "JTabbedPane.tabCloseCallback",
|
||||||
* (BiConsumer<JTabbedPane, Integer>) (tabbedPane, tabIndex) -> {
|
* (BiConsumer<JTabbedPane, Integer>) (tabbedPane, tabIndex) -> {
|
||||||
@@ -627,6 +733,18 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String PLACEHOLDER_TEXT = "JTextField.placeholderText";
|
String PLACEHOLDER_TEXT = "JTextField.placeholderText";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the padding of the text.
|
||||||
|
* This changes the location and size of the text view within the component bounds,
|
||||||
|
* but does not affect the size of the component.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
|
||||||
|
* <strong>Value type</strong> {@link java.awt.Insets}
|
||||||
|
*
|
||||||
|
* @since 1.4
|
||||||
|
*/
|
||||||
|
String TEXT_FIELD_PADDING = "JTextField.padding";
|
||||||
|
|
||||||
//---- JToggleButton ------------------------------------------------------
|
//---- JToggleButton ------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -653,6 +771,25 @@ 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 -----------------------------------------------------
|
//---- helper methods -----------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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 ) {
|
||||||
defaults.put( "Button.focusInputMap", new UIDefaults.LazyInputMap( new Object[] {
|
if( SystemInfo.isMacOS ) {
|
||||||
"SPACE", "pressed",
|
defaults.put( "Button.focusInputMap", new UIDefaults.LazyInputMap( new Object[] {
|
||||||
"released SPACE", "released"
|
"SPACE", "pressed",
|
||||||
} ) );
|
"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,8 +191,10 @@ public abstract class FlatLaf
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Icon getDisabledIcon( JComponent component, Icon icon ) {
|
public Icon getDisabledIcon( JComponent component, Icon icon ) {
|
||||||
if( icon instanceof DisabledIconProvider )
|
if( icon instanceof DisabledIconProvider ) {
|
||||||
return ((DisabledIconProvider)icon).getDisabledIcon();
|
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" );
|
||||||
@@ -248,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
|
||||||
@@ -290,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,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<>();
|
||||||
@@ -447,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 ) {
|
||||||
uiFont = createCompositeFont( winFont.getFamily(), winFont.getStyle(), winFont.getSize() );
|
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() );
|
||||||
|
}
|
||||||
|
|
||||||
} 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 ) {
|
||||||
// use Helvetica Neue font
|
if (SystemInfo.isJetBrainsJVM_11_orLater) {
|
||||||
fontName = "Helvetica Neue";
|
// See https://youtrack.jetbrains.com/issue/JBR-1915
|
||||||
|
fontName = ".AppleSystemUIFont";
|
||||||
|
} else {
|
||||||
|
// use Helvetica Neue font
|
||||||
|
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";
|
||||||
@@ -480,7 +504,7 @@ public abstract class FlatLaf
|
|||||||
// 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
|
||||||
// (similar as in Nimbus L&F) with:
|
// (similar as in Nimbus L&F) with:
|
||||||
// UIManager.put( "defaultFont", myFont );
|
// UIManager.put( "defaultFont", myFont );
|
||||||
Object activeFont = new ActiveFont( 1 );
|
Object activeFont = new ActiveFont( 1 );
|
||||||
|
|
||||||
// override fonts
|
// override fonts
|
||||||
for( Object key : defaults.keySet() ) {
|
for( Object key : defaults.keySet() ) {
|
||||||
@@ -503,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>
|
||||||
@@ -527,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;
|
||||||
@@ -551,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 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -660,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 );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
@@ -693,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();
|
||||||
}
|
}
|
||||||
@@ -736,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;
|
||||||
|
|
||||||
|
|||||||
@@ -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,10 +106,20 @@ 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>
|
* <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> none
|
||||||
*/
|
*/
|
||||||
String MENUBAR_EMBEDDED = "flatlaf.menuBarEmbedded";
|
String MENUBAR_EMBEDDED = "flatlaf.menuBarEmbedded";
|
||||||
|
|
||||||
|
|||||||
@@ -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" );
|
||||||
|
|||||||
@@ -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,24 +55,38 @@ 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)
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -264,6 +309,9 @@ class LinuxFontPolicy
|
|||||||
* - running on JetBrains Runtime 11 or later and scaling is enabled in system Settings
|
* - running on JetBrains Runtime 11 or later and scaling is enabled in system Settings
|
||||||
*/
|
*/
|
||||||
private static boolean isSystemScaling() {
|
private static boolean isSystemScaling() {
|
||||||
|
if( GraphicsEnvironment.isHeadless() )
|
||||||
|
return true;
|
||||||
|
|
||||||
GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment()
|
GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment()
|
||||||
.getDefaultScreenDevice().getDefaultConfiguration();
|
.getDefaultScreenDevice().getDefaultConfiguration();
|
||||||
return UIScale.getSystemScaleFactor( gc ) > 1;
|
return UIScale.getSystemScaleFactor( gc ) > 1;
|
||||||
|
|||||||
@@ -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 ) {
|
||||||
menuBar = ((JFrame)window).getJMenuBar();
|
// get menu bar from frame/dialog because there
|
||||||
|
// may be multiple nested root panes in a frame/dialog
|
||||||
|
// (e.g. each internal frame has its own root pane)
|
||||||
|
Window window = SwingUtilities.getWindowAncestor( c );
|
||||||
|
if( window instanceof JFrame )
|
||||||
|
menuBar = ((JFrame)window).getJMenuBar();
|
||||||
|
else if( window instanceof JDialog )
|
||||||
|
menuBar = ((JDialog)window).getJMenuBar();
|
||||||
|
}
|
||||||
JMenu firstMenu = (menuBar != null) ? menuBar.getMenu( 0 ) : null;
|
JMenu firstMenu = (menuBar != null) ? menuBar.getMenu( 0 ) : null;
|
||||||
|
|
||||||
// select first menu and show mnemonics
|
// select first menu and show mnemonics
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ import java.util.Map;
|
|||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import 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;
|
||||||
@@ -48,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;
|
||||||
@@ -70,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 )
|
||||||
@@ -119,7 +121,7 @@ class UIDefaultsLoader
|
|||||||
addonClassLoaders.add( addonClassLoader );
|
addonClassLoaders.add( addonClassLoader );
|
||||||
}
|
}
|
||||||
|
|
||||||
// load custom properties files (usually provides by applications)
|
// 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++ ) {
|
||||||
@@ -198,19 +200,19 @@ class UIDefaultsLoader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get (and remove) globals, which override all other defaults that end with same suffix
|
// get (and remove) wildcard replacements, which override all other defaults that end with same suffix
|
||||||
HashMap<String, String> globals = new HashMap<>();
|
HashMap<String, String> wildcards = new HashMap<>();
|
||||||
Iterator<Entry<Object, Object>> it = properties.entrySet().iterator();
|
Iterator<Entry<Object, Object>> it = properties.entrySet().iterator();
|
||||||
while( it.hasNext() ) {
|
while( it.hasNext() ) {
|
||||||
Entry<Object, Object> e = it.next();
|
Entry<Object, Object> e = it.next();
|
||||||
String key = (String) e.getKey();
|
String key = (String) e.getKey();
|
||||||
if( key.startsWith( GLOBAL_PREFIX ) ) {
|
if( key.startsWith( WILDCARD_PREFIX ) ) {
|
||||||
globals.put( key.substring( GLOBAL_PREFIX.length() ), (String) e.getValue() );
|
wildcards.put( key.substring( WILDCARD_PREFIX.length() ), (String) e.getValue() );
|
||||||
it.remove();
|
it.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// override UI defaults with globals
|
// override UI defaults with wildcard replacements
|
||||||
for( Object key : defaults.keySet() ) {
|
for( Object key : defaults.keySet() ) {
|
||||||
int dot;
|
int dot;
|
||||||
if( !(key instanceof String) ||
|
if( !(key instanceof String) ||
|
||||||
@@ -218,10 +220,10 @@ class UIDefaultsLoader
|
|||||||
(dot = ((String)key).lastIndexOf( '.' )) < 0 )
|
(dot = ((String)key).lastIndexOf( '.' )) < 0 )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
String globalKey = ((String)key).substring( dot + 1 );
|
String wildcardKey = ((String)key).substring( dot + 1 );
|
||||||
String globalValue = globals.get( globalKey );
|
String wildcardValue = wildcards.get( wildcardKey );
|
||||||
if( globalValue != null )
|
if( wildcardValue != null )
|
||||||
properties.put( key, globalValue );
|
properties.put( key, wildcardValue );
|
||||||
}
|
}
|
||||||
|
|
||||||
Function<String, String> propertiesGetter = key -> {
|
Function<String, String> propertiesGetter = key -> {
|
||||||
@@ -241,16 +243,20 @@ class UIDefaultsLoader
|
|||||||
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 ) {
|
||||||
@@ -341,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;
|
||||||
@@ -356,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;
|
||||||
}
|
}
|
||||||
@@ -433,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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -446,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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -577,22 +586,34 @@ class UIDefaultsLoader
|
|||||||
if( params.isEmpty() )
|
if( params.isEmpty() )
|
||||||
throw new IllegalArgumentException( "missing parameters in function '" + value + "'" );
|
throw new IllegalArgumentException( "missing parameters in function '" + value + "'" );
|
||||||
|
|
||||||
switch( function ) {
|
if( parseColorDepth > 100 )
|
||||||
case "rgb": return parseColorRgbOrRgba( false, params, resolver, reportError );
|
throw new IllegalArgumentException( "endless recursion in color function '" + value + "'" );
|
||||||
case "rgba": return parseColorRgbOrRgba( true, params, resolver, reportError );
|
|
||||||
case "hsl": return parseColorHslOrHsla( false, params );
|
parseColorDepth++;
|
||||||
case "hsla": return parseColorHslOrHsla( true, params );
|
try {
|
||||||
case "lighten": return parseColorHSLIncreaseDecrease( 2, true, params, resolver, reportError );
|
switch( function ) {
|
||||||
case "darken": return parseColorHSLIncreaseDecrease( 2, false, params, resolver, reportError );
|
case "rgb": return parseColorRgbOrRgba( false, params, resolver, reportError );
|
||||||
case "saturate": return parseColorHSLIncreaseDecrease( 1, true, params, resolver, reportError );
|
case "rgba": return parseColorRgbOrRgba( true, params, resolver, reportError );
|
||||||
case "desaturate": return parseColorHSLIncreaseDecrease( 1, false, params, resolver, reportError );
|
case "hsl": return parseColorHslOrHsla( false, params );
|
||||||
|
case "hsla": return parseColorHslOrHsla( true, params );
|
||||||
|
case "lighten": return parseColorHSLIncreaseDecrease( 2, true, params, resolver, reportError );
|
||||||
|
case "darken": return parseColorHSLIncreaseDecrease( 2, false, params, resolver, reportError );
|
||||||
|
case "saturate": return parseColorHSLIncreaseDecrease( 1, true, params, resolver, reportError );
|
||||||
|
case "desaturate": return parseColorHSLIncreaseDecrease( 1, false, params, resolver, reportError );
|
||||||
|
case "fadein": return parseColorHSLIncreaseDecrease( 3, true, params, resolver, reportError );
|
||||||
|
case "fadeout": return parseColorHSLIncreaseDecrease( 3, false, params, resolver, reportError );
|
||||||
|
case "fade": return parseColorFade( params, resolver, reportError );
|
||||||
|
case "spin": return parseColorSpin( params, resolver, reportError );
|
||||||
|
}
|
||||||
|
} 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%
|
||||||
@@ -603,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 );
|
||||||
|
|
||||||
@@ -639,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]
|
||||||
@@ -679,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 );
|
||||||
@@ -855,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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -44,7 +44,7 @@ public class FlatCapsLockIcon
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
<g fill="none" fill-rule="evenodd">
|
<g fill="none" fill-rule="evenodd">
|
||||||
<rect width="16" height="16" fill="#6E6E6E" rx="3"/>
|
<rect width="16" height="16" fill="#6E6E6E" rx="3"/>
|
||||||
<rect width="6" height="2" x="5" y="12" fill="#FFF"/>
|
<rect width="6" height="2" x="5" y="11.5" fill="#FFF"/>
|
||||||
<path fill="#FFF" d="M2,8 L8,2 L14,8 L11,8 L11,10 L5,10 L5,8 L2,8 Z"/>
|
<path fill="#FFF" d="M2,8 L8,2 L14,8 L11,8 L11,10 L5,10 L5,8 L2,8 Z"/>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -52,7 +52,7 @@ public class FlatCapsLockIcon
|
|||||||
|
|
||||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||||
path.append( new RoundRectangle2D.Float( 0, 0, 16, 16, 6, 6 ), false );
|
path.append( new RoundRectangle2D.Float( 0, 0, 16, 16, 6, 6 ), false );
|
||||||
path.append( new Rectangle2D.Float( 5, 12, 6, 2 ), false );
|
path.append( new Rectangle2D.Float( 5, 11.5f, 6, 2 ), false );
|
||||||
path.append( FlatUIUtils.createPath( 2,8, 8,2, 14,8, 11,8, 11,10, 5,10, 5,8 ), false );
|
path.append( FlatUIUtils.createPath( 2,8, 8,2, 14,8, 11,8, 11,10, 5,10, 5,8 ), false );
|
||||||
g.fill( path );
|
g.fill( path );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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,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;
|
||||||
@@ -51,24 +48,12 @@ public class FlatArrowButton
|
|||||||
protected final Color pressedBackground;
|
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,
|
|
||||||
Color hoverForeground, Color hoverBackground )
|
|
||||||
{
|
|
||||||
this( direction, type, foreground, disabledForeground, hoverForeground, hoverBackground, null );
|
|
||||||
}
|
|
||||||
|
|
||||||
public FlatArrowButton( int direction, String type, Color foreground, Color disabledForeground,
|
|
||||||
Color hoverForeground, Color hoverBackground, Color pressedBackground )
|
|
||||||
{
|
|
||||||
this( direction, type, foreground, disabledForeground, hoverForeground, hoverBackground, null, pressedBackground );
|
|
||||||
}
|
|
||||||
|
|
||||||
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 pressedForeground, Color pressedBackground )
|
Color hoverForeground, Color hoverBackground, Color pressedForeground, Color pressedBackground )
|
||||||
{
|
{
|
||||||
@@ -85,7 +70,9 @@ public class FlatArrowButton
|
|||||||
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 ) {
|
||||||
@@ -130,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +138,22 @@ public class FlatArrowButton
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Color deriveForeground( Color foreground ) {
|
protected Color deriveForeground( Color foreground ) {
|
||||||
return 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
|
||||||
@@ -166,8 +168,7 @@ 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 );
|
|
||||||
|
|
||||||
// paint hover or pressed background
|
// paint hover or pressed background
|
||||||
if( isEnabled() ) {
|
if( isEnabled() ) {
|
||||||
@@ -179,19 +180,15 @@ public class FlatArrowButton
|
|||||||
|
|
||||||
if( background != null ) {
|
if( background != null ) {
|
||||||
g.setColor( deriveBackground( background ) );
|
g.setColor( deriveBackground( background ) );
|
||||||
paintBackground( g2 );
|
paintBackground( (Graphics2D) g );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// paint arrow
|
// paint arrow
|
||||||
g.setColor( deriveForeground( isEnabled()
|
g.setColor( deriveForeground( getArrowColor() ) );
|
||||||
? (pressedForeground != null && isPressed()
|
paintArrow( (Graphics2D) g );
|
||||||
? pressedForeground
|
|
||||||
: (hoverForeground != null && isHover()
|
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||||
? hoverForeground
|
|
||||||
: foreground))
|
|
||||||
: disabledForeground ) );
|
|
||||||
paintArrow( g2 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void paintBackground( Graphics2D g ) {
|
protected void paintBackground( Graphics2D g ) {
|
||||||
@@ -199,73 +196,14 @@ public class FlatArrowButton
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void paintArrow( Graphics2D g ) {
|
protected void paintArrow( Graphics2D g ) {
|
||||||
int direction = getDirection();
|
|
||||||
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( (getWidth() - rw) / 2f + scale( (float) xOffset ) );
|
|
||||||
int y = Math.round( (getHeight() - 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.translate( x, y );
|
|
||||||
/*debug
|
|
||||||
debugPaint( g, vert, rw, rh );
|
|
||||||
debug*/
|
|
||||||
Shape arrowShape = createArrowShape( direction, chevron, w, h );
|
|
||||||
if( chevron ) {
|
|
||||||
g.setStroke( new BasicStroke( scale( 1f ) ) );
|
|
||||||
g.draw( arrowShape );
|
|
||||||
} else {
|
|
||||||
// triangle
|
|
||||||
g.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,12 +89,14 @@ 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;
|
||||||
|
|
||||||
g2.setColor( (outlineColor != null) ? outlineColor : getFocusColor( c ) );
|
if( focusWidth > 0 || innerWidth > 0 ) {
|
||||||
FlatUIUtils.paintComponentOuterBorder( g2, x, y, width, height,
|
g2.setColor( (outlineColor != null) ? outlineColor : getFocusColor( c ) );
|
||||||
focusWidth, borderWidth + scale( innerWidth ), arc );
|
FlatUIUtils.paintComponentOuterBorder( g2, x, y, width, height,
|
||||||
|
focusWidth, borderWidth + scale( innerWidth ), arc );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// paint border
|
// paint border
|
||||||
@@ -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 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,13 +176,14 @@ public class FlatBorder
|
|||||||
@Override
|
@Override
|
||||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
float focusWidth = scale( (float) getFocusWidth( c ) );
|
float focusWidth = scale( (float) getFocusWidth( c ) );
|
||||||
float ow = focusWidth + scale( (float) getLineWidth( c ) );
|
int ow = Math.round( focusWidth + scale( (float) getLineWidth( c ) ) );
|
||||||
|
|
||||||
insets = super.getBorderInsets( c, insets );
|
insets = super.getBorderInsets( c, insets );
|
||||||
insets.top = Math.round( scale( (float) insets.top ) + ow );
|
|
||||||
insets.left = Math.round( scale( (float) insets.left ) + ow );
|
insets.top = scale( insets.top ) + ow;
|
||||||
insets.bottom = Math.round( scale( (float) insets.bottom ) + ow );
|
insets.left = scale( insets.left ) + ow;
|
||||||
insets.right = Math.round( scale( (float) insets.right ) + ow );
|
insets.bottom = scale( insets.bottom ) + ow;
|
||||||
|
insets.right = scale( insets.right ) + ow;
|
||||||
|
|
||||||
if( isCellEditor( c ) ) {
|
if( isCellEditor( c ) ) {
|
||||||
// remove top and bottom insets if used as cell editor
|
// remove top and bottom insets if used as cell editor
|
||||||
@@ -236,6 +209,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}.
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import java.awt.Color;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.GradientPaint;
|
import java.awt.GradientPaint;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.Paint;
|
import java.awt.Paint;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
@@ -42,10 +43,13 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault Button.default.hoverBorderColor Color optional
|
* @uiDefault Button.default.hoverBorderColor Color optional
|
||||||
* @uiDefault Button.default.focusedBorderColor Color
|
* @uiDefault Button.default.focusedBorderColor Color
|
||||||
* @uiDefault Button.default.focusColor Color
|
* @uiDefault Button.default.focusColor Color
|
||||||
|
* @uiDefault Button.toolbar.focusColor Color optional; defaults to Component.focusColor
|
||||||
* @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.toolbar.focusWidth int or float optional; default is 1
|
||||||
* @uiDefault Button.arc int
|
* @uiDefault Button.arc int
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
@@ -63,24 +67,60 @@ public class FlatButtonBorder
|
|||||||
protected final Color defaultHoverBorderColor = UIManager.getColor( "Button.default.hoverBorderColor" );
|
protected final Color defaultHoverBorderColor = UIManager.getColor( "Button.default.hoverBorderColor" );
|
||||||
protected final Color defaultFocusedBorderColor = UIManager.getColor( "Button.default.focusedBorderColor" );
|
protected final Color defaultFocusedBorderColor = UIManager.getColor( "Button.default.focusedBorderColor" );
|
||||||
protected final Color defaultFocusColor = UIManager.getColor( "Button.default.focusColor" );
|
protected final Color defaultFocusColor = UIManager.getColor( "Button.default.focusColor" );
|
||||||
|
/** @since 1.4 */
|
||||||
|
protected final Color toolbarFocusColor = UIManager.getColor( "Button.toolbar.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" );
|
||||||
|
/** @since 1.4 */
|
||||||
|
protected final float toolbarFocusWidth = FlatUIUtils.getUIFloat( "Button.toolbar.focusWidth", 1.5f );
|
||||||
protected final int arc = UIManager.getInt( "Button.arc" );
|
protected final int arc = UIManager.getInt( "Button.arc" );
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
if( 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 );
|
||||||
|
else if( FlatButtonUI.isToolBarButton( c ) && isFocused( c ) )
|
||||||
|
paintToolBarFocus( c, g, x, y, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.4
|
||||||
|
*/
|
||||||
|
protected void paintToolBarFocus( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
|
try {
|
||||||
|
FlatUIUtils.setRenderingHints( g2 );
|
||||||
|
|
||||||
|
float focusWidth = UIScale.scale( toolbarFocusWidth );
|
||||||
|
float arc = UIScale.scale( (float) getArc( c ) );
|
||||||
|
Color outlineColor = getOutlineColor( c );
|
||||||
|
|
||||||
|
Insets spacing = UIScale.scale( toolbarSpacingInsets );
|
||||||
|
x += spacing.left;
|
||||||
|
y += spacing.top;
|
||||||
|
width -= spacing.left + spacing.right;
|
||||||
|
height -= spacing.top + spacing.bottom;
|
||||||
|
|
||||||
|
g2.setColor( (outlineColor != null) ? outlineColor : getFocusColor( c ) );
|
||||||
|
// not using paintComponentOuterBorder() here because its round edges look too "thick"
|
||||||
|
FlatUIUtils.paintComponentBorder( g2, x, y, width, height, 0, focusWidth, arc );
|
||||||
|
} finally {
|
||||||
|
g2.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Color getFocusColor( Component c ) {
|
protected Color getFocusColor( Component c ) {
|
||||||
return FlatButtonUI.isDefaultButton( c ) ? defaultFocusColor : super.getFocusColor( c );
|
return (toolbarFocusColor != null && FlatButtonUI.isToolBarButton( c ))
|
||||||
|
? toolbarFocusColor
|
||||||
|
: (FlatButtonUI.isDefaultButton( c ) ? defaultFocusColor : super.getFocusColor( c ));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -134,6 +174,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;
|
||||||
|
|||||||
@@ -251,7 +251,10 @@ 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 ) ))));
|
||||||
}
|
}
|
||||||
|
|
||||||
static final int TYPE_OTHER = -1;
|
static final int TYPE_OTHER = -1;
|
||||||
@@ -282,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
|
||||||
@@ -329,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 ),
|
||||||
@@ -385,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 );
|
||||||
@@ -436,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 ) {
|
||||||
return pressedColor;
|
ButtonModel model = ((AbstractButton)c).getModel();
|
||||||
|
|
||||||
if( hoverColor != null && b != null && b.getModel().isRollover() )
|
if( pressedColor != null && model.isPressed() )
|
||||||
return hoverColor;
|
return pressedColor;
|
||||||
|
|
||||||
|
if( hoverColor != null && model.isRollover() )
|
||||||
|
return hoverColor;
|
||||||
|
}
|
||||||
|
|
||||||
if( focusedColor != null && isFocusPainted( c ) && FlatUIUtils.isPermanentFocusOwner( c ) )
|
if( focusedColor != null && isFocusPainted( c ) && FlatUIUtils.isPermanentFocusOwner( c ) )
|
||||||
return focusedColor;
|
return focusedColor;
|
||||||
@@ -457,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
|
||||||
@@ -492,9 +501,9 @@ public class FlatButtonUI
|
|||||||
prefSize.width = Math.max( prefSize.width, prefSize.height );
|
prefSize.width = Math.max( prefSize.width, prefSize.height );
|
||||||
} else if( !isIconOnlyOrSingleCharacter && !isToolBarButton( c ) && c.getBorder() instanceof FlatButtonBorder ) {
|
} else if( !isIconOnlyOrSingleCharacter && !isToolBarButton( c ) && c.getBorder() instanceof FlatButtonBorder ) {
|
||||||
// apply minimum width/height
|
// apply minimum width/height
|
||||||
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
|
int fw = Math.round( FlatUIUtils.getBorderFocusWidth( c ) * 2 );
|
||||||
prefSize.width = Math.max( prefSize.width, scale( FlatUIUtils.minimumWidth( c, minimumWidth ) ) + Math.round( focusWidth * 2 ) );
|
prefSize.width = Math.max( prefSize.width, scale( FlatUIUtils.minimumWidth( c, minimumWidth ) ) + fw );
|
||||||
prefSize.height = Math.max( prefSize.height, scale( FlatUIUtils.minimumHeight( c, 0 ) ) + Math.round( focusWidth * 2 ) );
|
prefSize.height = Math.max( prefSize.height, scale( FlatUIUtils.minimumHeight( c, 0 ) ) + fw );
|
||||||
}
|
}
|
||||||
|
|
||||||
return prefSize;
|
return prefSize;
|
||||||
|
|||||||
@@ -18,10 +18,13 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import static com.formdev.flatlaf.FlatClientProperties.*;
|
import static com.formdev.flatlaf.FlatClientProperties.*;
|
||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
|
import java.awt.Insets;
|
||||||
|
import java.awt.Rectangle;
|
||||||
import java.awt.event.FocusEvent;
|
import java.awt.event.FocusEvent;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import javax.swing.JFormattedTextField;
|
import javax.swing.JFormattedTextField;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
|
import javax.swing.text.BadLocationException;
|
||||||
import javax.swing.text.DefaultCaret;
|
import javax.swing.text.DefaultCaret;
|
||||||
import javax.swing.text.Document;
|
import javax.swing.text.Document;
|
||||||
import javax.swing.text.JTextComponent;
|
import javax.swing.text.JTextComponent;
|
||||||
@@ -61,6 +64,19 @@ public class FlatCaret
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void adjustVisibility( Rectangle nloc ) {
|
||||||
|
JTextComponent c = getComponent();
|
||||||
|
if( c != null && c.getUI() instanceof FlatTextFieldUI ) {
|
||||||
|
Insets padding = ((FlatTextFieldUI)c.getUI()).getPadding();
|
||||||
|
if( padding != null ) {
|
||||||
|
nloc.x -= padding.left;
|
||||||
|
nloc.y -= padding.top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.adjustVisibility( nloc );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void focusGained( FocusEvent e ) {
|
public void focusGained( FocusEvent e ) {
|
||||||
if( !wasTemporaryLost && (!isMousePressed || selectAllOnMouseClick) )
|
if( !wasTemporaryLost && (!isMousePressed || selectAllOnMouseClick) )
|
||||||
@@ -127,4 +143,23 @@ public class FlatCaret
|
|||||||
moveDot( doc.getLength() );
|
moveDot( doc.getLength() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.4
|
||||||
|
*/
|
||||||
|
public void scrollCaretToVisible() {
|
||||||
|
JTextComponent c = getComponent();
|
||||||
|
if( c == null || c.getUI() == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Rectangle loc = c.getUI().modelToView( c, getDot(), getDotBias() );
|
||||||
|
if( loc != null ) {
|
||||||
|
adjustVisibility( loc );
|
||||||
|
damage( loc );
|
||||||
|
}
|
||||||
|
} catch( BadLocationException ex ) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
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 static com.formdev.flatlaf.util.UIScale.unscale;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.ComponentOrientation;
|
import java.awt.ComponentOrientation;
|
||||||
@@ -34,13 +35,14 @@ 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 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;
|
||||||
@@ -59,13 +61,13 @@ 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;
|
||||||
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.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JComboBox}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JComboBox}.
|
||||||
@@ -90,13 +92,17 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @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
|
||||||
*/
|
*/
|
||||||
@@ -112,19 +118,25 @@ 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 CellPaddingBorder paddingBorder;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatComboBoxUI();
|
return new FlatComboBoxUI();
|
||||||
@@ -134,13 +146,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 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,22 +202,26 @@ 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" );
|
||||||
if( maximumRowCount > 0 && maximumRowCount != 8 && comboBox.getMaximumRowCount() == 8 )
|
if( maximumRowCount > 0 && maximumRowCount != 8 && comboBox.getMaximumRowCount() == 8 )
|
||||||
comboBox.setMaximumRowCount( maximumRowCount );
|
comboBox.setMaximumRowCount( maximumRowCount );
|
||||||
|
|
||||||
// scale
|
paddingBorder = new CellPaddingBorder( padding );
|
||||||
padding = UIScale.scale( padding );
|
|
||||||
|
|
||||||
MigLayoutVisualPadding.install( comboBox );
|
MigLayoutVisualPadding.install( comboBox );
|
||||||
}
|
}
|
||||||
@@ -195,14 +234,21 @@ 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;
|
||||||
|
|
||||||
|
paddingBorder.uninstall();
|
||||||
|
|
||||||
MigLayoutVisualPadding.uninstall( comboBox );
|
MigLayoutVisualPadding.uninstall( comboBox );
|
||||||
}
|
}
|
||||||
@@ -214,9 +260,21 @@ 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 ) {
|
||||||
// fix editor bounds by subtracting padding
|
Insets insets = getInsets();
|
||||||
editor.setBounds( FlatUIUtils.subtractInsets( editor.getBounds(), padding ) );
|
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() );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -244,30 +302,28 @@ 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();
|
||||||
|
|
||||||
if( editor != null &&
|
if( editor != null &&
|
||||||
((source == comboBox && propertyName == "foreground") ||
|
((source == comboBox && propertyName == "foreground") ||
|
||||||
(source == editor && propertyName == "enabled")) )
|
(source == editor && propertyName == "enabled")) )
|
||||||
{
|
{
|
||||||
// fix editor component colors
|
// fix editor component colors
|
||||||
updateEditorColors();
|
updateEditorColors();
|
||||||
} else if( editor != null && source == comboBox && propertyName == "componentOrientation" ) {
|
} else if( editor != null && source == comboBox && propertyName == "componentOrientation" ) {
|
||||||
ComponentOrientation o = (ComponentOrientation) e.getNewValue();
|
ComponentOrientation o = (ComponentOrientation) e.getNewValue();
|
||||||
editor.applyComponentOrientation( o );
|
editor.applyComponentOrientation( o );
|
||||||
} else if( editor != null && FlatClientProperties.PLACEHOLDER_TEXT.equals( propertyName ) )
|
} else if( editor != null && FlatClientProperties.PLACEHOLDER_TEXT.equals( propertyName ) )
|
||||||
editor.repaint();
|
editor.repaint();
|
||||||
else if( FlatClientProperties.COMPONENT_ROUND_RECT.equals( propertyName ) )
|
else if( FlatClientProperties.COMPONENT_ROUND_RECT.equals( propertyName ) )
|
||||||
comboBox.repaint();
|
comboBox.repaint();
|
||||||
else if( FlatClientProperties.MINIMUM_WIDTH.equals( propertyName ) )
|
else if( FlatClientProperties.MINIMUM_WIDTH.equals( propertyName ) )
|
||||||
comboBox.revalidate();
|
comboBox.revalidate();
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,6 +366,7 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
editor.applyComponentOrientation( comboBox.getComponentOrientation() );
|
editor.applyComponentOrientation( comboBox.getComponentOrientation() );
|
||||||
|
|
||||||
|
updateEditorPadding();
|
||||||
updateEditorColors();
|
updateEditorColors();
|
||||||
|
|
||||||
// macOS
|
// macOS
|
||||||
@@ -326,6 +383,25 @@ public class FlatComboBoxUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateEditorPadding() {
|
||||||
|
if( !(editor instanceof JTextField) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
JTextField textField = (JTextField) editor;
|
||||||
|
Insets insets = textField.getInsets();
|
||||||
|
Insets pad = padding;
|
||||||
|
if( insets.top != 0 || insets.left != 0 || insets.bottom != 0 || insets.right != 0 ) {
|
||||||
|
// if text field has custom border, subtract text field insets from padding
|
||||||
|
pad = new Insets(
|
||||||
|
unscale( Math.max( scale( padding.top ) - insets.top, 0 ) ),
|
||||||
|
unscale( Math.max( scale( padding.left ) - insets.left, 0 ) ),
|
||||||
|
unscale( Math.max( scale( padding.bottom ) - insets.bottom, 0 ) ),
|
||||||
|
unscale( Math.max( scale( padding.right ) - insets.right, 0 ) )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
textField.putClientProperty( FlatClientProperties.TEXT_FIELD_PADDING, pad );
|
||||||
|
}
|
||||||
|
|
||||||
private void updateEditorColors() {
|
private void updateEditorColors() {
|
||||||
// use non-UIResource colors because when SwingUtilities.updateComponentTreeUI()
|
// use non-UIResource colors because when SwingUtilities.updateComponentTreeUI()
|
||||||
// is used, then the editor is updated after the combobox and the
|
// is used, then the editor is updated after the combobox and the
|
||||||
@@ -346,13 +422,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();
|
||||||
@@ -363,28 +448,37 @@ public class FlatComboBoxUI
|
|||||||
boolean isLeftToRight = comboBox.getComponentOrientation().isLeftToRight();
|
boolean isLeftToRight = comboBox.getComponentOrientation().isLeftToRight();
|
||||||
|
|
||||||
// paint background
|
// paint background
|
||||||
g2.setColor( getBackground( enabled ) );
|
if( paintBackground || c.isOpaque() ) {
|
||||||
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
|
g2.setColor( getBackground( enabled ) );
|
||||||
|
|
||||||
// paint arrow button background
|
|
||||||
if( enabled ) {
|
|
||||||
g2.setColor( paintButton ? buttonEditableBackground : buttonBackground );
|
|
||||||
Shape oldClip = g2.getClip();
|
|
||||||
if( isLeftToRight )
|
|
||||||
g2.clipRect( arrowX, 0, width - arrowX, height );
|
|
||||||
else
|
|
||||||
g2.clipRect( 0, 0, arrowX + arrowWidth, height );
|
|
||||||
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
|
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
|
||||||
g2.setClip( oldClip );
|
|
||||||
|
// paint arrow button background
|
||||||
|
if( enabled && !isCellRenderer ) {
|
||||||
|
g2.setColor( paintButton
|
||||||
|
? buttonEditableBackground
|
||||||
|
: (buttonFocusedBackground != null || focusedBackground != null) && isPermanentFocusOwner( comboBox )
|
||||||
|
? (buttonFocusedBackground != null ? buttonFocusedBackground : focusedBackground)
|
||||||
|
: buttonBackground );
|
||||||
|
Shape oldClip = g2.getClip();
|
||||||
|
if( isLeftToRight )
|
||||||
|
g2.clipRect( arrowX, 0, width - arrowX, height );
|
||||||
|
else
|
||||||
|
g2.clipRect( 0, 0, arrowX + arrowWidth, height );
|
||||||
|
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
|
||||||
|
g2.setClip( oldClip );
|
||||||
|
}
|
||||||
|
|
||||||
|
// paint vertical line between value and arrow button
|
||||||
|
if( paintButton ) {
|
||||||
|
g2.setColor( enabled ? borderColor : disabledBorderColor );
|
||||||
|
float lw = scale( 1f );
|
||||||
|
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
|
||||||
|
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2)) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// paint vertical line between value and arrow button
|
// avoid that the "current value" renderer is invoked with enabled antialiasing
|
||||||
if( paintButton ) {
|
FlatUIUtils.resetRenderingHints( g2, oldRenderingHints );
|
||||||
g2.setColor( enabled ? borderColor : disabledBorderColor );
|
|
||||||
float lw = scale( 1f );
|
|
||||||
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
|
|
||||||
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2)) );
|
|
||||||
}
|
|
||||||
|
|
||||||
paint( g, c );
|
paint( g, c );
|
||||||
}
|
}
|
||||||
@@ -392,30 +486,24 @@ public class FlatComboBoxUI
|
|||||||
@Override
|
@Override
|
||||||
@SuppressWarnings( "unchecked" )
|
@SuppressWarnings( "unchecked" )
|
||||||
public void paintCurrentValue( Graphics g, Rectangle bounds, boolean hasFocus ) {
|
public void paintCurrentValue( Graphics g, Rectangle bounds, boolean hasFocus ) {
|
||||||
|
paddingBorder.uninstall();
|
||||||
|
|
||||||
ListCellRenderer<Object> renderer = comboBox.getRenderer();
|
ListCellRenderer<Object> renderer = comboBox.getRenderer();
|
||||||
uninstallCellPaddingBorder( renderer );
|
|
||||||
if( renderer == null )
|
if( renderer == null )
|
||||||
renderer = new DefaultListCellRenderer();
|
renderer = new DefaultListCellRenderer();
|
||||||
Component c = renderer.getListCellRendererComponent( listBox, comboBox.getSelectedItem(), -1, false, false );
|
Component c = renderer.getListCellRendererComponent( listBox, comboBox.getSelectedItem(), -1, false, false );
|
||||||
c.setFont( comboBox.getFont() );
|
c.setFont( comboBox.getFont() );
|
||||||
c.applyComponentOrientation( comboBox.getComponentOrientation() );
|
c.applyComponentOrientation( comboBox.getComponentOrientation() );
|
||||||
uninstallCellPaddingBorder( c );
|
|
||||||
|
|
||||||
boolean enabled = comboBox.isEnabled();
|
boolean enabled = comboBox.isEnabled();
|
||||||
c.setBackground( getBackground( enabled ) );
|
c.setBackground( getBackground( enabled ) );
|
||||||
c.setForeground( getForeground( enabled ) );
|
c.setForeground( getForeground( enabled ) );
|
||||||
|
|
||||||
boolean shouldValidate = (c instanceof JPanel);
|
boolean shouldValidate = (c instanceof JPanel);
|
||||||
if( padding != null )
|
|
||||||
bounds = FlatUIUtils.subtractInsets( bounds, padding );
|
|
||||||
|
|
||||||
// increase the size of the rendering area to make sure that the text
|
|
||||||
// is vertically aligned with other component types (e.g. JTextField)
|
|
||||||
Insets rendererInsets = getRendererComponentInsets( c );
|
|
||||||
if( rendererInsets != null )
|
|
||||||
bounds = FlatUIUtils.addInsets( bounds, rendererInsets );
|
|
||||||
|
|
||||||
|
paddingBorder.install( c );
|
||||||
currentValuePane.paintComponent( g, c, comboBox, bounds.x, bounds.y, bounds.width, bounds.height, shouldValidate );
|
currentValuePane.paintComponent( g, c, comboBox, bounds.x, bounds.y, bounds.width, bounds.height, shouldValidate );
|
||||||
|
paddingBorder.uninstall();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -424,9 +512,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 ) {
|
||||||
@@ -436,75 +535,68 @@ public class FlatComboBoxUI
|
|||||||
@Override
|
@Override
|
||||||
public Dimension getMinimumSize( JComponent c ) {
|
public Dimension getMinimumSize( JComponent c ) {
|
||||||
Dimension minimumSize = super.getMinimumSize( c );
|
Dimension minimumSize = super.getMinimumSize( c );
|
||||||
minimumSize.width = Math.max( minimumSize.width, scale( FlatUIUtils.minimumWidth( c, minimumWidth ) ) );
|
int fw = Math.round( FlatUIUtils.getBorderFocusWidth( c ) * 2 );
|
||||||
|
minimumSize.width = Math.max( minimumSize.width, scale( FlatUIUtils.minimumWidth( c, minimumWidth ) ) + fw );
|
||||||
return minimumSize;
|
return minimumSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Dimension getDefaultSize() {
|
protected Dimension getDefaultSize() {
|
||||||
@SuppressWarnings( "unchecked" )
|
paddingBorder.uninstall();
|
||||||
ListCellRenderer<Object> renderer = comboBox.getRenderer();
|
|
||||||
uninstallCellPaddingBorder( renderer );
|
|
||||||
|
|
||||||
Dimension size = super.getDefaultSize();
|
Dimension size = super.getDefaultSize();
|
||||||
|
paddingBorder.uninstall();
|
||||||
uninstallCellPaddingBorder( renderer );
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Dimension getDisplaySize() {
|
protected Dimension getDisplaySize() {
|
||||||
@SuppressWarnings( "unchecked" )
|
paddingBorder.uninstall();
|
||||||
ListCellRenderer<Object> renderer = comboBox.getRenderer();
|
|
||||||
uninstallCellPaddingBorder( renderer );
|
|
||||||
|
|
||||||
Dimension displaySize = super.getDisplaySize();
|
Dimension displaySize = super.getDisplaySize();
|
||||||
|
paddingBorder.uninstall();
|
||||||
|
|
||||||
|
// remove padding added in super.getDisplaySize()
|
||||||
|
int displayWidth = displaySize.width - padding.left - padding.right;
|
||||||
|
int displayHeight = displaySize.height - padding.top - padding.bottom;
|
||||||
|
|
||||||
// recalculate width without hardcoded 100 under special conditions
|
// recalculate width without hardcoded 100 under special conditions
|
||||||
if( displaySize.width == 100 + padding.left + padding.right &&
|
if( displayWidth == 100 &&
|
||||||
comboBox.isEditable() &&
|
comboBox.isEditable() &&
|
||||||
comboBox.getItemCount() == 0 &&
|
comboBox.getItemCount() == 0 &&
|
||||||
comboBox.getPrototypeDisplayValue() == null )
|
comboBox.getPrototypeDisplayValue() == null )
|
||||||
{
|
{
|
||||||
int width = getDefaultSize().width;
|
displayWidth = Math.max( getDefaultSize().width, editor.getPreferredSize().width );
|
||||||
width = Math.max( width, editor.getPreferredSize().width );
|
|
||||||
width += padding.left + padding.right;
|
|
||||||
displaySize = new Dimension( width, displaySize.height );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uninstallCellPaddingBorder( renderer );
|
return new Dimension( displayWidth, displayHeight );
|
||||||
return displaySize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Dimension getSizeForComponent( Component comp ) {
|
protected Dimension getSizeForComponent( Component comp ) {
|
||||||
|
paddingBorder.install( comp );
|
||||||
Dimension size = super.getSizeForComponent( comp );
|
Dimension size = super.getSizeForComponent( comp );
|
||||||
|
paddingBorder.uninstall();
|
||||||
// remove the renderer border top/bottom insets from the size to make sure that
|
|
||||||
// the combobox gets the same height as other component types (e.g. JTextField)
|
|
||||||
Insets rendererInsets = getRendererComponentInsets( comp );
|
|
||||||
if( rendererInsets != null )
|
|
||||||
size = new Dimension( size.width, size.height - rendererInsets.top - rendererInsets.bottom );
|
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Insets getRendererComponentInsets( Component rendererComponent ) {
|
private boolean isCellRenderer() {
|
||||||
if( rendererComponent instanceof JComponent ) {
|
return comboBox.getParent() instanceof CellRendererPane;
|
||||||
Border rendererBorder = ((JComponent)rendererComponent).getBorder();
|
|
||||||
if( rendererBorder != null )
|
|
||||||
return rendererBorder.getBorderInsets( rendererComponent );
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void uninstallCellPaddingBorder( Object o ) {
|
private boolean isCellRendererBackgroundChanged() {
|
||||||
CellPaddingBorder.uninstall( o );
|
// parent is a CellRendererPane, parentParent is e.g. a JTable
|
||||||
if( lastRendererComponent != null ) {
|
Container parentParent = comboBox.getParent().getParent();
|
||||||
CellPaddingBorder.uninstall( lastRendererComponent );
|
return parentParent != null && !comboBox.getBackground().equals( parentParent.getBackground() );
|
||||||
lastRendererComponent = null;
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* @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 -------------------------------------------
|
||||||
@@ -513,19 +605,34 @@ public class FlatComboBoxUI
|
|||||||
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 -----------------------------------------------
|
||||||
@@ -534,13 +641,11 @@ public class FlatComboBoxUI
|
|||||||
protected class FlatComboPopup
|
protected class FlatComboPopup
|
||||||
extends BasicComboPopup
|
extends BasicComboPopup
|
||||||
{
|
{
|
||||||
private CellPaddingBorder paddingBorder;
|
|
||||||
|
|
||||||
protected FlatComboPopup( JComboBox combo ) {
|
protected FlatComboPopup( JComboBox combo ) {
|
||||||
super( combo );
|
super( combo );
|
||||||
|
|
||||||
// BasicComboPopup listens to JComboBox.componentOrientation and updates
|
// BasicComboPopup listens to JComboBox.componentOrientation and updates
|
||||||
// the component orientation of the list, scroller and popup, but when
|
// the component orientation of the list, scroll pane and popup, but when
|
||||||
// switching the LaF and a new combo popup is created, the component
|
// switching the LaF and a new combo popup is created, the component
|
||||||
// orientation is not applied.
|
// orientation is not applied.
|
||||||
ComponentOrientation o = comboBox.getComponentOrientation();
|
ComponentOrientation o = comboBox.getComponentOrientation();
|
||||||
@@ -604,21 +709,34 @@ 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() );
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getPopupHeightForRowCount( int maxRowCount ) {
|
||||||
|
int height = super.getPopupHeightForRowCount( maxRowCount );
|
||||||
|
paddingBorder.uninstall();
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintChildren( Graphics g ) {
|
||||||
|
super.paintChildren( g );
|
||||||
|
paddingBorder.uninstall();
|
||||||
|
}
|
||||||
|
|
||||||
//---- class PopupListCellRenderer -----
|
//---- class PopupListCellRenderer -----
|
||||||
|
|
||||||
private class PopupListCellRenderer
|
private class PopupListCellRenderer
|
||||||
@@ -628,22 +746,15 @@ public class FlatComboBoxUI
|
|||||||
public Component getListCellRendererComponent( JList list, Object value,
|
public Component getListCellRendererComponent( JList list, Object value,
|
||||||
int index, boolean isSelected, boolean cellHasFocus )
|
int index, boolean isSelected, boolean cellHasFocus )
|
||||||
{
|
{
|
||||||
ListCellRenderer renderer = comboBox.getRenderer();
|
paddingBorder.uninstall();
|
||||||
CellPaddingBorder.uninstall( renderer );
|
|
||||||
CellPaddingBorder.uninstall( lastRendererComponent );
|
|
||||||
|
|
||||||
|
ListCellRenderer renderer = comboBox.getRenderer();
|
||||||
if( renderer == null )
|
if( renderer == null )
|
||||||
renderer = new DefaultListCellRenderer();
|
renderer = new DefaultListCellRenderer();
|
||||||
Component c = renderer.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
|
Component c = renderer.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
|
||||||
c.applyComponentOrientation( comboBox.getComponentOrientation() );
|
c.applyComponentOrientation( comboBox.getComponentOrientation() );
|
||||||
|
|
||||||
if( c instanceof JComponent ) {
|
paddingBorder.install( c );
|
||||||
if( paddingBorder == null )
|
|
||||||
paddingBorder = new CellPaddingBorder( padding );
|
|
||||||
paddingBorder.install( (JComponent) c );
|
|
||||||
}
|
|
||||||
|
|
||||||
lastRendererComponent = (c != renderer) ? new WeakReference<>( c ) : null;
|
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
@@ -653,49 +764,69 @@ public class FlatComboBoxUI
|
|||||||
//---- class CellPaddingBorder --------------------------------------------
|
//---- class CellPaddingBorder --------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cell padding border used only in popup list.
|
* Cell padding border used in popup list and for current value if not editable.
|
||||||
*
|
* <p>
|
||||||
* The insets are the union of the cell padding and the renderer border insets,
|
* The insets are the union of the cell padding and the renderer border insets,
|
||||||
* which vertically aligns text in popup list with text in combobox.
|
* which vertically aligns text in popup list with text in combobox.
|
||||||
*
|
* <p>
|
||||||
* The renderer border is painted on the outside of this border.
|
* The renderer border is painted on the outer side of this border.
|
||||||
*/
|
*/
|
||||||
private static class CellPaddingBorder
|
private static class CellPaddingBorder
|
||||||
extends AbstractBorder
|
extends AbstractBorder
|
||||||
{
|
{
|
||||||
private final Insets padding;
|
private final Insets padding;
|
||||||
|
private JComponent rendererComponent;
|
||||||
private Border rendererBorder;
|
private Border rendererBorder;
|
||||||
|
|
||||||
CellPaddingBorder( Insets padding ) {
|
CellPaddingBorder( Insets padding ) {
|
||||||
this.padding = padding;
|
this.padding = padding;
|
||||||
}
|
}
|
||||||
|
|
||||||
void install( JComponent rendererComponent ) {
|
void install( Component c ) {
|
||||||
Border oldBorder = rendererComponent.getBorder();
|
if( !(c instanceof JComponent) )
|
||||||
if( !(oldBorder instanceof CellPaddingBorder) ) {
|
|
||||||
rendererBorder = oldBorder;
|
|
||||||
rendererComponent.setBorder( this );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void uninstall( Object o ) {
|
|
||||||
if( o instanceof WeakReference )
|
|
||||||
o = ((WeakReference<?>)o).get();
|
|
||||||
|
|
||||||
if( !(o instanceof JComponent) )
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
JComponent rendererComponent = (JComponent) o;
|
JComponent jc = (JComponent) c;
|
||||||
Border border = rendererComponent.getBorder();
|
Border oldBorder = jc.getBorder();
|
||||||
if( border instanceof CellPaddingBorder ) {
|
if( oldBorder == this )
|
||||||
CellPaddingBorder paddingBorder = (CellPaddingBorder) border;
|
return; // already installed
|
||||||
rendererComponent.setBorder( paddingBorder.rendererBorder );
|
|
||||||
paddingBorder.rendererBorder = null;
|
// component already has a padding border --> uninstall it
|
||||||
}
|
// (may happen if single renderer instance is used in multiple comboboxes)
|
||||||
|
if( oldBorder instanceof CellPaddingBorder )
|
||||||
|
((CellPaddingBorder)oldBorder).uninstall();
|
||||||
|
|
||||||
|
// this border can be installed only at one component
|
||||||
|
// (may happen if a renderer returns varying components)
|
||||||
|
uninstall();
|
||||||
|
|
||||||
|
// remember component where this border was installed for uninstall
|
||||||
|
rendererComponent = jc;
|
||||||
|
|
||||||
|
// remember old border and replace it
|
||||||
|
rendererBorder = jc.getBorder();
|
||||||
|
rendererComponent.setBorder( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uninstall border from previously installed component.
|
||||||
|
* Because this border is installed in PopupListCellRenderer.getListCellRendererComponent(),
|
||||||
|
* there is no single place to uninstall it.
|
||||||
|
* This is the reason why this method is called from various places.
|
||||||
|
*/
|
||||||
|
void uninstall() {
|
||||||
|
if( rendererComponent == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( rendererComponent.getBorder() == this )
|
||||||
|
rendererComponent.setBorder( rendererBorder );
|
||||||
|
rendererComponent = null;
|
||||||
|
rendererBorder = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
|
Insets padding = scale( this.padding );
|
||||||
if( rendererBorder != null ) {
|
if( rendererBorder != null ) {
|
||||||
Insets insideInsets = rendererBorder.getBorderInsets( c );
|
Insets insideInsets = rendererBorder.getBorderInsets( c );
|
||||||
insets.top = Math.max( padding.top, insideInsets.top );
|
insets.top = Math.max( padding.top, insideInsets.top );
|
||||||
|
|||||||
@@ -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
|
static void paintBackground( Graphics g, JTextComponent c, boolean isIntelliJTheme, Color focusedBackground ) {
|
||||||
if( isIntelliJTheme && (!c.isEnabled() || !c.isEditable()) && (c.getBackground() instanceof UIResource) ) {
|
g.setColor( FlatTextFieldUI.getBackground( c, isIntelliJTheme, focusedBackground ) );
|
||||||
FlatUIUtils.paintParentBackground( g, c );
|
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
super.paintBackground( g );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 ) );
|
||||||
|
|||||||
@@ -39,11 +39,10 @@ import javax.swing.plaf.ComponentUI;
|
|||||||
*
|
*
|
||||||
* <!-- FlatTextFieldUI -->
|
* <!-- FlatTextFieldUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault TextComponent.arc int
|
|
||||||
* @uiDefault Component.focusWidth int
|
|
||||||
* @uiDefault Component.minimumWidth int
|
* @uiDefault Component.minimumWidth int
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
* @uiDefault Component.isIntelliJTheme boolean
|
||||||
* @uiDefault FormattedTextField.placeholderForeground Color
|
* @uiDefault FormattedTextField.placeholderForeground Color
|
||||||
|
* @uiDefault FormattedTextField.focusedBackground Color optional
|
||||||
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
|
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
|
||||||
* @uiDefault TextComponent.selectAllOnMouseClick boolean
|
* @uiDefault TextComponent.selectAllOnMouseClick boolean
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -96,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 );
|
||||||
@@ -122,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 )
|
||||||
|
|||||||
@@ -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,36 +264,36 @@ 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 ) {
|
||||||
int width = menuItem.getWidth();
|
|
||||||
int height = menuItem.getHeight();
|
|
||||||
|
|
||||||
// paint background
|
// paint background
|
||||||
g.setColor( armedOrSelected
|
g.setColor( armedOrSelected
|
||||||
? (isUnderlineSelection()
|
? deriveBackground( selectionBackground )
|
||||||
? deriveBackground( underlineSelectionBackground )
|
|
||||||
: selectionBackground)
|
|
||||||
: menuItem.getBackground() );
|
: menuItem.getBackground() );
|
||||||
g.fillRect( 0, 0, width, height );
|
g.fillRect( 0, 0, menuItem.getWidth(), menuItem.getHeight() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// paint underline
|
protected void paintUnderlineSelection( Graphics g, Color underlineSelectionColor, int underlineSelectionHeight ) {
|
||||||
if( armedOrSelected && isUnderlineSelection() ) {
|
int width = menuItem.getWidth();
|
||||||
int underlineHeight = scale( underlineSelectionHeight );
|
int height = menuItem.getHeight();
|
||||||
g.setColor( underlineSelectionColor );
|
|
||||||
if( isTopLevelMenu( menuItem ) ) {
|
int underlineHeight = scale( underlineSelectionHeight );
|
||||||
// paint underline at bottom
|
g.setColor( underlineSelectionColor );
|
||||||
g.fillRect( 0, height - underlineHeight, width, underlineHeight );
|
if( isTopLevelMenu( menuItem ) ) {
|
||||||
} else if( menuItem.getComponentOrientation().isLeftToRight() ) {
|
// paint underline at bottom
|
||||||
// paint underline at left side
|
g.fillRect( 0, height - underlineHeight, width, underlineHeight );
|
||||||
g.fillRect( 0, 0, underlineHeight, height );
|
} else if( menuItem.getComponentOrientation().isLeftToRight() ) {
|
||||||
} else {
|
// paint underline at left side
|
||||||
// paint underline at right side
|
g.fillRect( 0, 0, underlineHeight, height );
|
||||||
g.fillRect( width - underlineHeight, 0, underlineHeight, height );
|
} else {
|
||||||
}
|
// paint underline at right side
|
||||||
}
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,7 +22,10 @@ import java.awt.Dimension;
|
|||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.GridBagConstraints;
|
import java.awt.GridBagConstraints;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
|
import javax.swing.Box;
|
||||||
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
@@ -160,12 +163,30 @@ public class FlatOptionPaneUI
|
|||||||
if( msg instanceof String && BasicHTML.isHTMLString( (String) msg ) )
|
if( msg instanceof String && BasicHTML.isHTMLString( (String) msg ) )
|
||||||
maxll = Integer.MAX_VALUE;
|
maxll = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
// fix right-to-left alignment if super.addMessageComponents() breaks longer lines
|
||||||
|
// into multiple labels and puts them into a box that aligns them to the left
|
||||||
|
if( msg instanceof Box ) {
|
||||||
|
Box box = (Box) msg;
|
||||||
|
if( "OptionPane.verticalBox".equals( box.getName() ) &&
|
||||||
|
box.getLayout() instanceof BoxLayout &&
|
||||||
|
((BoxLayout)box.getLayout()).getAxis() == BoxLayout.Y_AXIS )
|
||||||
|
{
|
||||||
|
box.addPropertyChangeListener( "componentOrientation", e -> {
|
||||||
|
float alignX = box.getComponentOrientation().isLeftToRight() ? 0 : 1;
|
||||||
|
for( Component c : box.getComponents() ) {
|
||||||
|
if( c instanceof JLabel && "OptionPane.label".equals( c.getName() ) )
|
||||||
|
((JLabel)c).setAlignmentX( alignX );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
super.addMessageComponents( container, cons, msg, maxll, internallyCreated );
|
super.addMessageComponents( container, cons, msg, maxll, internallyCreated );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateChildPanels( Container c ) {
|
private void updateChildPanels( Container c ) {
|
||||||
for( Component child : c.getComponents() ) {
|
for( Component child : c.getComponents() ) {
|
||||||
if( child instanceof JPanel ) {
|
if( child.getClass() == JPanel.class ) {
|
||||||
JPanel panel = (JPanel)child;
|
JPanel panel = (JPanel)child;
|
||||||
|
|
||||||
// make sub-panel non-opaque for OptionPane.background
|
// make sub-panel non-opaque for OptionPane.background
|
||||||
@@ -177,9 +198,8 @@ public class FlatOptionPaneUI
|
|||||||
panel.setBorder( new NonUIResourceBorder( border ) );
|
panel.setBorder( new NonUIResourceBorder( border ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( child instanceof Container ) {
|
if( child instanceof Container )
|
||||||
updateChildPanels( (Container) child );
|
updateChildPanels( (Container) child );
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,30 +16,31 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Dimension;
|
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Insets;
|
||||||
|
import java.awt.Shape;
|
||||||
import java.awt.Toolkit;
|
import java.awt.Toolkit;
|
||||||
import java.awt.event.FocusListener;
|
|
||||||
import java.awt.event.KeyAdapter;
|
import java.awt.event.KeyAdapter;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
import java.awt.event.KeyListener;
|
import java.awt.event.KeyListener;
|
||||||
import java.beans.PropertyChangeEvent;
|
import javax.swing.Action;
|
||||||
|
import javax.swing.ActionMap;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicPasswordFieldUI;
|
import javax.swing.text.DefaultEditorKit;
|
||||||
import javax.swing.text.Caret;
|
import javax.swing.text.Element;
|
||||||
import javax.swing.text.JTextComponent;
|
import javax.swing.text.JTextComponent;
|
||||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
import javax.swing.text.PasswordView;
|
||||||
|
import javax.swing.text.View;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JPasswordField}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JPasswordField}.
|
||||||
*
|
*
|
||||||
* <!-- BasicPasswordFieldUI -->
|
* <!-- BasicTextFieldUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault PasswordField.font Font
|
* @uiDefault PasswordField.font Font
|
||||||
* @uiDefault PasswordField.background Color
|
* @uiDefault PasswordField.background Color
|
||||||
@@ -52,68 +53,67 @@ import com.formdev.flatlaf.util.HiDPIUtils;
|
|||||||
* @uiDefault PasswordField.inactiveForeground Color used if not enabled (yes, this is confusing; this should be named disabledForeground)
|
* @uiDefault PasswordField.inactiveForeground Color used if not enabled (yes, this is confusing; this should be named disabledForeground)
|
||||||
* @uiDefault PasswordField.border Border
|
* @uiDefault PasswordField.border Border
|
||||||
* @uiDefault PasswordField.margin Insets
|
* @uiDefault PasswordField.margin Insets
|
||||||
* @uiDefault PasswordField.echoChar character
|
|
||||||
* @uiDefault PasswordField.caretBlinkRate int default is 500 milliseconds
|
* @uiDefault PasswordField.caretBlinkRate int default is 500 milliseconds
|
||||||
*
|
*
|
||||||
* <!-- FlatPasswordFieldUI -->
|
* <!-- FlatTextFieldUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault Component.minimumWidth int
|
* @uiDefault Component.minimumWidth int
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
* @uiDefault Component.isIntelliJTheme boolean
|
||||||
* @uiDefault PasswordField.placeholderForeground Color
|
* @uiDefault PasswordField.placeholderForeground Color
|
||||||
* @uiDefault PasswordField.showCapsLock boolean
|
* @uiDefault PasswordField.focusedBackground Color optional
|
||||||
* @uiDefault PasswordField.capsLockIcon Icon
|
|
||||||
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
|
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
|
||||||
* @uiDefault TextComponent.selectAllOnMouseClick boolean
|
* @uiDefault TextComponent.selectAllOnMouseClick boolean
|
||||||
*
|
*
|
||||||
|
* <!-- FlatPasswordFieldUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault PasswordField.echoChar character
|
||||||
|
* @uiDefault PasswordField.showCapsLock boolean
|
||||||
|
* @uiDefault PasswordField.capsLockIcon Icon
|
||||||
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatPasswordFieldUI
|
public class FlatPasswordFieldUI
|
||||||
extends BasicPasswordFieldUI
|
extends FlatTextFieldUI
|
||||||
{
|
{
|
||||||
protected int minimumWidth;
|
|
||||||
protected boolean isIntelliJTheme;
|
|
||||||
protected Color placeholderForeground;
|
|
||||||
protected boolean showCapsLock;
|
protected boolean showCapsLock;
|
||||||
protected Icon capsLockIcon;
|
protected Icon capsLockIcon;
|
||||||
|
|
||||||
private FocusListener focusListener;
|
|
||||||
private KeyListener capsLockListener;
|
private KeyListener capsLockListener;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatPasswordFieldUI();
|
return new FlatPasswordFieldUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getPropertyPrefix() {
|
||||||
|
return "PasswordField";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
String prefix = getPropertyPrefix();
|
String prefix = getPropertyPrefix();
|
||||||
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
Character echoChar = (Character) UIManager.get( prefix + ".echoChar" );
|
||||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
if( echoChar != null )
|
||||||
placeholderForeground = UIManager.getColor( prefix + ".placeholderForeground" );
|
LookAndFeel.installProperty( getComponent(), "echoChar", echoChar );
|
||||||
|
|
||||||
showCapsLock = UIManager.getBoolean( "PasswordField.showCapsLock" );
|
showCapsLock = UIManager.getBoolean( "PasswordField.showCapsLock" );
|
||||||
capsLockIcon = UIManager.getIcon( "PasswordField.capsLockIcon" );
|
capsLockIcon = UIManager.getIcon( "PasswordField.capsLockIcon" );
|
||||||
|
|
||||||
LookAndFeel.installProperty( getComponent(), "opaque", false );
|
|
||||||
|
|
||||||
MigLayoutVisualPadding.install( getComponent() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
placeholderForeground = null;
|
|
||||||
capsLockIcon = null;
|
capsLockIcon = null;
|
||||||
|
|
||||||
MigLayoutVisualPadding.uninstall( getComponent() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installListeners() {
|
protected void installListeners() {
|
||||||
super.installListeners();
|
super.installListeners();
|
||||||
|
|
||||||
focusListener = new FlatUIUtils.RepaintFocusListener( getComponent() );
|
// update caps lock indicator
|
||||||
capsLockListener = new KeyAdapter() {
|
capsLockListener = new KeyAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void keyPressed( KeyEvent e ) {
|
public void keyPressed( KeyEvent e ) {
|
||||||
@@ -124,12 +124,13 @@ public class FlatPasswordFieldUI
|
|||||||
repaint( e );
|
repaint( e );
|
||||||
}
|
}
|
||||||
private void repaint( KeyEvent e ) {
|
private void repaint( KeyEvent e ) {
|
||||||
if( e.getKeyCode() == KeyEvent.VK_CAPS_LOCK )
|
if( e.getKeyCode() == KeyEvent.VK_CAPS_LOCK ) {
|
||||||
e.getComponent().repaint();
|
e.getComponent().repaint();
|
||||||
|
scrollCaretToVisible();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
getComponent().addFocusListener( focusListener );
|
|
||||||
getComponent().addKeyListener( capsLockListener );
|
getComponent().addKeyListener( capsLockListener );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,59 +138,74 @@ public class FlatPasswordFieldUI
|
|||||||
protected void uninstallListeners() {
|
protected void uninstallListeners() {
|
||||||
super.uninstallListeners();
|
super.uninstallListeners();
|
||||||
|
|
||||||
getComponent().removeFocusListener( focusListener );
|
|
||||||
getComponent().removeKeyListener( capsLockListener );
|
getComponent().removeKeyListener( capsLockListener );
|
||||||
focusListener = null;
|
|
||||||
capsLockListener = null;
|
capsLockListener = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Caret createCaret() {
|
protected void installKeyboardActions() {
|
||||||
return new FlatCaret( UIManager.getString( "TextComponent.selectAllOnFocusPolicy" ),
|
super.installKeyboardActions();
|
||||||
UIManager.getBoolean( "TextComponent.selectAllOnMouseClick" ) );
|
|
||||||
|
// map "select-word" action (double-click) to "select-line" action
|
||||||
|
ActionMap map = SwingUtilities.getUIActionMap( getComponent() );
|
||||||
|
if( map != null && map.get( DefaultEditorKit.selectWordAction ) != null ) {
|
||||||
|
Action selectLineAction = map.get( DefaultEditorKit.selectLineAction );
|
||||||
|
if( selectLineAction != null )
|
||||||
|
map.put( DefaultEditorKit.selectWordAction, selectLineAction );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void propertyChange( PropertyChangeEvent e ) {
|
public View create( Element elem ) {
|
||||||
super.propertyChange( e );
|
return new PasswordView( elem );
|
||||||
FlatTextFieldUI.propertyChange( getComponent(), e );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintSafely( Graphics g ) {
|
protected void paintSafely( Graphics g ) {
|
||||||
FlatTextFieldUI.paintBackground( g, getComponent(), isIntelliJTheme );
|
// safe and restore clipping area because super.paintSafely() modifies it
|
||||||
FlatTextFieldUI.paintPlaceholder( g, getComponent(), placeholderForeground );
|
// and the caps lock icon would be truncated
|
||||||
paintCapsLock( g );
|
Shape oldClip = g.getClip();
|
||||||
|
super.paintSafely( g );
|
||||||
|
g.setClip( oldClip );
|
||||||
|
|
||||||
super.paintSafely( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ) );
|
paintCapsLock( g );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void paintCapsLock( Graphics g ) {
|
protected void paintCapsLock( Graphics g ) {
|
||||||
if( !showCapsLock )
|
if( !isCapsLockVisible() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
JTextComponent c = getComponent();
|
JTextComponent c = getComponent();
|
||||||
if( !FlatUIUtils.isPermanentFocusOwner( c ) ||
|
|
||||||
!Toolkit.getDefaultToolkit().getLockingKeyState( KeyEvent.VK_CAPS_LOCK ) )
|
|
||||||
return;
|
|
||||||
|
|
||||||
int y = (c.getHeight() - capsLockIcon.getIconHeight()) / 2;
|
int y = (c.getHeight() - capsLockIcon.getIconHeight()) / 2;
|
||||||
int x = c.getWidth() - capsLockIcon.getIconWidth() - y;
|
int x = c.getComponentOrientation().isLeftToRight()
|
||||||
|
? c.getWidth() - capsLockIcon.getIconWidth() - y
|
||||||
|
: y;
|
||||||
capsLockIcon.paintIcon( c, g, x, y );
|
capsLockIcon.paintIcon( c, g, x, y );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
protected void paintBackground( Graphics g ) {
|
* @since 1.4
|
||||||
// background is painted elsewhere
|
*/
|
||||||
|
protected boolean isCapsLockVisible() {
|
||||||
|
if( !showCapsLock )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
JTextComponent c = getComponent();
|
||||||
|
return FlatUIUtils.isPermanentFocusOwner( c ) &&
|
||||||
|
Toolkit.getDefaultToolkit().getLockingKeyState( KeyEvent.VK_CAPS_LOCK );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.4
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
protected Insets getPadding() {
|
||||||
return FlatTextFieldUI.applyMinimumWidth( c, super.getPreferredSize( c ), minimumWidth );
|
Insets padding = super.getPadding();
|
||||||
}
|
if( !isCapsLockVisible() )
|
||||||
|
return padding;
|
||||||
|
|
||||||
@Override
|
boolean ltr = getComponent().getComponentOrientation().isLeftToRight();
|
||||||
public Dimension getMinimumSize( JComponent c ) {
|
int iconWidth = capsLockIcon.getIconWidth();
|
||||||
return FlatTextFieldUI.applyMinimumWidth( c, super.getMinimumSize( c ), minimumWidth );
|
return FlatUIUtils.addInsets( padding, new Insets( 0, ltr ? 0 : iconWidth, 0, ltr ? iconWidth : 0 ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,11 +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.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;
|
||||||
@@ -39,6 +44,7 @@ import javax.swing.Popup;
|
|||||||
import javax.swing.PopupFactory;
|
import javax.swing.PopupFactory;
|
||||||
import javax.swing.RootPaneContainer;
|
import javax.swing.RootPaneContainer;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.ToolTipManager;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
@@ -68,19 +74,17 @@ public class FlatPopupFactory
|
|||||||
y = pt.y;
|
y = pt.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !isDropShadowPainted( owner, contents ) )
|
boolean forceHeavyWeight = isOptionEnabled( owner, contents, FlatClientProperties.POPUP_FORCE_HEAVY_WEIGHT, "Popup.forceHeavyWeight" );
|
||||||
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, false ), contents );
|
|
||||||
|
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 = getPopupForScreenOfOwner( owner, contents, x, y, true );
|
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), contents );
|
||||||
if( popup == null )
|
|
||||||
popup = getPopupForScreenOfOwner( owner, contents, x, y, false );
|
|
||||||
return new NonFlashingPopup( popup, contents );
|
|
||||||
}
|
|
||||||
|
|
||||||
// create drop shadow popup
|
// create drop shadow popup
|
||||||
return new DropShadowPopup( getPopupForScreenOfOwner( owner, contents, x, y, false ), owner, contents );
|
return new DropShadowPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), owner, contents );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -113,6 +117,7 @@ public class FlatPopupFactory
|
|||||||
|
|
||||||
// check whether heavy weight popup window is on same screen as owner component
|
// check whether heavy weight popup window is on same screen as owner component
|
||||||
if( popupWindow == null ||
|
if( popupWindow == null ||
|
||||||
|
owner == null ||
|
||||||
popupWindow.getGraphicsConfiguration() == owner.getGraphicsConfiguration() )
|
popupWindow.getGraphicsConfiguration() == owner.getGraphicsConfiguration() )
|
||||||
return popup;
|
return popup;
|
||||||
|
|
||||||
@@ -155,24 +160,20 @@ public class FlatPopupFactory
|
|||||||
popup.show();
|
popup.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isDropShadowPainted( Component owner, Component contents ) {
|
private boolean isOptionEnabled( Component owner, Component contents, String clientKey, String uiKey ) {
|
||||||
Boolean b = isDropShadowPainted( owner );
|
if( owner instanceof JComponent ) {
|
||||||
if( b != null )
|
Boolean b = FlatClientProperties.clientPropertyBooleanStrict( (JComponent) owner, clientKey, null );
|
||||||
return b;
|
if( b != null )
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
b = isDropShadowPainted( contents );
|
if( contents instanceof JComponent ) {
|
||||||
if( b != null )
|
Boolean b = FlatClientProperties.clientPropertyBooleanStrict( (JComponent) contents, clientKey, null );
|
||||||
return b;
|
if( b != null )
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
return UIManager.getBoolean( "Popup.dropShadowPainted" );
|
return UIManager.getBoolean( uiKey );
|
||||||
}
|
|
||||||
|
|
||||||
private Boolean isDropShadowPainted( Component c ) {
|
|
||||||
if( !(c instanceof JComponent) )
|
|
||||||
return null;
|
|
||||||
|
|
||||||
Object value = ((JComponent)c).getClientProperty( FlatClientProperties.POPUP_DROP_SHADOW_PAINTED );
|
|
||||||
return (value instanceof Boolean ) ? (Boolean) value : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -211,7 +212,7 @@ public class FlatPopupFactory
|
|||||||
/**
|
/**
|
||||||
* Usually ToolTipManager places a tooltip at (mouseLocation.x, mouseLocation.y + 20).
|
* Usually ToolTipManager places a tooltip at (mouseLocation.x, mouseLocation.y + 20).
|
||||||
* In case that the tooltip would be partly outside of the screen,
|
* In case that the tooltip would be partly outside of the screen,
|
||||||
* ToolTipManagerthe changes the location so that the entire tooltip fits on screen.
|
* the ToolTipManager changes the location so that the entire tooltip fits on screen.
|
||||||
* But this can place the tooltip under the mouse location and hide the owner component.
|
* But this can place the tooltip under the mouse location and hide the owner component.
|
||||||
* <p>
|
* <p>
|
||||||
* This method checks whether the current mouse location is within tooltip bounds
|
* This method checks whether the current mouse location is within tooltip bounds
|
||||||
@@ -221,7 +222,11 @@ public class FlatPopupFactory
|
|||||||
if( !(contents instanceof JToolTip) || !wasInvokedFromToolTipManager() )
|
if( !(contents instanceof JToolTip) || !wasInvokedFromToolTipManager() )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
Point mouseLocation = MouseInfo.getPointerInfo().getLocation();
|
PointerInfo pointerInfo = MouseInfo.getPointerInfo();
|
||||||
|
if( pointerInfo == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Point mouseLocation = pointerInfo.getLocation();
|
||||||
Dimension tipSize = contents.getPreferredSize();
|
Dimension tipSize = contents.getPreferredSize();
|
||||||
|
|
||||||
// check whether mouse location is within tooltip bounds
|
// check whether mouse location is within tooltip bounds
|
||||||
@@ -229,18 +234,34 @@ public class FlatPopupFactory
|
|||||||
if( !tipBounds.contains( mouseLocation ) )
|
if( !tipBounds.contains( mouseLocation ) )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// place tooltip above mouse location
|
// find GraphicsConfiguration at mouse location (similar to ToolTipManager.getDrawingGC())
|
||||||
return new Point( x, mouseLocation.y - tipSize.height - UIScale.scale( 20 ) );
|
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() {
|
private boolean wasInvokedFromToolTipManager() {
|
||||||
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
return StackUtils.wasInvokedFrom( ToolTipManager.class.getName(), "showTipWindow", 8 );
|
||||||
for( StackTraceElement stackTraceElement : stackTrace ) {
|
|
||||||
if( "javax.swing.ToolTipManager".equals( stackTraceElement.getClassName() ) &&
|
|
||||||
"showTipWindow".equals( stackTraceElement.getMethodName() ) )
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class NonFlashingPopup ---------------------------------------------
|
//---- class NonFlashingPopup ---------------------------------------------
|
||||||
@@ -277,16 +298,17 @@ public class FlatPopupFactory
|
|||||||
|
|
||||||
// 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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -455,10 +477,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;
|
||||||
|
|
||||||
|
|||||||
@@ -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,10 +187,12 @@ public class FlatProgressBarUI
|
|||||||
((Graphics2D)g).fill( area );
|
((Graphics2D)g).fill( area );
|
||||||
} else
|
} else
|
||||||
((Graphics2D)g).fill( progressShape );
|
((Graphics2D)g).fill( progressShape );
|
||||||
|
|
||||||
if( progressBar.isStringPainted() )
|
|
||||||
paintString( g, x, y, width, height, amountFull, insets );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||||
|
|
||||||
|
if( progressBar.isStringPainted() )
|
||||||
|
paintString( g, x, y, width, height, amountFull, insets );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -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,6 +60,8 @@ 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;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
@@ -74,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,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() );
|
||||||
@@ -157,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;
|
||||||
@@ -40,6 +42,7 @@ import javax.swing.UIManager;
|
|||||||
import javax.swing.border.Border;
|
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;
|
||||||
@@ -70,17 +73,15 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
public class FlatRootPaneUI
|
public class FlatRootPaneUI
|
||||||
extends BasicRootPaneUI
|
extends BasicRootPaneUI
|
||||||
{
|
{
|
||||||
// check this field before using class JBRCustomDecorations to avoid unnecessary loading of that class
|
|
||||||
static final boolean canUseJBRCustomDecorations
|
|
||||||
= SystemInfo.isJetBrainsJVM_11_orLater && SystemInfo.isWindows_10_orLater;
|
|
||||||
|
|
||||||
protected final Color borderColor = UIManager.getColor( "TitlePane.borderColor" );
|
protected final Color borderColor = UIManager.getColor( "TitlePane.borderColor" );
|
||||||
|
|
||||||
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();
|
||||||
@@ -97,8 +98,7 @@ public class FlatRootPaneUI
|
|||||||
else
|
else
|
||||||
installBorder();
|
installBorder();
|
||||||
|
|
||||||
if( canUseJBRCustomDecorations )
|
installNativeWindowBorder();
|
||||||
JBRCustomDecorations.install( rootPane );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void installBorder() {
|
protected void installBorder() {
|
||||||
@@ -113,6 +113,7 @@ public class FlatRootPaneUI
|
|||||||
public void uninstallUI( JComponent c ) {
|
public void uninstallUI( JComponent c ) {
|
||||||
super.uninstallUI( c );
|
super.uninstallUI( c );
|
||||||
|
|
||||||
|
uninstallNativeWindowBorder();
|
||||||
uninstallClientDecorations();
|
uninstallClientDecorations();
|
||||||
rootPane = null;
|
rootPane = null;
|
||||||
}
|
}
|
||||||
@@ -138,11 +139,72 @@ public class FlatRootPaneUI
|
|||||||
c.putClientProperty( "jetbrains.awt.windowDarkAppearance", FlatLaf.isLafDark() );
|
c.putClientProperty( "jetbrains.awt.windowDarkAppearance", FlatLaf.isLafDark() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installListeners( JRootPane root ) {
|
||||||
|
super.installListeners( root );
|
||||||
|
|
||||||
|
if( SystemInfo.isJava_9_orLater ) {
|
||||||
|
// On HiDPI screens, where scaling is used, there may be white lines at the
|
||||||
|
// bottom and at the right side of the window when it is initially shown.
|
||||||
|
// This is very disturbing in dark themes, but hard to notice in light themes.
|
||||||
|
// Seems to be a rounding issue when Swing adds dirty region of window
|
||||||
|
// using RepaintManager.nativeAddDirtyRegion().
|
||||||
|
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 );
|
||||||
@@ -155,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,6 +281,10 @@ public class FlatRootPaneUI
|
|||||||
installBorder();
|
installBorder();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.USE_WINDOW_DECORATIONS:
|
||||||
|
updateNativeWindowBorder( rootPane );
|
||||||
|
break;
|
||||||
|
|
||||||
case FlatClientProperties.MENU_BAR_EMBEDDED:
|
case FlatClientProperties.MENU_BAR_EMBEDDED:
|
||||||
if( titlePane != null ) {
|
if( titlePane != null ) {
|
||||||
titlePane.menuBarChanged();
|
titlePane.menuBarChanged();
|
||||||
@@ -226,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
|
||||||
@@ -263,7 +342,7 @@ public class FlatRootPaneUI
|
|||||||
? getSizeFunc.apply( rootPane.getContentPane() )
|
? getSizeFunc.apply( rootPane.getContentPane() )
|
||||||
: rootPane.getSize();
|
: rootPane.getSize();
|
||||||
|
|
||||||
int width = Math.max( titlePaneSize.width, contentSize.width );
|
int width = contentSize.width; // title pane width is not considered here
|
||||||
int height = titlePaneSize.height + contentSize.height;
|
int height = titlePaneSize.height + contentSize.height;
|
||||||
if( titlePane == null || !titlePane.isMenuBarEmbedded() ) {
|
if( titlePane == null || !titlePane.isMenuBarEmbedded() ) {
|
||||||
JMenuBar menuBar = rootPane.getJMenuBar();
|
JMenuBar menuBar = rootPane.getJMenuBar();
|
||||||
@@ -285,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;
|
||||||
@@ -299,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 && menuBar.isVisible() ) {
|
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 {
|
||||||
@@ -343,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
|
||||||
{
|
{
|
||||||
@@ -356,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
|
||||||
@@ -366,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();
|
||||||
@@ -420,7 +504,9 @@ public class FlatRootPaneUI
|
|||||||
(parent instanceof JFrame &&
|
(parent instanceof JFrame &&
|
||||||
(((JFrame)parent).getJMenuBar() == null ||
|
(((JFrame)parent).getJMenuBar() == null ||
|
||||||
!((JFrame)parent).getJMenuBar().isVisible())) ||
|
!((JFrame)parent).getJMenuBar().isVisible())) ||
|
||||||
parent instanceof JDialog;
|
(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;
|
||||||
@@ -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,30 +167,28 @@ 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:
|
||||||
scrollbar.revalidate();
|
scrollbar.revalidate();
|
||||||
scrollbar.repaint();
|
scrollbar.repaint();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "componentOrientation":
|
case "componentOrientation":
|
||||||
// this is missing in BasicScrollBarUI.Handler.propertyChange()
|
// this is missing in BasicScrollBarUI.Handler.propertyChange()
|
||||||
InputMap inputMap = (InputMap) UIManager.get( "ScrollBar.ancestorInputMap" );
|
InputMap inputMap = (InputMap) UIManager.get( "ScrollBar.ancestorInputMap" );
|
||||||
if( !scrollbar.getComponentOrientation().isLeftToRight() ) {
|
if( !scrollbar.getComponentOrientation().isLeftToRight() ) {
|
||||||
InputMap rtlInputMap = (InputMap) UIManager.get( "ScrollBar.ancestorInputMap.RightToLeft" );
|
InputMap rtlInputMap = (InputMap) UIManager.get( "ScrollBar.ancestorInputMap.RightToLeft" );
|
||||||
if( rtlInputMap != null ) {
|
if( rtlInputMap != null ) {
|
||||||
rtlInputMap.setParent( inputMap );
|
rtlInputMap.setParent( inputMap );
|
||||||
inputMap = rtlInputMap;
|
inputMap = rtlInputMap;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SwingUtilities.replaceUIInputMap( scrollbar, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap );
|
}
|
||||||
break;
|
SwingUtilities.replaceUIInputMap( scrollbar, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap );
|
||||||
}
|
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,19 +108,17 @@ 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 &&
|
e.getPreciseWheelRotation() != 0 &&
|
||||||
e.getPreciseWheelRotation() != 0 &&
|
e.getPreciseWheelRotation() != e.getWheelRotation() )
|
||||||
e.getPreciseWheelRotation() != e.getWheelRotation() )
|
{
|
||||||
{
|
mouseWheelMovedSmooth( e );
|
||||||
mouseWheelMovedSmooth( e );
|
} else
|
||||||
} else
|
superListener.mouseWheelMoved( e );
|
||||||
super.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,92 +183,96 @@ 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:
|
||||||
JScrollBar vsb = scrollpane.getVerticalScrollBar();
|
JScrollBar vsb = scrollpane.getVerticalScrollBar();
|
||||||
JScrollBar hsb = scrollpane.getHorizontalScrollBar();
|
JScrollBar hsb = scrollpane.getHorizontalScrollBar();
|
||||||
if( vsb != null ) {
|
if( vsb != null ) {
|
||||||
vsb.revalidate();
|
vsb.revalidate();
|
||||||
vsb.repaint();
|
vsb.repaint();
|
||||||
}
|
}
|
||||||
if( hsb != null ) {
|
if( hsb != null ) {
|
||||||
hsb.revalidate();
|
hsb.revalidate();
|
||||||
hsb.repaint();
|
hsb.repaint();
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
case ScrollPaneConstants.LOWER_LEFT_CORNER:
|
|
||||||
case ScrollPaneConstants.LOWER_RIGHT_CORNER:
|
|
||||||
case ScrollPaneConstants.UPPER_LEFT_CORNER:
|
|
||||||
case ScrollPaneConstants.UPPER_RIGHT_CORNER:
|
|
||||||
// remove border from buttons added to corners
|
|
||||||
Object corner = e.getNewValue();
|
|
||||||
if( corner instanceof JButton &&
|
|
||||||
((JButton)corner).getBorder() instanceof FlatButtonBorder &&
|
|
||||||
scrollpane.getViewport() != null &&
|
|
||||||
scrollpane.getViewport().getView() instanceof JTable )
|
|
||||||
{
|
|
||||||
((JButton)corner).setBorder( BorderFactory.createEmptyBorder() );
|
|
||||||
((JButton)corner).setFocusable( false );
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
case ScrollPaneConstants.LOWER_LEFT_CORNER:
|
||||||
|
case ScrollPaneConstants.LOWER_RIGHT_CORNER:
|
||||||
|
case ScrollPaneConstants.UPPER_LEFT_CORNER:
|
||||||
|
case ScrollPaneConstants.UPPER_RIGHT_CORNER:
|
||||||
|
// remove border from buttons added to corners
|
||||||
|
Object corner = e.getNewValue();
|
||||||
|
if( corner instanceof JButton &&
|
||||||
|
((JButton)corner).getBorder() instanceof FlatButtonBorder &&
|
||||||
|
scrollpane.getViewport() != null &&
|
||||||
|
scrollpane.getViewport().getView() instanceof JTable )
|
||||||
|
{
|
||||||
|
((JButton)corner).setBorder( BorderFactory.createEmptyBorder() );
|
||||||
|
((JButton)corner).setFocusable( false );
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
@@ -65,10 +66,12 @@ import com.formdev.flatlaf.FlatClientProperties;
|
|||||||
* @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,15 +115,14 @@ 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
|
|
||||||
padding = scale( padding );
|
|
||||||
|
|
||||||
MigLayoutVisualPadding.install( spinner );
|
MigLayoutVisualPadding.install( spinner );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,10 +134,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 );
|
||||||
@@ -175,6 +181,7 @@ public class FlatSpinnerUI
|
|||||||
if( textField != null )
|
if( textField != null )
|
||||||
textField.setOpaque( false );
|
textField.setOpaque( false );
|
||||||
|
|
||||||
|
updateEditorPadding();
|
||||||
updateEditorColors();
|
updateEditorColors();
|
||||||
return editor;
|
return editor;
|
||||||
}
|
}
|
||||||
@@ -185,6 +192,8 @@ public class FlatSpinnerUI
|
|||||||
|
|
||||||
removeEditorFocusListener( oldEditor );
|
removeEditorFocusListener( oldEditor );
|
||||||
addEditorFocusListener( newEditor );
|
addEditorFocusListener( newEditor );
|
||||||
|
|
||||||
|
updateEditorPadding();
|
||||||
updateEditorColors();
|
updateEditorColors();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,6 +209,12 @@ public class FlatSpinnerUI
|
|||||||
textField.removeFocusListener( getHandler() );
|
textField.removeFocusListener( getHandler() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateEditorPadding() {
|
||||||
|
JTextField textField = getEditorTextField( spinner.getEditor() );
|
||||||
|
if( textField != null )
|
||||||
|
textField.putClientProperty( FlatClientProperties.TEXT_FIELD_PADDING, padding );
|
||||||
|
}
|
||||||
|
|
||||||
private void updateEditorColors() {
|
private void updateEditorColors() {
|
||||||
JTextField textField = getEditorTextField( spinner.getEditor() );
|
JTextField textField = getEditorTextField( spinner.getEditor() );
|
||||||
if( textField != null ) {
|
if( textField != null ) {
|
||||||
@@ -217,10 +232,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 +283,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,7 +303,7 @@ 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();
|
||||||
@@ -303,6 +342,8 @@ public class FlatSpinnerUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
paint( g, c );
|
paint( g, c );
|
||||||
|
|
||||||
|
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class Handler ------------------------------------------------------
|
//---- class Handler ------------------------------------------------------
|
||||||
@@ -338,6 +379,7 @@ public class FlatSpinnerUI
|
|||||||
@Override
|
@Override
|
||||||
public Dimension preferredLayoutSize( Container parent ) {
|
public Dimension preferredLayoutSize( Container parent ) {
|
||||||
Insets insets = parent.getInsets();
|
Insets insets = parent.getInsets();
|
||||||
|
Insets padding = scale( FlatSpinnerUI.this.padding );
|
||||||
Dimension editorSize = (editor != null) ? editor.getPreferredSize() : new Dimension( 0, 0 );
|
Dimension editorSize = (editor != null) ? editor.getPreferredSize() : new Dimension( 0, 0 );
|
||||||
|
|
||||||
// the arrows width is the same as the inner height so that the arrows area is square
|
// the arrows width is the same as the inner height so that the arrows area is square
|
||||||
@@ -362,15 +404,15 @@ public class FlatSpinnerUI
|
|||||||
|
|
||||||
if( nextButton == null && previousButton == null ) {
|
if( nextButton == null && previousButton == null ) {
|
||||||
if( editor != null )
|
if( editor != null )
|
||||||
editor.setBounds( FlatUIUtils.subtractInsets( r, padding ) );
|
editor.setBounds( r );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle editorRect = new Rectangle( r );
|
Rectangle editorRect = new Rectangle( r );
|
||||||
Rectangle buttonsRect = new Rectangle( r );
|
Rectangle buttonsRect = new Rectangle( r );
|
||||||
|
|
||||||
// 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() ) {
|
||||||
@@ -382,7 +424,7 @@ public class FlatSpinnerUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
if( editor != null )
|
if( editor != null )
|
||||||
editor.setBounds( FlatUIUtils.subtractInsets( editorRect, padding ) );
|
editor.setBounds( editorRect );
|
||||||
|
|
||||||
int nextHeight = (buttonsRect.height / 2) + (buttonsRect.height % 2); // round up
|
int nextHeight = (buttonsRect.height / 2) + (buttonsRect.height % 2); // round up
|
||||||
if( nextButton != null )
|
if( nextButton != null )
|
||||||
@@ -399,6 +441,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
|
||||||
@@ -411,6 +454,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import java.awt.Color;
|
|||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.Cursor;
|
import java.awt.Cursor;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
@@ -47,12 +46,16 @@ 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 chevron (default) or triangle
|
* @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.style String grip (default) or plain
|
||||||
* @uiDefault SplitPaneDivider.gripColor Color
|
* @uiDefault SplitPaneDivider.gripColor Color
|
||||||
* @uiDefault SplitPaneDivider.gripDotCount int
|
* @uiDefault SplitPaneDivider.gripDotCount int
|
||||||
@@ -65,9 +68,9 @@ public class FlatSplitPaneUI
|
|||||||
extends BasicSplitPaneUI
|
extends BasicSplitPaneUI
|
||||||
{
|
{
|
||||||
protected String arrowType;
|
protected String arrowType;
|
||||||
private Boolean continuousLayout;
|
|
||||||
protected Color oneTouchArrowColor;
|
protected Color oneTouchArrowColor;
|
||||||
protected Color oneTouchHoverArrowColor;
|
protected Color oneTouchHoverArrowColor;
|
||||||
|
protected Color oneTouchPressedArrowColor;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatSplitPaneUI();
|
return new FlatSplitPaneUI();
|
||||||
@@ -81,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
|
||||||
@@ -148,10 +154,12 @@ public class FlatSplitPaneUI
|
|||||||
if( "plain".equals( style ) )
|
if( "plain".equals( style ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
FlatUIUtils.setRenderingHints( (Graphics2D) g );
|
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
||||||
|
|
||||||
g.setColor( gripColor );
|
g.setColor( gripColor );
|
||||||
paintGrip( g, 0, 0, getWidth(), getHeight() );
|
paintGrip( g, 0, 0, getWidth(), getHeight() );
|
||||||
|
|
||||||
|
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void paintGrip( Graphics g, int x, int y, int width, int height ) {
|
protected void paintGrip( Graphics g, int x, int y, int width, int height ) {
|
||||||
@@ -184,7 +192,8 @@ public class FlatSplitPaneUI
|
|||||||
protected final boolean left;
|
protected final boolean left;
|
||||||
|
|
||||||
protected 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 );
|
ToolTipManager.sharedInstance().registerComponent( this );
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ import java.util.function.BiConsumer;
|
|||||||
import java.util.function.IntConsumer;
|
import java.util.function.IntConsumer;
|
||||||
import javax.accessibility.Accessible;
|
import javax.accessibility.Accessible;
|
||||||
import javax.accessibility.AccessibleContext;
|
import javax.accessibility.AccessibleContext;
|
||||||
|
import javax.swing.Action;
|
||||||
|
import javax.swing.ActionMap;
|
||||||
import javax.swing.ButtonModel;
|
import javax.swing.ButtonModel;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
@@ -221,11 +223,15 @@ public class FlatTabbedPaneUI
|
|||||||
private Container leadingComponent;
|
private Container leadingComponent;
|
||||||
private Container trailingComponent;
|
private Container trailingComponent;
|
||||||
|
|
||||||
|
private Dimension scrollBackwardButtonPrefSize;
|
||||||
|
|
||||||
private Handler handler;
|
private Handler handler;
|
||||||
private boolean blockRollover;
|
private boolean blockRollover;
|
||||||
private boolean rolloverTabClose;
|
private boolean rolloverTabClose;
|
||||||
private boolean pressedTabClose;
|
private boolean pressedTabClose;
|
||||||
|
|
||||||
|
private Object[] oldRenderingHints;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatTabbedPaneUI();
|
return new FlatTabbedPaneUI();
|
||||||
}
|
}
|
||||||
@@ -321,7 +327,7 @@ public class FlatTabbedPaneUI
|
|||||||
// the default also includes Ctrl+TAB/Ctrl+Shift+TAB, which we need to switch tabs
|
// the default also includes Ctrl+TAB/Ctrl+Shift+TAB, which we need to switch tabs
|
||||||
if( focusForwardTraversalKeys == null ) {
|
if( focusForwardTraversalKeys == null ) {
|
||||||
focusForwardTraversalKeys = Collections.singleton( KeyStroke.getKeyStroke( KeyEvent.VK_TAB, 0 ) );
|
focusForwardTraversalKeys = Collections.singleton( KeyStroke.getKeyStroke( KeyEvent.VK_TAB, 0 ) );
|
||||||
focusBackwardTraversalKeys = Collections.singleton( KeyStroke.getKeyStroke( KeyEvent.VK_TAB, InputEvent.SHIFT_MASK ) );
|
focusBackwardTraversalKeys = Collections.singleton( KeyStroke.getKeyStroke( KeyEvent.VK_TAB, InputEvent.SHIFT_DOWN_MASK ) );
|
||||||
}
|
}
|
||||||
// Ideally we should use `LookAndFeel.installProperty( tabPane, "focusTraversalKeysForward", keys )` here
|
// Ideally we should use `LookAndFeel.installProperty( tabPane, "focusTraversalKeysForward", keys )` here
|
||||||
// instead of `tabPane.setFocusTraversalKeys()`, but WindowsTabbedPaneUI also uses later method
|
// instead of `tabPane.setFocusTraversalKeys()`, but WindowsTabbedPaneUI also uses later method
|
||||||
@@ -362,11 +368,6 @@ public class FlatTabbedPaneUI
|
|||||||
protected void installComponents() {
|
protected void installComponents() {
|
||||||
super.installComponents();
|
super.installComponents();
|
||||||
|
|
||||||
// create tab close button
|
|
||||||
tabCloseButton = new TabCloseButton();
|
|
||||||
tabCloseButton.setVisible( false );
|
|
||||||
tabPane.add( tabCloseButton );
|
|
||||||
|
|
||||||
// find scrollable tab viewport
|
// find scrollable tab viewport
|
||||||
tabViewport = null;
|
tabViewport = null;
|
||||||
if( isScrollTabLayout() ) {
|
if( isScrollTabLayout() ) {
|
||||||
@@ -393,11 +394,7 @@ public class FlatTabbedPaneUI
|
|||||||
|
|
||||||
super.uninstallComponents();
|
super.uninstallComponents();
|
||||||
|
|
||||||
if( tabCloseButton != null ) {
|
tabCloseButton = null;
|
||||||
tabPane.remove( tabCloseButton );
|
|
||||||
tabCloseButton = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
tabViewport = null;
|
tabViewport = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -495,6 +492,20 @@ public class FlatTabbedPaneUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installKeyboardActions() {
|
||||||
|
super.installKeyboardActions();
|
||||||
|
|
||||||
|
// get shared action map, used for all tabbed panes
|
||||||
|
ActionMap map = SwingUtilities.getUIActionMap( tabPane );
|
||||||
|
if( map != null ) {
|
||||||
|
// this is required for the case that those actions are used from outside
|
||||||
|
// (e.g. wheel tab scroller in NetBeans)
|
||||||
|
RunWithOriginalLayoutManagerDelegateAction.install( map, "scrollTabsForwardAction" );
|
||||||
|
RunWithOriginalLayoutManagerDelegateAction.install( map, "scrollTabsBackwardAction" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Handler getHandler() {
|
private Handler getHandler() {
|
||||||
if( handler == null )
|
if( handler == null )
|
||||||
handler = new Handler();
|
handler = new Handler();
|
||||||
@@ -693,6 +704,26 @@ public class FlatTabbedPaneUI
|
|||||||
return Math.max( tabHeight, scale( clientPropertyInt( tabPane, TABBED_PANE_TAB_HEIGHT, this.tabHeight ) ) );
|
return Math.max( tabHeight, scale( clientPropertyInt( tabPane, TABBED_PANE_TAB_HEIGHT, this.tabHeight ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int calculateMaxTabWidth( int tabPlacement ) {
|
||||||
|
return hideTabArea() ? 0 : super.calculateMaxTabWidth( tabPlacement );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int calculateMaxTabHeight( int tabPlacement ) {
|
||||||
|
return hideTabArea() ? 0 : super.calculateMaxTabHeight( tabPlacement );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int calculateTabAreaWidth( int tabPlacement, int vertRunCount, int maxTabWidth ) {
|
||||||
|
return hideTabArea() ? 0 : super.calculateTabAreaWidth( tabPlacement, vertRunCount, maxTabWidth );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int calculateTabAreaHeight( int tabPlacement, int horizRunCount, int maxTabHeight ) {
|
||||||
|
return hideTabArea() ? 0 : super.calculateTabAreaHeight( tabPlacement, horizRunCount, maxTabHeight );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Insets getTabInsets( int tabPlacement, int tabIndex ) {
|
protected Insets getTabInsets( int tabPlacement, int tabIndex ) {
|
||||||
Object value = getTabClientProperty( tabIndex, TABBED_PANE_TAB_INSETS );
|
Object value = getTabClientProperty( tabIndex, TABBED_PANE_TAB_INSETS );
|
||||||
@@ -707,6 +738,13 @@ public class FlatTabbedPaneUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Insets getRealTabAreaInsets( int tabPlacement ) {
|
protected Insets getRealTabAreaInsets( int tabPlacement ) {
|
||||||
|
// this is to avoid potential NPE in ensureSelectedTabIsVisible()
|
||||||
|
// (see https://github.com/JFormDesigner/FlatLaf/issues/299)
|
||||||
|
// but now should actually never occur because added more checks to
|
||||||
|
// ensureSelectedTabIsVisibleLater() and ensureSelectedTabIsVisible()
|
||||||
|
if( tabAreaInsets == null )
|
||||||
|
tabAreaInsets = new Insets( 0, 0, 0, 0 );
|
||||||
|
|
||||||
Insets currentTabAreaInsets = super.getTabAreaInsets( tabPlacement );
|
Insets currentTabAreaInsets = super.getTabAreaInsets( tabPlacement );
|
||||||
Insets insets = (Insets) currentTabAreaInsets.clone();
|
Insets insets = (Insets) currentTabAreaInsets.clone();
|
||||||
|
|
||||||
@@ -752,7 +790,7 @@ public class FlatTabbedPaneUI
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected Insets getContentBorderInsets( int tabPlacement ) {
|
protected Insets getContentBorderInsets( int tabPlacement ) {
|
||||||
if( contentSeparatorHeight == 0 || !clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_CONTENT_SEPARATOR, true ) )
|
if( hideTabArea() || contentSeparatorHeight == 0 || !clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_CONTENT_SEPARATOR, true ) )
|
||||||
return new Insets( 0, 0, 0, 0 );
|
return new Insets( 0, 0, 0, 0 );
|
||||||
|
|
||||||
boolean hasFullBorder = clientPropertyBoolean( tabPane, TABBED_PANE_HAS_FULL_BORDER, this.hasFullBorder );
|
boolean hasFullBorder = clientPropertyBoolean( tabPane, TABBED_PANE_HAS_FULL_BORDER, this.hasFullBorder );
|
||||||
@@ -780,13 +818,19 @@ public class FlatTabbedPaneUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update( Graphics g, JComponent c ) {
|
public void update( Graphics g, JComponent c ) {
|
||||||
FlatUIUtils.setRenderingHints( (Graphics2D) g );
|
oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
||||||
|
|
||||||
super.update( g, c );
|
super.update( g, c );
|
||||||
|
|
||||||
|
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||||
|
oldRenderingHints = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paint( Graphics g, JComponent c ) {
|
public void paint( Graphics g, JComponent c ) {
|
||||||
|
if( hideTabArea() )
|
||||||
|
return;
|
||||||
|
|
||||||
ensureCurrentLayout();
|
ensureCurrentLayout();
|
||||||
|
|
||||||
int tabPlacement = tabPane.getTabPlacement();
|
int tabPlacement = tabPane.getTabPlacement();
|
||||||
@@ -798,6 +842,17 @@ public class FlatTabbedPaneUI
|
|||||||
paintTabArea( g, tabPlacement, selectedIndex );
|
paintTabArea( g, tabPlacement, selectedIndex );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintTabArea( Graphics g, int tabPlacement, int selectedIndex ) {
|
||||||
|
// need to set rendering hints here too because this method is also invoked
|
||||||
|
// from BasicTabbedPaneUI.ScrollableTabPanel.paintComponent()
|
||||||
|
Object[] oldHints = FlatUIUtils.setRenderingHints( g );
|
||||||
|
|
||||||
|
super.paintTabArea( g, tabPlacement, selectedIndex );
|
||||||
|
|
||||||
|
FlatUIUtils.resetRenderingHints( g, oldHints );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintTab( Graphics g, int tabPlacement, Rectangle[] rects,
|
protected void paintTab( Graphics g, int tabPlacement, Rectangle[] rects,
|
||||||
int tabIndex, Rectangle iconRect, Rectangle textRect )
|
int tabIndex, Rectangle iconRect, Rectangle textRect )
|
||||||
@@ -860,27 +915,29 @@ public class FlatTabbedPaneUI
|
|||||||
{
|
{
|
||||||
g.setFont( font );
|
g.setFont( font );
|
||||||
|
|
||||||
// html
|
FlatUIUtils.runWithoutRenderingHints( g, oldRenderingHints, () -> {
|
||||||
View view = getTextViewForTab( tabIndex );
|
// html
|
||||||
if( view != null ) {
|
View view = getTextViewForTab( tabIndex );
|
||||||
view.paint( g, textRect );
|
if( view != null ) {
|
||||||
return;
|
view.paint( g, textRect );
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// plain text
|
// plain text
|
||||||
Color color;
|
Color color;
|
||||||
if( tabPane.isEnabled() && tabPane.isEnabledAt( tabIndex ) ) {
|
if( tabPane.isEnabled() && tabPane.isEnabledAt( tabIndex ) ) {
|
||||||
color = tabPane.getForegroundAt( tabIndex );
|
color = tabPane.getForegroundAt( tabIndex );
|
||||||
if( isSelected && (color instanceof UIResource) && selectedForeground != null )
|
if( isSelected && selectedForeground != null && color == tabPane.getForeground() )
|
||||||
color = selectedForeground;
|
color = selectedForeground;
|
||||||
} else
|
} else
|
||||||
color = disabledForeground;
|
color = disabledForeground;
|
||||||
|
|
||||||
int mnemIndex = FlatLaf.isShowMnemonics() ? tabPane.getDisplayedMnemonicIndexAt( tabIndex ) : -1;
|
int mnemIndex = FlatLaf.isShowMnemonics() ? tabPane.getDisplayedMnemonicIndexAt( tabIndex ) : -1;
|
||||||
|
|
||||||
g.setColor( color );
|
g.setColor( color );
|
||||||
FlatUIUtils.drawStringUnderlineCharAt( tabPane, g, title, mnemIndex,
|
FlatUIUtils.drawStringUnderlineCharAt( tabPane, g, title, mnemIndex,
|
||||||
textRect.x, textRect.y + metrics.getAscent() );
|
textRect.x, textRect.y + metrics.getAscent() );
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -911,6 +968,12 @@ public class FlatTabbedPaneUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void paintTabCloseButton( Graphics g, int tabIndex, int x, int y, int w, int h ) {
|
protected void paintTabCloseButton( Graphics g, int tabIndex, int x, int y, int w, int h ) {
|
||||||
|
// create tab close button
|
||||||
|
if( tabCloseButton == null ) {
|
||||||
|
tabCloseButton = new TabCloseButton();
|
||||||
|
tabCloseButton.setVisible( false );
|
||||||
|
}
|
||||||
|
|
||||||
// update state of tab close button
|
// update state of tab close button
|
||||||
boolean rollover = (tabIndex == getRolloverTab());
|
boolean rollover = (tabIndex == getRolloverTab());
|
||||||
ButtonModel bm = tabCloseButton.getModel();
|
ButtonModel bm = tabCloseButton.getModel();
|
||||||
@@ -1226,6 +1289,13 @@ public class FlatTabbedPaneUI
|
|||||||
return UIManager.getBoolean( "ScrollPane.smoothScrolling" );
|
return UIManager.getBoolean( "ScrollPane.smoothScrolling" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean hideTabArea() {
|
||||||
|
return tabPane.getTabCount() == 1 &&
|
||||||
|
leadingComponent == null &&
|
||||||
|
trailingComponent == null &&
|
||||||
|
clientPropertyBoolean( tabPane, TABBED_PANE_HIDE_TAB_AREA_WITH_ONE_TAB, false );
|
||||||
|
}
|
||||||
|
|
||||||
protected int getTabsPopupPolicy() {
|
protected int getTabsPopupPolicy() {
|
||||||
Object value = tabPane.getClientProperty( TABBED_PANE_TABS_POPUP_POLICY );
|
Object value = tabPane.getClientProperty( TABBED_PANE_TABS_POPUP_POLICY );
|
||||||
|
|
||||||
@@ -1350,13 +1420,18 @@ public class FlatTabbedPaneUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void ensureSelectedTabIsVisibleLater() {
|
protected void ensureSelectedTabIsVisibleLater() {
|
||||||
|
// do nothing if not yet displayable or if not invoked from dispatch thread,
|
||||||
|
// which may be the case when creating/modifying in another thread
|
||||||
|
if( !tabPane.isDisplayable() || !EventQueue.isDispatchThread() )
|
||||||
|
return;
|
||||||
|
|
||||||
EventQueue.invokeLater( () -> {
|
EventQueue.invokeLater( () -> {
|
||||||
ensureSelectedTabIsVisible();
|
ensureSelectedTabIsVisible();
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void ensureSelectedTabIsVisible() {
|
protected void ensureSelectedTabIsVisible() {
|
||||||
if( tabPane == null || tabViewport == null )
|
if( tabPane == null || tabViewport == null || !tabPane.isDisplayable() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ensureCurrentLayout();
|
ensureCurrentLayout();
|
||||||
@@ -1523,7 +1598,7 @@ public class FlatTabbedPaneUI
|
|||||||
FlatUIUtils.paintComponentBackground( g, left, top,
|
FlatUIUtils.paintComponentBackground( g, left, top,
|
||||||
getWidth() - left - right,
|
getWidth() - left - right,
|
||||||
getHeight() - top - bottom,
|
getHeight() - top - bottom,
|
||||||
0, scale( buttonArc ) );
|
0, scale( (float) buttonArc ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1836,33 +1911,78 @@ public class FlatTabbedPaneUI
|
|||||||
lastMouseY = e.getY();
|
lastMouseY = e.getY();
|
||||||
|
|
||||||
double preciseWheelRotation = e.getPreciseWheelRotation();
|
double preciseWheelRotation = e.getPreciseWheelRotation();
|
||||||
|
boolean isPreciseWheel = (preciseWheelRotation != 0 && preciseWheelRotation != e.getWheelRotation());
|
||||||
int amount = (int) (maxTabHeight * preciseWheelRotation);
|
int amount = (int) (maxTabHeight * preciseWheelRotation);
|
||||||
|
|
||||||
|
// scroll at least one pixel to avoid "hanging"
|
||||||
|
if( amount == 0 ) {
|
||||||
|
if( preciseWheelRotation > 0 )
|
||||||
|
amount = 1;
|
||||||
|
else if( preciseWheelRotation < 0 )
|
||||||
|
amount = -1;
|
||||||
|
}
|
||||||
|
|
||||||
// compute new view position
|
// compute new view position
|
||||||
Point viewPosition = (targetViewPosition != null)
|
Point viewPosition = (targetViewPosition != null)
|
||||||
? targetViewPosition
|
? targetViewPosition
|
||||||
: tabViewport.getViewPosition();
|
: tabViewport.getViewPosition();
|
||||||
Dimension viewSize = tabViewport.getViewSize();
|
Dimension viewSize = tabViewport.getViewSize();
|
||||||
|
boolean horizontal = isHorizontalTabPlacement();
|
||||||
int x = viewPosition.x;
|
int x = viewPosition.x;
|
||||||
int y = viewPosition.y;
|
int y = viewPosition.y;
|
||||||
int tabPlacement = tabPane.getTabPlacement();
|
if( horizontal )
|
||||||
if( tabPlacement == TOP || tabPlacement == BOTTOM ) {
|
|
||||||
x += isLeftToRight() ? amount : -amount;
|
x += isLeftToRight() ? amount : -amount;
|
||||||
x = Math.min( Math.max( x, 0 ), viewSize.width - tabViewport.getWidth() );
|
else
|
||||||
} else {
|
|
||||||
y += amount;
|
y += amount;
|
||||||
y = Math.min( Math.max( y, 0 ), viewSize.height - tabViewport.getHeight() );
|
|
||||||
|
// In case of having scroll buttons on both sides and hiding disabled buttons,
|
||||||
|
// the viewport is moved when the scroll backward button becomes visible
|
||||||
|
// or is hidden. For non-precise wheel scrolling (e.g. mouse wheel on Windows),
|
||||||
|
// this is no problem because the scroll amount is at least a tab-height.
|
||||||
|
// For precise wheel scrolling (e.g. touchpad on Mac), this is a problem
|
||||||
|
// because it is possible to scroll by a fraction of a tab-height.
|
||||||
|
if( isPreciseWheel &&
|
||||||
|
getScrollButtonsPlacement() == BOTH &&
|
||||||
|
getScrollButtonsPolicy() == AS_NEEDED_SINGLE &&
|
||||||
|
(isLeftToRight() || !horizontal) || // scroll buttons are hidden in right-to-left
|
||||||
|
scrollBackwardButtonPrefSize != null )
|
||||||
|
{
|
||||||
|
// special cases for scrolling with touchpad or high-resolution wheel:
|
||||||
|
// 1. if view is at 0/0 and scrolling right/down, then the scroll backward button
|
||||||
|
// becomes visible, which moves the viewport right/down by the width/height of
|
||||||
|
// the button --> add button width/height to new view position so that
|
||||||
|
// tabs seems to stay in place at screen
|
||||||
|
// 2. if scrolling left/up to the beginning, then the scroll backward button
|
||||||
|
// becomes hidden, which moves the viewport left/up by the width/height of
|
||||||
|
// the button --> set new view position to 0/0 so that
|
||||||
|
// tabs seems to stay in place at screen
|
||||||
|
if( horizontal ) {
|
||||||
|
//
|
||||||
|
if( viewPosition.x == 0 && x > 0 )
|
||||||
|
x += scrollBackwardButtonPrefSize.width;
|
||||||
|
else if( amount < 0 && x <= scrollBackwardButtonPrefSize.width )
|
||||||
|
x = 0;
|
||||||
|
} else {
|
||||||
|
if( viewPosition.y == 0 && y > 0 )
|
||||||
|
y += scrollBackwardButtonPrefSize.height;
|
||||||
|
else if( amount < 0 && y <= scrollBackwardButtonPrefSize.height )
|
||||||
|
y = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// limit new view position
|
||||||
|
if( horizontal )
|
||||||
|
x = Math.min( Math.max( x, 0 ), viewSize.width - tabViewport.getWidth() );
|
||||||
|
else
|
||||||
|
y = Math.min( Math.max( y, 0 ), viewSize.height - tabViewport.getHeight() );
|
||||||
|
|
||||||
// check whether view position has changed
|
// check whether view position has changed
|
||||||
Point newViewPosition = new Point( x, y );
|
Point newViewPosition = new Point( x, y );
|
||||||
if( newViewPosition.equals( viewPosition ) )
|
if( newViewPosition.equals( viewPosition ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// update view position
|
// update view position
|
||||||
if( preciseWheelRotation != 0 &&
|
if( isPreciseWheel ) {
|
||||||
preciseWheelRotation != e.getWheelRotation() )
|
|
||||||
{
|
|
||||||
// do not use animation for precise scrolling (e.g. with trackpad)
|
// do not use animation for precise scrolling (e.g. with trackpad)
|
||||||
|
|
||||||
// stop running animation (if any)
|
// stop running animation (if any)
|
||||||
@@ -2075,8 +2195,10 @@ public class FlatTabbedPaneUI
|
|||||||
public void mouseReleased( MouseEvent e ) {
|
public void mouseReleased( MouseEvent e ) {
|
||||||
if( isPressedTabClose() ) {
|
if( isPressedTabClose() ) {
|
||||||
updateRollover( e );
|
updateRollover( e );
|
||||||
if( pressedTabIndex >= 0 && pressedTabIndex == getRolloverTab() )
|
if( pressedTabIndex >= 0 && pressedTabIndex == getRolloverTab() ) {
|
||||||
|
restoreTabToolTip();
|
||||||
closeTab( pressedTabIndex );
|
closeTab( pressedTabIndex );
|
||||||
|
}
|
||||||
} else
|
} else
|
||||||
mouseDelegate.mouseReleased( e );
|
mouseDelegate.mouseReleased( e );
|
||||||
|
|
||||||
@@ -2154,7 +2276,8 @@ public class FlatTabbedPaneUI
|
|||||||
if( lastTipTabIndex < 0 )
|
if( lastTipTabIndex < 0 )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
tabPane.setToolTipTextAt( lastTipTabIndex, lastTip );
|
if( lastTipTabIndex < tabPane.getTabCount() )
|
||||||
|
tabPane.setToolTipTextAt( lastTipTabIndex, lastTip );
|
||||||
lastTip = null;
|
lastTip = null;
|
||||||
lastTipTabIndex = -1;
|
lastTipTabIndex = -1;
|
||||||
}
|
}
|
||||||
@@ -2193,6 +2316,7 @@ public class FlatTabbedPaneUI
|
|||||||
case TABBED_PANE_SHOW_TAB_SEPARATORS:
|
case TABBED_PANE_SHOW_TAB_SEPARATORS:
|
||||||
case TABBED_PANE_SHOW_CONTENT_SEPARATOR:
|
case TABBED_PANE_SHOW_CONTENT_SEPARATOR:
|
||||||
case TABBED_PANE_HAS_FULL_BORDER:
|
case TABBED_PANE_HAS_FULL_BORDER:
|
||||||
|
case TABBED_PANE_HIDE_TAB_AREA_WITH_ONE_TAB:
|
||||||
case TABBED_PANE_MINIMUM_TAB_WIDTH:
|
case TABBED_PANE_MINIMUM_TAB_WIDTH:
|
||||||
case TABBED_PANE_MAXIMUM_TAB_WIDTH:
|
case TABBED_PANE_MAXIMUM_TAB_WIDTH:
|
||||||
case TABBED_PANE_TAB_HEIGHT:
|
case TABBED_PANE_TAB_HEIGHT:
|
||||||
@@ -2858,6 +2982,55 @@ public class FlatTabbedPaneUI
|
|||||||
moreTabsButton.setVisible( moreTabsButtonVisible );
|
moreTabsButton.setVisible( moreTabsButtonVisible );
|
||||||
backwardButton.setVisible( backwardButtonVisible );
|
backwardButton.setVisible( backwardButtonVisible );
|
||||||
forwardButton.setVisible( forwardButtonVisible );
|
forwardButton.setVisible( forwardButtonVisible );
|
||||||
|
|
||||||
|
scrollBackwardButtonPrefSize = backwardButton.getPreferredSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class RunWithOriginalLayoutManagerDelegateAction -------------------
|
||||||
|
|
||||||
|
private static class RunWithOriginalLayoutManagerDelegateAction
|
||||||
|
implements Action
|
||||||
|
{
|
||||||
|
private final Action delegate;
|
||||||
|
|
||||||
|
static void install( ActionMap map, String key ) {
|
||||||
|
Action oldAction = map.get( key );
|
||||||
|
if( oldAction == null || oldAction instanceof RunWithOriginalLayoutManagerDelegateAction )
|
||||||
|
return; // not found or already installed
|
||||||
|
|
||||||
|
map.put( key, new RunWithOriginalLayoutManagerDelegateAction( oldAction ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private RunWithOriginalLayoutManagerDelegateAction( Action delegate ) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValue( String key ) {
|
||||||
|
return delegate.getValue( key );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return delegate.isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void putValue( String key, Object value ) {}
|
||||||
|
@Override public void setEnabled( boolean b ) {}
|
||||||
|
@Override public void addPropertyChangeListener( PropertyChangeListener listener ) {}
|
||||||
|
@Override public void removePropertyChangeListener( PropertyChangeListener listener ) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed( ActionEvent e ) {
|
||||||
|
JTabbedPane tabbedPane = (JTabbedPane) e.getSource();
|
||||||
|
ComponentUI ui = tabbedPane.getUI();
|
||||||
|
if( ui instanceof FlatTabbedPaneUI ) {
|
||||||
|
((FlatTabbedPaneUI)ui).runWithOriginalLayoutManager( () -> {
|
||||||
|
delegate.actionPerformed( e );
|
||||||
|
} );
|
||||||
|
} else
|
||||||
|
delegate.actionPerformed( e );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
TableCellRenderer defaultRenderer = header.getDefaultRenderer();
|
if( columnModel.getColumnCount() <= 0 )
|
||||||
boolean paintBorders = isSystemDefaultRenderer( defaultRenderer );
|
return;
|
||||||
if( !paintBorders && header.getColumnModel().getColumnCount() > 0 ) {
|
|
||||||
// check whether the renderer delegates to the system default renderer
|
|
||||||
Component rendererComponent = defaultRenderer.getTableCellRendererComponent(
|
|
||||||
header.getTable(), "", false, false, -1, 0 );
|
|
||||||
paintBorders = isSystemDefaultRenderer( rendererComponent );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( paintBorders )
|
// compute total width of all columns
|
||||||
paintColumnBorders( g, c );
|
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();
|
||||||
|
boolean paintBottomSeparator = isSystemDefaultRenderer( defaultRenderer );
|
||||||
|
if( !paintBottomSeparator && header.getTable() != null ) {
|
||||||
|
// check whether the renderer delegates to the system default renderer
|
||||||
|
Component rendererComponent = defaultRenderer.getTableCellRendererComponent(
|
||||||
|
header.getTable(), "", false, false, -1, 0 );
|
||||||
|
paintBottomSeparator = isSystemDefaultRenderer( rendererComponent );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( paintBottomSeparator ) {
|
||||||
|
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,111 @@ 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)) &&
|
||||||
|
wasInvokedFromMethod( "paintDraggedArea" ) )
|
||||||
|
{
|
||||||
|
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 wasInvokedFromMethod( String methodName ) {
|
||||||
|
return StackUtils.wasInvokedFrom( BasicTableUI.class.getName(), methodName, 8 );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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 );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import java.awt.FontMetrics;
|
|||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
|
import java.awt.Rectangle;
|
||||||
import java.awt.event.FocusListener;
|
import java.awt.event.FocusListener;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import javax.swing.JComboBox;
|
import javax.swing.JComboBox;
|
||||||
@@ -39,6 +40,8 @@ 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;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JTextField}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JTextField}.
|
||||||
@@ -63,6 +66,7 @@ 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
|
* @uiDefault TextComponent.selectAllOnMouseClick boolean
|
||||||
*
|
*
|
||||||
@@ -74,6 +78,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;
|
||||||
|
|
||||||
@@ -89,6 +94,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 );
|
||||||
|
|
||||||
@@ -100,6 +106,7 @@ public class FlatTextFieldUI
|
|||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
placeholderForeground = null;
|
placeholderForeground = null;
|
||||||
|
focusedBackground = null;
|
||||||
|
|
||||||
MigLayoutVisualPadding.uninstall( getComponent() );
|
MigLayoutVisualPadding.uninstall( getComponent() );
|
||||||
}
|
}
|
||||||
@@ -108,7 +115,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 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,6 +144,7 @@ public class FlatTextFieldUI
|
|||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
case FlatClientProperties.PLACEHOLDER_TEXT:
|
case FlatClientProperties.PLACEHOLDER_TEXT:
|
||||||
case FlatClientProperties.COMPONENT_ROUND_RECT:
|
case FlatClientProperties.COMPONENT_ROUND_RECT:
|
||||||
|
case FlatClientProperties.TEXT_FIELD_PADDING:
|
||||||
c.repaint();
|
c.repaint();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -147,8 +156,8 @@ 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 );
|
||||||
|
|
||||||
super.paintSafely( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ) );
|
super.paintSafely( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ) );
|
||||||
}
|
}
|
||||||
@@ -158,7 +167,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
|
||||||
@@ -179,19 +188,34 @@ 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 void paintPlaceholder( Graphics g, JTextComponent c, Color placeholderForeground ) {
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void paintPlaceholder( Graphics g ) {
|
||||||
|
JTextComponent c = getComponent();
|
||||||
|
|
||||||
// check whether text component is empty
|
// check whether text component is empty
|
||||||
if( c.getDocument().getLength() > 0 )
|
if( c.getDocument().getLength() > 0 )
|
||||||
return;
|
return;
|
||||||
@@ -206,14 +230,14 @@ public class FlatTextFieldUI
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// compute placeholder location
|
// compute placeholder location
|
||||||
Insets insets = c.getInsets();
|
Rectangle r = getVisibleEditorRect();
|
||||||
FontMetrics fm = c.getFontMetrics( c.getFont() );
|
FontMetrics fm = c.getFontMetrics( c.getFont() );
|
||||||
int x = insets.left;
|
int y = r.y + fm.getAscent() + ((r.height - fm.getHeight()) / 2);
|
||||||
int y = insets.top + fm.getAscent() + ((c.getHeight() - insets.top - insets.bottom - fm.getHeight()) / 2);
|
|
||||||
|
|
||||||
// paint placeholder
|
// paint placeholder
|
||||||
g.setColor( placeholderForeground );
|
g.setColor( placeholderForeground );
|
||||||
FlatUIUtils.drawString( c, g, (String) placeholder, x, y );
|
String clippedPlaceholder = JavaCompatibility.getClippedString( c, fm, (String) placeholder, r.width );
|
||||||
|
FlatUIUtils.drawString( c, g, clippedPlaceholder, r.x, y );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -243,4 +267,36 @@ public class FlatTextFieldUI
|
|||||||
size.width = Math.max( size.width, scale( minimumWidth ) + Math.round( focusWidth * 2 ) );
|
size.width = Math.max( size.width, scale( minimumWidth ) + Math.round( focusWidth * 2 ) );
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Rectangle getVisibleEditorRect() {
|
||||||
|
Rectangle r = super.getVisibleEditorRect();
|
||||||
|
if( r != null ) {
|
||||||
|
// remove padding
|
||||||
|
Insets padding = getPadding();
|
||||||
|
if( padding != null ) {
|
||||||
|
r = FlatUIUtils.subtractInsets( r, padding );
|
||||||
|
r.width = Math.max( r.width, 0 );
|
||||||
|
r.height = Math.max( r.height, 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.4
|
||||||
|
*/
|
||||||
|
protected Insets getPadding() {
|
||||||
|
Object padding = getComponent().getClientProperty( FlatClientProperties.TEXT_FIELD_PADDING );
|
||||||
|
return (padding instanceof Insets) ? UIScale.scale( (Insets) padding ) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.4
|
||||||
|
*/
|
||||||
|
protected void scrollCaretToVisible() {
|
||||||
|
Caret caret = getComponent().getCaret();
|
||||||
|
if( caret instanceof FlatCaret )
|
||||||
|
((FlatCaret)caret).scrollCaretToVisible();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -77,12 +79,16 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault TitlePane.inactiveForeground Color
|
* @uiDefault TitlePane.inactiveForeground Color
|
||||||
* @uiDefault TitlePane.embeddedForeground Color
|
* @uiDefault TitlePane.embeddedForeground Color
|
||||||
* @uiDefault TitlePane.borderColor Color optional
|
* @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
|
||||||
@@ -100,9 +106,11 @@ public class FlatTitlePane
|
|||||||
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 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;
|
||||||
|
|
||||||
@@ -147,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 );
|
||||||
@@ -159,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 && menuBar.isVisible() && isMenuBarEmbedded())
|
return hasVisibleEmbeddedMenuBar( menuBar ) ? menuBar.getPreferredSize() : new Dimension();
|
||||||
? FlatUIUtils.addInsets( menuBar.getPreferredSize(), UIScale.scale( menuBarMargins ) )
|
|
||||||
: new Dimension();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
leftPanel.add( menuBarPlaceholder );
|
leftPanel.add( menuBarPlaceholder );
|
||||||
@@ -184,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() );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
@@ -240,10 +266,17 @@ public class FlatTitlePane
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void activeChanged( boolean active ) {
|
protected void activeChanged( boolean active ) {
|
||||||
boolean hasEmbeddedMenuBar = rootPane.getJMenuBar() != null && rootPane.getJMenuBar().isVisible() && 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 );
|
||||||
@@ -252,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 );
|
||||||
@@ -320,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 ) {
|
||||||
@@ -337,7 +368,7 @@ public class FlatTitlePane
|
|||||||
// show/hide icon
|
// show/hide icon
|
||||||
iconLabel.setVisible( hasIcon );
|
iconLabel.setVisible( hasIcon );
|
||||||
|
|
||||||
updateJBRHitTestSpotsAndTitleBarHeightLater();
|
updateNativeTitleBarHeightAndHitTestSpotsLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -355,7 +386,7 @@ public class FlatTitlePane
|
|||||||
installWindowListeners();
|
installWindowListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateJBRHitTestSpotsAndTitleBarHeightLater();
|
updateNativeTitleBarHeightAndHitTestSpotsLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -394,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() {
|
||||||
@@ -412,13 +455,42 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Insets getMenuBarMargins() {
|
protected Component findHorizontalGlue( JMenuBar menuBar ) {
|
||||||
return getComponentOrientation().isLeftToRight()
|
if( menuBar == null )
|
||||||
? menuBarMargins
|
return null;
|
||||||
: new Insets( menuBarMargins.top, menuBarMargins.right, menuBarMargins.bottom, menuBarMargins.left );
|
|
||||||
|
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() {
|
||||||
@@ -435,7 +507,8 @@ public class FlatTitlePane
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void menuBarLayouted() {
|
protected void menuBarLayouted() {
|
||||||
updateJBRHitTestSpotsAndTitleBarHeightLater();
|
updateNativeTitleBarHeightAndHitTestSpotsLater();
|
||||||
|
revalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*debug
|
/*debug
|
||||||
@@ -448,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() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,10 +559,12 @@ debug*/
|
|||||||
* Iconifies the window.
|
* Iconifies the window.
|
||||||
*/
|
*/
|
||||||
protected void iconify() {
|
protected void iconify() {
|
||||||
if( window instanceof Frame ) {
|
if( !(window instanceof Frame) )
|
||||||
Frame frame = (Frame) window;
|
return;
|
||||||
|
|
||||||
|
Frame frame = (Frame) window;
|
||||||
|
if( !FlatNativeWindowBorder.showWindow( window, FlatNativeWindowBorder.Provider.SW_MINIMIZE ) )
|
||||||
frame.setExtendedState( frame.getExtendedState() | Frame.ICONIFIED );
|
frame.setExtendedState( frame.getExtendedState() | Frame.ICONIFIED );
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -496,16 +582,17 @@ debug*/
|
|||||||
rootPane.putClientProperty( "_flatlaf.maximizedBoundsUpToDate", true );
|
rootPane.putClientProperty( "_flatlaf.maximizedBoundsUpToDate", true );
|
||||||
|
|
||||||
// maximize window
|
// maximize window
|
||||||
frame.setExtendedState( frame.getExtendedState() | Frame.MAXIMIZED_BOTH );
|
if( !FlatNativeWindowBorder.showWindow( frame, FlatNativeWindowBorder.Provider.SW_MAXIMIZE ) )
|
||||||
|
frame.setExtendedState( frame.getExtendedState() | Frame.MAXIMIZED_BOTH );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void updateMaximizedBounds() {
|
protected void updateMaximizedBounds() {
|
||||||
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" ) )) )
|
||||||
{
|
{
|
||||||
@@ -584,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) )
|
||||||
Frame frame = (Frame) window;
|
return;
|
||||||
|
|
||||||
|
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)
|
||||||
@@ -601,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
|
||||||
@@ -676,8 +834,8 @@ debug*/
|
|||||||
} else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) )
|
} else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) )
|
||||||
insets.bottom += UIScale.scale( 1 );
|
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;
|
||||||
}
|
}
|
||||||
@@ -695,13 +853,57 @@ debug*/
|
|||||||
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight );
|
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 && menuBar.isVisible() && 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 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -730,7 +932,7 @@ debug*/
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "componentOrientation":
|
case "componentOrientation":
|
||||||
updateJBRHitTestSpotsAndTitleBarHeightLater();
|
updateNativeTitleBarHeightAndHitTestSpotsLater();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -740,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();
|
||||||
}
|
}
|
||||||
@@ -751,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();
|
||||||
}
|
}
|
||||||
@@ -762,7 +964,7 @@ debug*/
|
|||||||
@Override
|
@Override
|
||||||
public void windowStateChanged( WindowEvent e ) {
|
public void windowStateChanged( WindowEvent e ) {
|
||||||
frameStateChanged();
|
frameStateChanged();
|
||||||
updateJBRHitTestSpotsAndTitleBarHeight();
|
updateNativeTitleBarHeightAndHitTestSpots();
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- interface MouseListener ----
|
//---- interface MouseListener ----
|
||||||
@@ -775,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() )
|
||||||
{
|
{
|
||||||
@@ -808,8 +1010,8 @@ debug*/
|
|||||||
if( window == null )
|
if( window == null )
|
||||||
return; // should newer occur
|
return; // should newer occur
|
||||||
|
|
||||||
if( hasJBRCustomDecoration() )
|
if( hasNativeCustomDecoration() )
|
||||||
return; // do nothing if running in JBR
|
return; // do nothing if having native window border
|
||||||
|
|
||||||
// restore window if it is maximized
|
// restore window if it is maximized
|
||||||
if( window instanceof Frame ) {
|
if( window instanceof Frame ) {
|
||||||
@@ -852,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 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,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 );
|
||||||
|
|||||||
@@ -106,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 ) {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import java.awt.event.ContainerEvent;
|
|||||||
import java.awt.event.ContainerListener;
|
import java.awt.event.ContainerListener;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.UIManager;
|
||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicToolBarUI;
|
import javax.swing.plaf.basic.BasicToolBarUI;
|
||||||
@@ -41,15 +42,47 @@ import javax.swing.plaf.basic.BasicToolBarUI;
|
|||||||
* @uiDefault ToolBar.floatingForeground Color
|
* @uiDefault ToolBar.floatingForeground Color
|
||||||
* @uiDefault ToolBar.isRollover boolean
|
* @uiDefault ToolBar.isRollover boolean
|
||||||
*
|
*
|
||||||
|
* <!-- FlatToolBarUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault ToolBar.focusableButtons boolean
|
||||||
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatToolBarUI
|
public class FlatToolBarUI
|
||||||
extends BasicToolBarUI
|
extends BasicToolBarUI
|
||||||
{
|
{
|
||||||
|
/** @since 1.4 */
|
||||||
|
protected boolean focusableButtons;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatToolBarUI();
|
return new FlatToolBarUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
// disable focusable state of buttons (when switching from another Laf)
|
||||||
|
if( !focusableButtons )
|
||||||
|
setButtonsFocusable( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uninstallUI( JComponent c ) {
|
||||||
|
super.uninstallUI( c );
|
||||||
|
|
||||||
|
// re-enable focusable state of buttons (when switching to another Laf)
|
||||||
|
if( !focusableButtons )
|
||||||
|
setButtonsFocusable( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installDefaults() {
|
||||||
|
super.installDefaults();
|
||||||
|
|
||||||
|
focusableButtons = UIManager.getBoolean( "ToolBar.focusableButtons" );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ContainerListener createToolBarContListener() {
|
protected ContainerListener createToolBarContListener() {
|
||||||
return new ToolBarContListener() {
|
return new ToolBarContListener() {
|
||||||
@@ -57,22 +90,36 @@ public class FlatToolBarUI
|
|||||||
public void componentAdded( ContainerEvent e ) {
|
public void componentAdded( ContainerEvent e ) {
|
||||||
super.componentAdded( e );
|
super.componentAdded( e );
|
||||||
|
|
||||||
Component c = e.getChild();
|
if( !focusableButtons ) {
|
||||||
if( c instanceof AbstractButton )
|
Component c = e.getChild();
|
||||||
c.setFocusable( false );
|
if( c instanceof AbstractButton )
|
||||||
|
c.setFocusable( false );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void componentRemoved( ContainerEvent e ) {
|
public void componentRemoved( ContainerEvent e ) {
|
||||||
super.componentRemoved( e );
|
super.componentRemoved( e );
|
||||||
|
|
||||||
Component c = e.getChild();
|
if( !focusableButtons ) {
|
||||||
if( c instanceof AbstractButton )
|
Component c = e.getChild();
|
||||||
c.setFocusable( true );
|
if( c instanceof AbstractButton )
|
||||||
|
c.setFocusable( true );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.4
|
||||||
|
*/
|
||||||
|
protected void setButtonsFocusable( boolean focusable ) {
|
||||||
|
for( Component c : toolBar.getComponents() ) {
|
||||||
|
if( c instanceof AbstractButton )
|
||||||
|
c.setFocusable( focusable );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// disable rollover border
|
// disable rollover border
|
||||||
@Override protected void setBorderToRollover( Component c ) {}
|
@Override protected void setBorderToRollover( Component c ) {}
|
||||||
@Override protected void setBorderToNonRollover( Component c ) {}
|
@Override protected void setBorderToNonRollover( Component c ) {}
|
||||||
|
|||||||
@@ -71,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 );
|
||||||
}
|
}
|
||||||
@@ -116,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,32 +198,38 @@ 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":
|
||||||
JTree.DropLocation oldValue = (JTree.DropLocation) e.getOldValue();
|
if( isWideSelection() ) {
|
||||||
repaintWideDropLocation( oldValue );
|
JTree.DropLocation oldValue = (JTree.DropLocation) e.getOldValue();
|
||||||
repaintWideDropLocation( tree.getDropLocation() );
|
repaintWideDropLocation( oldValue );
|
||||||
|
repaintWideDropLocation( tree.getDropLocation() );
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void repaintWideDropLocation(JTree.DropLocation loc) {
|
|
||||||
if( loc == null || isDropLine( loc ) )
|
|
||||||
return;
|
|
||||||
|
|
||||||
Rectangle r = tree.getPathBounds( loc.getPath() );
|
|
||||||
if( r != null )
|
|
||||||
tree.repaint( 0, r.y, tree.getWidth(), r.height );
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void repaintWideDropLocation(JTree.DropLocation loc) {
|
||||||
|
if( loc == null || isDropLine( loc ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Rectangle r = tree.getPathBounds( loc.getPath() );
|
||||||
|
if( r != null )
|
||||||
|
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
|
||||||
* inactive selection background/foreground if tree is not focused.
|
* inactive selection background/foreground if tree is not focused.
|
||||||
@@ -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,8 +290,51 @@ 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
|
||||||
rendererPane.paintComponent( g, rendererComponent, tree, bounds.x, bounds.y, bounds.width, bounds.height, true );
|
if( !isEditing )
|
||||||
|
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
|
||||||
if( oldBackgroundSelectionColor != null )
|
if( oldBackgroundSelectionColor != null )
|
||||||
@@ -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,26 +24,29 @@ 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.event.MouseEvent;
|
|
||||||
import java.awt.geom.Ellipse2D;
|
import java.awt.geom.Ellipse2D;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.awt.geom.RoundRectangle2D;
|
import java.awt.geom.RoundRectangle2D;
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Predicate;
|
||||||
import java.util.function.Supplier;
|
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;
|
||||||
@@ -50,6 +54,7 @@ import javax.swing.border.CompoundBorder;
|
|||||||
import javax.swing.plaf.ComponentUI;
|
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;
|
||||||
@@ -89,6 +94,11 @@ public class FlatUIUtils
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Insets addInsets( Insets insets1, Insets insets2 ) {
|
public static Insets addInsets( Insets insets1, Insets insets2 ) {
|
||||||
|
if( insets1 == null )
|
||||||
|
return insets2;
|
||||||
|
if( insets2 == null )
|
||||||
|
return insets1;
|
||||||
|
|
||||||
return new Insets(
|
return new Insets(
|
||||||
insets1.top + insets2.top,
|
insets1.top + insets2.top,
|
||||||
insets1.left + insets2.left,
|
insets1.left + insets2.left,
|
||||||
@@ -118,6 +128,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;
|
||||||
@@ -128,6 +146,25 @@ 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 ) {
|
public static boolean isChevron( String arrowType ) {
|
||||||
return !"triangle".equals( arrowType );
|
return !"triangle".equals( arrowType );
|
||||||
}
|
}
|
||||||
@@ -173,12 +210,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 ) {
|
||||||
@@ -227,10 +291,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 ) {
|
||||||
@@ -277,7 +388,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 );
|
||||||
@@ -506,20 +617,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,
|
||||||
rect.lineTo( x2, y2 - arcBottomRight );
|
x2, y + ciTopRight,
|
||||||
rect.quadTo( x2, y2, x2 - arcBottomRight, y2 );
|
x2, y + arcTopRight );
|
||||||
rect.lineTo( x + arcBottomLeft, y2 );
|
rect.lineTo( x2, y2 - arcBottomRight );
|
||||||
rect.quadTo( x, y2, x, y2 - arcBottomLeft );
|
rect.curveTo( x2, y2 - ciBottomRight,
|
||||||
rect.lineTo( x, y + arcTopLeft );
|
x2 - ciBottomRight, y2,
|
||||||
rect.quadTo( x, y, x + arcTopLeft, y );
|
x2 - arcBottomRight, y2 );
|
||||||
|
rect.lineTo( x + arcBottomLeft, y2 );
|
||||||
|
rect.curveTo( x + ciBottomLeft, y2,
|
||||||
|
x, y2 - ciBottomLeft,
|
||||||
|
x, y2 - arcBottomLeft );
|
||||||
|
rect.lineTo( x, y + arcTopLeft );
|
||||||
|
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.
|
||||||
*/
|
*/
|
||||||
@@ -604,56 +838,29 @@ public class FlatUIUtils
|
|||||||
.computeIfAbsent( key, k -> newInstanceSupplier.get() );
|
.computeIfAbsent( key, k -> newInstanceSupplier.get() );
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class HoverListener ------------------------------------------------
|
|
||||||
|
|
||||||
public static class HoverListener
|
|
||||||
extends MouseAdapter
|
|
||||||
{
|
|
||||||
private final Component repaintComponent;
|
|
||||||
private final Consumer<Boolean> hoverChanged;
|
|
||||||
|
|
||||||
public HoverListener( Component repaintComponent, Consumer<Boolean> hoverChanged ) {
|
|
||||||
this.repaintComponent = repaintComponent;
|
|
||||||
this.hoverChanged = hoverChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
@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 -----------------------------------------
|
||||||
|
|
||||||
public static class RepaintFocusListener
|
public static class RepaintFocusListener
|
||||||
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 ) {
|
||||||
repaintComponent.repaint();
|
if( repaintCondition == null || repaintCondition.test( repaintComponent ) )
|
||||||
|
repaintComponent.repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void focusLost( FocusEvent e ) {
|
public void focusLost( FocusEvent e ) {
|
||||||
repaintComponent.repaint();
|
if( repaintCondition == null || repaintCondition.test( repaintComponent ) )
|
||||||
|
repaintComponent.repaint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -256,6 +256,8 @@ public abstract class FlatWindowResizer
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isWindowResizable() {
|
protected boolean isWindowResizable() {
|
||||||
|
if( FlatUIUtils.isFullScreen( resizeComp ) )
|
||||||
|
return false;
|
||||||
if( window instanceof Frame )
|
if( window instanceof Frame )
|
||||||
return ((Frame)window).isResizable() && (((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) == 0;
|
return ((Frame)window).isResizable() && (((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) == 0;
|
||||||
if( window instanceof Dialog )
|
if( window instanceof Dialog )
|
||||||
@@ -429,9 +431,9 @@ public abstract 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( isDialog() )
|
// for frames: necessary because GraphicsDevice.setFullScreenWindow() does not fire events
|
||||||
updateVisibility();
|
updateVisibility();
|
||||||
|
|
||||||
/*debug
|
/*debug
|
||||||
int width = getWidth();
|
int width = getWidth();
|
||||||
|
|||||||
@@ -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 {
|
||||||
Window_setHasCustomDecoration.invoke( window );
|
if( hasCustomDecoration )
|
||||||
|
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,40 +233,50 @@ 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;
|
colorizationColorBalance = 100;
|
||||||
if( colorizationColorBalance < 0 )
|
|
||||||
colorizationColorBalance = 100;
|
|
||||||
|
|
||||||
if( colorizationColorBalance == 0 )
|
if( colorizationColorBalance == 0 )
|
||||||
return new Color( 0xD9D9D9 );
|
return new Color( 0xD9D9D9 );
|
||||||
if( colorizationColorBalance == 100 )
|
if( colorizationColorBalance == 100 )
|
||||||
return colorizationColor;
|
return colorizationColor;
|
||||||
|
|
||||||
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 );
|
||||||
return new Color( r, g, b );
|
|
||||||
}
|
// avoid potential IllegalArgumentException in Color constructor
|
||||||
return colorizationColor;
|
r = Math.min( Math.max( r, 0 ), 255 );
|
||||||
|
g = Math.min( Math.max( g, 0 ), 255 );
|
||||||
|
b = Math.min( Math.max( b, 0 ), 255 );
|
||||||
|
|
||||||
|
return new Color( r, g, b );
|
||||||
}
|
}
|
||||||
|
|
||||||
Color activeBorderColor = (Color) toolkit.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,50 @@
|
|||||||
|
/*
|
||||||
|
* 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.util.function.BiPredicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
class StackUtils
|
||||||
|
{
|
||||||
|
private static final StackUtils INSTANCE = new StackUtilsImpl();
|
||||||
|
|
||||||
|
// hide from javadoc
|
||||||
|
StackUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether current method was invoked from the given class and method.
|
||||||
|
*/
|
||||||
|
public static boolean wasInvokedFrom( String className, String methodName, int limit ) {
|
||||||
|
return wasInvokedFrom( (c,m) -> c.equals( className ) && m.equals( methodName ), limit );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether current method was invoked from a class and method using the given predicate,
|
||||||
|
* which gets the class name of the stack frame as first parameter and the method name as second parameter.
|
||||||
|
*/
|
||||||
|
public static boolean wasInvokedFrom( BiPredicate<String, String> predicate, int limit ) {
|
||||||
|
return INSTANCE.wasInvokedFromImpl( predicate, limit );
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean wasInvokedFromImpl( BiPredicate<String, String> predicate, int limit ) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* 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.util.function.BiPredicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
class StackUtilsImpl
|
||||||
|
extends StackUtils
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
boolean wasInvokedFromImpl( BiPredicate<String, String> predicate, int limit ) {
|
||||||
|
int count = -2;
|
||||||
|
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
||||||
|
for( StackTraceElement stackTraceElement : stackTrace ) {
|
||||||
|
if( predicate.test( stackTraceElement.getClassName(), stackTraceElement.getMethodName() ) )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
count++;
|
||||||
|
if( limit > 0 && count > limit )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
amount2 = autoInverse && shouldInverse( hsla ) ? -amount2 : amount2;
|
||||||
|
hsla[hslIndex] = clamp( relative
|
||||||
|
? (hsla[hslIndex] * ((100 + amount2) / 100))
|
||||||
|
: (hsla[hslIndex] + amount2) );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean shouldInverse( float[] hsl ) {
|
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")
|
||||||
|
|||||||
@@ -21,10 +21,7 @@ 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.
|
||||||
@@ -58,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 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,7 +67,7 @@ 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 );
|
throw new RuntimeException( ex );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,7 +91,7 @@ public class JavaCompatibility
|
|||||||
: "clipStringIfNecessary",
|
: "clipStringIfNecessary",
|
||||||
new Class[] { JComponent.class, FontMetrics.class, String.class, int.class } );
|
new Class[] { JComponent.class, FontMetrics.class, String.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 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,7 +100,7 @@ public class JavaCompatibility
|
|||||||
try {
|
try {
|
||||||
return (String) getClippedStringMethod.invoke( null, c, fm, string, availTextWidth );
|
return (String) getClippedStringMethod.invoke( null, c, fm, string, availTextWidth );
|
||||||
} 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 );
|
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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
/*
|
||||||
|
* 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 java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.FileAlreadyExistsException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to load native library (.dll, .so or .dylib) stored in Jar.
|
||||||
|
* <p>
|
||||||
|
* Copies native library to users temporary folder before loading it.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public class NativeLibrary
|
||||||
|
{
|
||||||
|
private static final String DELETE_SUFFIX = ".delete";
|
||||||
|
private static boolean deletedTemporary;
|
||||||
|
|
||||||
|
private final boolean loaded;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load native library from given classloader.
|
||||||
|
* <p>
|
||||||
|
* Note regarding Java Platform Module System (JPMS):
|
||||||
|
* If classloader is {@code null}, the library can be only loaded from the module
|
||||||
|
* that contains this class.
|
||||||
|
* If classloader is not {@code null}, then the package that contains the library
|
||||||
|
* must be specified as "open" in module-info.java of the module that contains the library.
|
||||||
|
*
|
||||||
|
* @param libraryName resource name of the native library (without "lib" prefix and without extension)
|
||||||
|
* @param classLoader the classloader used to locate the library, or {@code null}
|
||||||
|
* @param supported whether the native library is supported on the current platform
|
||||||
|
*/
|
||||||
|
public NativeLibrary( String libraryName, ClassLoader classLoader, boolean supported ) {
|
||||||
|
this.loaded = supported
|
||||||
|
? loadLibraryFromJar( libraryName, classLoader )
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the native library is loaded.
|
||||||
|
* <p>
|
||||||
|
* Returns {@code false} if not supported on current platform as specified in constructor
|
||||||
|
* or if loading failed.
|
||||||
|
*/
|
||||||
|
public boolean isLoaded() {
|
||||||
|
return loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean loadLibraryFromJar( String libraryName, ClassLoader classLoader ) {
|
||||||
|
// add prefix and suffix to library name
|
||||||
|
libraryName = decorateLibraryName( libraryName );
|
||||||
|
|
||||||
|
// find library
|
||||||
|
URL libraryUrl = (classLoader != null)
|
||||||
|
? classLoader.getResource( libraryName )
|
||||||
|
: NativeLibrary.class.getResource( "/" + libraryName );
|
||||||
|
if( libraryUrl == null ) {
|
||||||
|
log( "Library '" + libraryName + "' not found", null );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
File tempFile = null;
|
||||||
|
try {
|
||||||
|
// for development environment
|
||||||
|
if( "file".equals( libraryUrl.getProtocol() ) ) {
|
||||||
|
File libraryFile = new File( libraryUrl.getPath() );
|
||||||
|
if( libraryFile.isFile() ) {
|
||||||
|
// load library without copying
|
||||||
|
System.load( libraryFile.getCanonicalPath() );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create temporary file
|
||||||
|
Path tempPath = createTempFile( libraryName );
|
||||||
|
tempFile = tempPath.toFile();
|
||||||
|
|
||||||
|
// copy library to temporary file
|
||||||
|
try( InputStream in = libraryUrl.openStream() ) {
|
||||||
|
Files.copy( in, tempPath, StandardCopyOption.REPLACE_EXISTING );
|
||||||
|
}
|
||||||
|
|
||||||
|
// load library
|
||||||
|
System.load( tempFile.getCanonicalPath() );
|
||||||
|
|
||||||
|
// delete library
|
||||||
|
deleteOrMarkForDeletion( tempFile );
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch( Throwable ex ) {
|
||||||
|
log( null, ex );
|
||||||
|
|
||||||
|
if( tempFile != null )
|
||||||
|
deleteOrMarkForDeletion( tempFile );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String decorateLibraryName( String libraryName ) {
|
||||||
|
if( SystemInfo.isWindows )
|
||||||
|
return libraryName.concat( ".dll" );
|
||||||
|
|
||||||
|
String suffix = SystemInfo.isMacOS ? ".dylib" : ".so";
|
||||||
|
|
||||||
|
int sep = libraryName.lastIndexOf( '/' );
|
||||||
|
return (sep >= 0)
|
||||||
|
? libraryName.substring( 0, sep + 1 ) + "lib" + libraryName.substring( sep + 1 ) + suffix
|
||||||
|
: "lib" + libraryName + suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void log( String msg, Throwable thrown ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( msg, thrown );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Path createTempFile( String libraryName ) throws IOException {
|
||||||
|
int sep = libraryName.lastIndexOf( '/' );
|
||||||
|
String name = (sep >= 0) ? libraryName.substring( sep + 1 ) : libraryName;
|
||||||
|
|
||||||
|
int dot = name.lastIndexOf( '.' );
|
||||||
|
String prefix = ((dot >= 0) ? name.substring( 0, dot ) : name) + '-';
|
||||||
|
String suffix = (dot >= 0) ? name.substring( dot ) : "";
|
||||||
|
|
||||||
|
Path tempDir = getTempDir();
|
||||||
|
|
||||||
|
// Note:
|
||||||
|
// Not using Files.createTempFile() here because it uses random number generator SecureRandom,
|
||||||
|
// which may take 5-10 seconds to initialize under particular conditions.
|
||||||
|
|
||||||
|
// Use current time in nanoseconds instead of a random number.
|
||||||
|
// To avoid (theoretical) collisions, append a counter.
|
||||||
|
long nanoTime = System.nanoTime();
|
||||||
|
for( int i = 0;; i++ ) {
|
||||||
|
String s = prefix + Long.toUnsignedString( nanoTime ) + i + suffix;
|
||||||
|
try {
|
||||||
|
return Files.createFile( tempDir.resolve( s ) );
|
||||||
|
} catch( FileAlreadyExistsException ex ) {
|
||||||
|
// ignore --> increment counter and try again
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Path getTempDir() throws IOException {
|
||||||
|
// get standard temporary directory
|
||||||
|
String tmpdir = System.getProperty( "java.io.tmpdir" );
|
||||||
|
|
||||||
|
if( SystemInfo.isWindows ) {
|
||||||
|
// On Windows, where File.delete() and File.deleteOnExit() does not work
|
||||||
|
// for loaded native libraries, they will be deleted on next application startup.
|
||||||
|
// The default temporary directory may contain hundreds or thousands of files.
|
||||||
|
// To make searching for "marked for deletion" files as fast as possible,
|
||||||
|
// use a sub directory that contains only our temporary native libraries.
|
||||||
|
tmpdir += "\\flatlaf.temp";
|
||||||
|
}
|
||||||
|
|
||||||
|
// create temporary directory
|
||||||
|
Path tempDir = Paths.get( tmpdir );
|
||||||
|
Files.createDirectories( tempDir );
|
||||||
|
|
||||||
|
// delete no longer needed temporary files (from already exited applications)
|
||||||
|
if( SystemInfo.isWindows )
|
||||||
|
deleteTemporaryFiles( tempDir );
|
||||||
|
|
||||||
|
return tempDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void deleteTemporaryFiles( Path tempDir ) {
|
||||||
|
if( deletedTemporary )
|
||||||
|
return;
|
||||||
|
deletedTemporary = true;
|
||||||
|
|
||||||
|
File[] markerFiles = tempDir.toFile().listFiles( (dir, name) -> name.endsWith( DELETE_SUFFIX ) );
|
||||||
|
if( markerFiles == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
for( File markerFile : markerFiles ) {
|
||||||
|
File toDeleteFile = new File( markerFile.getParent(), StringUtils.removeTrailing( markerFile.getName(), DELETE_SUFFIX ) );
|
||||||
|
if( !toDeleteFile.exists() || toDeleteFile.delete() )
|
||||||
|
markerFile.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void deleteOrMarkForDeletion( File file ) {
|
||||||
|
// try to delete the native library
|
||||||
|
if( file.delete() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// not possible to delete on Windows because native library file is locked
|
||||||
|
// --> create "to delete" marker file (used at next startup)
|
||||||
|
try {
|
||||||
|
File markFile = new File( file.getParent(), file.getName() + DELETE_SUFFIX );
|
||||||
|
markFile.createNewFile();
|
||||||
|
} catch( IOException ex2 ) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -77,7 +77,7 @@ debug*/
|
|||||||
double scaleFactor = systemScaleFactor * userScaleFactor;
|
double scaleFactor = systemScaleFactor * userScaleFactor;
|
||||||
|
|
||||||
// paint input image icon if not necessary to scale
|
// paint input image icon if not necessary to scale
|
||||||
if( scaleFactor == 1 && iconWidth == imageIcon.getIconWidth() && iconHeight == imageIcon.getIconHeight() ) {
|
if( scaleFactor == 1 && imageIcon != null && iconWidth == imageIcon.getIconWidth() && iconHeight == imageIcon.getIconHeight() ) {
|
||||||
imageIcon.paintIcon( c, g, x, y );
|
imageIcon.paintIcon( c, g, x, y );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ public class SystemInfo
|
|||||||
public static final boolean isMacOS_10_14_Mojave_orLater;
|
public static final boolean isMacOS_10_14_Mojave_orLater;
|
||||||
public static final boolean isMacOS_10_15_Catalina_orLater;
|
public static final boolean isMacOS_10_15_Catalina_orLater;
|
||||||
|
|
||||||
|
// OS architecture
|
||||||
|
/** @since 1.1 */ public static final boolean isX86_64;
|
||||||
|
|
||||||
// Java versions
|
// Java versions
|
||||||
public static final long javaVersion;
|
public static final long javaVersion;
|
||||||
public static final boolean isJava_9_orLater;
|
public static final boolean isJava_9_orLater;
|
||||||
@@ -51,6 +54,11 @@ public class SystemInfo
|
|||||||
// UI toolkits
|
// UI toolkits
|
||||||
public static final boolean isKDE;
|
public static final boolean isKDE;
|
||||||
|
|
||||||
|
// other
|
||||||
|
/** @since 1.1 */ public static final boolean isProjector;
|
||||||
|
/** @since 1.1.2 */ public static final boolean isWebswing;
|
||||||
|
/** @since 1.1.1 */ public static final boolean isWinPE;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// platforms
|
// platforms
|
||||||
String osName = System.getProperty( "os.name" ).toLowerCase( Locale.ENGLISH );
|
String osName = System.getProperty( "os.name" ).toLowerCase( Locale.ENGLISH );
|
||||||
@@ -65,6 +73,10 @@ public class SystemInfo
|
|||||||
isMacOS_10_14_Mojave_orLater = (isMacOS && osVersion >= toVersion( 10, 14, 0, 0 ));
|
isMacOS_10_14_Mojave_orLater = (isMacOS && osVersion >= toVersion( 10, 14, 0, 0 ));
|
||||||
isMacOS_10_15_Catalina_orLater = (isMacOS && osVersion >= toVersion( 10, 15, 0, 0 ));
|
isMacOS_10_15_Catalina_orLater = (isMacOS && osVersion >= toVersion( 10, 15, 0, 0 ));
|
||||||
|
|
||||||
|
// OS architecture
|
||||||
|
String osArch = System.getProperty( "os.arch" );
|
||||||
|
isX86_64 = osArch.equals( "amd64" ) || osArch.equals( "x86_64" );
|
||||||
|
|
||||||
// Java versions
|
// Java versions
|
||||||
javaVersion = scanVersion( System.getProperty( "java.version" ) );
|
javaVersion = scanVersion( System.getProperty( "java.version" ) );
|
||||||
isJava_9_orLater = (javaVersion >= toVersion( 9, 0, 0, 0 ));
|
isJava_9_orLater = (javaVersion >= toVersion( 9, 0, 0, 0 ));
|
||||||
@@ -78,6 +90,11 @@ public class SystemInfo
|
|||||||
|
|
||||||
// UI toolkits
|
// UI toolkits
|
||||||
isKDE = (isLinux && System.getenv( "KDE_FULL_SESSION" ) != null);
|
isKDE = (isLinux && System.getenv( "KDE_FULL_SESSION" ) != null);
|
||||||
|
|
||||||
|
// other
|
||||||
|
isProjector = Boolean.getBoolean( "org.jetbrains.projector.server.enable" );
|
||||||
|
isWebswing = (System.getProperty( "webswing.rootDir" ) != null);
|
||||||
|
isWinPE = isWindows && "X:\\Windows\\System32".equalsIgnoreCase( System.getProperty( "user.dir" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long scanVersion( String version ) {
|
public static long scanVersion( String version ) {
|
||||||
|
|||||||
@@ -36,9 +36,14 @@ import javax.swing.plaf.UIResource;
|
|||||||
import com.formdev.flatlaf.FlatSystemProperties;
|
import com.formdev.flatlaf.FlatSystemProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Two scaling modes are supported for HiDPI displays:
|
* This class handles scaling in Swing UIs.
|
||||||
|
* It computes user scaling factor based on font size and
|
||||||
|
* provides methods to scale integer, float, {@link Dimension} and {@link Insets}.
|
||||||
|
* This class is look and feel independent.
|
||||||
|
* <p>
|
||||||
|
* Two scaling modes are supported by FlatLaf for HiDPI displays:
|
||||||
*
|
*
|
||||||
* 1) system scaling mode
|
* <h2>1) system scaling mode</h2>
|
||||||
*
|
*
|
||||||
* This mode is supported since Java 9 on all platforms and in some Java 8 VMs
|
* This mode is supported since Java 9 on all platforms and in some Java 8 VMs
|
||||||
* (e.g. Apple and JetBrains). The JRE determines the scale factor per-display and
|
* (e.g. Apple and JetBrains). The JRE determines the scale factor per-display and
|
||||||
@@ -49,7 +54,7 @@ import com.formdev.flatlaf.FlatSystemProperties;
|
|||||||
* The scale factor may be different for each connected display.
|
* The scale factor may be different for each connected display.
|
||||||
* The scale factor may change for a window when moving the window from one display to another one.
|
* The scale factor may change for a window when moving the window from one display to another one.
|
||||||
*
|
*
|
||||||
* 2) user scaling mode
|
* <h2>2) user scaling mode</h2>
|
||||||
*
|
*
|
||||||
* This mode is mainly for Java 8 compatibility, but is also used on Linux
|
* This mode is mainly for Java 8 compatibility, but is also used on Linux
|
||||||
* or if the default font is changed.
|
* or if the default font is changed.
|
||||||
@@ -85,6 +90,9 @@ public class UIScale
|
|||||||
|
|
||||||
private static Boolean jreHiDPI;
|
private static Boolean jreHiDPI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether system scaling is enabled.
|
||||||
|
*/
|
||||||
public static boolean isSystemScalingEnabled() {
|
public static boolean isSystemScalingEnabled() {
|
||||||
if( jreHiDPI != null )
|
if( jreHiDPI != null )
|
||||||
return jreHiDPI;
|
return jreHiDPI;
|
||||||
@@ -112,10 +120,16 @@ public class UIScale
|
|||||||
return jreHiDPI;
|
return jreHiDPI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the system scale factor for the given graphics context.
|
||||||
|
*/
|
||||||
public static double getSystemScaleFactor( Graphics2D g ) {
|
public static double getSystemScaleFactor( Graphics2D g ) {
|
||||||
return isSystemScalingEnabled() ? g.getDeviceConfiguration().getDefaultTransform().getScaleX() : 1;
|
return isSystemScalingEnabled() ? getSystemScaleFactor( g.getDeviceConfiguration() ) : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the system scale factor for the given graphics configuration.
|
||||||
|
*/
|
||||||
public static double getSystemScaleFactor( GraphicsConfiguration gc ) {
|
public static double getSystemScaleFactor( GraphicsConfiguration gc ) {
|
||||||
return (isSystemScalingEnabled() && gc != null) ? gc.getDefaultTransform().getScaleX() : 1;
|
return (isSystemScalingEnabled() && gc != null) ? gc.getDefaultTransform().getScaleX() : 1;
|
||||||
}
|
}
|
||||||
@@ -166,7 +180,7 @@ public class UIScale
|
|||||||
// apply custom scale factor specified in system property "flatlaf.uiScale"
|
// apply custom scale factor specified in system property "flatlaf.uiScale"
|
||||||
float customScaleFactor = getCustomScaleFactor();
|
float customScaleFactor = getCustomScaleFactor();
|
||||||
if( customScaleFactor > 0 ) {
|
if( customScaleFactor > 0 ) {
|
||||||
setUserScaleFactor( customScaleFactor );
|
setUserScaleFactor( customScaleFactor, false );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,7 +230,7 @@ public class UIScale
|
|||||||
} else
|
} else
|
||||||
newScaleFactor = computeScaleFactor( font );
|
newScaleFactor = computeScaleFactor( font );
|
||||||
|
|
||||||
setUserScaleFactor( newScaleFactor );
|
setUserScaleFactor( newScaleFactor, true );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static float computeScaleFactor( Font font ) {
|
private static float computeScaleFactor( Font font ) {
|
||||||
@@ -260,7 +274,7 @@ public class UIScale
|
|||||||
if( scaleFactor == fontScaleFactor )
|
if( scaleFactor == fontScaleFactor )
|
||||||
return font;
|
return font;
|
||||||
|
|
||||||
int newFontSize = Math.round( (font.getSize() / fontScaleFactor) * scaleFactor );
|
int newFontSize = Math.max( Math.round( (font.getSize() / fontScaleFactor) * scaleFactor ), 1 );
|
||||||
return new FontUIResource( font.deriveFont( (float) newFontSize ) );
|
return new FontUIResource( font.deriveFont( (float) newFontSize ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,16 +311,29 @@ public class UIScale
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the user scale factor.
|
||||||
|
*/
|
||||||
public static float getUserScaleFactor() {
|
public static float getUserScaleFactor() {
|
||||||
initialize();
|
initialize();
|
||||||
return scaleFactor;
|
return scaleFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setUserScaleFactor( float scaleFactor ) {
|
/**
|
||||||
if( scaleFactor <= 1f )
|
* Sets the user scale factor.
|
||||||
scaleFactor = 1f;
|
*/
|
||||||
else // round scale factor to 1/4
|
private static void setUserScaleFactor( float scaleFactor, boolean normalize ) {
|
||||||
scaleFactor = Math.round( scaleFactor * 4f ) / 4f;
|
if( normalize ) {
|
||||||
|
if( scaleFactor < 1f ) {
|
||||||
|
scaleFactor = FlatSystemProperties.getBoolean( FlatSystemProperties.UI_SCALE_ALLOW_SCALE_DOWN, false )
|
||||||
|
? Math.round( scaleFactor * 10f ) / 10f // round small scale factor to 1/10
|
||||||
|
: 1f;
|
||||||
|
} else if( scaleFactor > 1f ) // round scale factor to 1/4
|
||||||
|
scaleFactor = Math.round( scaleFactor * 4f ) / 4f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// minimum scale factor
|
||||||
|
scaleFactor = Math.max( scaleFactor, 0.1f );
|
||||||
|
|
||||||
float oldScaleFactor = UIScale.scaleFactor;
|
float oldScaleFactor = UIScale.scaleFactor;
|
||||||
UIScale.scaleFactor = scaleFactor;
|
UIScale.scaleFactor = scaleFactor;
|
||||||
@@ -318,40 +345,65 @@ public class UIScale
|
|||||||
changeSupport.firePropertyChange( "userScaleFactor", oldScaleFactor, scaleFactor );
|
changeSupport.firePropertyChange( "userScaleFactor", oldScaleFactor, scaleFactor );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiplies the given value by the user scale factor.
|
||||||
|
*/
|
||||||
public static float scale( float value ) {
|
public static float scale( float value ) {
|
||||||
initialize();
|
initialize();
|
||||||
return (scaleFactor == 1) ? value : (value * scaleFactor);
|
return (scaleFactor == 1) ? value : (value * scaleFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiplies the given value by the user scale factor and rounds the result.
|
||||||
|
*/
|
||||||
public static int scale( int value ) {
|
public static int scale( int value ) {
|
||||||
initialize();
|
initialize();
|
||||||
return (scaleFactor == 1) ? value : Math.round( value * scaleFactor );
|
return (scaleFactor == 1) ? value : Math.round( value * scaleFactor );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Similar as scale(int) but always "rounds down".
|
* Similar as {@link #scale(int)} but always "rounds down".
|
||||||
|
* <p>
|
||||||
|
* For use in special cases. {@link #scale(int)} is the preferred method.
|
||||||
*/
|
*/
|
||||||
public static int scale2( int value ) {
|
public static int scale2( int value ) {
|
||||||
initialize();
|
initialize();
|
||||||
return (scaleFactor == 1) ? value : (int) (value * scaleFactor);
|
return (scaleFactor == 1) ? value : (int) (value * scaleFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Divides the given value by the user scale factor.
|
||||||
|
*/
|
||||||
public static float unscale( float value ) {
|
public static float unscale( float value ) {
|
||||||
initialize();
|
initialize();
|
||||||
return (scaleFactor == 1f) ? value : (value / scaleFactor);
|
return (scaleFactor == 1f) ? value : (value / scaleFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Divides the given value by the user scale factor and rounds the result.
|
||||||
|
*/
|
||||||
public static int unscale( int value ) {
|
public static int unscale( int value ) {
|
||||||
initialize();
|
initialize();
|
||||||
return (scaleFactor == 1f) ? value : Math.round( value / scaleFactor );
|
return (scaleFactor == 1f) ? value : Math.round( value / scaleFactor );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If user scale factor is not 1, scale the given graphics context by invoking
|
||||||
|
* {@link Graphics2D#scale(double, double)} with user scale factor.
|
||||||
|
*/
|
||||||
public static void scaleGraphics( Graphics2D g ) {
|
public static void scaleGraphics( Graphics2D g ) {
|
||||||
initialize();
|
initialize();
|
||||||
if( scaleFactor != 1f )
|
if( scaleFactor != 1f )
|
||||||
g.scale( scaleFactor, scaleFactor );
|
g.scale( scaleFactor, scaleFactor );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scales the given dimension with the user scale factor.
|
||||||
|
* <p>
|
||||||
|
* If user scale factor is 1, then the given dimension is simply returned.
|
||||||
|
* Otherwise a new instance of {@link Dimension} or {@link DimensionUIResource}
|
||||||
|
* is returned, depending on whether the passed dimension implements {@link UIResource}.
|
||||||
|
*/
|
||||||
public static Dimension scale( Dimension dimension ) {
|
public static Dimension scale( Dimension dimension ) {
|
||||||
initialize();
|
initialize();
|
||||||
return (dimension == null || scaleFactor == 1f)
|
return (dimension == null || scaleFactor == 1f)
|
||||||
@@ -361,6 +413,13 @@ public class UIScale
|
|||||||
: new Dimension ( scale( dimension.width ), scale( dimension.height ) ));
|
: new Dimension ( scale( dimension.width ), scale( dimension.height ) ));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scales the given insets with the user scale factor.
|
||||||
|
* <p>
|
||||||
|
* If user scale factor is 1, then the given insets is simply returned.
|
||||||
|
* Otherwise a new instance of {@link Insets} or {@link InsetsUIResource}
|
||||||
|
* is returned, depending on whether the passed dimension implements {@link UIResource}.
|
||||||
|
*/
|
||||||
public static Insets scale( Insets insets ) {
|
public static Insets scale( Insets insets ) {
|
||||||
initialize();
|
initialize();
|
||||||
return (insets == null || scaleFactor == 1f)
|
return (insets == null || scaleFactor == 1f)
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* 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.util.function.BiPredicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
class StackUtilsImpl
|
||||||
|
extends StackUtils
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
boolean wasInvokedFromImpl( BiPredicate<String, String> predicate, int limit ) {
|
||||||
|
return StackWalker.getInstance().walk( stream -> {
|
||||||
|
if( limit > 0 )
|
||||||
|
stream = stream.limit( limit + 2 );
|
||||||
|
return stream.anyMatch( f -> {
|
||||||
|
return predicate.test( f.getClassName(), f.getMethodName() );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
class LoggingFacadeImpl
|
||||||
|
implements LoggingFacade
|
||||||
|
{
|
||||||
|
private static final System.Logger LOG = System.getLogger( FlatLaf.class.getName() );
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logSevere( String message, Throwable t ) {
|
||||||
|
LOG.log( System.Logger.Level.ERROR, message, t );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logConfig( String message, Throwable t ) {
|
||||||
|
LOG.log( System.Logger.Level.DEBUG, message, t );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,7 +19,6 @@
|
|||||||
*/
|
*/
|
||||||
module com.formdev.flatlaf {
|
module com.formdev.flatlaf {
|
||||||
requires java.desktop;
|
requires java.desktop;
|
||||||
requires java.logging;
|
|
||||||
|
|
||||||
exports com.formdev.flatlaf;
|
exports com.formdev.flatlaf;
|
||||||
exports com.formdev.flatlaf.icons;
|
exports com.formdev.flatlaf.icons;
|
||||||
|
|||||||
@@ -14,29 +14,49 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# This file is loaded for "FlatLaf Darcula" theme (that extend class FlatDarculaLaf)
|
||||||
|
# and for all dark IntelliJ Platform themes.
|
||||||
|
#
|
||||||
|
# Documentation:
|
||||||
|
# - https://www.formdev.com/flatlaf/properties-files/
|
||||||
|
# - https://www.formdev.com/flatlaf/how-to-customize/
|
||||||
|
#
|
||||||
|
# NOTE: Avoid copying the whole content of this file to own properties files.
|
||||||
|
# This will make upgrading to newer FlatLaf versions complex and error-prone.
|
||||||
|
# Instead copy and modify only those properties that you need to alter.
|
||||||
|
#
|
||||||
|
|
||||||
# Colors and style mostly based on Darcula theme from IntelliJ IDEA Community Edition,
|
# Colors and style mostly based on Darcula theme from IntelliJ IDEA Community Edition,
|
||||||
# which is licensed under the Apache 2.0 license. Copyright 2000-2019 JetBrains s.r.o.
|
# which is licensed under the Apache 2.0 license. Copyright 2000-2019 JetBrains s.r.o.
|
||||||
# See: https://github.com/JetBrains/intellij-community/
|
# See: https://github.com/JetBrains/intellij-community/
|
||||||
|
|
||||||
#---- Button ----
|
#---- Button ----
|
||||||
|
|
||||||
Button.default.boldText=true
|
Button.innerFocusWidth = 0
|
||||||
|
|
||||||
|
Button.default.boldText = true
|
||||||
|
|
||||||
|
|
||||||
|
#---- CheckBox ----
|
||||||
|
|
||||||
|
CheckBox.icon.focusedBackground = null
|
||||||
|
|
||||||
|
|
||||||
#---- Component ----
|
#---- Component ----
|
||||||
|
|
||||||
Component.focusWidth=2
|
Component.focusWidth = 2
|
||||||
Component.innerFocusWidth=0
|
Component.innerFocusWidth = 0
|
||||||
Component.innerOutlineWidth=0
|
Component.innerOutlineWidth = 0
|
||||||
Component.arrowType=triangle
|
Component.arrowType = triangle
|
||||||
|
|
||||||
|
|
||||||
#---- ProgressBar ----
|
#---- ProgressBar ----
|
||||||
|
|
||||||
ProgressBar.foreground=#a0a0a0
|
ProgressBar.foreground = #a0a0a0
|
||||||
ProgressBar.selectionForeground=@background
|
ProgressBar.selectionForeground = @background
|
||||||
|
|
||||||
|
|
||||||
#---- RadioButton ----
|
#---- RadioButton ----
|
||||||
|
|
||||||
RadioButton.icon.centerDiameter=5
|
RadioButton.icon.centerDiameter = 5
|
||||||
|
|||||||
@@ -14,293 +14,314 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# This file is loaded for all dark themes (that extend class FlatDarkLaf).
|
||||||
|
#
|
||||||
|
# Documentation:
|
||||||
|
# - https://www.formdev.com/flatlaf/properties-files/
|
||||||
|
# - https://www.formdev.com/flatlaf/how-to-customize/
|
||||||
|
#
|
||||||
|
# NOTE: Avoid copying the whole content of this file to own properties files.
|
||||||
|
# This will make upgrading to newer FlatLaf versions complex and error-prone.
|
||||||
|
# Instead copy and modify only those properties that you need to alter.
|
||||||
|
#
|
||||||
|
|
||||||
# Colors and style mostly based on Darcula theme from IntelliJ IDEA Community Edition,
|
# Colors and style mostly based on Darcula theme from IntelliJ IDEA Community Edition,
|
||||||
# which is licensed under the Apache 2.0 license. Copyright 2000-2019 JetBrains s.r.o.
|
# which is licensed under the Apache 2.0 license. Copyright 2000-2019 JetBrains s.r.o.
|
||||||
# See: https://github.com/JetBrains/intellij-community/
|
# See: https://github.com/JetBrains/intellij-community/
|
||||||
|
|
||||||
#---- variables ----
|
#---- variables ----
|
||||||
|
|
||||||
@background=#3c3f41
|
@background = #3c3f41
|
||||||
@foreground=#bbb
|
@foreground = #bbb
|
||||||
@selectionBackground=#4B6EAF
|
@selectionBackground = #4B6EAF
|
||||||
@selectionForeground=@foreground
|
@selectionForeground = @foreground
|
||||||
@selectionInactiveBackground=#0D293E
|
@selectionInactiveBackground = #0D293E
|
||||||
@selectionInactiveForeground=@foreground
|
@selectionInactiveForeground = @foreground
|
||||||
@disabledText=#888
|
@disabledText = #888
|
||||||
@textComponentBackground=#45494A
|
@textComponentBackground = #45494A
|
||||||
@menuBackground=darken(@background,5%)
|
@menuBackground = darken(@background,5%)
|
||||||
@menuHoverBackground=lighten(@menuBackground,10%,derived)
|
@menuHoverBackground = lighten(@menuBackground,10%,derived)
|
||||||
@menuCheckBackground=darken(@selectionBackground,10%)
|
@menuCheckBackground = darken(@selectionBackground,10%,derived noAutoInverse)
|
||||||
@menuAcceleratorForeground=darken(@foreground,15%)
|
@menuAcceleratorForeground = darken(@foreground,15%)
|
||||||
@menuAcceleratorSelectionForeground=@selectionForeground
|
@menuAcceleratorSelectionForeground = @selectionForeground
|
||||||
@cellFocusColor=#000
|
@cellFocusColor = #000
|
||||||
@icon=#adadad
|
@icon = #adadad
|
||||||
|
|
||||||
|
# for buttons within components (e.g. combobox or spinner)
|
||||||
|
@buttonArrowColor = #9A9DA1
|
||||||
|
@buttonDisabledArrowColor = darken(@buttonArrowColor,25%)
|
||||||
|
@buttonHoverArrowColor = lighten(@buttonArrowColor,10%,derived noAutoInverse)
|
||||||
|
@buttonPressedArrowColor = lighten(@buttonArrowColor,20%,derived noAutoInverse)
|
||||||
|
|
||||||
# Drop (use lazy colors for IntelliJ platform themes, which usually do not specify these colors)
|
# Drop (use lazy colors for IntelliJ platform themes, which usually do not specify these colors)
|
||||||
@dropCellBackground=darken(List.selectionBackground,10%,lazy)
|
@dropCellBackground = darken(List.selectionBackground,10%,lazy)
|
||||||
@dropCellForeground=lazy(List.selectionForeground)
|
@dropCellForeground = lazy(List.selectionForeground)
|
||||||
@dropLineColor=lighten(List.selectionBackground,10%,lazy)
|
@dropLineColor = lighten(List.selectionBackground,10%,lazy)
|
||||||
@dropLineShortColor=lighten(List.selectionBackground,30%,lazy)
|
@dropLineShortColor = lighten(List.selectionBackground,30%,lazy)
|
||||||
|
|
||||||
|
|
||||||
#---- system colors ----
|
#---- system colors ----
|
||||||
|
|
||||||
activeCaption=#434E60
|
activeCaption = #434E60
|
||||||
inactiveCaption=#393C3D
|
inactiveCaption = #393C3D
|
||||||
controlHighlight=darken($controlShadow,20%)
|
controlHighlight = darken($controlShadow,20%)
|
||||||
controlLtHighlight=darken($controlShadow,25%)
|
controlLtHighlight = darken($controlShadow,25%)
|
||||||
controlDkShadow=lighten($controlShadow,10%)
|
controlDkShadow = lighten($controlShadow,10%)
|
||||||
|
|
||||||
|
|
||||||
#---- Button ----
|
#---- Button ----
|
||||||
|
|
||||||
Button.background=#4c5052
|
Button.background = #4c5052
|
||||||
Button.hoverBackground=lighten($Button.background,3%,derived)
|
Button.hoverBackground = lighten($Button.background,3%,derived)
|
||||||
Button.pressedBackground=lighten($Button.background,6%,derived)
|
Button.pressedBackground = lighten($Button.background,6%,derived)
|
||||||
Button.selectedBackground=lighten($Button.background,10%,derived)
|
Button.selectedBackground = lighten($Button.background,10%,derived)
|
||||||
Button.selectedForeground=@foreground
|
Button.selectedForeground = @foreground
|
||||||
Button.disabledSelectedBackground=lighten($Button.background,3%,derived)
|
Button.disabledSelectedBackground = lighten($Button.background,3%,derived)
|
||||||
|
|
||||||
Button.borderColor=#5e6060
|
Button.borderColor = #5e6060
|
||||||
Button.disabledBorderColor=#5e6060
|
Button.disabledBorderColor = $Button.borderColor
|
||||||
Button.focusedBorderColor=#466d94
|
Button.focusedBorderColor = $Component.focusedBorderColor
|
||||||
Button.hoverBorderColor=$Button.focusedBorderColor
|
Button.hoverBorderColor = $Button.focusedBorderColor
|
||||||
|
|
||||||
Button.default.background=#365880
|
Button.innerFocusWidth = 1
|
||||||
Button.default.foreground=#bbb
|
|
||||||
Button.default.hoverBackground=lighten($Button.default.background,3%,derived)
|
|
||||||
Button.default.pressedBackground=lighten($Button.default.background,6%,derived)
|
|
||||||
Button.default.borderColor=#4c708c
|
|
||||||
Button.default.hoverBorderColor=#537699
|
|
||||||
Button.default.focusedBorderColor=#537699
|
|
||||||
Button.default.focusColor=#43688c
|
|
||||||
Button.default.boldText=true
|
|
||||||
|
|
||||||
Button.toolbar.hoverBackground=lighten($Button.background,1%,derived)
|
Button.default.background = #365880
|
||||||
Button.toolbar.pressedBackground=lighten($Button.background,4%,derived)
|
Button.default.foreground = #bbb
|
||||||
Button.toolbar.selectedBackground=lighten($Button.background,7%,derived)
|
Button.default.hoverBackground = lighten($Button.default.background,3%,derived)
|
||||||
|
Button.default.pressedBackground = lighten($Button.default.background,6%,derived)
|
||||||
|
Button.default.borderColor = #4c708c
|
||||||
|
Button.default.hoverBorderColor = #537699
|
||||||
|
Button.default.focusedBorderColor = #537699
|
||||||
|
Button.default.focusColor = #43688c
|
||||||
|
Button.default.boldText = true
|
||||||
|
|
||||||
|
Button.toolbar.hoverBackground = lighten($Button.background,1%,derived)
|
||||||
|
Button.toolbar.pressedBackground = lighten($Button.background,4%,derived)
|
||||||
|
Button.toolbar.selectedBackground = lighten($Button.background,7%,derived)
|
||||||
|
|
||||||
|
|
||||||
#---- CheckBox ----
|
#---- CheckBox ----
|
||||||
|
|
||||||
# enabled
|
# enabled
|
||||||
CheckBox.icon.borderColor=#6B6B6B
|
CheckBox.icon.borderColor = #6B6B6B
|
||||||
CheckBox.icon.background=#43494A
|
CheckBox.icon.background = #43494A
|
||||||
CheckBox.icon.selectedBorderColor=$CheckBox.icon.borderColor
|
CheckBox.icon.selectedBorderColor = $CheckBox.icon.borderColor
|
||||||
CheckBox.icon.selectedBackground=$CheckBox.icon.background
|
CheckBox.icon.selectedBackground = $CheckBox.icon.background
|
||||||
CheckBox.icon.checkmarkColor=#A7A7A7
|
CheckBox.icon.checkmarkColor = #A7A7A7
|
||||||
|
|
||||||
# disabled
|
# disabled
|
||||||
CheckBox.icon.disabledBorderColor=#545556
|
CheckBox.icon.disabledBorderColor = #545556
|
||||||
CheckBox.icon.disabledBackground=@background
|
CheckBox.icon.disabledBackground = @background
|
||||||
CheckBox.icon.disabledCheckmarkColor=#606060
|
CheckBox.icon.disabledCheckmarkColor = #606060
|
||||||
|
|
||||||
# focused
|
# focused
|
||||||
CheckBox.icon.focusedBorderColor=#466D94
|
CheckBox.icon.focusedBorderColor = #466D94
|
||||||
CheckBox.icon.selectedFocusedBorderColor=#466D94
|
CheckBox.icon.focusedBackground = fade($CheckBox.icon.focusedBorderColor,30%)
|
||||||
|
|
||||||
# hover
|
# hover
|
||||||
CheckBox.icon.hoverBorderColor=$CheckBox.icon.focusedBorderColor
|
CheckBox.icon.hoverBorderColor = $CheckBox.icon.focusedBorderColor
|
||||||
CheckBox.icon.hoverBackground=lighten($CheckBox.icon.background,3%,derived)
|
CheckBox.icon.hoverBackground = lighten($CheckBox.icon.background,3%,derived)
|
||||||
|
|
||||||
# pressed
|
# pressed
|
||||||
CheckBox.icon.pressedBackground=lighten($CheckBox.icon.background,6%,derived)
|
CheckBox.icon.pressedBackground = lighten($CheckBox.icon.background,6%,derived)
|
||||||
|
|
||||||
# used if CheckBox.icon.style=filled
|
|
||||||
|
# used if CheckBox.icon.style = filled
|
||||||
# enabled
|
# enabled
|
||||||
CheckBox.icon[filled].selectedBorderColor=$CheckBox.icon.checkmarkColor
|
CheckBox.icon[filled].selectedBorderColor = $CheckBox.icon.checkmarkColor
|
||||||
CheckBox.icon[filled].selectedBackground=$CheckBox.icon.checkmarkColor
|
CheckBox.icon[filled].selectedBackground = $CheckBox.icon.checkmarkColor
|
||||||
CheckBox.icon[filled].checkmarkColor=$CheckBox.icon.background
|
CheckBox.icon[filled].checkmarkColor = $CheckBox.icon.background
|
||||||
# hover
|
# hover
|
||||||
CheckBox.icon[filled].selectedHoverBackground=darken($CheckBox.icon[filled].selectedBackground,3%)
|
CheckBox.icon[filled].selectedHoverBackground = darken($CheckBox.icon[filled].selectedBackground,3%,derived)
|
||||||
# pressed
|
# pressed
|
||||||
CheckBox.icon[filled].selectedPressedBackground=darken($CheckBox.icon[filled].selectedBackground,6%)
|
CheckBox.icon[filled].selectedPressedBackground = darken($CheckBox.icon[filled].selectedBackground,6%,derived)
|
||||||
|
|
||||||
|
|
||||||
#---- ComboBox ----
|
#---- ComboBox ----
|
||||||
|
|
||||||
ComboBox.buttonEditableBackground=#404445
|
ComboBox.buttonEditableBackground = darken($ComboBox.background,2%)
|
||||||
ComboBox.buttonArrowColor=#9A9DA1
|
|
||||||
ComboBox.buttonDisabledArrowColor=#585858
|
|
||||||
ComboBox.buttonHoverArrowColor=#bbb
|
|
||||||
|
|
||||||
|
|
||||||
#---- Component ----
|
#---- Component ----
|
||||||
|
|
||||||
Component.borderColor=#646464
|
Component.borderColor = #646464
|
||||||
Component.disabledBorderColor=#646464
|
Component.disabledBorderColor = #646464
|
||||||
Component.focusedBorderColor=#466d94
|
Component.focusedBorderColor = #466d94
|
||||||
Component.focusColor=#3d6185
|
Component.focusColor = #3d6185
|
||||||
Component.linkColor=#589df6
|
Component.linkColor = #589df6
|
||||||
Component.grayFilter=-20,-70,100
|
Component.grayFilter = -20,-70,100
|
||||||
|
|
||||||
Component.error.borderColor=desaturate($Component.error.focusedBorderColor,25%)
|
Component.error.borderColor = desaturate($Component.error.focusedBorderColor,25%)
|
||||||
Component.error.focusedBorderColor=#8b3c3c
|
Component.error.focusedBorderColor = #8b3c3c
|
||||||
Component.warning.borderColor=darken(desaturate($Component.warning.focusedBorderColor,20%),10%)
|
Component.warning.borderColor = darken(desaturate($Component.warning.focusedBorderColor,20%),10%)
|
||||||
Component.warning.focusedBorderColor=#ac7920
|
Component.warning.focusedBorderColor = #ac7920
|
||||||
Component.custom.borderColor=desaturate(#f00,50%,relative derived noAutoInverse)
|
Component.custom.borderColor = desaturate(#f00,50%,relative derived noAutoInverse)
|
||||||
|
|
||||||
|
|
||||||
#---- Desktop ----
|
#---- Desktop ----
|
||||||
|
|
||||||
Desktop.background=#3E434C
|
Desktop.background = #3E434C
|
||||||
|
|
||||||
|
|
||||||
#---- DesktopIcon ----
|
#---- DesktopIcon ----
|
||||||
|
|
||||||
DesktopIcon.background=lighten($Desktop.background,10%)
|
DesktopIcon.background = lighten($Desktop.background,10%,derived)
|
||||||
|
|
||||||
|
|
||||||
#---- InternalFrame ----
|
#---- InternalFrame ----
|
||||||
|
|
||||||
InternalFrame.activeTitleBackground=darken(@background,10%)
|
InternalFrame.activeTitleBackground = darken(@background,10%)
|
||||||
InternalFrame.activeTitleForeground=@foreground
|
InternalFrame.activeTitleForeground = @foreground
|
||||||
InternalFrame.inactiveTitleBackground=darken(@background,5%)
|
InternalFrame.inactiveTitleBackground = darken(@background,5%)
|
||||||
InternalFrame.inactiveTitleForeground=@disabledText
|
InternalFrame.inactiveTitleForeground = @disabledText
|
||||||
|
|
||||||
InternalFrame.activeBorderColor=darken(@background,7%)
|
InternalFrame.activeBorderColor = darken(@background,7%)
|
||||||
InternalFrame.inactiveBorderColor=darken(@background,3%)
|
InternalFrame.inactiveBorderColor = darken(@background,3%)
|
||||||
|
|
||||||
InternalFrame.buttonHoverBackground=lighten($InternalFrame.activeTitleBackground,10%,derived)
|
InternalFrame.buttonHoverBackground = lighten($InternalFrame.activeTitleBackground,10%,derived)
|
||||||
InternalFrame.buttonPressedBackground=lighten($InternalFrame.activeTitleBackground,20%,derived)
|
InternalFrame.buttonPressedBackground = lighten($InternalFrame.activeTitleBackground,20%,derived)
|
||||||
InternalFrame.closeHoverBackground=lazy(Actions.Red)
|
InternalFrame.closeHoverBackground = lazy(Actions.Red)
|
||||||
InternalFrame.closePressedBackground=darken(Actions.Red,10%,lazy)
|
InternalFrame.closePressedBackground = darken(Actions.Red,10%,lazy)
|
||||||
InternalFrame.closeHoverForeground=#fff
|
InternalFrame.closeHoverForeground = #fff
|
||||||
InternalFrame.closePressedForeground=#fff
|
InternalFrame.closePressedForeground = #fff
|
||||||
|
|
||||||
InternalFrame.activeDropShadowOpacity=0.5
|
InternalFrame.activeDropShadowOpacity = 0.5
|
||||||
InternalFrame.inactiveDropShadowOpacity=0.75
|
InternalFrame.inactiveDropShadowOpacity = 0.75
|
||||||
|
|
||||||
|
|
||||||
#---- Menu ----
|
#---- Menu ----
|
||||||
|
|
||||||
Menu.icon.arrowColor=#A7A7A7
|
Menu.icon.arrowColor = #A7A7A7
|
||||||
Menu.icon.disabledArrowColor=#606060
|
Menu.icon.disabledArrowColor = #606060
|
||||||
|
|
||||||
|
|
||||||
#---- MenuBar ----
|
#---- MenuBar ----
|
||||||
|
|
||||||
MenuBar.borderColor=#515151
|
MenuBar.borderColor = #515151
|
||||||
|
|
||||||
|
|
||||||
#---- MenuItemCheckBox ----
|
#---- MenuItemCheckBox ----
|
||||||
|
|
||||||
MenuItemCheckBox.icon.checkmarkColor=#A7A7A7
|
MenuItemCheckBox.icon.checkmarkColor = #A7A7A7
|
||||||
MenuItemCheckBox.icon.disabledCheckmarkColor=#606060
|
MenuItemCheckBox.icon.disabledCheckmarkColor = #606060
|
||||||
|
|
||||||
|
|
||||||
#---- PasswordField ----
|
#---- PasswordField ----
|
||||||
|
|
||||||
PasswordField.capsLockIconColor=#ffffff64
|
PasswordField.capsLockIconColor = #ffffff64
|
||||||
|
|
||||||
|
|
||||||
#---- Popup ----
|
#---- Popup ----
|
||||||
|
|
||||||
Popup.dropShadowColor=#000
|
Popup.dropShadowColor = #000
|
||||||
Popup.dropShadowOpacity=0.25
|
Popup.dropShadowOpacity = 0.25
|
||||||
|
|
||||||
|
|
||||||
#---- PopupMenu ----
|
#---- PopupMenu ----
|
||||||
|
|
||||||
PopupMenu.borderColor=#5e5e5e
|
PopupMenu.borderColor = #5e5e5e
|
||||||
|
|
||||||
|
|
||||||
#---- ProgressBar ----
|
#---- ProgressBar ----
|
||||||
|
|
||||||
ProgressBar.background=#555
|
ProgressBar.background = #555
|
||||||
ProgressBar.foreground=#4A88C7
|
ProgressBar.foreground = #4A88C7
|
||||||
ProgressBar.selectionForeground=@foreground
|
ProgressBar.selectionForeground = @foreground
|
||||||
ProgressBar.selectionBackground=@foreground
|
ProgressBar.selectionBackground = @foreground
|
||||||
|
|
||||||
|
|
||||||
#---- RootPane ----
|
#---- RootPane ----
|
||||||
|
|
||||||
RootPane.activeBorderColor=darken(@background,7%,derived)
|
RootPane.activeBorderColor = lighten(@background,7%,derived)
|
||||||
RootPane.inactiveBorderColor=darken(@background,5%,derived)
|
RootPane.inactiveBorderColor = lighten(@background,5%,derived)
|
||||||
|
|
||||||
|
|
||||||
#---- ScrollBar ----
|
#---- ScrollBar ----
|
||||||
|
|
||||||
ScrollBar.track=lighten(@background,1%,derived noAutoInverse)
|
ScrollBar.track = lighten(@background,1%,derived noAutoInverse)
|
||||||
ScrollBar.thumb=lighten($ScrollBar.track,10%,derived noAutoInverse)
|
ScrollBar.thumb = lighten($ScrollBar.track,10%,derived noAutoInverse)
|
||||||
ScrollBar.hoverTrackColor=lighten($ScrollBar.track,4%,derived noAutoInverse)
|
ScrollBar.hoverTrackColor = lighten($ScrollBar.track,4%,derived noAutoInverse)
|
||||||
ScrollBar.hoverThumbColor=lighten($ScrollBar.thumb,10%,derived noAutoInverse)
|
ScrollBar.hoverThumbColor = lighten($ScrollBar.thumb,10%,derived noAutoInverse)
|
||||||
ScrollBar.pressedThumbColor=lighten($ScrollBar.thumb,15%,derived noAutoInverse)
|
ScrollBar.pressedThumbColor = lighten($ScrollBar.thumb,15%,derived noAutoInverse)
|
||||||
ScrollBar.hoverButtonBackground=lighten(@background,5%,derived noAutoInverse)
|
ScrollBar.hoverButtonBackground = lighten(@background,5%,derived noAutoInverse)
|
||||||
ScrollBar.pressedButtonBackground=lighten(@background,10%,derived noAutoInverse)
|
ScrollBar.pressedButtonBackground = lighten(@background,10%,derived noAutoInverse)
|
||||||
|
|
||||||
|
|
||||||
#---- Separator ----
|
#---- Separator ----
|
||||||
|
|
||||||
Separator.foreground=#515151
|
Separator.foreground = #515151
|
||||||
|
|
||||||
|
|
||||||
#---- Slider ----
|
#---- Slider ----
|
||||||
|
|
||||||
Slider.trackColor=#646464
|
Slider.trackValueColor = #4A88C7
|
||||||
Slider.thumbColor=#A6A6A6
|
Slider.trackColor = #646464
|
||||||
Slider.tickColor=#888
|
Slider.thumbColor = $Slider.trackValueColor
|
||||||
Slider.hoverColor=darken($Slider.thumbColor,15%,derived)
|
Slider.tickColor = #888
|
||||||
Slider.disabledForeground=#4c5052
|
Slider.focusedColor = fade($Component.focusColor,70%,derived)
|
||||||
|
Slider.hoverThumbColor = lighten($Slider.thumbColor,5%,derived)
|
||||||
|
Slider.pressedThumbColor = lighten($Slider.thumbColor,8%,derived)
|
||||||
|
Slider.disabledTrackColor = #4c5052
|
||||||
|
Slider.disabledThumbColor = $Slider.disabledTrackColor
|
||||||
|
|
||||||
|
|
||||||
#---- SplitPane ----
|
#---- SplitPane ----
|
||||||
|
|
||||||
SplitPaneDivider.draggingColor=#646464
|
SplitPaneDivider.draggingColor = #646464
|
||||||
SplitPaneDivider.oneTouchHoverArrowColor=#7A7D81
|
|
||||||
|
|
||||||
|
|
||||||
#---- TabbedPane ----
|
#---- TabbedPane ----
|
||||||
|
|
||||||
TabbedPane.underlineColor=#4A88C7
|
TabbedPane.underlineColor = #4A88C7
|
||||||
TabbedPane.disabledUnderlineColor=#7a7a7a
|
TabbedPane.disabledUnderlineColor = #7a7a7a
|
||||||
TabbedPane.hoverColor=darken($TabbedPane.background,5%,derived noAutoInverse)
|
TabbedPane.hoverColor = darken($TabbedPane.background,5%,derived noAutoInverse)
|
||||||
TabbedPane.focusColor=#3d4b5c
|
TabbedPane.focusColor = #3d4b5c
|
||||||
TabbedPane.contentAreaColor=#646464
|
TabbedPane.contentAreaColor = #646464
|
||||||
|
|
||||||
TabbedPane.buttonHoverBackground=darken($TabbedPane.background,5%,derived noAutoInverse)
|
TabbedPane.buttonHoverBackground = darken($TabbedPane.background,5%,derived noAutoInverse)
|
||||||
TabbedPane.buttonPressedBackground=darken($TabbedPane.background,8%,derived noAutoInverse)
|
TabbedPane.buttonPressedBackground = darken($TabbedPane.background,8%,derived noAutoInverse)
|
||||||
|
|
||||||
TabbedPane.closeBackground=null
|
TabbedPane.closeBackground = null
|
||||||
TabbedPane.closeForeground=@disabledText
|
TabbedPane.closeForeground = @disabledText
|
||||||
TabbedPane.closeHoverBackground=lighten($TabbedPane.background,5%,derived)
|
TabbedPane.closeHoverBackground = lighten($TabbedPane.background,5%,derived)
|
||||||
TabbedPane.closeHoverForeground=@foreground
|
TabbedPane.closeHoverForeground = @foreground
|
||||||
TabbedPane.closePressedBackground=lighten($TabbedPane.background,10%,derived)
|
TabbedPane.closePressedBackground = lighten($TabbedPane.background,10%,derived)
|
||||||
TabbedPane.closePressedForeground=$TabbedPane.closeHoverForeground
|
TabbedPane.closePressedForeground = $TabbedPane.closeHoverForeground
|
||||||
|
|
||||||
|
|
||||||
#---- Table ----
|
#---- Table ----
|
||||||
|
|
||||||
Table.gridColor=lighten($Table.background,3%)
|
Table.gridColor = lighten($Table.background,5%)
|
||||||
|
|
||||||
|
|
||||||
#---- TableHeader ----
|
#---- TableHeader ----
|
||||||
|
|
||||||
TableHeader.separatorColor=lighten($TableHeader.background,10%)
|
TableHeader.separatorColor = lighten($TableHeader.background,10%)
|
||||||
TableHeader.bottomSeparatorColor=$TableHeader.separatorColor
|
TableHeader.bottomSeparatorColor = $TableHeader.separatorColor
|
||||||
|
|
||||||
|
|
||||||
#---- TitlePane ----
|
#---- TitlePane ----
|
||||||
|
|
||||||
TitlePane.embeddedForeground=darken($TitlePane.foreground,15%)
|
TitlePane.embeddedForeground = darken($TitlePane.foreground,15%)
|
||||||
TitlePane.buttonHoverBackground=lighten($TitlePane.background,10%,derived)
|
TitlePane.buttonHoverBackground = lighten($TitlePane.background,10%,derived)
|
||||||
TitlePane.buttonPressedBackground=lighten($TitlePane.background,20%,derived)
|
TitlePane.buttonPressedBackground = lighten($TitlePane.background,20%,derived)
|
||||||
|
|
||||||
|
|
||||||
#---- ToggleButton ----
|
#---- ToggleButton ----
|
||||||
|
|
||||||
ToggleButton.selectedBackground=lighten($ToggleButton.background,10%,derived)
|
ToggleButton.selectedBackground = lighten($ToggleButton.background,10%,derived)
|
||||||
ToggleButton.disabledSelectedBackground=lighten($ToggleButton.background,3%,derived)
|
ToggleButton.disabledSelectedBackground = lighten($ToggleButton.background,3%,derived)
|
||||||
|
|
||||||
ToggleButton.toolbar.selectedBackground=lighten($ToggleButton.background,7%,derived)
|
ToggleButton.toolbar.selectedBackground = lighten($ToggleButton.background,7%,derived)
|
||||||
|
|
||||||
|
|
||||||
#---- ToolTip ----
|
#---- ToolTip ----
|
||||||
|
|
||||||
ToolTip.border=4,6,4,6
|
ToolTip.border = 4,6,4,6
|
||||||
ToolTip.background=#1e2123
|
ToolTip.background = #1e2123
|
||||||
|
|
||||||
|
|
||||||
#---- Tree ----
|
#---- Tree ----
|
||||||
|
|
||||||
Tree.hash=#505355
|
Tree.hash = lighten($Tree.background,5%)
|
||||||
|
|||||||
@@ -14,33 +14,46 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# This file is loaded for "FlatLaf IntelliJ" theme (that extend class FlatIntelliJLaf)
|
||||||
|
# and for all light IntelliJ Platform themes.
|
||||||
|
#
|
||||||
|
# Documentation:
|
||||||
|
# - https://www.formdev.com/flatlaf/properties-files/
|
||||||
|
# - https://www.formdev.com/flatlaf/how-to-customize/
|
||||||
|
#
|
||||||
|
# NOTE: Avoid copying the whole content of this file to own properties files.
|
||||||
|
# This will make upgrading to newer FlatLaf versions complex and error-prone.
|
||||||
|
# Instead copy and modify only those properties that you need to alter.
|
||||||
|
#
|
||||||
|
|
||||||
# Colors and style mostly based on IntelliJ theme from IntelliJ IDEA Community Edition,
|
# Colors and style mostly based on IntelliJ theme from IntelliJ IDEA Community Edition,
|
||||||
# which is licensed under the Apache 2.0 license. Copyright 2000-2019 JetBrains s.r.o.
|
# which is licensed under the Apache 2.0 license. Copyright 2000-2019 JetBrains s.r.o.
|
||||||
# See: https://github.com/JetBrains/intellij-community/
|
# See: https://github.com/JetBrains/intellij-community/
|
||||||
|
|
||||||
#---- Button ----
|
#---- Button ----
|
||||||
|
|
||||||
Button.focusedBackground=null
|
Button.focusedBackground = null
|
||||||
|
|
||||||
Button.default.background=#4D8AC9
|
Button.default.background = #4D8AC9
|
||||||
Button.default.foreground=#fff
|
Button.default.foreground = #fff
|
||||||
Button.default.focusedBackground=null
|
Button.default.focusedBackground = null
|
||||||
Button.default.borderColor=#3D75B2
|
Button.default.borderColor = #3D75B2
|
||||||
Button.default.hoverBorderColor=#A9C9F5
|
Button.default.hoverBorderColor = #A9C9F5
|
||||||
Button.default.focusedBorderColor=#A9C9F5
|
Button.default.focusedBorderColor = #A9C9F5
|
||||||
Button.default.focusColor=#97c3f3
|
Button.default.focusColor = #97c3f3
|
||||||
Button.default.boldText=true
|
Button.default.boldText = true
|
||||||
Button.default.borderWidth=1
|
Button.default.borderWidth = 1
|
||||||
|
|
||||||
|
|
||||||
#---- CheckBox ----
|
#---- CheckBox ----
|
||||||
|
|
||||||
CheckBox.icon.style=filled
|
CheckBox.icon.style = filled
|
||||||
|
|
||||||
|
|
||||||
#---- Component ----
|
#---- Component ----
|
||||||
|
|
||||||
Component.focusWidth=2
|
Component.focusWidth = 2
|
||||||
Component.innerFocusWidth=0
|
Component.innerFocusWidth = 0
|
||||||
Component.innerOutlineWidth=0
|
Component.innerOutlineWidth = 0
|
||||||
Component.arrowType=triangle
|
Component.arrowType = triangle
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user