mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2026-02-11 06:27:13 -06:00
Compare commits
420 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69851b7f3a | ||
|
|
92b53bf0df | ||
|
|
93e0496fd2 | ||
|
|
5151951f46 | ||
|
|
58dbccec2d | ||
|
|
90de14d013 | ||
|
|
5f961618bf | ||
|
|
37c375e2fa | ||
|
|
1758c175ed | ||
|
|
96f2a02cfa | ||
|
|
96d4bda6c8 | ||
|
|
02cf6050a1 | ||
|
|
38cf32a2e9 | ||
|
|
2ae7589d14 | ||
|
|
bcb2e1f0a1 | ||
|
|
14932d3f07 | ||
|
|
c3b9dc397d | ||
|
|
58b653f55d | ||
|
|
1dcdc42dde | ||
|
|
58a0a16985 | ||
|
|
a117243f14 | ||
|
|
22411060be | ||
|
|
045263ae58 | ||
|
|
024b6daaf6 | ||
|
|
bd5512c121 | ||
|
|
9afce83a02 | ||
|
|
07a8bd9486 | ||
|
|
bcdc0a8fce | ||
|
|
b295809432 | ||
|
|
52763ab932 | ||
|
|
99666265c9 | ||
|
|
af3e280d74 | ||
|
|
b57e4c0565 | ||
|
|
aca9931560 | ||
|
|
d09e166e4a | ||
|
|
68a7a60ff2 | ||
|
|
f21261914b | ||
|
|
7b11339fdc | ||
|
|
081fd43d98 | ||
|
|
ef2eedfc7c | ||
|
|
0dba9265be | ||
|
|
301aae9b8e | ||
|
|
c63f4e9662 | ||
|
|
47508dc6ac | ||
|
|
3a8879608a | ||
|
|
b221889549 | ||
|
|
c00d99b85f | ||
|
|
0bf87b753d | ||
|
|
53f2730064 | ||
|
|
d487c3b005 | ||
|
|
fef6ae7ff7 | ||
|
|
f6b42754de | ||
|
|
2ae9bb381d | ||
|
|
53bde84710 | ||
|
|
d006ac27ff | ||
|
|
c478d28b71 | ||
|
|
99f7b9ad84 | ||
|
|
ab58101ce3 | ||
|
|
d8f3682dc0 | ||
|
|
1fec7ba553 | ||
|
|
418f55f34e | ||
|
|
05d795b2ae | ||
|
|
a365b750d9 | ||
|
|
0aecfb565f | ||
|
|
0cf4edd9e5 | ||
|
|
dd7b7c6aef | ||
|
|
0bd677c46b | ||
|
|
1a131d5206 | ||
|
|
016e515ae2 | ||
|
|
456ceb3c58 | ||
|
|
2169be1b45 | ||
|
|
49eb0b0201 | ||
|
|
2e222bcdea | ||
|
|
c7fa475128 | ||
|
|
4174b065f3 | ||
|
|
df6256d989 | ||
|
|
c27db56321 | ||
|
|
97bed8554a | ||
|
|
751c0e16e9 | ||
|
|
936de60700 | ||
|
|
f6b64d48ec | ||
|
|
b043da7d4c | ||
|
|
7e47cc2443 | ||
|
|
b8b45f9442 | ||
|
|
66337f9af6 | ||
|
|
54646706a0 | ||
|
|
e8ee037d09 | ||
|
|
6d705e568a | ||
|
|
e768791eba | ||
|
|
2aff7c97f9 | ||
|
|
ca6fc7773e | ||
|
|
a1395a5490 | ||
|
|
61dd4d71d6 | ||
|
|
6beda53238 | ||
|
|
941441d7e1 | ||
|
|
d10ea41b47 | ||
|
|
9458870f70 | ||
|
|
095794bbd1 | ||
|
|
c7fc0aa936 | ||
|
|
a8d98ced61 | ||
|
|
831b3d851a | ||
|
|
8c891c7016 | ||
|
|
5c4706cbc9 | ||
|
|
db66a6c4f0 | ||
|
|
0517e4fc02 | ||
|
|
dd7fa4a87d | ||
|
|
e5956900ea | ||
|
|
3755593c14 | ||
|
|
8ddd3b6d68 | ||
|
|
840083940d | ||
|
|
0cdfd29ecf | ||
|
|
bb32c727b6 | ||
|
|
f978c04750 | ||
|
|
b6a504e121 | ||
|
|
5fae367fab | ||
|
|
6e807f44b2 | ||
|
|
53ebed7f89 | ||
|
|
1c10c41808 | ||
|
|
01170b669b | ||
|
|
b56215e5e3 | ||
|
|
221e801561 | ||
|
|
90edbe23d7 | ||
|
|
5b16a814c8 | ||
|
|
ef01721464 | ||
|
|
efd8cf8236 | ||
|
|
8dbfc6d5d6 | ||
|
|
ef343397d4 | ||
|
|
cae02d31db | ||
|
|
96c78cbc16 | ||
|
|
f8c769644d | ||
|
|
0bd1e413b0 | ||
|
|
07c9ad484a | ||
|
|
5fd5b1206e | ||
|
|
8e107647bd | ||
|
|
12b7389376 | ||
|
|
45332c8126 | ||
|
|
02a9d4e31d | ||
|
|
a4377e81cb | ||
|
|
8d2ed3faf6 | ||
|
|
e7dacb8fef | ||
|
|
60e2ffac5f | ||
|
|
73c37b2018 | ||
|
|
1b3cc223da | ||
|
|
51be7ad832 | ||
|
|
f93d035e4e | ||
|
|
a3885d7a48 | ||
|
|
bbf2331766 | ||
|
|
2164bd363b | ||
|
|
6205e18c45 | ||
|
|
959b3e46fa | ||
|
|
09d8d09aad | ||
|
|
70336e31c7 | ||
|
|
600e0f3d67 | ||
|
|
023e356057 | ||
|
|
27786ec00a | ||
|
|
e52e72c5a8 | ||
|
|
802dd08ce7 | ||
|
|
568ec5a1a2 | ||
|
|
035d196392 | ||
|
|
dd3ffc64b9 | ||
|
|
c9a38f0a13 | ||
|
|
78461a9d5a | ||
|
|
79b8fb910a | ||
|
|
405e3df1f0 | ||
|
|
f7126d154f | ||
|
|
d8df8c9631 | ||
|
|
37b35f9063 | ||
|
|
f61a7288eb | ||
|
|
47a1122f04 | ||
|
|
e1bfabbce5 | ||
|
|
9708fec0e0 | ||
|
|
7f4efaf0a3 | ||
|
|
269eb0ba29 | ||
|
|
428c6b7813 | ||
|
|
db2452a4ec | ||
|
|
7dac3825d7 | ||
|
|
7c99872278 | ||
|
|
64c7318cfc | ||
|
|
a9d6483829 | ||
|
|
13a6b92e47 | ||
|
|
9ba008002b | ||
|
|
8914cf78a1 | ||
|
|
d360375b4f | ||
|
|
1caab194af | ||
|
|
31754eba5d | ||
|
|
3cfa16b8b7 | ||
|
|
f80d2bacf4 | ||
|
|
5df3717d94 | ||
|
|
68897f04a2 | ||
|
|
4cb6aeae36 | ||
|
|
0a765a35bf | ||
|
|
6c0b122fbc | ||
|
|
4da2bd90cb | ||
|
|
f0275192c6 | ||
|
|
df905a1d73 | ||
|
|
ad8ad06f44 | ||
|
|
d6b9e2df62 | ||
|
|
5c9b36556f | ||
|
|
80a8348a99 | ||
|
|
005c9f471e | ||
|
|
b40532a830 | ||
|
|
fc7a4408e9 | ||
|
|
93b5f0081d | ||
|
|
ce049ea3ee | ||
|
|
fcc39b2db5 | ||
|
|
cb70fb4e82 | ||
|
|
2593a43d72 | ||
|
|
e44ff5b72a | ||
|
|
22cb1b50a6 | ||
|
|
a42c413705 | ||
|
|
d59d38dc7c | ||
|
|
77582be7fd | ||
|
|
0cb50355b7 | ||
|
|
a2d66e91ff | ||
|
|
ccdb981917 | ||
|
|
d80b581ace | ||
|
|
53efb6711d | ||
|
|
1de6e875f9 | ||
|
|
95a15c3cf8 | ||
|
|
ab320684f5 | ||
|
|
a284b69a1e | ||
|
|
b590f41254 | ||
|
|
a97076ead5 | ||
|
|
0b6df8be1c | ||
|
|
150bab0b57 | ||
|
|
d3355eda65 | ||
|
|
fbf10e553d | ||
|
|
fb37be5734 | ||
|
|
54e6cefa67 | ||
|
|
33b25c1129 | ||
|
|
44d8545c09 | ||
|
|
7a2808243c | ||
|
|
1be84de26b | ||
|
|
78e37f7ab4 | ||
|
|
e49459fd8b | ||
|
|
52f6e7fc32 | ||
|
|
c8ea61dc79 | ||
|
|
16a769ea61 | ||
|
|
6b880af447 | ||
|
|
475781db91 | ||
|
|
4dae082cd5 | ||
|
|
57405b2f56 | ||
|
|
88576f68fd | ||
|
|
aa4e013097 | ||
|
|
d67cfc911b | ||
|
|
00a3ad738f | ||
|
|
1d39d34d7c | ||
|
|
5f6013edd4 | ||
|
|
3e198ecd28 | ||
|
|
d48b98f582 | ||
|
|
9b839231f7 | ||
|
|
dd80614465 | ||
|
|
8c2be1b406 | ||
|
|
8152b7dad6 | ||
|
|
fb4fe175d9 | ||
|
|
5e03eb9b51 | ||
|
|
ef25575f85 | ||
|
|
b77b338c7a | ||
|
|
0e4fe4e9bb | ||
|
|
f742f83834 | ||
|
|
e6e4e53a73 | ||
|
|
7c594ba7a9 | ||
|
|
0156a9a9d5 | ||
|
|
3facbc0900 | ||
|
|
78cef1b3c7 | ||
|
|
d907c469ed | ||
|
|
cc238d3e34 | ||
|
|
0f9b38895e | ||
|
|
8fa1eae352 | ||
|
|
e13fb25f14 | ||
|
|
e36f942129 | ||
|
|
d34619824c | ||
|
|
80297f113f | ||
|
|
80f51bfe1e | ||
|
|
f8b9f4c1fa | ||
|
|
587f431ef4 | ||
|
|
65a4f66d2c | ||
|
|
a253b6c0cf | ||
|
|
efcbc1fbdb | ||
|
|
e560f9cbd6 | ||
|
|
80235d53f4 | ||
|
|
892b9a732e | ||
|
|
d8a0a015e4 | ||
|
|
e60e3b9fae | ||
|
|
6715f01b8c | ||
|
|
465af9bc41 | ||
|
|
d10bcfc72f | ||
|
|
942e5b9cd1 | ||
|
|
51a90d32f8 | ||
|
|
ac46632e73 | ||
|
|
1192bef1ae | ||
|
|
b9ec382589 | ||
|
|
5ecf19ef4f | ||
|
|
9636809b4d | ||
|
|
ba1c1ed952 | ||
|
|
7452390614 | ||
|
|
69042e42b7 | ||
|
|
1e93deab2a | ||
|
|
16ea809bb3 | ||
|
|
78aa4343b7 | ||
|
|
6815109e15 | ||
|
|
e34fbcec58 | ||
|
|
bb2a21270b | ||
|
|
49b9ec9025 | ||
|
|
a2e896e102 | ||
|
|
2e1ef647a9 | ||
|
|
f0c314df80 | ||
|
|
4db39828ef | ||
|
|
b2d40825ac | ||
|
|
6df5d3334e | ||
|
|
0e982df90c | ||
|
|
3834d93c9d | ||
|
|
16920a5b82 | ||
|
|
d66e35fdde | ||
|
|
d93dde0a03 | ||
|
|
2d232124dd | ||
|
|
ac6702fcf7 | ||
|
|
c4b016c9c8 | ||
|
|
6baa583a28 | ||
|
|
82df2ecfa9 | ||
|
|
06b3de720a | ||
|
|
b0edd5659f | ||
|
|
bb5c2eea10 | ||
|
|
e31e4dfe3a | ||
|
|
caf2cd8487 | ||
|
|
15c6f11a5e | ||
|
|
a4ea88f4be | ||
|
|
36d5747fbf | ||
|
|
3d8c535ffa | ||
|
|
1c067d0284 | ||
|
|
b6be0462a5 | ||
|
|
cce91ea16d | ||
|
|
d756041b06 | ||
|
|
2d0eb0a05b | ||
|
|
02f3239669 | ||
|
|
14a9240c45 | ||
|
|
c659638fb4 | ||
|
|
fd15b63044 | ||
|
|
263e6c34b5 | ||
|
|
eb62a3dc17 | ||
|
|
161ee090a8 | ||
|
|
560ec437b9 | ||
|
|
ccd0597b35 | ||
|
|
c5c0a3768a | ||
|
|
5aa2d24d58 | ||
|
|
e0dddfceba | ||
|
|
08ca2aa266 | ||
|
|
fe15758e59 | ||
|
|
674efae184 | ||
|
|
4a65bc88d5 | ||
|
|
a8f3d59729 | ||
|
|
a2c0df5891 | ||
|
|
dc33c26960 | ||
|
|
cdbdccf1ad | ||
|
|
397c369114 | ||
|
|
6f9bbb184a | ||
|
|
b12c818862 | ||
|
|
9118dcf925 | ||
|
|
d333d0c9e4 | ||
|
|
7f9cf6f45c | ||
|
|
9b465cb550 | ||
|
|
9144b7206e | ||
|
|
dd14843f2e | ||
|
|
299bd67151 | ||
|
|
4d4bb3fd7f | ||
|
|
7fd64a1b73 | ||
|
|
e3e8765b91 | ||
|
|
435cf05f9f | ||
|
|
943dfe0619 | ||
|
|
be7114d3e6 | ||
|
|
713a01bfa9 | ||
|
|
ac291b688d | ||
|
|
84f7e244f2 | ||
|
|
4a8207f367 | ||
|
|
9cfd4d27e9 | ||
|
|
1b23cfd747 | ||
|
|
5801bf3bdf | ||
|
|
2b1c55ee67 | ||
|
|
925ddaa63a | ||
|
|
2b60b18d47 | ||
|
|
d502406fa2 | ||
|
|
afdbf711f7 | ||
|
|
b4f7b1d71d | ||
|
|
69061cd41c | ||
|
|
8ba7f7f961 | ||
|
|
5e5aa17e14 | ||
|
|
551f5fc929 | ||
|
|
4e7b0d11d0 | ||
|
|
06bc53692a | ||
|
|
007ee38cb4 | ||
|
|
82192bef91 | ||
|
|
0c51dfe19c | ||
|
|
24a9fa1ccc | ||
|
|
14b06507cb | ||
|
|
b46233087b | ||
|
|
28fb2e2a08 | ||
|
|
943e211cf1 | ||
|
|
ad0a13004e | ||
|
|
04bb6a5275 | ||
|
|
d3c917eac1 | ||
|
|
4c13271a5b | ||
|
|
20027c2db7 | ||
|
|
6affc70a66 | ||
|
|
ab4c9bdeda | ||
|
|
b4a9c9b7f5 | ||
|
|
5e20d50abf | ||
|
|
53abbbbe56 | ||
|
|
1938cb586d | ||
|
|
50490ece84 | ||
|
|
f291cc2bd3 | ||
|
|
2542c8bd53 | ||
|
|
b457fd634e | ||
|
|
041fd0e0cd | ||
|
|
a983edde1e | ||
|
|
7eb642dd13 | ||
|
|
e0bc93371e | ||
|
|
db56486506 | ||
|
|
c99be13697 | ||
|
|
0830c78728 | ||
|
|
edade93054 |
71
.github/workflows/ci.yml
vendored
71
.github/workflows/ci.yml
vendored
@@ -19,45 +19,32 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
# test against
|
# test against
|
||||||
# - Java 1.8 (minimum requirement)
|
# - Java 8 (minimum requirement)
|
||||||
# - Java 9 (first version with JPMS)
|
|
||||||
# - Java LTS versions (11, 17, ...)
|
# - Java LTS versions (11, 17, ...)
|
||||||
# - lastest Java version(s)
|
# - lastest Java version(s)
|
||||||
java:
|
java:
|
||||||
- 1.8
|
- 8
|
||||||
- 9
|
|
||||||
- 11 # LTS
|
- 11 # LTS
|
||||||
- 14
|
- 17 # LTS
|
||||||
- 15
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: gradle/wrapper-validation-action@v1
|
- uses: gradle/wrapper-validation-action@v1
|
||||||
|
if: matrix.java == '8'
|
||||||
|
|
||||||
- name: Setup Java ${{ matrix.java }}
|
- name: Setup Java ${{ matrix.java }}
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
java-version: ${{ matrix.java }}
|
java-version: ${{ matrix.java }}
|
||||||
|
distribution: adopt # Java 8 and 11 are pre-installed on ubuntu-latest
|
||||||
- name: Cache Gradle wrapper
|
cache: gradle
|
||||||
uses: actions/cache@v1
|
|
||||||
with:
|
|
||||||
path: ~/.gradle/wrapper
|
|
||||||
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
|
|
||||||
|
|
||||||
- name: Cache Gradle cache
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: ~/.gradle/caches
|
|
||||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
|
|
||||||
restore-keys: ${{ runner.os }}-gradle
|
|
||||||
|
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
run: ./gradlew build
|
run: ./gradlew build
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
if: matrix.java == '11'
|
if: matrix.java == '11'
|
||||||
with:
|
with:
|
||||||
name: FlatLaf-build-artifacts
|
name: FlatLaf-build-artifacts
|
||||||
@@ -72,29 +59,18 @@ jobs:
|
|||||||
needs: build
|
needs: build
|
||||||
if: |
|
if: |
|
||||||
github.event_name == 'push' &&
|
github.event_name == 'push' &&
|
||||||
github.ref == 'refs/heads/main' &&
|
(github.ref == 'refs/heads/main' || startsWith( github.ref, 'refs/heads/develop-' )) &&
|
||||||
github.repository == 'JFormDesigner/FlatLaf'
|
github.repository == 'JFormDesigner/FlatLaf'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Java 11
|
- name: Setup Java 11
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 11
|
||||||
|
distribution: adopt # pre-installed on ubuntu-latest
|
||||||
- name: Cache Gradle wrapper
|
cache: gradle
|
||||||
uses: actions/cache@v1
|
|
||||||
with:
|
|
||||||
path: ~/.gradle/wrapper
|
|
||||||
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
|
|
||||||
|
|
||||||
- name: Cache Gradle cache
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: ~/.gradle/caches
|
|
||||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
|
|
||||||
restore-keys: ${{ runner.os }}-gradle
|
|
||||||
|
|
||||||
- name: Publish snapshot to oss.sonatype.org
|
- name: Publish snapshot to oss.sonatype.org
|
||||||
run: ./gradlew publish :flatlaf-theme-editor:build -Dorg.gradle.internal.publish.checksums.insecure=true
|
run: ./gradlew publish :flatlaf-theme-editor:build -Dorg.gradle.internal.publish.checksums.insecure=true
|
||||||
@@ -123,25 +99,14 @@ jobs:
|
|||||||
github.repository == 'JFormDesigner/FlatLaf'
|
github.repository == 'JFormDesigner/FlatLaf'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Java 11
|
- name: Setup Java 11
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 11
|
||||||
|
distribution: adopt # pre-installed on ubuntu-latest
|
||||||
- name: Cache Gradle wrapper
|
cache: gradle
|
||||||
uses: actions/cache@v1
|
|
||||||
with:
|
|
||||||
path: ~/.gradle/wrapper
|
|
||||||
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
|
|
||||||
|
|
||||||
- name: Cache Gradle cache
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: ~/.gradle/caches
|
|
||||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
|
|
||||||
restore-keys: ${{ runner.os }}-gradle
|
|
||||||
|
|
||||||
- name: Release a new stable version to Maven Central
|
- name: Release a new stable version to Maven Central
|
||||||
run: ./gradlew publish :flatlaf-demo:build :flatlaf-theme-editor:build -Drelease=true
|
run: ./gradlew publish :flatlaf-demo:build :flatlaf-theme-editor:build -Drelease=true
|
||||||
|
|||||||
34
.github/workflows/natives.yml
vendored
34
.github/workflows/natives.yml
vendored
@@ -11,48 +11,40 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- 'flatlaf-natives/flatlaf-natives-windows/**'
|
- 'flatlaf-natives/flatlaf-natives-windows/**'
|
||||||
- '.github/workflows/natives.yml'
|
- '.github/workflows/natives.yml'
|
||||||
|
- 'gradle/wrapper/gradle-wrapper.properties'
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- '*'
|
- '*'
|
||||||
paths:
|
paths:
|
||||||
- 'flatlaf-natives/flatlaf-natives-windows/**'
|
- 'flatlaf-natives/flatlaf-natives-windows/**'
|
||||||
- '.github/workflows/natives.yml'
|
- '.github/workflows/natives.yml'
|
||||||
|
- 'gradle/wrapper/gradle-wrapper.properties'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Windows:
|
Windows:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: gradle/wrapper-validation-action@v1
|
- uses: gradle/wrapper-validation-action@v1
|
||||||
|
|
||||||
- name: Setup Java 1.8
|
- name: Setup Java 11
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
java-version: 1.8
|
java-version: 11
|
||||||
|
distribution: adopt
|
||||||
- name: Cache Gradle wrapper
|
cache: gradle
|
||||||
uses: actions/cache@v1
|
|
||||||
with:
|
|
||||||
path: ~/.gradle/wrapper
|
|
||||||
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
|
|
||||||
|
|
||||||
- name: Cache Gradle cache
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.gradle/caches
|
|
||||||
!~/.gradle/caches/modules-2/modules-2.lock
|
|
||||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
|
|
||||||
restore-keys: ${{ runner.os }}-gradle
|
|
||||||
|
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
run: ./gradlew :flatlaf-natives-windows:build
|
# --no-daemon is necessary on Windows otherwise caching Gradle would fail with:
|
||||||
|
# tar.exe: Couldn't open ~/.gradle/caches/modules-2/modules-2.lock: Permission denied
|
||||||
|
run: ./gradlew :flatlaf-natives-windows:build-natives --no-daemon
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: FlatLaf-natives-windows-build-artifacts
|
name: FlatLaf-natives-windows-build-artifacts
|
||||||
path: |
|
path: |
|
||||||
|
flatlaf-core/src/main/resources/com/formdev/flatlaf/natives
|
||||||
flatlaf-natives/flatlaf-natives-windows/build
|
flatlaf-natives/flatlaf-natives-windows/build
|
||||||
|
|||||||
313
CHANGELOG.md
313
CHANGELOG.md
@@ -1,6 +1,313 @@
|
|||||||
FlatLaf Change Log
|
FlatLaf Change Log
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
## 2.3
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- FileChooser: Added (optional) shortcuts panel. On Windows it contains "Recent
|
||||||
|
Items", "Desktop", "Documents", "This PC" and "Network". On macOS and Linux it
|
||||||
|
is empty/hidden. (issue #100)
|
||||||
|
- Button and ToggleButton: Added missing foreground colors for hover, pressed,
|
||||||
|
focused and selected states. (issue #535)
|
||||||
|
- Table: Optionally paint alternating rows below table if table is smaller than
|
||||||
|
scroll pane. Set UI value `Table.paintOutsideAlternateRows` to `true`.
|
||||||
|
Requires that `Table.alternateRowColor` is set to a color. (issue #504)
|
||||||
|
- ToggleButton: Made the underline placement of tab-style toggle buttons
|
||||||
|
configurable. (PR #530; issue #529)
|
||||||
|
- Added spanish translation. (PR #525)
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- IntelliJ Themes: Fixed `TitledBorder` text color in "Monokai Pro" theme.
|
||||||
|
(issue #524)
|
||||||
|
|
||||||
|
|
||||||
|
## 2.2
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- SplitPane: Allow limiting one-touch expanding to a single side (set client
|
||||||
|
property `JSplitPane.expandableSide` to `"left"` or `"right"`). (issue #355)
|
||||||
|
- TabbedPane: Selected tab underline color now changes depending on whether the
|
||||||
|
focus is within the tab content. (issue #398)
|
||||||
|
- IntelliJ Themes:
|
||||||
|
- Added "Monokai Pro" and "Xcode-Dark" themes.
|
||||||
|
- TabbedPane now use different background color for selected tabs in all "Arc"
|
||||||
|
themes, in "Hiberbee Dark" and in all "Material UI Lite" themes.
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Native window decorations (Windows 10/11 only): Fixed wrong window title
|
||||||
|
character encoding used in Windows taskbar. (issue #502)
|
||||||
|
- Button: Fixed icon layout and preferred width of default buttons that use bold
|
||||||
|
font. (issue #506)
|
||||||
|
- FileChooser: Enabled full row selection for details view to fix alternate row
|
||||||
|
coloring. (issue #512)
|
||||||
|
- SplitPane: Fixed `StackOverflowError` caused by layout loop that may occur
|
||||||
|
under special circumstances. (issue #513)
|
||||||
|
- Table: Slightly changed grid colors to make grid better recognizable. (issue
|
||||||
|
#514)
|
||||||
|
- ToolBar: Fixed endless loop in focus navigation that may occur under special
|
||||||
|
circumstances. (issue #505)
|
||||||
|
- IntelliJ Themes: `Component.accentColor` UI property now has useful theme
|
||||||
|
specific values. (issue #507)
|
||||||
|
|
||||||
|
|
||||||
|
## 2.1
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Menus: Improved usability of submenus. (PR #490; issue #247)
|
||||||
|
- Menus: Scroll large menus using mouse wheel or up/down arrows. (issue #225)
|
||||||
|
- Linux: Support using custom window decorations. Enable with
|
||||||
|
`JFrame.setDefaultLookAndFeelDecorated(true)` and
|
||||||
|
`JDialog.setDefaultLookAndFeelDecorated(true)` before creating a window.
|
||||||
|
(issue #482)
|
||||||
|
- ScrollBar: Added UI value `ScrollBar.minimumButtonSize` to specify minimum
|
||||||
|
scroll arrow button size (if shown). (issue #493)
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- PasswordField: Fixed reveal button appearance in IntelliJ themes. (issue #494)
|
||||||
|
- ScrollBar: Center and scale arrows in scroll up/down buttons (if shown).
|
||||||
|
(issue #493)
|
||||||
|
- TextArea, TextPane and EditorPane: No longer select all text when component is
|
||||||
|
focused for the first time. (issue #498; regression in FlatLaf 2.0)
|
||||||
|
- TabbedPane: Disable all items in "Show Hidden Tabs" popup menu if tabbed pane
|
||||||
|
is disabled.
|
||||||
|
|
||||||
|
#### Incompatibilities
|
||||||
|
|
||||||
|
- Method `FlatUIUtils.paintArrow()` (and class `FlatArrowButton`) now paints
|
||||||
|
arrows one pixel smaller than before. To fix this, increase parameter
|
||||||
|
`arrowSize` by one.
|
||||||
|
|
||||||
|
|
||||||
|
## 2.0.2
|
||||||
|
|
||||||
|
- Native window decorations (Windows 10/11 only): Fixed rendering artifacts on
|
||||||
|
HiDPI screens when dragging window partly offscreen and back into screen
|
||||||
|
bounds. (issue #477)
|
||||||
|
- Repaint component when setting client property `JComponent.outline` (issue
|
||||||
|
#480).
|
||||||
|
- macOS: Fixed NPE when using some icons in main menu items. (issue #483)
|
||||||
|
|
||||||
|
|
||||||
|
## 2.0.1
|
||||||
|
|
||||||
|
- Fixed memory leak in Panel, Separator and ToolBarSeparator. (issue #471;
|
||||||
|
regression in FlatLaf 2.0)
|
||||||
|
- ToolTip: Fixed wrong tooltip location if component overrides
|
||||||
|
`JComponent.getToolTipLocation()` and wants place tooltip under mouse
|
||||||
|
location. (issue #468)
|
||||||
|
- Extras: Added copy constructor to `FlatSVGIcon`. (issue #465)
|
||||||
|
- Moved `module-info.class` from `META-INF\versions\9\` to root folder of JARs.
|
||||||
|
(issue #466)
|
||||||
|
|
||||||
|
|
||||||
|
## 2.0
|
||||||
|
|
||||||
|
- Added system property `flatlaf.nativeLibraryPath` to load native libraries
|
||||||
|
from a directory. (PR #453)
|
||||||
|
- Fixed "endless recursion in font" exception in
|
||||||
|
`FlatLaf$ActiveFont.createValue()` if `UIManager.getFont()` is invoked from
|
||||||
|
multiple threads. (issue #456)
|
||||||
|
- PasswordField: Preserve reveal button state when switching theme. (PR #442;
|
||||||
|
issue #173)
|
||||||
|
- PasswordField: Reveal button did not show password if
|
||||||
|
`JPasswordField.setEchoChar()` was invoked from application. (PR #442; issue
|
||||||
|
#173)
|
||||||
|
- Slider: Fixed/improved focused indicator color when changing accent color. (PR
|
||||||
|
#375)
|
||||||
|
- TextField:
|
||||||
|
- Improved hover/pressed/selected colors of leading/trailing buttons (e.g.
|
||||||
|
"reveal" button in password field). (issue #452)
|
||||||
|
- Clear button no longer paints over round border. (issue #451)
|
||||||
|
- Extras: Fixed concurrent loading of SVG icons on multiple threads. (issue
|
||||||
|
#459)
|
||||||
|
- Use FlatLaf native window decorations by default when running in
|
||||||
|
[JetBrains Runtime](https://github.com/JetBrains/JetBrainsRuntime/wiki)
|
||||||
|
(instead of using JetBrains custom decorations). System variable
|
||||||
|
`flatlaf.useJetBrainsCustomDecorations` is now `false` by default (was `true`
|
||||||
|
in FlatLaf 1.x). (issue #454)
|
||||||
|
- Native window decorations:
|
||||||
|
- Fixed blurry iconify/maximize/close button hover rectangles at 125%, 150% or
|
||||||
|
175% scaling. (issue #431)
|
||||||
|
- Updated maximize and restore icons for Windows 11 style. (requires Java
|
||||||
|
8u321, 11.0.14, 17.0.2 or 18+)
|
||||||
|
- Updated hover and pressed colors of iconify/maximize/close buttons for
|
||||||
|
Windows 11 style.
|
||||||
|
|
||||||
|
|
||||||
|
## 2.0-rc1
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Styling:
|
||||||
|
- Styling individual components using string in CSS syntax or `java.util.Map`.
|
||||||
|
(PR #341)\
|
||||||
|
E.g.: `mySlider.putClientProperty( "FlatLaf.style", "trackWidth: 2" );`
|
||||||
|
- Style classes allow defining style rules at a single place (in UI defaults)
|
||||||
|
and use them in any component. (PR #388)\
|
||||||
|
E.g.: `mySlider.putClientProperty( "FlatLaf.styleClass", "myclass" );`
|
||||||
|
- Typography defines several font styles for headers and various text sizes,
|
||||||
|
which makes it easy to use consistent font styles across the application. (PR
|
||||||
|
#396)
|
||||||
|
- Native window decorations (Windows 10/11 only):
|
||||||
|
- Unified backgrounds for window title bar is now enabled by default (window
|
||||||
|
title bar has now same background color as window content). Bottom separator
|
||||||
|
for menu bars is no longer painted (if unified background is enabled).
|
||||||
|
- Show Windows 11 snap layouts menu when hovering the mouse over the maximize
|
||||||
|
button. (issues #397 and #407)
|
||||||
|
- Possibility to hide window title bar icon (for single window set client
|
||||||
|
property `JRootPane.titleBarShowIcon` to `false`; for all windows set UI
|
||||||
|
value `TitlePane.showIcon` to `false`).
|
||||||
|
- OptionPane: Hide window title bar icon by default. Can be made visibly by
|
||||||
|
setting UI default `OptionPane.showIcon` to `true`. (issue #416)
|
||||||
|
- No longer show the Java "duke/cup" icon if no window icon image is set.
|
||||||
|
(issue #416)
|
||||||
|
- TextField, FormattedTextField and PasswordField:
|
||||||
|
- Support leading and trailing icons (set client property
|
||||||
|
`JTextField.leadingIcon` or `JTextField.trailingIcon` to a
|
||||||
|
`javax.swing.Icon`). (PR #378; issue #368)
|
||||||
|
- Support leading and trailing components (set client property
|
||||||
|
`JTextField.leadingComponent` or `JTextField.trailingComponent` to a
|
||||||
|
`java.awt.Component`). (PR #386)
|
||||||
|
- Support "clear" (or "cancel") button to empty text field. Only shown if text
|
||||||
|
field is not empty, editable and enabled. (set client property
|
||||||
|
`JTextField.showClearButton` to `true`). (PR #442)
|
||||||
|
- PasswordField: Support reveal (or "eye") button to show password. (see UI
|
||||||
|
value `PasswordField.showRevealButton`) (PR #442; issue #173)
|
||||||
|
- TextComponents: Double/triple-click-and-drag now extends selection by whole
|
||||||
|
words/lines.
|
||||||
|
- Theming improvements: Reworks core themes to make it easier to create new
|
||||||
|
themes (e.g. reduced explicit colors by using color functions). **Note**:
|
||||||
|
There are minor incompatible changes in FlatLaf properties files. (PR #390)
|
||||||
|
- ToolBar:
|
||||||
|
- Toolbars are no longer floatable by default (dots on left side of toolbar
|
||||||
|
that allows dragging toolbar). Use `UIManager.put( "ToolBar.floatable", true
|
||||||
|
)` if you want the old behavior.
|
||||||
|
- Skip components with empty input map (e.g. `JLabel`) when using arrow keys
|
||||||
|
to navigate in focusable buttons (if UI value `ToolBar.focusableButtons` is
|
||||||
|
`true`).
|
||||||
|
- Support arrow-keys-only navigation within focusable buttons of toolbar (if
|
||||||
|
UI value `ToolBar.focusableButtons` is `true`):
|
||||||
|
- arrow keys move focus within toolbar
|
||||||
|
- tab-key moves focus out of toolbar
|
||||||
|
- if moving focus into the toolbar, focus recently focused toolbar button
|
||||||
|
- ComboBox, Spinner, TextField and subclasses: Support specifying width of
|
||||||
|
border (see UI value `Component.borderWidth`).
|
||||||
|
- CheckBox and RadioButton:
|
||||||
|
- Made selected icon better recognizable in **FlatLaf Light** (use blue
|
||||||
|
border), **Dark** and **Darcula** (use lighter border) themes. **IntelliJ**
|
||||||
|
theme is not changed.
|
||||||
|
- Support specifying width of icon border (see UI value
|
||||||
|
`CheckBox.icon.borderWidth`).
|
||||||
|
- Reworked icon UI defaults and added missing ones. **Note**: There are minor
|
||||||
|
incompatible changes in FlatLaf properties files.
|
||||||
|
- Slider: Support specifying width of thumb border (see UI value
|
||||||
|
`Slider.thumbBorderWidth`).
|
||||||
|
- TabbedPane: Optionally paint selected tab as card. (PR #343)
|
||||||
|
- MenuItem:
|
||||||
|
- Paint the selected icon when the item is selected. (PR #415)
|
||||||
|
- Vertically align text if icons have different widths. (issue #437)
|
||||||
|
- Panel: Support painting background with rounded corners. (issue #367)
|
||||||
|
- Added more color functions to class `ColorFunctions` for easy use in
|
||||||
|
applications: `lighten()`, `darken()`, `saturate()`, `desaturate()`, `spin()`,
|
||||||
|
`tint()`, `shade()` and `luma()`.
|
||||||
|
- Support defining fonts in FlatLaf properties files. (issue #384)
|
||||||
|
- Added method `FlatLaf.registerCustomDefaultsSource(URL packageUrl)` for JPMS.
|
||||||
|
(issue #325)
|
||||||
|
- Extras:
|
||||||
|
- Added class `FlatDesktop` for easy integration into macOS screen menu
|
||||||
|
(About, Preferences and Quit) when using Java 8.
|
||||||
|
- `FlatSVGIcon`: Support loading SVG from `URL` (for JPMS), `URI`, `File` or
|
||||||
|
`InputStream`. (issues #419 and #325)
|
||||||
|
- `FlatSVGUtils`: Support loading SVG from `URL` (for JPMS). (issue #325)
|
||||||
|
- SwingX:
|
||||||
|
- New "column control" icon for `JXTable` that scales and uses antialiasing.
|
||||||
|
(issue #434)
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Native window decorations: Fixed `UnsatisfiedLinkError` on Windows 11 for ARM
|
||||||
|
processors. (issue #443)
|
||||||
|
- MenuBar: Do not fill background if non-opaque and having custom background
|
||||||
|
color. (issue #409)
|
||||||
|
- InternalFrame: Fill background to avoid that parent may shine through internal
|
||||||
|
frame if it contains non-opaque components. (better fix for issue #274)
|
||||||
|
- SwingX: Fixed `NullPointerException` in `FlatCaret` when using
|
||||||
|
`org.jdesktop.swingx.prompt.PromptSupport.setPrompt()` on a text field and
|
||||||
|
then switching theme.
|
||||||
|
|
||||||
|
|
||||||
|
## 1.6.5
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Linux: Fixed font problems when running on Oracle Java (OpenJDK is not
|
||||||
|
affected):
|
||||||
|
- oversized text if system font is "Inter" (issue #427)
|
||||||
|
- missing text if system font is "Cantarell" (on Fedora)
|
||||||
|
- MenuItem: Changed accelerator delimiter from `-` to `+`. (Windows and Linux).
|
||||||
|
- ComboBox: Fixed occasional `StackOverflowError` when modifying combo box not
|
||||||
|
on AWT thread. (issue #432)
|
||||||
|
- macOS: Fixed `NullPointerException` when using AWT component
|
||||||
|
`java.awt.Choice`. (issue #439)
|
||||||
|
- Native window decorations: Do not exit application with `UnsatisfiedLinkError`
|
||||||
|
in case that FlatLaf DLL cannot be executed because of restrictions on
|
||||||
|
temporary directory. Instead, continue with default window decorations. (issue
|
||||||
|
#436)
|
||||||
|
|
||||||
|
|
||||||
|
## 1.6.4
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- ComboBox: Fixed regression in FlatLaf 1.6.3 that makes selected item invisible
|
||||||
|
in popup list if `DefaultListCellRenderer` is used as renderer. If using
|
||||||
|
default renderer, it works. (issue #426)
|
||||||
|
|
||||||
|
|
||||||
|
## 1.6.3
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- ComboBox (not editable): Fixed regression in FlatLaf 1.6.2 that may display
|
||||||
|
text in non-editable combo boxes in bold. (issue #423)
|
||||||
|
- Tree: Fixed editing cell issue with custom cell renderer and cell editor that
|
||||||
|
use same component for rendering and editing. (issue #385)
|
||||||
|
|
||||||
|
|
||||||
|
## 1.6.2
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- ComboBox (not editable): Fixed background painted outside of border if round
|
||||||
|
edges are enabled (client property `JComponent.roundRect` is `true`). (similar
|
||||||
|
to issue #382; regression since fixing #330 in FlatLaf 1.4)
|
||||||
|
- ComboBox: Fixed `NullPointerException`, which may occur under special
|
||||||
|
circumstances. (issue #408)
|
||||||
|
- Table: Do not select text in cell editor when it gets focus (when
|
||||||
|
`JTable.surrendersFocusOnKeystroke` is `true`) and
|
||||||
|
`TextComponent.selectAllOnFocusPolicy` is `once` (the default) or `always`.
|
||||||
|
(issue #395)
|
||||||
|
- Linux: Fixed NPE when using `java.awt.TrayIcon`. (issue #405)
|
||||||
|
- FileChooser: Workaround for crash on Windows with Java 17 32-bit (disabled
|
||||||
|
Windows icons). Java 17 64-bit is not affected. (issue #403)
|
||||||
|
- Native window decorations: Fixed layout loop, which may occur under special
|
||||||
|
circumstances and slows down the application. (issue #420)
|
||||||
|
|
||||||
|
|
||||||
|
## 1.6.1
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Native window decorations: Catch `UnsatisfiedLinkError` when trying to load
|
||||||
|
`jawt.dll` to avoid an application crash (Java 8 on Windows 10 only).
|
||||||
|
|
||||||
|
|
||||||
## 1.6
|
## 1.6
|
||||||
|
|
||||||
#### New features and improvements
|
#### New features and improvements
|
||||||
@@ -25,7 +332,7 @@ FlatLaf Change Log
|
|||||||
- ComboBox (editable): Fixed wrong border of internal text field under special
|
- ComboBox (editable): Fixed wrong border of internal text field under special
|
||||||
circumstances.
|
circumstances.
|
||||||
- Spinner: Fixed painting of border corners on left side. (issue #382;
|
- Spinner: Fixed painting of border corners on left side. (issue #382;
|
||||||
regression since FlatLaf 1.4)
|
regression since fixing #330 in FlatLaf 1.4)
|
||||||
- TableHeader: Do not show resize cursor for last column if resizing last column
|
- TableHeader: Do not show resize cursor for last column if resizing last column
|
||||||
is not possible because auto resize mode of table is not off. (issue #332)
|
is not possible because auto resize mode of table is not off. (issue #332)
|
||||||
- TableHeader: Fixed missing trailing vertical separator line if used in upper
|
- TableHeader: Fixed missing trailing vertical separator line if used in upper
|
||||||
@@ -70,8 +377,8 @@ FlatLaf Change Log
|
|||||||
|
|
||||||
#### New features and improvements
|
#### New features and improvements
|
||||||
|
|
||||||
- TextField, FormattedTextField and PasswordField: Support adding extra padding.
|
- TextField, FormattedTextField and PasswordField: Support adding extra padding
|
||||||
(set client property `JTextField.padding` to `Insets`).
|
(set client property `JTextField.padding` to an `Insets`).
|
||||||
- PasswordField: UI delegate `FlatPasswordFieldUI` now extends `FlatTextFieldUI`
|
- PasswordField: UI delegate `FlatPasswordFieldUI` now extends `FlatTextFieldUI`
|
||||||
(instead of `BasicPasswordFieldUI`) to avoid duplicate code and for easier
|
(instead of `BasicPasswordFieldUI`) to avoid duplicate code and for easier
|
||||||
extensibility.
|
extensibility.
|
||||||
|
|||||||
34
README.md
34
README.md
@@ -11,9 +11,9 @@ scales on **HiDPI** displays and runs on Java 8 or newer.
|
|||||||
The look is heavily inspired by **Darcula** and **IntelliJ** themes from
|
The look is heavily inspired by **Darcula** and **IntelliJ** themes from
|
||||||
IntelliJ IDEA 2019.2+ and uses almost the same colors and icons.
|
IntelliJ IDEA 2019.2+ and uses almost the same colors and icons.
|
||||||
|
|
||||||

|

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

|

|
||||||
|
|
||||||
|
|
||||||
IntelliJ Platform Themes
|
IntelliJ Platform Themes
|
||||||
@@ -99,10 +99,22 @@ For more information and documentation visit
|
|||||||
- [Customizing](https://www.formdev.com/flatlaf/customizing/)
|
- [Customizing](https://www.formdev.com/flatlaf/customizing/)
|
||||||
- [How to Customize](https://www.formdev.com/flatlaf/how-to-customize/)
|
- [How to Customize](https://www.formdev.com/flatlaf/how-to-customize/)
|
||||||
- [Properties Files](https://www.formdev.com/flatlaf/properties-files/)
|
- [Properties Files](https://www.formdev.com/flatlaf/properties-files/)
|
||||||
|
- [Components UI Properties](https://www.formdev.com/flatlaf/components/)
|
||||||
|
- [Typography](https://www.formdev.com/flatlaf/typography/)
|
||||||
- [Client Properties](https://www.formdev.com/flatlaf/client-properties/)
|
- [Client Properties](https://www.formdev.com/flatlaf/client-properties/)
|
||||||
- [System Properties](https://www.formdev.com/flatlaf/system-properties/)
|
- [System Properties](https://www.formdev.com/flatlaf/system-properties/)
|
||||||
|
|
||||||
|
|
||||||
|
Theme Editor
|
||||||
|
------------
|
||||||
|
|
||||||
|
The Theme Editor that supports editing FlatLaf theme properties files. See
|
||||||
|
[Theme Editor documentation](https://www.formdev.com/flatlaf/theme-editor/) for
|
||||||
|
details and downloads.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
Buzz
|
Buzz
|
||||||
----
|
----
|
||||||
|
|
||||||
@@ -114,6 +126,13 @@ Buzz
|
|||||||
Applications using FlatLaf
|
Applications using FlatLaf
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
|
-  [Ultorg](https://www.ultorg.com/) (**commercial**) - a
|
||||||
|
visual query system for relational databases
|
||||||
|
-  [MooInfo](https://github.com/rememberber/MooInfo) -
|
||||||
|
visual implementation of OSHI, to view information about the system and
|
||||||
|
hardware
|
||||||
|
-  [Jailer](https://github.com/Wisser/Jailer) 11.2 -
|
||||||
|
database subsetting and relational data browsing tool
|
||||||
- [Apache NetBeans](https://netbeans.apache.org/) 11.3 - IDE for Java, PHP, HTML
|
- [Apache NetBeans](https://netbeans.apache.org/) 11.3 - IDE for Java, PHP, HTML
|
||||||
and much more
|
and much more
|
||||||
- [jclasslib bytecode viewer](https://github.com/ingokegel/jclasslib) 5.5
|
- [jclasslib bytecode viewer](https://github.com/ingokegel/jclasslib) 5.5
|
||||||
@@ -141,13 +160,16 @@ Applications using FlatLaf
|
|||||||
[OpenStreetMap](https://www.openstreetmap.org/) (requires FlatLaf JOSM plugin)
|
[OpenStreetMap](https://www.openstreetmap.org/) (requires FlatLaf JOSM plugin)
|
||||||
- [jAlbum](https://jalbum.net/) 21 (**commercial**) - creates photo album
|
- [jAlbum](https://jalbum.net/) 21 (**commercial**) - creates photo album
|
||||||
websites
|
websites
|
||||||
|
-  [PDF Studio](https://www.qoppa.com/pdfstudio/) 2021
|
||||||
|
(**commercial**) - create, review and edit PDF documents
|
||||||
- [XMLmind XML Editor](https://www.xmlmind.com/xmleditor/) 9.3 (**commercial**)
|
- [XMLmind XML Editor](https://www.xmlmind.com/xmleditor/) 9.3 (**commercial**)
|
||||||
- [Total Validator](https://www.totalvalidator.com/) 15 (**commercial**) -
|
- [Total Validator](https://www.totalvalidator.com/) 15 (**commercial**) -
|
||||||
checks your website
|
checks your website
|
||||||
- [j-lawyer](https://github.com/jlawyerorg/j-lawyer-org) - Kanzleisoftware
|
- [j-lawyer](https://github.com/jlawyerorg/j-lawyer-org) - Kanzleisoftware
|
||||||
- [MegaMek](https://github.com/MegaMek/megamek) v0.47.4 and
|
- [MegaMek](https://github.com/MegaMek/megamek),
|
||||||
[MekHQ](https://github.com/MegaMek/mekhq) v0.47.5 - a turn-based sci-fi board
|
[MegaMekLab](https://github.com/MegaMek/megameklab) and
|
||||||
game
|
[MekHQ](https://github.com/MegaMek/mekhq) v0.47.5+ - a sci-fi tabletop
|
||||||
|
BattleTech simulator suite handling battles, unit building, and campaigns
|
||||||
- [GUIslice Builder](https://github.com/ImpulseAdventure/GUIslice-Builder)
|
- [GUIslice Builder](https://github.com/ImpulseAdventure/GUIslice-Builder)
|
||||||
0.13.b024 - GUI builder for
|
0.13.b024 - GUI builder for
|
||||||
[GUIslice](https://github.com/ImpulseAdventure/GUIslice), a lightweight GUI
|
[GUIslice](https://github.com/ImpulseAdventure/GUIslice), a lightweight GUI
|
||||||
@@ -173,7 +195,7 @@ Applications using FlatLaf
|
|||||||
systems development platform
|
systems development platform
|
||||||
- [JPass](https://github.com/gaborbata/jpass) - password manager with strong
|
- [JPass](https://github.com/gaborbata/jpass) - password manager with strong
|
||||||
encryption
|
encryption
|
||||||
- [Jes - Die Java-EÜR](https://www.jes-eur.de)
|
- [Jes - Die Java-EÜR](https://www.jes-eur.de)
|
||||||
- [Mapton](https://mapton.org/) 2.0
|
- [Mapton](https://mapton.org/) 2.0
|
||||||
([source code](https://github.com/trixon/mapton)) - some kind of map
|
([source code](https://github.com/trixon/mapton)) - some kind of map
|
||||||
application (based on NetBeans platform)
|
application (based on NetBeans platform)
|
||||||
|
|||||||
@@ -14,8 +14,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
val releaseVersion = "1.6"
|
val releaseVersion = "2.3"
|
||||||
val developmentVersion = "2.0-SNAPSHOT"
|
val developmentVersion = "2.4-SNAPSHOT"
|
||||||
|
|
||||||
version = if( java.lang.Boolean.getBoolean( "release" ) ) releaseVersion else developmentVersion
|
version = if( java.lang.Boolean.getBoolean( "release" ) ) releaseVersion else developmentVersion
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
add( "java9Compile", sourceSets.main.get().output )
|
add( "java9Implementation", sourceSets.main.get().output )
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
|
|||||||
@@ -63,10 +63,8 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
|
|||||||
jar {
|
jar {
|
||||||
manifest.attributes( "Multi-Release" to "true" )
|
manifest.attributes( "Multi-Release" to "true" )
|
||||||
|
|
||||||
into( "META-INF/versions/9" ) {
|
from( sourceSets["module-info"].output ) {
|
||||||
from( sourceSets["module-info"].output ) {
|
include( "module-info.class" )
|
||||||
include( "module-info.class" )
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,10 +21,15 @@ plugins {
|
|||||||
`flatlaf-publish`
|
`flatlaf-publish`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val sigtest = configurations.create( "sigtest" )
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testImplementation( "org.junit.jupiter:junit-jupiter-api:5.7.2" )
|
testImplementation( "org.junit.jupiter:junit-jupiter-api:5.7.2" )
|
||||||
testImplementation( "org.junit.jupiter:junit-jupiter-params" )
|
testImplementation( "org.junit.jupiter:junit-jupiter-params" )
|
||||||
testRuntimeOnly( "org.junit.jupiter:junit-jupiter-engine" )
|
testRuntimeOnly( "org.junit.jupiter:junit-jupiter-engine" )
|
||||||
|
|
||||||
|
// https://github.com/jtulach/netbeans-apitest
|
||||||
|
sigtest( "org.netbeans.tools:sigtest-maven-plugin:1.4" )
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
@@ -38,11 +43,6 @@ tasks {
|
|||||||
options.headerOutputDirectory.set( buildDir.resolve( "generated/jni-headers" ) )
|
options.headerOutputDirectory.set( buildDir.resolve( "generated/jni-headers" ) )
|
||||||
}
|
}
|
||||||
|
|
||||||
processResources {
|
|
||||||
// build native libraries
|
|
||||||
dependsOn( ":flatlaf-natives-windows:assemble" )
|
|
||||||
}
|
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
archiveBaseName.set( "flatlaf" )
|
archiveBaseName.set( "flatlaf" )
|
||||||
|
|
||||||
@@ -59,10 +59,60 @@ tasks {
|
|||||||
archiveBaseName.set( "flatlaf" )
|
archiveBaseName.set( "flatlaf" )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
check {
|
||||||
|
dependsOn( "sigtestCheck" )
|
||||||
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
register( "sigtestGenerate" ) {
|
||||||
|
group = "verification"
|
||||||
|
dependsOn( "jar" )
|
||||||
|
|
||||||
|
doLast {
|
||||||
|
ant.withGroovyBuilder {
|
||||||
|
"taskdef"(
|
||||||
|
"name" to "sigtest",
|
||||||
|
"classname" to "org.netbeans.apitest.Sigtest",
|
||||||
|
"classpath" to sigtest.asPath )
|
||||||
|
|
||||||
|
"sigtest"(
|
||||||
|
"action" to "generate",
|
||||||
|
"fileName" to "${project.name}-sigtest.txt",
|
||||||
|
"classpath" to jar.get().outputs.files.asPath,
|
||||||
|
"packages" to "com.formdev.flatlaf,com.formdev.flatlaf.util",
|
||||||
|
"version" to version,
|
||||||
|
"release" to "1.8", // Java version
|
||||||
|
"failonerror" to "true" )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
register( "sigtestCheck" ) {
|
||||||
|
group = "verification"
|
||||||
|
dependsOn( "jar" )
|
||||||
|
|
||||||
|
doLast {
|
||||||
|
ant.withGroovyBuilder {
|
||||||
|
"taskdef"(
|
||||||
|
"name" to "sigtest",
|
||||||
|
"classname" to "org.netbeans.apitest.Sigtest",
|
||||||
|
"classpath" to sigtest.asPath )
|
||||||
|
|
||||||
|
"sigtest"(
|
||||||
|
"action" to "check",
|
||||||
|
"fileName" to "${project.name}-sigtest.txt",
|
||||||
|
"classpath" to jar.get().outputs.files.asPath,
|
||||||
|
"packages" to "com.formdev.flatlaf,com.formdev.flatlaf.util",
|
||||||
|
"version" to version,
|
||||||
|
"release" to "1.8", // Java version
|
||||||
|
"failonerror" to "true" )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flatlafPublish {
|
flatlafPublish {
|
||||||
|
|||||||
1127
flatlaf-core/flatlaf-core-sigtest.txt
Normal file
1127
flatlaf-core/flatlaf-core-sigtest.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -126,6 +126,57 @@ public interface FlatClientProperties
|
|||||||
|
|
||||||
//---- JComponent ---------------------------------------------------------
|
//---- JComponent ---------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the style of a component as String in CSS syntax ("key1: value1; key2: value2; ...")
|
||||||
|
* or as {@link java.util.Map}<String, Object> with binary values.
|
||||||
|
* <p>
|
||||||
|
* The keys are the same as used in UI defaults, but without component type prefix.
|
||||||
|
* E.g. for UI default {@code Slider.thumbSize} use key {@code thumbSize}.
|
||||||
|
* <p>
|
||||||
|
* The syntax of the CSS values is the same as used in FlatLaf properties files
|
||||||
|
* (<a href="https://www.formdev.com/flatlaf/properties-files/">https://www.formdev.com/flatlaf/properties-files/</a>),
|
||||||
|
* but some features are not supported (e.g. variables).
|
||||||
|
* When using a map, the values are not parsed from a string. They must be binary.
|
||||||
|
* <p>
|
||||||
|
* <strong>Components</strong> {@link javax.swing.JComponent}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.String} or {@link java.util.Map}<String, Object><br>
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
String STYLE = "FlatLaf.style";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the style class(es) of a component as String (single class or multiple classes separated by space characters)
|
||||||
|
* or as {@code String[]} or {@link java.util.List}<String> (multiple classes).
|
||||||
|
* <p>
|
||||||
|
* The style rules must be defined in UI defaults either as strings (in CSS syntax)
|
||||||
|
* or as {@link java.util.Map}<String, Object> (with binary values).
|
||||||
|
* The key must be in syntax: {@code [style]type.styleClass}, where the type is optional.
|
||||||
|
* E.g. in FlatLaf properties file:
|
||||||
|
* <pre>{@code
|
||||||
|
* [style]Button.primary = borderColor: #08f; background: #08f; foreground: #fff
|
||||||
|
* [style].secondary = borderColor: #0f8; background: #0f8
|
||||||
|
* }</pre>
|
||||||
|
* or in Java code:
|
||||||
|
* <pre>{@code
|
||||||
|
* UIManager.put( "[style]Button.primary", "borderColor: #08f; background: #08f; foreground: #fff" );
|
||||||
|
* UIManager.put( "[style].secondary", "borderColor: #0f8; background: #0f8" );
|
||||||
|
* }</pre>
|
||||||
|
* The rule "Button.primary" can be applied to buttons only.
|
||||||
|
* The rule ".secondary" can be applied to any component.
|
||||||
|
* <p>
|
||||||
|
* To have similar behavior as in CSS, first the rule without type is applied,
|
||||||
|
* then the rule with type.
|
||||||
|
* E.g. setting style class to "foo" on a {@code JButton} uses rules
|
||||||
|
* from UI default keys "[style].foo" and "[style]Button.foo".
|
||||||
|
* <p>
|
||||||
|
* <strong>Components</strong> {@link javax.swing.JComponent}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.String}, {@code String[]} or {@link java.util.List}<String><br>
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
String STYLE_CLASS = "FlatLaf.styleClass";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies minimum width of a component.
|
* Specifies minimum width of a component.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -280,6 +331,24 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded";
|
String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether the window icon should be shown in the window title bar
|
||||||
|
* (requires enabled window decorations).
|
||||||
|
* <p>
|
||||||
|
* Setting this shows/hides the windows icon
|
||||||
|
* for the {@code JFrame} or {@code JDialog} that contains the root pane.
|
||||||
|
* <p>
|
||||||
|
* This client property has higher priority than UI default {@code TitlePane.showIcon}.
|
||||||
|
* <p>
|
||||||
|
* (requires Window 10)
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
String TITLE_BAR_SHOW_ICON = "JRootPane.titleBarShowIcon";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Background color of window title bar (requires enabled window decorations).
|
* Background color of window title bar (requires enabled window decorations).
|
||||||
* <p>
|
* <p>
|
||||||
@@ -322,8 +391,71 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String SCROLL_PANE_SMOOTH_SCROLLING = "JScrollPane.smoothScrolling";
|
String SCROLL_PANE_SMOOTH_SCROLLING = "JScrollPane.smoothScrolling";
|
||||||
|
|
||||||
|
//---- JSplitPane ---------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies what side of the spilt pane is allowed to expand
|
||||||
|
* via one-touch expanding arrow buttons.
|
||||||
|
* Requires that one-touch expanding is enabled with
|
||||||
|
* {@link javax.swing.JSplitPane#setOneTouchExpandable(boolean)}.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JSplitPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||||
|
* <strong>Allowed Values</strong>
|
||||||
|
* {@link #SPLIT_PANE_EXPANDABLE_SIDE_LEFT} or
|
||||||
|
* {@link #SPLIT_PANE_EXPANDABLE_SIDE_RIGHT}
|
||||||
|
*
|
||||||
|
* @since 2.2
|
||||||
|
*/
|
||||||
|
String SPLIT_PANE_EXPANDABLE_SIDE = "JSplitPane.expandableSide";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow expanding only left/top side of the split pane.
|
||||||
|
*
|
||||||
|
* @see #SPLIT_PANE_EXPANDABLE_SIDE
|
||||||
|
* @since 2.2
|
||||||
|
*/
|
||||||
|
String SPLIT_PANE_EXPANDABLE_SIDE_LEFT = "left";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow expanding only right/bottom side of the split pane.
|
||||||
|
*
|
||||||
|
* @see #SPLIT_PANE_EXPANDABLE_SIDE
|
||||||
|
* @since 2.2
|
||||||
|
*/
|
||||||
|
String SPLIT_PANE_EXPANDABLE_SIDE_RIGHT = "right";
|
||||||
|
|
||||||
//---- JTabbedPane --------------------------------------------------------
|
//---- JTabbedPane --------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies type of the selected tab.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||||
|
* <strong>Allowed Values</strong>
|
||||||
|
* {@link #TABBED_PANE_TAB_TYPE_UNDERLINED} or
|
||||||
|
* {@link #TABBED_PANE_TAB_TYPE_CARD}
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_TYPE = "JTabbedPane.tabType";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paint the selected tab underlined.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TAB_TYPE
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_TYPE_UNDERLINED = "underlined";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paint the selected tab as card.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_TAB_TYPE
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_TYPE_CARD = "card";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether separators are shown between tabs.
|
* Specifies whether separators are shown between tabs.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -665,9 +797,9 @@ public interface FlatClientProperties
|
|||||||
/**
|
/**
|
||||||
* Specifies a component that will be placed at the leading edge of the tabs area.
|
* Specifies a component that will be placed at the leading edge of the tabs area.
|
||||||
* <p>
|
* <p>
|
||||||
* For top and bottom tab placement, the layed out component size will be
|
* For top and bottom tab placement, the laid out component size will be
|
||||||
* the preferred component width and the tab area height.<br>
|
* the preferred component width and the tab area height.<br>
|
||||||
* For left and right tab placement, the layed out component size will be
|
* For left and right tab placement, the laid out component size will be
|
||||||
* the tab area width and the preferred component height.
|
* the tab area width and the preferred component height.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||||
@@ -678,9 +810,9 @@ public interface FlatClientProperties
|
|||||||
/**
|
/**
|
||||||
* Specifies a component that will be placed at the trailing edge of the tabs area.
|
* Specifies a component that will be placed at the trailing edge of the tabs area.
|
||||||
* <p>
|
* <p>
|
||||||
* For top and bottom tab placement, the layed out component size will be
|
* For top and bottom tab placement, the laid out component size will be
|
||||||
* the available horizontal space (minimum is preferred component width) and the tab area height.<br>
|
* the available horizontal space (minimum is preferred component width) and the tab area height.<br>
|
||||||
* For left and right tab placement, the layed out component size will be
|
* For left and right tab placement, the laid out component size will be
|
||||||
* the tab area width and the available vertical space (minimum is preferred component height).
|
* the tab area width and the available vertical space (minimum is preferred component height).
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||||
@@ -745,10 +877,137 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String TEXT_FIELD_PADDING = "JTextField.padding";
|
String TEXT_FIELD_PADDING = "JTextField.padding";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies an icon that will be placed at the leading edge of the text field.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
|
||||||
|
* <strong>Value type</strong> {@link javax.swing.Icon}
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
String TEXT_FIELD_LEADING_ICON = "JTextField.leadingIcon";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies an icon that will be placed at the trailing edge of the text field.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
|
||||||
|
* <strong>Value type</strong> {@link javax.swing.Icon}
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
String TEXT_FIELD_TRAILING_ICON = "JTextField.trailingIcon";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies a component that will be placed at the leading edge of the text field.
|
||||||
|
* <p>
|
||||||
|
* The component will be positioned inside and aligned to the visible text field border.
|
||||||
|
* There is no gap between the visible border and the component.
|
||||||
|
* The laid out component size will be the preferred component width
|
||||||
|
* and the inner text field height.
|
||||||
|
* <p>
|
||||||
|
* The component should be not opaque because the text field border is painted
|
||||||
|
* slightly inside the usually visible border in some cases.
|
||||||
|
* E.g. when focused (in some themes) or when an outline color is specified
|
||||||
|
* (see {@link #OUTLINE}).
|
||||||
|
* <p>
|
||||||
|
* The component is prepared in the following way:
|
||||||
|
* <ul>
|
||||||
|
* <li>Component client property {@link #STYLE_CLASS} is set to {@code inTextField}.
|
||||||
|
* <li>If component is a button or toggle button, client property {@link #BUTTON_TYPE}
|
||||||
|
* is set to {@link #BUTTON_TYPE_TOOLBAR_BUTTON}
|
||||||
|
* and button cursor is set to default cursor (if not set).
|
||||||
|
* <li>If component is a toolbar, client property {@link #STYLE_CLASS}
|
||||||
|
* is set to {@code inTextField} on all toolbar children
|
||||||
|
* and toolbar cursor is set to default cursor (if not set).
|
||||||
|
* </ul>
|
||||||
|
* Because text fields use the text cursor by default and the cursor is inherited by child components,
|
||||||
|
* it may be necessary to explicitly set component cursor if you e.g. need the default arrow cursor.
|
||||||
|
* E.g. {@code comp.setCursor( Cursor.getDefaultCursor() )}.
|
||||||
|
* <p>
|
||||||
|
* Styling is used to modify insets/margins and appearance of buttons and toolbars
|
||||||
|
* so that they fit nicely into the text field and do not increase text field height.
|
||||||
|
* See styles {@code [style]Button.inTextField} and {@code [style]ToolBar.inTextField}
|
||||||
|
* in {@code Flat[Light|Dark]Laf.properties}.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
|
||||||
|
* <strong>Value type</strong> {@link javax.swing.JComponent}
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
String TEXT_FIELD_LEADING_COMPONENT = "JTextField.leadingComponent";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies a component that will be placed at the trailing edge of the text field.
|
||||||
|
* <p>
|
||||||
|
* See {@link #TEXT_FIELD_LEADING_COMPONENT} for details.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
|
||||||
|
* <strong>Value type</strong> {@link javax.swing.JComponent}
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
String TEXT_FIELD_TRAILING_COMPONENT = "JTextField.trailingComponent";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether a "clear" (or "cancel") button is shown on the trailing side
|
||||||
|
* if the text field is not empty, editable and enabled. Default is {@code false}.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
String TEXT_FIELD_SHOW_CLEAR_BUTTON = "JTextField.showClearButton";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the callback that is invoked when a "clear" (or "cancel") button is clicked.
|
||||||
|
* If a callback is specified than it is responsible for clearing the text field.
|
||||||
|
* Without callback, the text field clears itself.
|
||||||
|
* <p>
|
||||||
|
* Either use a {@link java.lang.Runnable}:
|
||||||
|
* <pre>{@code
|
||||||
|
* myTextField.putClientProperty( "JTextField.clearCallback",
|
||||||
|
* (Runnable) () -> {
|
||||||
|
* // clear field here or cancel search
|
||||||
|
* } );
|
||||||
|
* }</pre>
|
||||||
|
* Or use a {@link java.util.function.Consumer}<javax.swing.text.JTextComponent>
|
||||||
|
* that receives the text field as parameter:
|
||||||
|
* <pre>{@code
|
||||||
|
* myTextField.putClientProperty( "JTextField.clearCallback",
|
||||||
|
* (Consumer<JTextComponent>) textField -> {
|
||||||
|
* // clear field here or cancel search
|
||||||
|
* } );
|
||||||
|
* }</pre>
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Runnable}
|
||||||
|
* or {@link java.util.function.Consumer}<javax.swing.text.JTextComponent>
|
||||||
|
*
|
||||||
|
* @see #TEXT_FIELD_SHOW_CLEAR_BUTTON
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
String TEXT_FIELD_CLEAR_CALLBACK = "JTextField.clearCallback";
|
||||||
|
|
||||||
//---- JToggleButton ------------------------------------------------------
|
//---- JToggleButton ------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Height of underline if toggle button type is {@link #BUTTON_TYPE_TAB}.
|
* Placement of underline if toggle button type is {@link #BUTTON_TYPE_TAB}
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Integer}<br>
|
||||||
|
* <strong>SupportedValues:</strong>
|
||||||
|
* {@link SwingConstants#BOTTOM} (default)
|
||||||
|
* {@link SwingConstants#TOP},
|
||||||
|
* {@link SwingConstants#LEFT} or
|
||||||
|
* {@link SwingConstants#RIGHT}
|
||||||
|
*
|
||||||
|
* @since 2.3
|
||||||
|
*/
|
||||||
|
String TAB_BUTTON_UNDERLINE_PLACEMENT = "JToggleButton.tab.underlinePlacement";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thickness of underline if toggle button type is {@link #BUTTON_TYPE_TAB}.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
|
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Integer}
|
* <strong>Value type</strong> {@link java.lang.Integer}
|
||||||
@@ -813,8 +1072,7 @@ public interface FlatClientProperties
|
|||||||
* If the client property is not set, or not a {@link Boolean}, defaultValue is returned.
|
* If the client property is not set, or not a {@link Boolean}, defaultValue is returned.
|
||||||
*/
|
*/
|
||||||
static Boolean clientPropertyBooleanStrict( JComponent c, String key, Boolean defaultValue ) {
|
static Boolean clientPropertyBooleanStrict( JComponent c, String key, Boolean defaultValue ) {
|
||||||
Object value = c.getClientProperty( key );
|
return clientProperty( c, key, defaultValue, Boolean.class );
|
||||||
return (value instanceof Boolean) ? (Boolean) value : defaultValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -831,7 +1089,18 @@ public interface FlatClientProperties
|
|||||||
* If the client property is not set, or not a color, defaultValue is returned.
|
* If the client property is not set, or not a color, defaultValue is returned.
|
||||||
*/
|
*/
|
||||||
static Color clientPropertyColor( JComponent c, String key, Color defaultValue ) {
|
static Color clientPropertyColor( JComponent c, String key, Color defaultValue ) {
|
||||||
|
return clientProperty( c, key, defaultValue, Color.class );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of the specified client property if it is an instance of
|
||||||
|
* the specified type. Otherwise, defaultValue is returned.
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
@SuppressWarnings( "unchecked" )
|
||||||
|
static <T> T clientProperty( JComponent c, String key, T defaultValue, Class<T> type ) {
|
||||||
Object value = c.getClientProperty( key );
|
Object value = c.getClientProperty( key );
|
||||||
return (value instanceof Color) ? (Color) value : defaultValue;
|
return type.isInstance( value ) ? (T) value : defaultValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import javax.swing.UIDefaults;
|
|||||||
* Allows loading of additional .properties files from addon JARs.
|
* Allows loading of additional .properties files from addon JARs.
|
||||||
* {@link java.util.ServiceLoader} is used to load extensions of this class from addon JARs.
|
* {@link java.util.ServiceLoader} is used to load extensions of this class from addon JARs.
|
||||||
* <p>
|
* <p>
|
||||||
* If you extend this class in a addon JAR, you also have to add a text file named
|
* If you extend this class in an addon JAR, you also have to add a text file named
|
||||||
* {@code META-INF/services/com.formdev.flatlaf.FlatDefaultsAddon}
|
* {@code META-INF/services/com.formdev.flatlaf.FlatDefaultsAddon}
|
||||||
* to the addon JAR. The file must contain a single line with the class name.
|
* to the addon JAR. The file must contain a single line with the class name.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -61,7 +61,7 @@ public abstract class FlatDefaultsAddon
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the priority used to sort addon loading.
|
* Returns the priority used to sort addon loading.
|
||||||
* The order is only important if you want overwrite UI defaults of other addons.
|
* The order is only important if you want to overwrite UI defaults of other addons.
|
||||||
* Lower numbers mean higher priority.
|
* Lower numbers mean higher priority.
|
||||||
* Returns 10000 by default.
|
* Returns 10000 by default.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package com.formdev.flatlaf;
|
|||||||
/**
|
/**
|
||||||
* Default color palette for action icons and object icons.
|
* Default color palette for action icons and object icons.
|
||||||
* <p>
|
* <p>
|
||||||
* The idea is to use only this well defined set of colors in SVG icons and
|
* The idea is to use only this well-defined set of colors in SVG icons, and
|
||||||
* then they are replaced at runtime to dark variants or to other theme colors.
|
* then they are replaced at runtime to dark variants or to other theme colors.
|
||||||
* Then a single SVG icon (light variant) can be used for dark themes too.
|
* Then a single SVG icon (light variant) can be used for dark themes too.
|
||||||
* IntelliJ Platform uses this mechanism to allow themes to change IntelliJ Platform icons.
|
* IntelliJ Platform uses this mechanism to allow themes to change IntelliJ Platform icons.
|
||||||
@@ -35,7 +35,7 @@ package com.formdev.flatlaf;
|
|||||||
* <p>
|
* <p>
|
||||||
* You may use these colors also in your application (outside of SVG icons), but do
|
* You may use these colors also in your application (outside of SVG icons), but do
|
||||||
* not use the RGB values defined in this enum.<br>
|
* not use the RGB values defined in this enum.<br>
|
||||||
* Instead use {@code UIManager.getColor( FlatIconColors.ACTIONS_GREY.key )}.
|
* Instead, use {@code UIManager.getColor( FlatIconColors.ACTIONS_GREY.key )}.
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -596,7 +596,7 @@ class FlatInputMaps
|
|||||||
//---- class LazyInputMapEx -----------------------------------------------
|
//---- class LazyInputMapEx -----------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lazily creates a input map.
|
* Lazily creates an input map.
|
||||||
* Similar to {@link UIDefaults.LazyInputMap}, but can use multiple bindings arrays.
|
* Similar to {@link UIDefaults.LazyInputMap}, but can use multiple bindings arrays.
|
||||||
*/
|
*/
|
||||||
private static class LazyInputMapEx
|
private static class LazyInputMapEx
|
||||||
|
|||||||
@@ -31,7 +31,9 @@ import java.beans.PropertyChangeEvent;
|
|||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -43,18 +45,21 @@ import java.util.ResourceBundle;
|
|||||||
import java.util.ServiceLoader;
|
import java.util.ServiceLoader;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.function.IntUnaryOperator;
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
import javax.swing.JFrame;
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JMenuBar;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.PopupFactory;
|
import javax.swing.PopupFactory;
|
||||||
import javax.swing.RootPaneContainer;
|
import javax.swing.RootPaneContainer;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIDefaults;
|
import javax.swing.UIDefaults;
|
||||||
import javax.swing.UIDefaults.ActiveValue;
|
import javax.swing.UIDefaults.ActiveValue;
|
||||||
|
import javax.swing.UIDefaults.LazyValue;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.UnsupportedLookAndFeelException;
|
import javax.swing.UnsupportedLookAndFeelException;
|
||||||
import javax.swing.plaf.ColorUIResource;
|
import javax.swing.plaf.ColorUIResource;
|
||||||
@@ -86,6 +91,8 @@ public abstract class FlatLaf
|
|||||||
private static final String DESKTOPFONTHINTS = "awt.font.desktophints";
|
private static final String DESKTOPFONTHINTS = "awt.font.desktophints";
|
||||||
|
|
||||||
private static List<Object> customDefaultsSources;
|
private static List<Object> customDefaultsSources;
|
||||||
|
private static Map<String, String> globalExtraDefaults;
|
||||||
|
private Map<String, String> extraDefaults;
|
||||||
|
|
||||||
private String desktopPropertyName;
|
private String desktopPropertyName;
|
||||||
private String desktopPropertyName2;
|
private String desktopPropertyName2;
|
||||||
@@ -96,6 +103,7 @@ public abstract class FlatLaf
|
|||||||
|
|
||||||
private PopupFactory oldPopupFactory;
|
private PopupFactory oldPopupFactory;
|
||||||
private MnemonicHandler mnemonicHandler;
|
private MnemonicHandler mnemonicHandler;
|
||||||
|
private SubMenuUsabilityHelper subMenuUsabilityHelper;
|
||||||
|
|
||||||
private Consumer<UIDefaults> postInitialization;
|
private Consumer<UIDefaults> postInitialization;
|
||||||
private List<Function<Object, Object>> uiDefaultsGetters;
|
private List<Function<Object, Object>> uiDefaultsGetters;
|
||||||
@@ -159,18 +167,19 @@ public abstract class FlatLaf
|
|||||||
* Returns whether FlatLaf supports custom window decorations.
|
* Returns whether FlatLaf supports custom window decorations.
|
||||||
* This depends on the operating system and on the used Java runtime.
|
* This depends on the operating system and on the used Java runtime.
|
||||||
* <p>
|
* <p>
|
||||||
* This method returns {@code true} on Windows 10 (see exception below), {@code false} otherwise.
|
* This method returns {@code true} on Windows 10/11 (see exception below)
|
||||||
|
* and on Linux, {@code false} otherwise.
|
||||||
* <p>
|
* <p>
|
||||||
* Returns also {@code false} on Windows 10 if:
|
* Returns also {@code false} on Windows 10/11 if:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>FlatLaf native window border support is available (requires Windows 10)</li>
|
* <li>FlatLaf native window border support is available (requires Windows 10/11)</li>
|
||||||
* <li>running in
|
* <li>running in
|
||||||
* <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime 11 (or later)</a>
|
* <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime 11 (or later)</a>
|
||||||
* (<a href="https://github.com/JetBrains/JetBrainsRuntime">source code on github</a>)
|
* (<a href="https://github.com/JetBrains/JetBrainsRuntime">source code on github</a>)
|
||||||
* and JBR supports custom window decorations
|
* and JBR supports custom window decorations
|
||||||
* </li>
|
* </li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* In this cases, custom decorations are enabled by the root pane.
|
* In these cases, custom decorations are enabled by the root pane.
|
||||||
* Usage of {@link JFrame#setDefaultLookAndFeelDecorated(boolean)} or
|
* Usage of {@link JFrame#setDefaultLookAndFeelDecorated(boolean)} or
|
||||||
* {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} is not necessary.
|
* {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} is not necessary.
|
||||||
*/
|
*/
|
||||||
@@ -183,7 +192,7 @@ public abstract class FlatLaf
|
|||||||
FlatNativeWindowBorder.isSupported() )
|
FlatNativeWindowBorder.isSupported() )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return SystemInfo.isWindows_10_orLater;
|
return SystemInfo.isWindows_10_orLater || SystemInfo.isLinux;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -236,6 +245,10 @@ public abstract class FlatLaf
|
|||||||
mnemonicHandler = new MnemonicHandler();
|
mnemonicHandler = new MnemonicHandler();
|
||||||
mnemonicHandler.install();
|
mnemonicHandler.install();
|
||||||
|
|
||||||
|
// install submenu usability helper
|
||||||
|
subMenuUsabilityHelper = new SubMenuUsabilityHelper();
|
||||||
|
subMenuUsabilityHelper.install();
|
||||||
|
|
||||||
// listen to desktop property changes to update UI if system font or scaling changes
|
// listen to desktop property changes to update UI if system font or scaling changes
|
||||||
if( SystemInfo.isWindows ) {
|
if( SystemInfo.isWindows ) {
|
||||||
// Windows 10 allows increasing font size independent of scaling:
|
// Windows 10 allows increasing font size independent of scaling:
|
||||||
@@ -264,6 +277,12 @@ public abstract class FlatLaf
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
||||||
|
|
||||||
|
// make sure that AWT desktop properties are initialized (on Linux)
|
||||||
|
// before invoking toolkit.addPropertyChangeListener()
|
||||||
|
// https://github.com/JFormDesigner/FlatLaf/issues/405#issuecomment-960242342
|
||||||
|
toolkit.getDesktopProperty( "dummy" );
|
||||||
|
|
||||||
toolkit.addPropertyChangeListener( desktopPropertyName, desktopPropertyListener );
|
toolkit.addPropertyChangeListener( desktopPropertyName, desktopPropertyListener );
|
||||||
if( desktopPropertyName2 != null )
|
if( desktopPropertyName2 != null )
|
||||||
toolkit.addPropertyChangeListener( desktopPropertyName2, desktopPropertyListener );
|
toolkit.addPropertyChangeListener( desktopPropertyName2, desktopPropertyListener );
|
||||||
@@ -309,6 +328,12 @@ public abstract class FlatLaf
|
|||||||
mnemonicHandler = null;
|
mnemonicHandler = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// uninstall submenu usability helper
|
||||||
|
if( subMenuUsabilityHelper != null ) {
|
||||||
|
subMenuUsabilityHelper.uninstall();
|
||||||
|
subMenuUsabilityHelper = null;
|
||||||
|
}
|
||||||
|
|
||||||
// restore default link color
|
// restore default link color
|
||||||
new HTMLEditorKit().getStyleSheet().addRule( "a, address { color: blue; }" );
|
new HTMLEditorKit().getStyleSheet().addRule( "a, address { color: blue; }" );
|
||||||
postInitialization = null;
|
postInitialization = null;
|
||||||
@@ -381,6 +406,7 @@ public abstract class FlatLaf
|
|||||||
"EditorPane.inactiveBackground",
|
"EditorPane.inactiveBackground",
|
||||||
"FormattedTextField.disabledBackground",
|
"FormattedTextField.disabledBackground",
|
||||||
"PasswordField.disabledBackground",
|
"PasswordField.disabledBackground",
|
||||||
|
"RootPane.background",
|
||||||
"Spinner.disabledBackground",
|
"Spinner.disabledBackground",
|
||||||
"TextArea.disabledBackground",
|
"TextArea.disabledBackground",
|
||||||
"TextArea.inactiveBackground",
|
"TextArea.inactiveBackground",
|
||||||
@@ -399,7 +425,8 @@ public abstract class FlatLaf
|
|||||||
"Spinner.disabledForeground",
|
"Spinner.disabledForeground",
|
||||||
"ToggleButton.disabledText" );
|
"ToggleButton.disabledText" );
|
||||||
putDefaults( defaults, defaults.getColor( "textText" ),
|
putDefaults( defaults, defaults.getColor( "textText" ),
|
||||||
"DesktopIcon.foreground" );
|
"DesktopIcon.foreground",
|
||||||
|
"RootPane.foreground" );
|
||||||
|
|
||||||
initFonts( defaults );
|
initFonts( defaults );
|
||||||
initIconColors( defaults, isDark() );
|
initIconColors( defaults, isDark() );
|
||||||
@@ -409,7 +436,7 @@ public abstract class FlatLaf
|
|||||||
// (using defaults.remove() to avoid that lazy value is resolved and icon loaded here)
|
// (using defaults.remove() to avoid that lazy value is resolved and icon loaded here)
|
||||||
Object icon = defaults.remove( "InternalFrame.icon" );
|
Object icon = defaults.remove( "InternalFrame.icon" );
|
||||||
defaults.put( "InternalFrame.icon", icon );
|
defaults.put( "InternalFrame.icon", icon );
|
||||||
defaults.put( "TitlePane.icon", icon );
|
defaults.put( "TitlePane.icon", icon ); // no longer used, but keep for compatibility
|
||||||
|
|
||||||
// get addons and sort them by priority
|
// get addons and sort them by priority
|
||||||
ServiceLoader<FlatDefaultsAddon> addonLoader = ServiceLoader.load( FlatDefaultsAddon.class );
|
ServiceLoader<FlatDefaultsAddon> addonLoader = ServiceLoader.load( FlatDefaultsAddon.class );
|
||||||
@@ -425,6 +452,10 @@ public abstract class FlatLaf
|
|||||||
else
|
else
|
||||||
UIDefaultsLoader.loadDefaultsFromProperties( getClass(), addons, getAdditionalDefaults(), isDark(), defaults );
|
UIDefaultsLoader.loadDefaultsFromProperties( getClass(), addons, getAdditionalDefaults(), isDark(), defaults );
|
||||||
|
|
||||||
|
// setup default font after loading defaults from properties
|
||||||
|
// to allow defining "defaultFont" in properties
|
||||||
|
initDefaultFont( defaults );
|
||||||
|
|
||||||
// use Aqua MenuBarUI if Mac screen menubar is enabled
|
// use Aqua MenuBarUI if Mac screen menubar is enabled
|
||||||
if( SystemInfo.isMacOS && Boolean.getBoolean( "apple.laf.useScreenMenuBar" ) ) {
|
if( SystemInfo.isMacOS && Boolean.getBoolean( "apple.laf.useScreenMenuBar" ) ) {
|
||||||
defaults.put( "MenuBarUI", "com.apple.laf.AquaMenuBarUI" );
|
defaults.put( "MenuBarUI", "com.apple.laf.AquaMenuBarUI" );
|
||||||
@@ -464,7 +495,15 @@ public abstract class FlatLaf
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Properties getAdditionalDefaults() {
|
protected Properties getAdditionalDefaults() {
|
||||||
return null;
|
if( globalExtraDefaults == null && extraDefaults == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Properties properties = new Properties();
|
||||||
|
if( globalExtraDefaults != null )
|
||||||
|
properties.putAll( globalExtraDefaults );
|
||||||
|
if( extraDefaults != null )
|
||||||
|
properties.putAll( extraDefaults );
|
||||||
|
return properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initResourceBundle( UIDefaults defaults, String bundleName ) {
|
private void initResourceBundle( UIDefaults defaults, String bundleName ) {
|
||||||
@@ -507,8 +546,25 @@ public abstract class FlatLaf
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initFonts( UIDefaults defaults ) {
|
private void initFonts( UIDefaults defaults ) {
|
||||||
|
// use active value for all fonts to allow changing fonts in all components with:
|
||||||
|
// UIManager.put( "defaultFont", myFont );
|
||||||
|
// (this is similar as in Nimbus L&F)
|
||||||
|
Object activeFont = new ActiveFont( null, null, -1, 0, 0, 0, 0 );
|
||||||
|
|
||||||
|
// override fonts
|
||||||
|
for( Object key : defaults.keySet() ) {
|
||||||
|
if( key instanceof String && (((String)key).endsWith( ".font" ) || ((String)key).endsWith( "Font" )) )
|
||||||
|
defaults.put( key, activeFont );
|
||||||
|
}
|
||||||
|
|
||||||
|
// add fonts that are not set in BasicLookAndFeel
|
||||||
|
defaults.put( "RootPane.font", activeFont );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initDefaultFont( UIDefaults defaults ) {
|
||||||
FontUIResource uiFont = null;
|
FontUIResource uiFont = null;
|
||||||
|
|
||||||
|
// determine UI font based on operating system
|
||||||
if( SystemInfo.isWindows ) {
|
if( SystemInfo.isWindows ) {
|
||||||
Font winFont = (Font) Toolkit.getDefaultToolkit().getDesktopProperty( "win.messagebox.font" );
|
Font winFont = (Font) Toolkit.getDefaultToolkit().getDesktopProperty( "win.messagebox.font" );
|
||||||
if( winFont != null ) {
|
if( winFont != null ) {
|
||||||
@@ -551,22 +607,20 @@ public abstract class FlatLaf
|
|||||||
if( uiFont == null )
|
if( uiFont == null )
|
||||||
uiFont = createCompositeFont( Font.SANS_SERIF, Font.PLAIN, 12 );
|
uiFont = createCompositeFont( Font.SANS_SERIF, Font.PLAIN, 12 );
|
||||||
|
|
||||||
// increase font size if system property "flatlaf.uiScale" is set
|
// get/remove "defaultFont" from defaults if set in properties files
|
||||||
uiFont = UIScale.applyCustomScaleFactor( uiFont );
|
// (use remove() to avoid that ActiveFont.createValue() gets invoked)
|
||||||
|
Object defaultFont = defaults.remove( "defaultFont" );
|
||||||
|
|
||||||
// use active value for all fonts to allow changing fonts in all components
|
// use font from OS as base font and derive the UI font from it
|
||||||
// (similar as in Nimbus L&F) with:
|
if( defaultFont instanceof ActiveFont ) {
|
||||||
// UIManager.put( "defaultFont", myFont );
|
Font baseFont = uiFont;
|
||||||
Object activeFont = new ActiveFont( 1 );
|
uiFont = ((ActiveFont)defaultFont).derive( baseFont, fontSize -> {
|
||||||
|
return Math.round( fontSize * UIScale.computeFontScaleFactor( baseFont ) );
|
||||||
// override fonts
|
} );
|
||||||
for( Object key : defaults.keySet() ) {
|
|
||||||
if( key instanceof String && (((String)key).endsWith( ".font" ) || ((String)key).endsWith( "Font" )) )
|
|
||||||
defaults.put( key, activeFont );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// use smaller font for progress bar
|
// increase font size if system property "flatlaf.uiScale" is set
|
||||||
defaults.put( "ProgressBar.font", new ActiveFont( 0.85f ) );
|
uiFont = UIScale.applyCustomScaleFactor( uiFont );
|
||||||
|
|
||||||
// set default font
|
// set default font
|
||||||
defaults.put( "defaultFont", uiFont );
|
defaults.put( "defaultFont", uiFont );
|
||||||
@@ -580,11 +634,9 @@ public abstract class FlatLaf
|
|||||||
return (font instanceof FontUIResource) ? (FontUIResource) font : new FontUIResource( font );
|
return (font instanceof FontUIResource) ? (FontUIResource) font : new FontUIResource( font );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @since 1.1 */
|
||||||
* @since 1.1
|
|
||||||
*/
|
|
||||||
public static ActiveValue createActiveFontValue( float scaleFactor ) {
|
public static ActiveValue createActiveFontValue( float scaleFactor ) {
|
||||||
return new ActiveFont( scaleFactor );
|
return new ActiveFont( null, null, -1, 0, 0, 0, scaleFactor );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -718,6 +770,9 @@ public abstract class FlatLaf
|
|||||||
* and can therefore override all UI defaults.
|
* and can therefore override all UI defaults.
|
||||||
* <p>
|
* <p>
|
||||||
* Invoke this method before setting the look and feel.
|
* Invoke this method before setting the look and feel.
|
||||||
|
* <p>
|
||||||
|
* If using Java modules, the package must be opened in {@code module-info.java}.
|
||||||
|
* Otherwise, use {@link #registerCustomDefaultsSource(URL)}.
|
||||||
*
|
*
|
||||||
* @param packageName a package name (e.g. "com.myapp.resources")
|
* @param packageName a package name (e.g. "com.myapp.resources")
|
||||||
*/
|
*/
|
||||||
@@ -759,6 +814,32 @@ public abstract class FlatLaf
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a package where FlatLaf searches for properties files with custom UI defaults.
|
||||||
|
* <p>
|
||||||
|
* See {@link #registerCustomDefaultsSource(String)} for details.
|
||||||
|
* <p>
|
||||||
|
* This method is useful if using Java modules and the package containing the properties files
|
||||||
|
* is not opened in {@code module-info.java}.
|
||||||
|
* E.g. {@code FlatLaf.registerCustomDefaultsSource( MyApp.class.getResource( "/com/myapp/themes/" ) )}.
|
||||||
|
*
|
||||||
|
* @param packageUrl a package URL
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
public static void registerCustomDefaultsSource( URL packageUrl ) {
|
||||||
|
if( customDefaultsSources == null )
|
||||||
|
customDefaultsSources = new ArrayList<>();
|
||||||
|
customDefaultsSources.add( packageUrl );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public static void unregisterCustomDefaultsSource( URL packageUrl ) {
|
||||||
|
if( customDefaultsSources == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
customDefaultsSources.remove( packageUrl );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a folder where FlatLaf searches for properties files with custom UI defaults.
|
* Registers a folder where FlatLaf searches for properties files with custom UI defaults.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -779,6 +860,102 @@ public abstract class FlatLaf
|
|||||||
customDefaultsSources.remove( folder );
|
customDefaultsSources.remove( folder );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets global extra UI defaults; or {@code null}.
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
public static Map<String, String> getGlobalExtraDefaults() {
|
||||||
|
return globalExtraDefaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets global extra UI defaults, which are only used when setting up the application look and feel.
|
||||||
|
* E.g. using {@link UIManager#setLookAndFeel(LookAndFeel)} or {@link #setup(LookAndFeel)}.
|
||||||
|
* <p>
|
||||||
|
* The global extra defaults are useful for smaller additional defaults that may change.
|
||||||
|
* E.g. accent color. Otherwise, FlatLaf properties files should be used.
|
||||||
|
* See {@link #registerCustomDefaultsSource(String)}.
|
||||||
|
* <p>
|
||||||
|
* The keys and values are strings in same format as in FlatLaf properties files.
|
||||||
|
* <p>
|
||||||
|
* Sample that setups "FlatLaf Light" theme with red accent color:
|
||||||
|
* <pre>{@code
|
||||||
|
* FlatLaf.setGlobalExtraDefaults( Collections.singletonMap( "@accentColor", "#f00" ) );
|
||||||
|
* FlatLightLaf.setup();
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @see #setExtraDefaults(Map)
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
public static void setGlobalExtraDefaults( Map<String, String> globalExtraDefaults ) {
|
||||||
|
FlatLaf.globalExtraDefaults = globalExtraDefaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets extra UI defaults; or {@code null}.
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
public Map<String, String> getExtraDefaults() {
|
||||||
|
return extraDefaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets extra UI defaults, which are only used when setting up the application look and feel.
|
||||||
|
* E.g. using {@link UIManager#setLookAndFeel(LookAndFeel)} or {@link #setup(LookAndFeel)}.
|
||||||
|
* <p>
|
||||||
|
* The extra defaults are useful for smaller additional defaults that may change.
|
||||||
|
* E.g. accent color. Otherwise, FlatLaf properties files should be used.
|
||||||
|
* See {@link #registerCustomDefaultsSource(String)}.
|
||||||
|
* <p>
|
||||||
|
* The keys and values are strings in same format as in FlatLaf properties files.
|
||||||
|
* <p>
|
||||||
|
* Sample that setups "FlatLaf Light" theme with red accent color:
|
||||||
|
* <pre>{@code
|
||||||
|
* FlatLaf laf = new FlatLightLaf();
|
||||||
|
* laf.setExtraDefaults( Collections.singletonMap( "@accentColor", "#f00" ) );
|
||||||
|
* FlatLaf.setup( laf );
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @see #setGlobalExtraDefaults(Map)
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
public void setExtraDefaults( Map<String, String> extraDefaults ) {
|
||||||
|
this.extraDefaults = extraDefaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a UI defaults value string and converts it into a binary object.
|
||||||
|
* <p>
|
||||||
|
* See: <a href="https://www.formdev.com/flatlaf/properties-files/">https://www.formdev.com/flatlaf/properties-files/</a>
|
||||||
|
*
|
||||||
|
* @param key the key, which is used to determine the value type if parameter {@code valueType} is {@code null}
|
||||||
|
* @param value the value string
|
||||||
|
* @param valueType the expected value type, or {@code null}
|
||||||
|
* @return the binary value
|
||||||
|
* @throws IllegalArgumentException on syntax errors
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
public static Object parseDefaultsValue( String key, String value, Class<?> valueType )
|
||||||
|
throws IllegalArgumentException
|
||||||
|
{
|
||||||
|
// resolve variables
|
||||||
|
value = UIDefaultsLoader.resolveValueFromUIManager( value );
|
||||||
|
|
||||||
|
// parse value
|
||||||
|
Object val = UIDefaultsLoader.parseValue( key, value, valueType, null,
|
||||||
|
v -> UIDefaultsLoader.resolveValueFromUIManager( v ), Collections.emptyList() );
|
||||||
|
|
||||||
|
// create actual value if lazy or active
|
||||||
|
if( val instanceof LazyValue )
|
||||||
|
val = ((LazyValue)val).createValue( null );
|
||||||
|
else if( val instanceof ActiveValue )
|
||||||
|
val = ((ActiveValue)val).createValue( null );
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
private static void reSetLookAndFeel() {
|
private static void reSetLookAndFeel() {
|
||||||
EventQueue.invokeLater( () -> {
|
EventQueue.invokeLater( () -> {
|
||||||
LookAndFeel lookAndFeel = UIManager.getLookAndFeel();
|
LookAndFeel lookAndFeel = UIManager.getLookAndFeel();
|
||||||
@@ -786,7 +963,7 @@ public abstract class FlatLaf
|
|||||||
// re-set current LaF
|
// re-set current LaF
|
||||||
UIManager.setLookAndFeel( lookAndFeel );
|
UIManager.setLookAndFeel( lookAndFeel );
|
||||||
|
|
||||||
// must fire property change events ourself because old and new LaF are the same
|
// must fire property change events ourselves because old and new LaF are the same
|
||||||
PropertyChangeEvent e = new PropertyChangeEvent( UIManager.class, "lookAndFeel", lookAndFeel, lookAndFeel );
|
PropertyChangeEvent e = new PropertyChangeEvent( UIManager.class, "lookAndFeel", lookAndFeel, lookAndFeel );
|
||||||
for( PropertyChangeListener l : UIManager.getPropertyChangeListeners() )
|
for( PropertyChangeListener l : UIManager.getPropertyChangeListeners() )
|
||||||
l.propertyChange( e );
|
l.propertyChange( e );
|
||||||
@@ -830,7 +1007,7 @@ public abstract class FlatLaf
|
|||||||
/**
|
/**
|
||||||
* Returns whether native window decorations are supported on current platform.
|
* Returns whether native window decorations are supported on current platform.
|
||||||
* <p>
|
* <p>
|
||||||
* This requires Windows 10, but may be disabled if running in special environments
|
* This requires Windows 10/11, but may be disabled if running in special environments
|
||||||
* (JetBrains Projector, Webswing or WinPE) or if loading native library fails.
|
* (JetBrains Projector, Webswing or WinPE) or if loading native library fails.
|
||||||
* If system property {@link FlatSystemProperties#USE_WINDOW_DECORATIONS} is set to
|
* If system property {@link FlatSystemProperties#USE_WINDOW_DECORATIONS} is set to
|
||||||
* {@code false}, then this method also returns {@code false}.
|
* {@code false}, then this method also returns {@code false}.
|
||||||
@@ -872,12 +1049,23 @@ public abstract class FlatLaf
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Revalidate and repaint all displayable frames and dialogs.
|
* Revalidate and repaint all displayable frames and dialogs.
|
||||||
|
* <p>
|
||||||
|
* Useful to update UI after changing {@code TitlePane.menuBarEmbedded}.
|
||||||
*
|
*
|
||||||
* @since 1.1.2
|
* @since 1.1.2
|
||||||
*/
|
*/
|
||||||
public static void revalidateAndRepaintAllFramesAndDialogs() {
|
public static void revalidateAndRepaintAllFramesAndDialogs() {
|
||||||
for( Window w : Window.getWindows() ) {
|
for( Window w : Window.getWindows() ) {
|
||||||
if( isDisplayableFrameOrDialog( w ) ) {
|
if( isDisplayableFrameOrDialog( w ) ) {
|
||||||
|
// revalidate menu bar
|
||||||
|
JMenuBar menuBar = (w instanceof JFrame)
|
||||||
|
? ((JFrame)w).getJMenuBar()
|
||||||
|
: (w instanceof JDialog
|
||||||
|
? ((JDialog)w).getJMenuBar()
|
||||||
|
: null);
|
||||||
|
if( menuBar != null )
|
||||||
|
menuBar.revalidate();
|
||||||
|
|
||||||
w.revalidate();
|
w.revalidate();
|
||||||
w.repaint();
|
w.repaint();
|
||||||
}
|
}
|
||||||
@@ -886,6 +1074,9 @@ public abstract class FlatLaf
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Repaint all displayable frames and dialogs.
|
* Repaint all displayable frames and dialogs.
|
||||||
|
* <p>
|
||||||
|
* Useful to update UI after changing {@code TitlePane.unifiedBackground},
|
||||||
|
* {@code MenuItem.selectionType} or {@code Component.hideMnemonics}.
|
||||||
*
|
*
|
||||||
* @since 1.1.2
|
* @since 1.1.2
|
||||||
*/
|
*/
|
||||||
@@ -1044,6 +1235,9 @@ public abstract class FlatLaf
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Object getValue( Object key ) {
|
private Object getValue( Object key ) {
|
||||||
|
// use local variable for getters to avoid potential multi-threading issues
|
||||||
|
List<Function<Object, Object>> uiDefaultsGetters = FlatLaf.this.uiDefaultsGetters;
|
||||||
|
|
||||||
if( uiDefaultsGetters == null )
|
if( uiDefaultsGetters == null )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@@ -1059,44 +1253,134 @@ public abstract class FlatLaf
|
|||||||
|
|
||||||
//---- class ActiveFont ---------------------------------------------------
|
//---- class ActiveFont ---------------------------------------------------
|
||||||
|
|
||||||
private static class ActiveFont
|
static class ActiveFont
|
||||||
implements ActiveValue
|
implements ActiveValue
|
||||||
{
|
{
|
||||||
private final float scaleFactor;
|
private final String baseFontKey;
|
||||||
|
private final List<String> families;
|
||||||
|
private final int style;
|
||||||
|
private final int styleChange;
|
||||||
|
private final int absoluteSize;
|
||||||
|
private final int relativeSize;
|
||||||
|
private final float scaleSize;
|
||||||
|
|
||||||
// cache (scaled) font
|
// cache (scaled/derived) font
|
||||||
private Font font;
|
private FontUIResource font;
|
||||||
private Font lastDefaultFont;
|
private Font lastBaseFont;
|
||||||
|
|
||||||
ActiveFont( float scaleFactor ) {
|
private boolean inCreateValue;
|
||||||
this.scaleFactor = scaleFactor;
|
|
||||||
|
/**
|
||||||
|
* @param families list of font families, or {@code null}
|
||||||
|
* @param style new style of font, or {@code -1}
|
||||||
|
* @param styleChange derive style of base font; or {@code 0}
|
||||||
|
* (the lower 16 bits are added; the upper 16 bits are removed)
|
||||||
|
* @param absoluteSize new size of font, or {@code 0}
|
||||||
|
* @param relativeSize added to size of base font, or {@code 0}
|
||||||
|
* @param scaleSize multiply size of base font, or {@code 0}
|
||||||
|
*/
|
||||||
|
ActiveFont( String baseFontKey, List<String> families, int style, int styleChange,
|
||||||
|
int absoluteSize, int relativeSize, float scaleSize )
|
||||||
|
{
|
||||||
|
this.baseFontKey = baseFontKey;
|
||||||
|
this.families = families;
|
||||||
|
this.style = style;
|
||||||
|
this.styleChange = styleChange;
|
||||||
|
this.absoluteSize = absoluteSize;
|
||||||
|
this.relativeSize = relativeSize;
|
||||||
|
this.scaleSize = scaleSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// using synchronized to avoid exception if invoked at the same time on multiple threads
|
||||||
@Override
|
@Override
|
||||||
public Object createValue( UIDefaults table ) {
|
public synchronized Object createValue( UIDefaults table ) {
|
||||||
Font defaultFont = UIManager.getFont( "defaultFont" );
|
if( inCreateValue )
|
||||||
|
throw new IllegalStateException( "FlatLaf: endless recursion in font" );
|
||||||
|
|
||||||
// fallback (to avoid NPE in case that this is used in another Laf)
|
Font baseFont = null;
|
||||||
if( defaultFont == null )
|
|
||||||
defaultFont = UIManager.getFont( "Label.font" );
|
|
||||||
|
|
||||||
if( lastDefaultFont != defaultFont ) {
|
inCreateValue = true;
|
||||||
lastDefaultFont = defaultFont;
|
try {
|
||||||
|
if( baseFontKey != null )
|
||||||
|
baseFont = (Font) UIDefaultsLoader.lazyUIManagerGet( baseFontKey );
|
||||||
|
|
||||||
if( scaleFactor != 1 ) {
|
if( baseFont == null )
|
||||||
// scale font
|
baseFont = UIManager.getFont( "defaultFont" );
|
||||||
int newFontSize = Math.round( defaultFont.getSize() * scaleFactor );
|
|
||||||
font = new FontUIResource( defaultFont.deriveFont( (float) newFontSize ) );
|
// fallback (to avoid NPE in case that this is used in another Laf)
|
||||||
} else {
|
if( baseFont == null )
|
||||||
// make sure that font is a UIResource for LaF switching
|
baseFont = UIManager.getFont( "Label.font" );
|
||||||
font = (defaultFont instanceof UIResource)
|
} finally {
|
||||||
? defaultFont
|
inCreateValue = false;
|
||||||
: new FontUIResource( defaultFont );
|
}
|
||||||
}
|
|
||||||
|
if( lastBaseFont != baseFont ) {
|
||||||
|
lastBaseFont = baseFont;
|
||||||
|
|
||||||
|
font = derive( baseFont, fontSize -> UIScale.scale( fontSize ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
return font;
|
return font;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FontUIResource derive( Font baseFont, IntUnaryOperator scale ) {
|
||||||
|
int baseStyle = baseFont.getStyle();
|
||||||
|
int baseSize = baseFont.getSize();
|
||||||
|
|
||||||
|
// new style
|
||||||
|
int newStyle = (style != -1)
|
||||||
|
? style
|
||||||
|
: (styleChange != 0)
|
||||||
|
? baseStyle & ~((styleChange >> 16) & 0xffff) | (styleChange & 0xffff)
|
||||||
|
: baseStyle;
|
||||||
|
|
||||||
|
// new size
|
||||||
|
int newSize = (absoluteSize > 0)
|
||||||
|
? scale.applyAsInt( absoluteSize )
|
||||||
|
: (relativeSize != 0)
|
||||||
|
? (baseSize + scale.applyAsInt( relativeSize ))
|
||||||
|
: (scaleSize > 0)
|
||||||
|
? Math.round( baseSize * scaleSize )
|
||||||
|
: baseSize;
|
||||||
|
if( newSize <= 0 )
|
||||||
|
newSize = 1;
|
||||||
|
|
||||||
|
// create font for family
|
||||||
|
if( families != null && !families.isEmpty() ) {
|
||||||
|
for( String family : families ) {
|
||||||
|
Font font = createCompositeFont( family, newStyle, newSize );
|
||||||
|
if( !isFallbackFont( font ) || family.equalsIgnoreCase( Font.DIALOG ) )
|
||||||
|
return toUIResource( font );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// derive font
|
||||||
|
if( newStyle != baseStyle || newSize != baseSize ) {
|
||||||
|
// hack for font "Ubuntu Medium" on Linux, which curiously belongs
|
||||||
|
// to family "Ubuntu Light" and using deriveFont() would create a light font
|
||||||
|
if( "Ubuntu Medium".equalsIgnoreCase( baseFont.getName() ) &&
|
||||||
|
"Ubuntu Light".equalsIgnoreCase( baseFont.getFamily() ) )
|
||||||
|
{
|
||||||
|
Font font = createCompositeFont( "Ubuntu Medium", newStyle, newSize );
|
||||||
|
if( !isFallbackFont( font ) )
|
||||||
|
return toUIResource( font );
|
||||||
|
}
|
||||||
|
|
||||||
|
return toUIResource( baseFont.deriveFont( newStyle, newSize ) );
|
||||||
|
} else
|
||||||
|
return toUIResource( baseFont );
|
||||||
|
}
|
||||||
|
|
||||||
|
private FontUIResource toUIResource( Font font ) {
|
||||||
|
// make sure that font is a UIResource for LaF switching
|
||||||
|
return (font instanceof FontUIResource)
|
||||||
|
? (FontUIResource) font
|
||||||
|
: new FontUIResource( font );
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isFallbackFont( Font font ) {
|
||||||
|
return Font.DIALOG.equalsIgnoreCase( font.getFamily() );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class ImageIconUIResource ------------------------------------------
|
//---- class ImageIconUIResource ------------------------------------------
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public interface FlatSystemProperties
|
|||||||
* To replace the Java 9+ system scale factor, use system property "sun.java2d.uiScale",
|
* To replace the Java 9+ system scale factor, use system property "sun.java2d.uiScale",
|
||||||
* which has the same syntax as this one.
|
* which has the same syntax as this one.
|
||||||
* <p>
|
* <p>
|
||||||
* Since FlatLaf 1.1.2: Scale factors less then 100% are allowed.
|
* Since FlatLaf 1.1.2: Scale factors less than 100% are allowed.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Allowed Values</strong> e.g. {@code 1.5}, {@code 1.5x}, {@code 150%} or {@code 144dpi} (96dpi is 100%)<br>
|
* <strong>Allowed Values</strong> e.g. {@code 1.5}, {@code 1.5x}, {@code 150%} or {@code 144dpi} (96dpi is 100%)<br>
|
||||||
*/
|
*/
|
||||||
@@ -81,7 +81,7 @@ public interface FlatSystemProperties
|
|||||||
* {@link FlatClientProperties#USE_WINDOW_DECORATIONS} and
|
* {@link FlatClientProperties#USE_WINDOW_DECORATIONS} and
|
||||||
* UI default {@code TitlePane.useWindowDecorations}.
|
* UI default {@code TitlePane.useWindowDecorations}.
|
||||||
* <p>
|
* <p>
|
||||||
* (requires Window 10)
|
* (requires Window 10/11)
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||||
* <strong>Default</strong> none
|
* <strong>Default</strong> none
|
||||||
@@ -92,16 +92,16 @@ public interface FlatSystemProperties
|
|||||||
* Specifies whether JetBrains Runtime custom window decorations should be used
|
* Specifies whether JetBrains Runtime custom window decorations should be used
|
||||||
* when creating {@code JFrame} or {@code JDialog}.
|
* when creating {@code JFrame} or {@code JDialog}.
|
||||||
* Requires that the application runs in a
|
* Requires that the application runs in a
|
||||||
* <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime</a>
|
* <a href="https://github.com/JetBrains/JetBrainsRuntime/wiki">JetBrains Runtime</a>
|
||||||
* (based on OpenJDK).
|
* (based on OpenJDK).
|
||||||
* <p>
|
* <p>
|
||||||
* Setting this to {@code false} disables using JetBrains Runtime custom window decorations.
|
* Setting this to {@code false} disables using JetBrains Runtime custom window decorations.
|
||||||
* Then FlatLaf native window decorations are used.
|
* Then FlatLaf native window decorations are used.
|
||||||
* <p>
|
* <p>
|
||||||
* (requires Window 10)
|
* (requires Window 10/11)
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||||
* <strong>Default</strong> {@code true}
|
* <strong>Default</strong> {@code false} (since v2; was {@code true} in v1)
|
||||||
*/
|
*/
|
||||||
String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations";
|
String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations";
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ public interface FlatSystemProperties
|
|||||||
* {@link FlatClientProperties#MENU_BAR_EMBEDDED} and
|
* {@link FlatClientProperties#MENU_BAR_EMBEDDED} and
|
||||||
* UI default {@code TitlePane.menuBarEmbedded}.
|
* UI default {@code TitlePane.menuBarEmbedded}.
|
||||||
* <p>
|
* <p>
|
||||||
* (requires Window 10)
|
* (requires Window 10/11)
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||||
* <strong>Default</strong> none
|
* <strong>Default</strong> none
|
||||||
@@ -139,6 +139,15 @@ public interface FlatSystemProperties
|
|||||||
*/
|
*/
|
||||||
String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection";
|
String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies a directory in which the native FlatLaf library have been extracted.
|
||||||
|
* The path can be absolute or relative to current application working directory.
|
||||||
|
* This can be used to avoid extraction of the native libraries to the temporary directory at runtime.
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
String NATIVE_LIBRARY_PATH = "flatlaf.nativeLibraryPath";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether a system property is set and returns {@code true} if its value
|
* Checks whether a system property is set and returns {@code true} if its value
|
||||||
* is {@code "true"} (case-insensitive), otherwise it returns {@code false}.
|
* is {@code "true"} (case-insensitive), otherwise it returns {@code false}.
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import com.formdev.flatlaf.json.ParseException;
|
|||||||
import com.formdev.flatlaf.util.ColorFunctions;
|
import com.formdev.flatlaf.util.ColorFunctions;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.StringUtils;
|
import com.formdev.flatlaf.util.StringUtils;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class supports loading IntelliJ .theme.json files and using them as a Laf.
|
* This class supports loading IntelliJ .theme.json files and using them as a Laf.
|
||||||
@@ -162,8 +163,11 @@ public class IntelliJTheme
|
|||||||
applyCheckBoxColors( defaults );
|
applyCheckBoxColors( defaults );
|
||||||
|
|
||||||
// copy values
|
// copy values
|
||||||
for( Map.Entry<String, String> e : uiKeyCopying.entrySet() )
|
for( Map.Entry<String, String> e : uiKeyCopying.entrySet() ) {
|
||||||
defaults.put( e.getKey(), defaults.get( e.getValue() ) );
|
Object value = defaults.get( e.getValue() );
|
||||||
|
if( value != null )
|
||||||
|
defaults.put( e.getKey(), value );
|
||||||
|
}
|
||||||
|
|
||||||
// IDEA does not paint button background if disabled, but FlatLaf does
|
// IDEA does not paint button background if disabled, but FlatLaf does
|
||||||
Object panelBackground = defaults.get( "Panel.background" );
|
Object panelBackground = defaults.get( "Panel.background" );
|
||||||
@@ -175,7 +179,7 @@ public class IntelliJTheme
|
|||||||
defaults.put( "Button.hoverBorderColor", defaults.get( "Button.focusedBorderColor" ) );
|
defaults.put( "Button.hoverBorderColor", defaults.get( "Button.focusedBorderColor" ) );
|
||||||
defaults.put( "HelpButton.hoverBorderColor", defaults.get( "Button.focusedBorderColor" ) );
|
defaults.put( "HelpButton.hoverBorderColor", defaults.get( "Button.focusedBorderColor" ) );
|
||||||
|
|
||||||
// IDEA uses a SVG icon for the help button, but paints the background with Button.startBackground and Button.endBackground
|
// IDEA uses an SVG icon for the help button, but paints the background with Button.startBackground and Button.endBackground
|
||||||
Object helpButtonBackground = defaults.get( "Button.startBackground" );
|
Object helpButtonBackground = defaults.get( "Button.startBackground" );
|
||||||
Object helpButtonBorderColor = defaults.get( "Button.startBorderColor" );
|
Object helpButtonBorderColor = defaults.get( "Button.startBorderColor" );
|
||||||
if( helpButtonBackground == null )
|
if( helpButtonBackground == null )
|
||||||
@@ -242,14 +246,26 @@ public class IntelliJTheme
|
|||||||
defaults.put( "Tree.rowHeight", 22 );
|
defaults.put( "Tree.rowHeight", 22 );
|
||||||
|
|
||||||
// apply theme specific UI defaults at the end to allow overwriting
|
// apply theme specific UI defaults at the end to allow overwriting
|
||||||
defaults.putAll( themeSpecificDefaults );
|
for( Map.Entry<Object, Object> e : themeSpecificDefaults.entrySet() ) {
|
||||||
|
Object key = e.getKey();
|
||||||
|
Object value = e.getValue();
|
||||||
|
|
||||||
|
// append styles to existing styles
|
||||||
|
if( key instanceof String && ((String)key).startsWith( "[style]" ) ) {
|
||||||
|
Object oldValue = defaults.get( key );
|
||||||
|
if( oldValue != null )
|
||||||
|
value = oldValue + "; " + value;
|
||||||
|
}
|
||||||
|
|
||||||
|
defaults.put( key, value );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<Object, Object> removeThemeSpecificDefaults( UIDefaults defaults ) {
|
private Map<Object, Object> removeThemeSpecificDefaults( UIDefaults defaults ) {
|
||||||
// search for theme specific UI defaults keys
|
// search for theme specific UI defaults keys
|
||||||
ArrayList<String> themeSpecificKeys = new ArrayList<>();
|
ArrayList<String> themeSpecificKeys = new ArrayList<>();
|
||||||
for( Object key : defaults.keySet() ) {
|
for( Object key : defaults.keySet() ) {
|
||||||
if( key instanceof String && ((String)key).startsWith( "[" ) )
|
if( key instanceof String && ((String)key).startsWith( "[" ) && !((String)key).startsWith( "[style]" ) )
|
||||||
themeSpecificKeys.add( (String) key );
|
themeSpecificKeys.add( (String) key );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,8 +315,19 @@ public class IntelliJTheme
|
|||||||
@SuppressWarnings( "unchecked" )
|
@SuppressWarnings( "unchecked" )
|
||||||
private void apply( String key, Object value, UIDefaults defaults, ArrayList<Object> defaultsKeysCache, Set<String> uiKeys ) {
|
private void apply( String key, Object value, UIDefaults defaults, ArrayList<Object> defaultsKeysCache, Set<String> uiKeys ) {
|
||||||
if( value instanceof Map ) {
|
if( value instanceof Map ) {
|
||||||
for( Map.Entry<String, Object> e : ((Map<String, Object>)value).entrySet() )
|
Map<String, Object> map = (Map<String, Object>)value;
|
||||||
apply( key + '.' + e.getKey(), e.getValue(), defaults, defaultsKeysCache, uiKeys );
|
if( map.containsKey( "os.default" ) || map.containsKey( "os.windows" ) || map.containsKey( "os.mac" ) || map.containsKey( "os.linux" ) ) {
|
||||||
|
String osKey = SystemInfo.isWindows ? "os.windows"
|
||||||
|
: SystemInfo.isMacOS ? "os.mac"
|
||||||
|
: SystemInfo.isLinux ? "os.linux" : null;
|
||||||
|
if( osKey != null && map.containsKey( osKey ) )
|
||||||
|
apply( key, map.get( osKey ), defaults, defaultsKeysCache, uiKeys );
|
||||||
|
else if( map.containsKey( "os.default" ) )
|
||||||
|
apply( key, map.get( "os.default" ), defaults, defaultsKeysCache, uiKeys );
|
||||||
|
} else {
|
||||||
|
for( Map.Entry<String, Object> e : map.entrySet() )
|
||||||
|
apply( key + '.' + e.getKey(), e.getValue(), defaults, defaultsKeysCache, uiKeys );
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if( "".equals( value ) )
|
if( "".equals( value ) )
|
||||||
return; // ignore empty value
|
return; // ignore empty value
|
||||||
@@ -338,7 +365,7 @@ public class IntelliJTheme
|
|||||||
|
|
||||||
// parse value
|
// parse value
|
||||||
try {
|
try {
|
||||||
uiValue = UIDefaultsLoader.parseValue( key, valueStr );
|
uiValue = UIDefaultsLoader.parseValue( key, valueStr, null );
|
||||||
} catch( RuntimeException ex ) {
|
} catch( RuntimeException ex ) {
|
||||||
UIDefaultsLoader.logParseError( key, valueStr, ex, false );
|
UIDefaultsLoader.logParseError( key, valueStr, ex, false );
|
||||||
return; // ignore invalid value
|
return; // ignore invalid value
|
||||||
@@ -504,7 +531,7 @@ public class IntelliJTheme
|
|||||||
// for filled checkbox/radiobutton used in light themes
|
// for filled checkbox/radiobutton used in light themes
|
||||||
defaults.remove( "CheckBox.icon[filled].focusWidth" );
|
defaults.remove( "CheckBox.icon[filled].focusWidth" );
|
||||||
defaults.put( "CheckBox.icon[filled].hoverBorderColor", defaults.get( "CheckBox.icon[filled].focusedBorderColor" ) );
|
defaults.put( "CheckBox.icon[filled].hoverBorderColor", defaults.get( "CheckBox.icon[filled].focusedBorderColor" ) );
|
||||||
defaults.put( "CheckBox.icon[filled].selectedFocusedBackground", defaults.get( "CheckBox.icon[filled].selectedBackground" ) );
|
defaults.put( "CheckBox.icon[filled].focusedSelectedBackground", defaults.get( "CheckBox.icon[filled].selectedBackground" ) );
|
||||||
|
|
||||||
if( dark ) {
|
if( dark ) {
|
||||||
// IDEA Darcula checkBoxFocused.svg, checkBoxSelectedFocused.svg,
|
// IDEA Darcula checkBoxFocused.svg, checkBoxSelectedFocused.svg,
|
||||||
@@ -513,9 +540,9 @@ public class IntelliJTheme
|
|||||||
// --> add alpha to focused border colors
|
// --> add alpha to focused border colors
|
||||||
String[] focusedBorderColorKeys = new String[] {
|
String[] focusedBorderColorKeys = new String[] {
|
||||||
"CheckBox.icon.focusedBorderColor",
|
"CheckBox.icon.focusedBorderColor",
|
||||||
"CheckBox.icon.selectedFocusedBorderColor",
|
"CheckBox.icon.focusedSelectedBorderColor",
|
||||||
"CheckBox.icon[filled].focusedBorderColor",
|
"CheckBox.icon[filled].focusedBorderColor",
|
||||||
"CheckBox.icon[filled].selectedFocusedBorderColor",
|
"CheckBox.icon[filled].focusedSelectedBorderColor",
|
||||||
};
|
};
|
||||||
for( String key : focusedBorderColorKeys ) {
|
for( String key : focusedBorderColorKeys ) {
|
||||||
Color color = defaults.getColor( key );
|
Color color = defaults.getColor( key );
|
||||||
@@ -534,12 +561,12 @@ public class IntelliJTheme
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Rename UI default keys (key --> value). */
|
/** Rename UI default keys (key --> value). */
|
||||||
private static Map<String, String> uiKeyMapping = new HashMap<>();
|
private static final Map<String, String> uiKeyMapping = new HashMap<>();
|
||||||
/** Copy UI default keys (value --> key). */
|
/** Copy UI default keys (value --> key). */
|
||||||
private static Map<String, String> uiKeyCopying = new HashMap<>();
|
private static final Map<String, String> uiKeyCopying = new HashMap<>();
|
||||||
private static Map<String, String> uiKeyInverseMapping = new HashMap<>();
|
private static final Map<String, String> uiKeyInverseMapping = new HashMap<>();
|
||||||
private static Map<String, String> checkboxKeyMapping = new HashMap<>();
|
private static final Map<String, String> checkboxKeyMapping = new HashMap<>();
|
||||||
private static Map<String, String> checkboxDuplicateColors = new HashMap<>();
|
private static final Map<String, String> checkboxDuplicateColors = new HashMap<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// ComboBox
|
// ComboBox
|
||||||
@@ -549,6 +576,8 @@ public class IntelliJTheme
|
|||||||
uiKeyMapping.put( "ComboBox.ArrowButton.disabledIconColor", "ComboBox.buttonDisabledArrowColor" );
|
uiKeyMapping.put( "ComboBox.ArrowButton.disabledIconColor", "ComboBox.buttonDisabledArrowColor" );
|
||||||
uiKeyMapping.put( "ComboBox.ArrowButton.iconColor", "ComboBox.buttonArrowColor" );
|
uiKeyMapping.put( "ComboBox.ArrowButton.iconColor", "ComboBox.buttonArrowColor" );
|
||||||
uiKeyMapping.put( "ComboBox.ArrowButton.nonEditableBackground", "ComboBox.buttonBackground" );
|
uiKeyMapping.put( "ComboBox.ArrowButton.nonEditableBackground", "ComboBox.buttonBackground" );
|
||||||
|
uiKeyCopying.put( "ComboBox.buttonSeparatorColor", "Component.borderColor" );
|
||||||
|
uiKeyCopying.put( "ComboBox.buttonDisabledSeparatorColor", "Component.disabledBorderColor" );
|
||||||
|
|
||||||
// Component
|
// Component
|
||||||
uiKeyMapping.put( "Component.inactiveErrorFocusColor", "Component.error.borderColor" );
|
uiKeyMapping.put( "Component.inactiveErrorFocusColor", "Component.error.borderColor" );
|
||||||
@@ -594,6 +623,15 @@ public class IntelliJTheme
|
|||||||
uiKeyCopying.put( "Slider.thumbColor", "ProgressBar.foreground" );
|
uiKeyCopying.put( "Slider.thumbColor", "ProgressBar.foreground" );
|
||||||
uiKeyCopying.put( "Slider.trackColor", "ProgressBar.background" );
|
uiKeyCopying.put( "Slider.trackColor", "ProgressBar.background" );
|
||||||
|
|
||||||
|
// Spinner
|
||||||
|
uiKeyCopying.put( "Spinner.buttonSeparatorColor", "Component.borderColor" );
|
||||||
|
uiKeyCopying.put( "Spinner.buttonDisabledSeparatorColor", "Component.disabledBorderColor" );
|
||||||
|
|
||||||
|
// TabbedPane
|
||||||
|
uiKeyCopying.put( "TabbedPane.selectedBackground", "DefaultTabs.underlinedTabBackground" );
|
||||||
|
uiKeyCopying.put( "TabbedPane.selectedForeground", "DefaultTabs.underlinedTabForeground" );
|
||||||
|
uiKeyCopying.put( "TabbedPane.inactiveUnderlineColor", "DefaultTabs.inactiveUnderlineColor" );
|
||||||
|
|
||||||
// TitlePane
|
// TitlePane
|
||||||
uiKeyCopying.put( "TitlePane.inactiveBackground", "TitlePane.background" );
|
uiKeyCopying.put( "TitlePane.inactiveBackground", "TitlePane.background" );
|
||||||
uiKeyMapping.put( "TitlePane.infoForeground", "TitlePane.foreground" );
|
uiKeyMapping.put( "TitlePane.infoForeground", "TitlePane.foreground" );
|
||||||
@@ -618,7 +656,7 @@ public class IntelliJTheme
|
|||||||
checkboxKeyMapping.put( "Checkbox.Background.Selected", "CheckBox.icon.selectedBackground" );
|
checkboxKeyMapping.put( "Checkbox.Background.Selected", "CheckBox.icon.selectedBackground" );
|
||||||
checkboxKeyMapping.put( "Checkbox.Border.Selected", "CheckBox.icon.selectedBorderColor" );
|
checkboxKeyMapping.put( "Checkbox.Border.Selected", "CheckBox.icon.selectedBorderColor" );
|
||||||
checkboxKeyMapping.put( "Checkbox.Foreground.Selected", "CheckBox.icon.checkmarkColor" );
|
checkboxKeyMapping.put( "Checkbox.Foreground.Selected", "CheckBox.icon.checkmarkColor" );
|
||||||
checkboxKeyMapping.put( "Checkbox.Focus.Thin.Selected", "CheckBox.icon.selectedFocusedBorderColor" );
|
checkboxKeyMapping.put( "Checkbox.Focus.Thin.Selected", "CheckBox.icon.focusedSelectedBorderColor" );
|
||||||
|
|
||||||
checkboxDuplicateColors.put( "Checkbox.Background.Default.Dark", "Checkbox.Background.Selected.Dark" );
|
checkboxDuplicateColors.put( "Checkbox.Background.Default.Dark", "Checkbox.Background.Selected.Dark" );
|
||||||
checkboxDuplicateColors.put( "Checkbox.Border.Default.Dark", "Checkbox.Border.Selected.Dark" );
|
checkboxDuplicateColors.put( "Checkbox.Border.Default.Dark", "Checkbox.Border.Selected.Dark" );
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.formdev.flatlaf;
|
package com.formdev.flatlaf;
|
||||||
|
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
|
import java.awt.FontMetrics;
|
||||||
import java.awt.GraphicsConfiguration;
|
import java.awt.GraphicsConfiguration;
|
||||||
import java.awt.GraphicsEnvironment;
|
import java.awt.GraphicsEnvironment;
|
||||||
import java.awt.Toolkit;
|
import java.awt.Toolkit;
|
||||||
@@ -28,7 +29,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
import javax.swing.text.StyleContext;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.StringUtils;
|
import com.formdev.flatlaf.util.StringUtils;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
@@ -121,14 +122,25 @@ class LinuxFontPolicy
|
|||||||
for(;;) {
|
for(;;) {
|
||||||
Font font = createFont( family, style, size, dsize );
|
Font font = createFont( family, style, size, dsize );
|
||||||
|
|
||||||
// if the font family does not match any font on the system, "Dialog" family is returned
|
if( Font.DIALOG.equals( family ) )
|
||||||
if( !"Dialog".equals( font.getFamily() ) || "Dialog".equals( family ) )
|
|
||||||
return font;
|
return font;
|
||||||
|
|
||||||
|
// if the font family does not match any font on the system, "Dialog" family is returned
|
||||||
|
if( !Font.DIALOG.equals( font.getFamily() ) ) {
|
||||||
|
// check for font problems
|
||||||
|
// - font height much larger than expected (e.g. font Inter; Oracle Java 8)
|
||||||
|
// - character width is zero (e.g. font Cantarell; Fedora; Oracle Java 8)
|
||||||
|
FontMetrics fm = StyleContext.getDefaultStyleContext().getFontMetrics( font );
|
||||||
|
if( fm.getHeight() > size * 2 || fm.stringWidth( "a" ) == 0 )
|
||||||
|
return createFont( Font.DIALOG, style, size, dsize );
|
||||||
|
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
|
||||||
// find last word in family
|
// find last word in family
|
||||||
int index = family.lastIndexOf( ' ' );
|
int index = family.lastIndexOf( ' ' );
|
||||||
if( index < 0 )
|
if( index < 0 )
|
||||||
return createFont( "Dialog", style, size, dsize );
|
return createFont( Font.DIALOG, style, size, dsize );
|
||||||
|
|
||||||
// check whether last work contains some font weight (e.g. Ultra-Bold or Heavy)
|
// check whether last work contains some font weight (e.g. Ultra-Bold or Heavy)
|
||||||
String lastWord = family.substring( index + 1 ).toLowerCase();
|
String lastWord = family.substring( index + 1 ).toLowerCase();
|
||||||
@@ -158,7 +170,7 @@ class LinuxFontPolicy
|
|||||||
|
|
||||||
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "gnome.Xft/DPI" );
|
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "gnome.Xft/DPI" );
|
||||||
if( value instanceof Integer ) {
|
if( value instanceof Integer ) {
|
||||||
int dpi = ((Integer)value).intValue() / 1024;
|
int dpi = (Integer) value / 1024;
|
||||||
if( dpi == -1 )
|
if( dpi == -1 )
|
||||||
dpi = 96;
|
dpi = 96;
|
||||||
if( dpi < 50 )
|
if( dpi < 50 )
|
||||||
@@ -185,7 +197,7 @@ class LinuxFontPolicy
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the default font for KDE for KDE configuration files.
|
* Gets the default font for KDE from KDE configuration files.
|
||||||
*
|
*
|
||||||
* The Swing fonts are not updated when the user changes system font size
|
* The Swing fonts are not updated when the user changes system font size
|
||||||
* (System Settings > Fonts > Force Font DPI). A application restart is necessary.
|
* (System Settings > Fonts > Force Font DPI). A application restart is necessary.
|
||||||
@@ -266,7 +278,7 @@ class LinuxFontPolicy
|
|||||||
// read config file
|
// read config file
|
||||||
ArrayList<String> lines = new ArrayList<>( 200 );
|
ArrayList<String> lines = new ArrayList<>( 200 );
|
||||||
try( BufferedReader reader = new BufferedReader( new FileReader( file ) ) ) {
|
try( BufferedReader reader = new BufferedReader( new FileReader( file ) ) ) {
|
||||||
String line = null;
|
String line;
|
||||||
while( (line = reader.readLine()) != null )
|
while( (line = reader.readLine()) != null )
|
||||||
lines.add( line );
|
lines.add( line );
|
||||||
} catch( IOException ex ) {
|
} catch( IOException ex ) {
|
||||||
|
|||||||
@@ -0,0 +1,322 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf;
|
||||||
|
|
||||||
|
import java.awt.AWTEvent;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Container;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.EventQueue;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.MouseInfo;
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.awt.PointerInfo;
|
||||||
|
import java.awt.Polygon;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.Toolkit;
|
||||||
|
import java.awt.Window;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JLayeredPane;
|
||||||
|
import javax.swing.JMenu;
|
||||||
|
import javax.swing.JPopupMenu;
|
||||||
|
import javax.swing.MenuElement;
|
||||||
|
import javax.swing.MenuSelectionManager;
|
||||||
|
import javax.swing.RootPaneContainer;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.Timer;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Improves usability of submenus by using a
|
||||||
|
* <a href="https://height.app/blog/guide-to-build-context-menus#safe-triangle">safe triangle</a>
|
||||||
|
* to avoid that the submenu closes while the user moves the mouse to it.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
class SubMenuUsabilityHelper
|
||||||
|
implements ChangeListener
|
||||||
|
{
|
||||||
|
private static final String KEY_USE_SAFE_TRIANGLE = "Menu.useSafeTriangle";
|
||||||
|
private static final String KEY_SHOW_SAFE_TRIANGLE = "FlatLaf.debug.menu.showSafeTriangle";
|
||||||
|
|
||||||
|
private SubMenuEventQueue subMenuEventQueue;
|
||||||
|
private SafeTrianglePainter safeTrianglePainter;
|
||||||
|
private boolean changePending;
|
||||||
|
|
||||||
|
// mouse location in screen coordinates
|
||||||
|
private int mouseX;
|
||||||
|
private int mouseY;
|
||||||
|
|
||||||
|
// target popup bounds in screen coordinates
|
||||||
|
private int targetX;
|
||||||
|
private int targetTopY;
|
||||||
|
private int targetBottomY;
|
||||||
|
|
||||||
|
private Rectangle invokerBounds;
|
||||||
|
|
||||||
|
void install() {
|
||||||
|
MenuSelectionManager.defaultManager().addChangeListener( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
void uninstall() {
|
||||||
|
MenuSelectionManager.defaultManager().removeChangeListener( this );
|
||||||
|
uninstallEventQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stateChanged( ChangeEvent e ) {
|
||||||
|
if( !FlatUIUtils.getUIBoolean( KEY_USE_SAFE_TRIANGLE, true ))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// handle menu selection change later, but only once in case of temporary changes
|
||||||
|
// e.g. moving mouse from one menu item to another one, fires two events:
|
||||||
|
// 1. old menu item is removed from menu selection
|
||||||
|
// 2. new menu item is added to menu selection
|
||||||
|
synchronized( this ) {
|
||||||
|
if( changePending )
|
||||||
|
return;
|
||||||
|
changePending = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventQueue.invokeLater( () -> {
|
||||||
|
synchronized( this ) {
|
||||||
|
changePending = false;
|
||||||
|
}
|
||||||
|
menuSelectionChanged();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void menuSelectionChanged() {
|
||||||
|
MenuElement[] path = MenuSelectionManager.defaultManager().getSelectedPath();
|
||||||
|
|
||||||
|
/*debug
|
||||||
|
System.out.println( "--- " + path.length );
|
||||||
|
for( int i = 0; i < path.length; i++ )
|
||||||
|
System.out.println( " " + i + ": " + path[i].getClass().getName() );
|
||||||
|
debug*/
|
||||||
|
|
||||||
|
// find submenu in menu selection
|
||||||
|
int subMenuIndex = findSubMenu( path );
|
||||||
|
|
||||||
|
// uninstall if there is no submenu in selection
|
||||||
|
if( subMenuIndex < 0 || subMenuIndex != path.length - 1 ) {
|
||||||
|
uninstallEventQueue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get current mouse location
|
||||||
|
PointerInfo pointerInfo = MouseInfo.getPointerInfo();
|
||||||
|
Point mouseLocation = (pointerInfo != null) ? pointerInfo.getLocation() : new Point();
|
||||||
|
mouseX = mouseLocation.x;
|
||||||
|
mouseY = mouseLocation.y;
|
||||||
|
|
||||||
|
// check whether popup is showing, which is e.g. not the case if it is empty
|
||||||
|
JPopupMenu popup = (JPopupMenu) path[subMenuIndex];
|
||||||
|
if( !popup.isShowing() ) {
|
||||||
|
uninstallEventQueue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get invoker screen bounds
|
||||||
|
Component invoker = popup.getInvoker();
|
||||||
|
invokerBounds = (invoker != null)
|
||||||
|
? new Rectangle( invoker.getLocationOnScreen(), invoker.getSize() )
|
||||||
|
: null;
|
||||||
|
|
||||||
|
// check whether mouse location is within invoker
|
||||||
|
if( invokerBounds != null && !invokerBounds.contains( mouseX, mouseY ) ) {
|
||||||
|
uninstallEventQueue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute top/bottom target locations
|
||||||
|
Point popupLocation = popup.getLocationOnScreen();
|
||||||
|
Dimension popupSize = popup.getSize();
|
||||||
|
targetX = (mouseX < popupLocation.x + (popupSize.width / 2))
|
||||||
|
? popupLocation.x
|
||||||
|
: popupLocation.x + popupSize.width;
|
||||||
|
targetTopY = popupLocation.y;
|
||||||
|
targetBottomY = popupLocation.y + popupSize.height;
|
||||||
|
|
||||||
|
// install own event queue to supress mouse events when mouse is moved within safe triangle
|
||||||
|
if( subMenuEventQueue == null )
|
||||||
|
subMenuEventQueue = new SubMenuEventQueue();
|
||||||
|
|
||||||
|
// create safe triangle painter
|
||||||
|
if( safeTrianglePainter == null && UIManager.getBoolean( KEY_SHOW_SAFE_TRIANGLE ) )
|
||||||
|
safeTrianglePainter = new SafeTrianglePainter( popup );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void uninstallEventQueue() {
|
||||||
|
if( subMenuEventQueue != null ) {
|
||||||
|
subMenuEventQueue.uninstall();
|
||||||
|
subMenuEventQueue = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( safeTrianglePainter != null ) {
|
||||||
|
safeTrianglePainter.uninstall();
|
||||||
|
safeTrianglePainter = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int findSubMenu( MenuElement[] path ) {
|
||||||
|
for( int i = path.length - 1; i >= 1; i-- ) {
|
||||||
|
if( path[i] instanceof JPopupMenu &&
|
||||||
|
path[i - 1] instanceof JMenu &&
|
||||||
|
!((JMenu)path[i - 1]).isTopLevelMenu() )
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Polygon createSafeTriangle() {
|
||||||
|
return new Polygon(
|
||||||
|
new int[] { mouseX, targetX, targetX },
|
||||||
|
new int[] { mouseY, targetTopY, targetBottomY },
|
||||||
|
3 );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class SubMenuEventQueue --------------------------------------------
|
||||||
|
|
||||||
|
private class SubMenuEventQueue
|
||||||
|
extends EventQueue
|
||||||
|
{
|
||||||
|
private Timer mouseUpdateTimer;
|
||||||
|
private Timer timeoutTimer;
|
||||||
|
|
||||||
|
private int newMouseX;
|
||||||
|
private int newMouseY;
|
||||||
|
private AWTEvent lastMouseEvent;
|
||||||
|
|
||||||
|
SubMenuEventQueue() {
|
||||||
|
// timer used to slightly delay update of mouse location used for safe triangle
|
||||||
|
mouseUpdateTimer = new Timer( 50, e -> {
|
||||||
|
mouseX = newMouseX;
|
||||||
|
mouseY = newMouseY;
|
||||||
|
|
||||||
|
if( safeTrianglePainter != null )
|
||||||
|
safeTrianglePainter.repaint();
|
||||||
|
} );
|
||||||
|
mouseUpdateTimer.setRepeats( false );
|
||||||
|
|
||||||
|
// timer used to timeout safe triangle when mouse stops moving
|
||||||
|
timeoutTimer = new Timer( 200, e -> {
|
||||||
|
if( invokerBounds != null && !invokerBounds.contains( newMouseX, newMouseY ) ) {
|
||||||
|
// post last mouse event, which selects menu item at mouse location
|
||||||
|
if( lastMouseEvent != null ) {
|
||||||
|
postEvent( lastMouseEvent );
|
||||||
|
lastMouseEvent = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstallEventQueue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
timeoutTimer.setRepeats( false );
|
||||||
|
|
||||||
|
Toolkit.getDefaultToolkit().getSystemEventQueue().push( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
void uninstall() {
|
||||||
|
mouseUpdateTimer.stop();
|
||||||
|
mouseUpdateTimer = null;
|
||||||
|
|
||||||
|
timeoutTimer.stop();
|
||||||
|
timeoutTimer = null;
|
||||||
|
|
||||||
|
lastMouseEvent = null;
|
||||||
|
|
||||||
|
super.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void dispatchEvent( AWTEvent e ) {
|
||||||
|
int id = e.getID();
|
||||||
|
|
||||||
|
if( e instanceof MouseEvent &&
|
||||||
|
(id == MouseEvent.MOUSE_MOVED || id == MouseEvent.MOUSE_DRAGGED) )
|
||||||
|
{
|
||||||
|
newMouseX = ((MouseEvent)e).getXOnScreen();
|
||||||
|
newMouseY = ((MouseEvent)e).getYOnScreen();
|
||||||
|
|
||||||
|
if( safeTrianglePainter != null )
|
||||||
|
safeTrianglePainter.repaint();
|
||||||
|
|
||||||
|
mouseUpdateTimer.stop();
|
||||||
|
timeoutTimer.stop();
|
||||||
|
|
||||||
|
// check whether mouse moved within safe triangle
|
||||||
|
if( createSafeTriangle().contains( newMouseX, newMouseY ) ) {
|
||||||
|
// update mouse location delayed (this changes the safe triangle)
|
||||||
|
mouseUpdateTimer.start();
|
||||||
|
|
||||||
|
timeoutTimer.start();
|
||||||
|
|
||||||
|
// remember last mouse event, which will be posted if the mouse stops moving
|
||||||
|
lastMouseEvent = e;
|
||||||
|
|
||||||
|
// ignore mouse event
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update mouse location immediately (this changes the safe triangle)
|
||||||
|
mouseX = newMouseX;
|
||||||
|
mouseY = newMouseY;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.dispatchEvent( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class SafeTrianglePainter ------------------------------------------
|
||||||
|
|
||||||
|
private class SafeTrianglePainter
|
||||||
|
extends JComponent
|
||||||
|
{
|
||||||
|
SafeTrianglePainter( JPopupMenu popup ) {
|
||||||
|
Window window = SwingUtilities.windowForComponent( popup.getInvoker() );
|
||||||
|
if( window instanceof RootPaneContainer ) {
|
||||||
|
JLayeredPane layeredPane = ((RootPaneContainer)window).getLayeredPane();
|
||||||
|
setSize( layeredPane.getSize() );
|
||||||
|
layeredPane.add( this, new Integer( JLayeredPane.POPUP_LAYER + 1 ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void uninstall() {
|
||||||
|
Container parent = getParent();
|
||||||
|
if( parent != null ) {
|
||||||
|
parent.remove( this );
|
||||||
|
parent.repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintComponent( Graphics g ) {
|
||||||
|
Point locationOnScreen = getLocationOnScreen();
|
||||||
|
g.translate( -locationOnScreen.x, -locationOnScreen.y );
|
||||||
|
|
||||||
|
g.setColor( Color.red );
|
||||||
|
((Graphics2D)g).draw( createSafeTriangle() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,11 +18,16 @@ package com.formdev.flatlaf;
|
|||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Font;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.StreamTokenizer;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -33,8 +38,10 @@ import java.util.Map;
|
|||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import javax.swing.Icon;
|
||||||
import javax.swing.UIDefaults;
|
import javax.swing.UIDefaults;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.border.Border;
|
||||||
import javax.swing.UIDefaults.ActiveValue;
|
import javax.swing.UIDefaults.ActiveValue;
|
||||||
import javax.swing.UIDefaults.LazyValue;
|
import javax.swing.UIDefaults.LazyValue;
|
||||||
import javax.swing.plaf.ColorUIResource;
|
import javax.swing.plaf.ColorUIResource;
|
||||||
@@ -48,6 +55,7 @@ import com.formdev.flatlaf.util.DerivedColor;
|
|||||||
import com.formdev.flatlaf.util.GrayFilter;
|
import com.formdev.flatlaf.util.GrayFilter;
|
||||||
import com.formdev.flatlaf.util.HSLColor;
|
import com.formdev.flatlaf.util.HSLColor;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
import com.formdev.flatlaf.util.SoftCache;
|
||||||
import com.formdev.flatlaf.util.StringUtils;
|
import com.formdev.flatlaf.util.StringUtils;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
@@ -72,8 +80,12 @@ class UIDefaultsLoader
|
|||||||
private static final String OPTIONAL_PREFIX = "?";
|
private static final String OPTIONAL_PREFIX = "?";
|
||||||
private static final String WILDCARD_PREFIX = "*.";
|
private static final String WILDCARD_PREFIX = "*.";
|
||||||
|
|
||||||
|
static final String KEY_VARIABLES = "FlatLaf.internal.variables";
|
||||||
|
|
||||||
private static int parseColorDepth;
|
private static int parseColorDepth;
|
||||||
|
|
||||||
|
private static final SoftCache<String, Object> fontCache = new SoftCache<>();
|
||||||
|
|
||||||
static void loadDefaultsFromProperties( Class<?> lookAndFeelClass, List<FlatDefaultsAddon> addons,
|
static void loadDefaultsFromProperties( Class<?> lookAndFeelClass, List<FlatDefaultsAddon> addons,
|
||||||
Properties additionalDefaults, boolean dark, UIDefaults defaults )
|
Properties additionalDefaults, boolean dark, UIDefaults defaults )
|
||||||
{
|
{
|
||||||
@@ -146,6 +158,18 @@ class UIDefaultsLoader
|
|||||||
properties.load( in );
|
properties.load( in );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if( source instanceof URL ) {
|
||||||
|
// load from package URL
|
||||||
|
URL packageUrl = (URL) source;
|
||||||
|
for( Class<?> lafClass : lafClasses ) {
|
||||||
|
URL propertiesUrl = new URL( packageUrl + lafClass.getSimpleName() + ".properties" );
|
||||||
|
|
||||||
|
try( InputStream in = propertiesUrl.openStream() ) {
|
||||||
|
properties.load( in );
|
||||||
|
} catch( FileNotFoundException ex ) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if( source instanceof File ) {
|
} else if( source instanceof File ) {
|
||||||
// load from folder
|
// load from folder
|
||||||
File folder = (File) source;
|
File folder = (File) source;
|
||||||
@@ -234,18 +258,24 @@ class UIDefaultsLoader
|
|||||||
};
|
};
|
||||||
|
|
||||||
// parse and add properties to UI defaults
|
// parse and add properties to UI defaults
|
||||||
|
Map<String, String> variables = new HashMap<>( 50 );
|
||||||
for( Map.Entry<Object, Object> e : properties.entrySet() ) {
|
for( Map.Entry<Object, Object> e : properties.entrySet() ) {
|
||||||
String key = (String) e.getKey();
|
String key = (String) e.getKey();
|
||||||
if( key.startsWith( VARIABLE_PREFIX ) )
|
if( key.startsWith( VARIABLE_PREFIX ) ) {
|
||||||
|
variables.put( key, (String) e.getValue() );
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
String value = resolveValue( (String) e.getValue(), propertiesGetter );
|
String value = resolveValue( (String) e.getValue(), propertiesGetter );
|
||||||
try {
|
try {
|
||||||
defaults.put( key, parseValue( key, value, null, resolver, addonClassLoaders ) );
|
defaults.put( key, parseValue( key, value, null, null, resolver, addonClassLoaders ) );
|
||||||
} catch( RuntimeException ex ) {
|
} catch( RuntimeException ex ) {
|
||||||
logParseError( key, value, ex, true );
|
logParseError( key, value, ex, true );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remember variables in defaults to allow using them in styles
|
||||||
|
defaults.put( KEY_VARIABLES, variables );
|
||||||
} catch( IOException ex ) {
|
} catch( IOException ex ) {
|
||||||
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to load properties files.", ex );
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to load properties files.", ex );
|
||||||
}
|
}
|
||||||
@@ -288,87 +318,204 @@ class UIDefaultsLoader
|
|||||||
return resolveValue( newValue, propertiesGetter );
|
return resolveValue( newValue, propertiesGetter );
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ValueType { UNKNOWN, STRING, BOOLEAN, CHARACTER, INTEGER, FLOAT, BORDER, ICON, INSETS, DIMENSION, COLOR,
|
static String resolveValueFromUIManager( String value ) {
|
||||||
SCALEDINTEGER, SCALEDFLOAT, SCALEDINSETS, SCALEDDIMENSION, INSTANCE, CLASS, GRAYFILTER, NULL, LAZY }
|
if( value.startsWith( VARIABLE_PREFIX ) ) {
|
||||||
|
@SuppressWarnings( "unchecked" )
|
||||||
|
Map<String, String> variables = (Map<String, String>) UIManager.get( KEY_VARIABLES );
|
||||||
|
String newValue = (variables != null) ? variables.get( value ) : null;
|
||||||
|
if( newValue == null )
|
||||||
|
throw new IllegalArgumentException( "variable '" + value + "' not found" );
|
||||||
|
|
||||||
private static ValueType[] tempResultValueType = new ValueType[1];
|
return newValue;
|
||||||
|
}
|
||||||
|
|
||||||
static Object parseValue( String key, String value ) {
|
if( !value.startsWith( PROPERTY_PREFIX ) )
|
||||||
return parseValue( key, value, null, v -> v, Collections.emptyList() );
|
return value;
|
||||||
|
|
||||||
|
String key = value.substring( PROPERTY_PREFIX.length() );
|
||||||
|
Object newValue = UIManager.get( key );
|
||||||
|
if( newValue == null )
|
||||||
|
throw new IllegalArgumentException( "property '" + key + "' not found" );
|
||||||
|
|
||||||
|
// convert binary color to string
|
||||||
|
if( newValue instanceof Color ) {
|
||||||
|
Color color = (Color) newValue;
|
||||||
|
int alpha = color.getAlpha();
|
||||||
|
return String.format( (alpha != 255) ? "#%06x%02x" : "#%06x", color.getRGB() & 0xffffff, alpha );
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException( "property value type '" + newValue.getClass().getName() + "' not supported in references" );
|
||||||
}
|
}
|
||||||
|
|
||||||
static Object parseValue( String key, String value, ValueType[] resultValueType,
|
enum ValueType { UNKNOWN, STRING, BOOLEAN, CHARACTER, INTEGER, INTEGERORFLOAT, FLOAT, BORDER, ICON, INSETS, DIMENSION, COLOR, FONT,
|
||||||
|
SCALEDINTEGER, SCALEDFLOAT, SCALEDINSETS, SCALEDDIMENSION, INSTANCE, CLASS, GRAYFILTER, NULL, LAZY }
|
||||||
|
|
||||||
|
private static final ValueType[] tempResultValueType = new ValueType[1];
|
||||||
|
private static Map<Class<?>, ValueType> javaValueTypes;
|
||||||
|
private static Map<String, ValueType> knownValueTypes;
|
||||||
|
|
||||||
|
static Object parseValue( String key, String value, Class<?> valueType ) {
|
||||||
|
return parseValue( key, value, valueType, null, v -> v, Collections.emptyList() );
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object parseValue( String key, String value, Class<?> javaValueType, ValueType[] resultValueType,
|
||||||
Function<String, String> resolver, List<ClassLoader> addonClassLoaders )
|
Function<String, String> resolver, List<ClassLoader> addonClassLoaders )
|
||||||
{
|
{
|
||||||
if( resultValueType == null )
|
if( resultValueType == null )
|
||||||
resultValueType = tempResultValueType;
|
resultValueType = tempResultValueType;
|
||||||
|
|
||||||
value = value.trim();
|
// do not parse styles here
|
||||||
|
if( key.startsWith( "[style]" ) ) {
|
||||||
// null, false, true
|
resultValueType[0] = ValueType.STRING;
|
||||||
switch( value ) {
|
return value;
|
||||||
case "null": resultValueType[0] = ValueType.NULL; return null;
|
|
||||||
case "false": resultValueType[0] = ValueType.BOOLEAN; return false;
|
|
||||||
case "true": resultValueType[0] = ValueType.BOOLEAN; return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for function "lazy"
|
value = value.trim();
|
||||||
// Syntax: lazy(uiKey)
|
|
||||||
if( value.startsWith( "lazy(" ) && value.endsWith( ")" ) ) {
|
// null
|
||||||
resultValueType[0] = ValueType.LAZY;
|
if( value.equals( "null" ) || value.isEmpty() ) {
|
||||||
String uiKey = value.substring( 5, value.length() - 1 ).trim();
|
resultValueType[0] = ValueType.NULL;
|
||||||
return (LazyValue) t -> {
|
return null;
|
||||||
return lazyUIManagerGet( uiKey );
|
}
|
||||||
};
|
|
||||||
|
// check for function "if"
|
||||||
|
// Syntax: if(condition,trueValue,falseValue)
|
||||||
|
// - condition: evaluates to true if:
|
||||||
|
// - is not "null"
|
||||||
|
// - is not "false"
|
||||||
|
// - is not an integer with zero value
|
||||||
|
// - trueValue: used if condition is true
|
||||||
|
// - falseValue: used if condition is false
|
||||||
|
if( value.startsWith( "if(" ) && value.endsWith( ")" ) ) {
|
||||||
|
List<String> params = splitFunctionParams( value.substring( 3, value.length() - 1 ), ',' );
|
||||||
|
if( params.size() != 3 )
|
||||||
|
throwMissingParametersException( value );
|
||||||
|
|
||||||
|
boolean ifCondition = parseCondition( params.get( 0 ), resolver, addonClassLoaders );
|
||||||
|
String ifValue = params.get( ifCondition ? 1 : 2 );
|
||||||
|
return parseValue( key, resolver.apply( ifValue ), javaValueType, resultValueType, resolver, addonClassLoaders );
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueType valueType = ValueType.UNKNOWN;
|
ValueType valueType = ValueType.UNKNOWN;
|
||||||
|
|
||||||
// check whether value type is specified in the value
|
if( javaValueType != null ) {
|
||||||
if( value.startsWith( "#" ) )
|
if( javaValueTypes == null ) {
|
||||||
valueType = ValueType.COLOR;
|
// create lazy
|
||||||
else if( value.startsWith( "\"" ) && value.endsWith( "\"" ) ) {
|
javaValueTypes = new HashMap<>();
|
||||||
valueType = ValueType.STRING;
|
javaValueTypes.put( String.class, ValueType.STRING );
|
||||||
value = value.substring( 1, value.length() - 1 );
|
javaValueTypes.put( boolean.class, ValueType.BOOLEAN );
|
||||||
} else if( value.startsWith( TYPE_PREFIX ) ) {
|
javaValueTypes.put( Boolean.class, ValueType.BOOLEAN );
|
||||||
int end = value.indexOf( TYPE_PREFIX_END );
|
javaValueTypes.put( char.class, ValueType.CHARACTER );
|
||||||
if( end != -1 ) {
|
javaValueTypes.put( Character.class, ValueType.CHARACTER );
|
||||||
try {
|
javaValueTypes.put( int.class, ValueType.INTEGER );
|
||||||
String typeStr = value.substring( TYPE_PREFIX.length(), end );
|
javaValueTypes.put( Integer.class, ValueType.INTEGER );
|
||||||
valueType = ValueType.valueOf( typeStr.toUpperCase( Locale.ENGLISH ) );
|
javaValueTypes.put( float.class, ValueType.FLOAT );
|
||||||
|
javaValueTypes.put( Float.class, ValueType.FLOAT );
|
||||||
|
javaValueTypes.put( Border.class, ValueType.BORDER );
|
||||||
|
javaValueTypes.put( Icon.class, ValueType.ICON );
|
||||||
|
javaValueTypes.put( Insets.class, ValueType.INSETS );
|
||||||
|
javaValueTypes.put( Dimension.class, ValueType.DIMENSION );
|
||||||
|
javaValueTypes.put( Color.class, ValueType.COLOR );
|
||||||
|
javaValueTypes.put( Font.class, ValueType.FONT );
|
||||||
|
}
|
||||||
|
|
||||||
// remove type from value
|
// map java value type to parser value type
|
||||||
value = value.substring( end + TYPE_PREFIX_END.length() );
|
valueType = javaValueTypes.get( javaValueType );
|
||||||
} catch( IllegalArgumentException ex ) {
|
if( valueType == null )
|
||||||
// ignore
|
throw new IllegalArgumentException( "unsupported value type '" + javaValueType.getName() + "'" );
|
||||||
|
|
||||||
|
// remove '"' from strings
|
||||||
|
if( valueType == ValueType.STRING && value.startsWith( "\"" ) && value.endsWith( "\"" ) )
|
||||||
|
value = value.substring( 1, value.length() - 1 );
|
||||||
|
} else {
|
||||||
|
// false, true
|
||||||
|
switch( value ) {
|
||||||
|
case "false": resultValueType[0] = ValueType.BOOLEAN; return false;
|
||||||
|
case "true": resultValueType[0] = ValueType.BOOLEAN; return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for function "lazy"
|
||||||
|
// Syntax: lazy(uiKey)
|
||||||
|
if( value.startsWith( "lazy(" ) && value.endsWith( ")" ) ) {
|
||||||
|
resultValueType[0] = ValueType.LAZY;
|
||||||
|
String uiKey = StringUtils.substringTrimmed( value, 5, value.length() - 1 );
|
||||||
|
return (LazyValue) t -> {
|
||||||
|
return lazyUIManagerGet( uiKey );
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// check whether value type is specified in the value
|
||||||
|
if( value.startsWith( "#" ) )
|
||||||
|
valueType = ValueType.COLOR;
|
||||||
|
else if( value.startsWith( TYPE_PREFIX ) ) {
|
||||||
|
int end = value.indexOf( TYPE_PREFIX_END );
|
||||||
|
if( end != -1 ) {
|
||||||
|
try {
|
||||||
|
String typeStr = value.substring( TYPE_PREFIX.length(), end );
|
||||||
|
valueType = ValueType.valueOf( typeStr.toUpperCase( Locale.ENGLISH ) );
|
||||||
|
|
||||||
|
// remove type from value
|
||||||
|
value = value.substring( end + TYPE_PREFIX_END.length() );
|
||||||
|
} catch( IllegalArgumentException ex ) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// determine value type from key
|
if( valueType == ValueType.UNKNOWN ) {
|
||||||
if( valueType == ValueType.UNKNOWN ) {
|
if( knownValueTypes == null ) {
|
||||||
if( key.endsWith( "UI" ) )
|
// create lazy
|
||||||
valueType = ValueType.STRING;
|
knownValueTypes = new HashMap<>();
|
||||||
else if( key.endsWith( "Color" ) ||
|
// system colors
|
||||||
(key.endsWith( "ground" ) &&
|
knownValueTypes.put( "activeCaptionBorder", ValueType.COLOR );
|
||||||
(key.endsWith( ".background" ) || key.endsWith( "Background" ) ||
|
knownValueTypes.put( "inactiveCaptionBorder", ValueType.COLOR );
|
||||||
key.endsWith( ".foreground" ) || key.endsWith( "Foreground" ))) )
|
knownValueTypes.put( "windowBorder", ValueType.COLOR );
|
||||||
valueType = ValueType.COLOR;
|
// SplitPane
|
||||||
else if( key.endsWith( ".border" ) || key.endsWith( "Border" ) )
|
knownValueTypes.put( "SplitPane.dividerSize", ValueType.INTEGER );
|
||||||
valueType = ValueType.BORDER;
|
knownValueTypes.put( "SplitPaneDivider.gripDotSize", ValueType.INTEGER );
|
||||||
else if( key.endsWith( ".icon" ) || key.endsWith( "Icon" ) )
|
knownValueTypes.put( "dividerSize", ValueType.INTEGER );
|
||||||
valueType = ValueType.ICON;
|
knownValueTypes.put( "gripDotSize", ValueType.INTEGER );
|
||||||
else if( key.endsWith( ".margin" ) || key.endsWith( ".padding" ) ||
|
// TabbedPane
|
||||||
key.endsWith( "Margins" ) || key.endsWith( "Insets" ) )
|
knownValueTypes.put( "TabbedPane.closeCrossPlainSize", ValueType.FLOAT );
|
||||||
valueType = ValueType.INSETS;
|
knownValueTypes.put( "TabbedPane.closeCrossFilledSize", ValueType.FLOAT );
|
||||||
else if( key.endsWith( "Size" ) )
|
knownValueTypes.put( "closeCrossPlainSize", ValueType.FLOAT );
|
||||||
valueType = ValueType.DIMENSION;
|
knownValueTypes.put( "closeCrossFilledSize", ValueType.FLOAT );
|
||||||
else if( key.endsWith( "Width" ) || key.endsWith( "Height" ) )
|
// Table
|
||||||
valueType = ValueType.INTEGER;
|
knownValueTypes.put( "Table.intercellSpacing", ValueType.DIMENSION );
|
||||||
else if( key.endsWith( "Char" ) )
|
knownValueTypes.put( "intercellSpacing", ValueType.DIMENSION );
|
||||||
valueType = ValueType.CHARACTER;
|
}
|
||||||
else if( key.endsWith( "grayFilter" ) )
|
|
||||||
valueType = ValueType.GRAYFILTER;
|
valueType = knownValueTypes.getOrDefault( key, ValueType.UNKNOWN );
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine value type from key
|
||||||
|
if( valueType == ValueType.UNKNOWN ) {
|
||||||
|
if( key.endsWith( "UI" ) )
|
||||||
|
valueType = ValueType.STRING;
|
||||||
|
else if( key.endsWith( "Color" ) ||
|
||||||
|
(key.endsWith( "ground" ) &&
|
||||||
|
(key.endsWith( ".background" ) || key.endsWith( "Background" ) || key.equals( "background" ) ||
|
||||||
|
key.endsWith( ".foreground" ) || key.endsWith( "Foreground" ) || key.equals( "foreground" ))) )
|
||||||
|
valueType = ValueType.COLOR;
|
||||||
|
else if( key.endsWith( ".font" ) || key.endsWith( "Font" ) || key.equals( "font" ) )
|
||||||
|
valueType = ValueType.FONT;
|
||||||
|
else if( key.endsWith( ".border" ) || key.endsWith( "Border" ) || key.equals( "border" ) )
|
||||||
|
valueType = ValueType.BORDER;
|
||||||
|
else if( key.endsWith( ".icon" ) || key.endsWith( "Icon" ) || key.equals( "icon" ) )
|
||||||
|
valueType = ValueType.ICON;
|
||||||
|
else if( key.endsWith( ".margin" ) || key.equals( "margin" ) ||
|
||||||
|
key.endsWith( ".padding" ) || key.equals( "padding" ) ||
|
||||||
|
key.endsWith( "Margins" ) || key.endsWith( "Insets" ) )
|
||||||
|
valueType = ValueType.INSETS;
|
||||||
|
else if( key.endsWith( "Size" ) )
|
||||||
|
valueType = ValueType.DIMENSION;
|
||||||
|
else if( key.endsWith( "Width" ) || key.endsWith( "Height" ) )
|
||||||
|
valueType = ValueType.INTEGERORFLOAT;
|
||||||
|
else if( key.endsWith( "Char" ) )
|
||||||
|
valueType = ValueType.CHARACTER;
|
||||||
|
else if( key.endsWith( "grayFilter" ) )
|
||||||
|
valueType = ValueType.GRAYFILTER;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resultValueType[0] = valueType;
|
resultValueType[0] = valueType;
|
||||||
@@ -376,14 +523,17 @@ class UIDefaultsLoader
|
|||||||
// parse value
|
// parse value
|
||||||
switch( valueType ) {
|
switch( valueType ) {
|
||||||
case STRING: return value;
|
case STRING: return value;
|
||||||
|
case BOOLEAN: return parseBoolean( value );
|
||||||
case CHARACTER: return parseCharacter( value );
|
case CHARACTER: return parseCharacter( value );
|
||||||
case INTEGER: return parseInteger( value, true );
|
case INTEGER: return parseInteger( value, true );
|
||||||
|
case INTEGERORFLOAT:return parseIntegerOrFloat( value, true );
|
||||||
case FLOAT: return parseFloat( value, true );
|
case FLOAT: return parseFloat( value, true );
|
||||||
case BORDER: return parseBorder( value, resolver, addonClassLoaders );
|
case BORDER: return parseBorder( value, resolver, addonClassLoaders );
|
||||||
case ICON: return parseInstance( value, addonClassLoaders );
|
case ICON: return parseInstance( value, addonClassLoaders );
|
||||||
case INSETS: return parseInsets( value );
|
case INSETS: return parseInsets( value );
|
||||||
case DIMENSION: return parseDimension( value );
|
case DIMENSION: return parseDimension( value );
|
||||||
case COLOR: return parseColorOrFunction( value, resolver, true );
|
case COLOR: return parseColorOrFunction( value, resolver, true );
|
||||||
|
case FONT: return parseFont( value );
|
||||||
case SCALEDINTEGER: return parseScaledInteger( value );
|
case SCALEDINTEGER: return parseScaledInteger( value );
|
||||||
case SCALEDFLOAT: return parseScaledFloat( value );
|
case SCALEDFLOAT: return parseScaledFloat( value );
|
||||||
case SCALEDINSETS: return parseScaledInsets( value );
|
case SCALEDINSETS: return parseScaledInsets( value );
|
||||||
@@ -393,6 +543,12 @@ class UIDefaultsLoader
|
|||||||
case GRAYFILTER: return parseGrayFilter( value );
|
case GRAYFILTER: return parseGrayFilter( value );
|
||||||
case UNKNOWN:
|
case UNKNOWN:
|
||||||
default:
|
default:
|
||||||
|
// string
|
||||||
|
if( value.startsWith( "\"" ) && value.endsWith( "\"" ) ) {
|
||||||
|
resultValueType[0] = ValueType.STRING;
|
||||||
|
return value.substring( 1, value.length() - 1 );
|
||||||
|
}
|
||||||
|
|
||||||
// colors
|
// colors
|
||||||
Object color = parseColorOrFunction( value, resolver, false );
|
Object color = parseColorOrFunction( value, resolver, false );
|
||||||
if( color != null ) {
|
if( color != null ) {
|
||||||
@@ -420,19 +576,34 @@ class UIDefaultsLoader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean parseCondition( String condition,
|
||||||
|
Function<String, String> resolver, List<ClassLoader> addonClassLoaders )
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
Object conditionValue = parseValue( "", resolver.apply( condition ), null, null, resolver, addonClassLoaders );
|
||||||
|
return (conditionValue != null &&
|
||||||
|
!conditionValue.equals( false ) &&
|
||||||
|
!conditionValue.equals( 0 ) );
|
||||||
|
} catch( IllegalArgumentException ex ) {
|
||||||
|
// ignore errors (e.g. variable or property not found) and evaluate to false
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static Object parseBorder( String value, Function<String, String> resolver, List<ClassLoader> addonClassLoaders ) {
|
private static Object parseBorder( String value, Function<String, String> resolver, List<ClassLoader> addonClassLoaders ) {
|
||||||
if( value.indexOf( ',' ) >= 0 ) {
|
if( value.indexOf( ',' ) >= 0 ) {
|
||||||
// top,left,bottom,right[,lineColor[,lineThickness]]
|
// top,left,bottom,right[,lineColor[,lineThickness[,arc]]]
|
||||||
List<String> parts = split( value, ',' );
|
List<String> parts = splitFunctionParams( value, ',' );
|
||||||
Insets insets = parseInsets( value );
|
Insets insets = parseInsets( value );
|
||||||
ColorUIResource lineColor = (parts.size() >= 5)
|
ColorUIResource lineColor = (parts.size() >= 5)
|
||||||
? (ColorUIResource) parseColorOrFunction( resolver.apply( parts.get( 4 ) ), resolver, true )
|
? (ColorUIResource) parseColorOrFunction( resolver.apply( parts.get( 4 ) ), resolver, true )
|
||||||
: null;
|
: null;
|
||||||
float lineThickness = (parts.size() >= 6) ? parseFloat( parts.get( 5 ), true ) : 1f;
|
float lineThickness = (parts.size() >= 6 && !parts.get( 5 ).isEmpty()) ? parseFloat( parts.get( 5 ), true ) : 1f;
|
||||||
|
int arc = (parts.size() >= 7) ? parseInteger( parts.get( 6 ), true ) : 0;
|
||||||
|
|
||||||
return (LazyValue) t -> {
|
return (LazyValue) t -> {
|
||||||
return (lineColor != null)
|
return (lineColor != null)
|
||||||
? new FlatLineBorder( insets, lineColor, lineThickness )
|
? new FlatLineBorder( insets, lineColor, lineThickness, arc )
|
||||||
: new FlatEmptyBorder( insets );
|
: new FlatEmptyBorder( insets );
|
||||||
};
|
};
|
||||||
} else
|
} else
|
||||||
@@ -480,7 +651,7 @@ class UIDefaultsLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Insets parseInsets( String value ) {
|
private static Insets parseInsets( String value ) {
|
||||||
List<String> numbers = split( value, ',' );
|
List<String> numbers = StringUtils.split( value, ',', true, false );
|
||||||
try {
|
try {
|
||||||
return new InsetsUIResource(
|
return new InsetsUIResource(
|
||||||
Integer.parseInt( numbers.get( 0 ) ),
|
Integer.parseInt( numbers.get( 0 ) ),
|
||||||
@@ -493,7 +664,7 @@ class UIDefaultsLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Dimension parseDimension( String value ) {
|
private static Dimension parseDimension( String value ) {
|
||||||
List<String> numbers = split( value, ',' );
|
List<String> numbers = StringUtils.split( value, ',', true, false );
|
||||||
try {
|
try {
|
||||||
return new DimensionUIResource(
|
return new DimensionUIResource(
|
||||||
Integer.parseInt( numbers.get( 0 ) ),
|
Integer.parseInt( numbers.get( 0 ) ),
|
||||||
@@ -581,10 +752,10 @@ class UIDefaultsLoader
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
String function = value.substring( 0, paramsStart ).trim();
|
String function = StringUtils.substringTrimmed( value, 0, paramsStart );
|
||||||
List<String> params = splitFunctionParams( value.substring( paramsStart + 1, value.length() - 1 ), ',' );
|
List<String> params = splitFunctionParams( value.substring( paramsStart + 1, value.length() - 1 ), ',' );
|
||||||
if( params.isEmpty() )
|
if( params.isEmpty() )
|
||||||
throw new IllegalArgumentException( "missing parameters in function '" + value + "'" );
|
throwMissingParametersException( value );
|
||||||
|
|
||||||
if( parseColorDepth > 100 )
|
if( parseColorDepth > 100 )
|
||||||
throw new IllegalArgumentException( "endless recursion in color function '" + value + "'" );
|
throw new IllegalArgumentException( "endless recursion in color function '" + value + "'" );
|
||||||
@@ -592,6 +763,7 @@ class UIDefaultsLoader
|
|||||||
parseColorDepth++;
|
parseColorDepth++;
|
||||||
try {
|
try {
|
||||||
switch( function ) {
|
switch( function ) {
|
||||||
|
case "if": return parseColorIf( value, params, resolver, reportError );
|
||||||
case "rgb": return parseColorRgbOrRgba( false, params, resolver, reportError );
|
case "rgb": return parseColorRgbOrRgba( false, params, resolver, reportError );
|
||||||
case "rgba": return parseColorRgbOrRgba( true, params, resolver, reportError );
|
case "rgba": return parseColorRgbOrRgba( true, params, resolver, reportError );
|
||||||
case "hsl": return parseColorHslOrHsla( false, params );
|
case "hsl": return parseColorHslOrHsla( false, params );
|
||||||
@@ -611,6 +783,8 @@ class UIDefaultsLoader
|
|||||||
case "mix": return parseColorMix( null, params, resolver, reportError );
|
case "mix": return parseColorMix( null, params, resolver, reportError );
|
||||||
case "tint": return parseColorMix( "#fff", params, resolver, reportError );
|
case "tint": return parseColorMix( "#fff", params, resolver, reportError );
|
||||||
case "shade": return parseColorMix( "#000", params, resolver, reportError );
|
case "shade": return parseColorMix( "#000", params, resolver, reportError );
|
||||||
|
case "contrast": return parseColorContrast( params, resolver, reportError );
|
||||||
|
case "over": return parseColorOver( params, resolver, reportError );
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
parseColorDepth--;
|
parseColorDepth--;
|
||||||
@@ -619,6 +793,21 @@ class UIDefaultsLoader
|
|||||||
throw new IllegalArgumentException( "unknown color function '" + value + "'" );
|
throw new IllegalArgumentException( "unknown color function '" + value + "'" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Syntax: if(condition,trueValue,falseValue)
|
||||||
|
* <p>
|
||||||
|
* This "if" function is only used if the "if" is passed as parameter to another
|
||||||
|
* color function. Otherwise, the general "if" function is used.
|
||||||
|
*/
|
||||||
|
private static Object parseColorIf( String value, List<String> params, Function<String, String> resolver, boolean reportError ) {
|
||||||
|
if( params.size() != 3 )
|
||||||
|
throwMissingParametersException( value );
|
||||||
|
|
||||||
|
boolean ifCondition = parseCondition( params.get( 0 ), resolver, Collections.emptyList() );
|
||||||
|
String ifValue = params.get( ifCondition ? 1 : 2 );
|
||||||
|
return parseColorOrFunction( resolver.apply( ifValue ), resolver, reportError );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Syntax: rgb(red,green,blue) or rgba(red,green,blue,alpha)
|
* Syntax: rgb(red,green,blue) or rgba(red,green,blue,alpha)
|
||||||
* - red: an integer 0-255 or a percentage 0-100%
|
* - red: an integer 0-255 or a percentage 0-100%
|
||||||
@@ -663,7 +852,7 @@ class UIDefaultsLoader
|
|||||||
int lightness = parsePercentage( params.get( 2 ) );
|
int lightness = parsePercentage( params.get( 2 ) );
|
||||||
int alpha = hasAlpha ? parsePercentage( params.get( 3 ) ) : 100;
|
int alpha = hasAlpha ? parsePercentage( params.get( 3 ) ) : 100;
|
||||||
|
|
||||||
float[] hsl = new float[] { hue, saturation, lightness };
|
float[] hsl = { hue, saturation, lightness };
|
||||||
return new ColorUIResource( HSLColor.toRGB( hsl, alpha / 100f ) );
|
return new ColorUIResource( HSLColor.toRGB( hsl, alpha / 100f ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -718,21 +907,32 @@ class UIDefaultsLoader
|
|||||||
* Syntax: fade(color,amount[,options])
|
* Syntax: fade(color,amount[,options])
|
||||||
* - color: a color (e.g. #f00) or a color function
|
* - color: a color (e.g. #f00) or a color function
|
||||||
* - amount: percentage 0-100%
|
* - amount: percentage 0-100%
|
||||||
* - options: [derived]
|
* - options: [derived] [lazy]
|
||||||
*/
|
*/
|
||||||
private static Object parseColorFade( List<String> params, Function<String, String> resolver, boolean reportError ) {
|
private static Object parseColorFade( List<String> params, Function<String, String> resolver, boolean reportError ) {
|
||||||
String colorStr = params.get( 0 );
|
String colorStr = params.get( 0 );
|
||||||
int amount = parsePercentage( params.get( 1 ) );
|
int amount = parsePercentage( params.get( 1 ) );
|
||||||
boolean derived = false;
|
boolean derived = false;
|
||||||
|
boolean lazy = false;
|
||||||
|
|
||||||
if( params.size() > 2 ) {
|
if( params.size() > 2 ) {
|
||||||
String options = params.get( 2 );
|
String options = params.get( 2 );
|
||||||
derived = options.contains( "derived" );
|
derived = options.contains( "derived" );
|
||||||
|
lazy = options.contains( "lazy" );
|
||||||
}
|
}
|
||||||
|
|
||||||
// create function
|
// create function
|
||||||
ColorFunction function = new ColorFunctions.Fade( amount );
|
ColorFunction function = new ColorFunctions.Fade( amount );
|
||||||
|
|
||||||
|
if( lazy ) {
|
||||||
|
return (LazyValue) t -> {
|
||||||
|
Object color = lazyUIManagerGet( colorStr );
|
||||||
|
return (color instanceof Color)
|
||||||
|
? new ColorUIResource( ColorFunctions.applyFunctions( (Color) color, function ) )
|
||||||
|
: null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// parse base color, apply function and create derived color
|
// parse base color, apply function and create derived color
|
||||||
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
|
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
|
||||||
}
|
}
|
||||||
@@ -804,14 +1004,10 @@ class UIDefaultsLoader
|
|||||||
if( color1Str == null )
|
if( color1Str == null )
|
||||||
color1Str = params.get( i++ );
|
color1Str = params.get( i++ );
|
||||||
String color2Str = params.get( i++ );
|
String color2Str = params.get( i++ );
|
||||||
int weight = 50;
|
int weight = (params.size() > i) ? parsePercentage( params.get( i ) ) : 50;
|
||||||
|
|
||||||
if( params.size() > i )
|
|
||||||
weight = parsePercentage( params.get( i++ ) );
|
|
||||||
|
|
||||||
// parse second color
|
// parse second color
|
||||||
String resolvedColor2Str = resolver.apply( color2Str );
|
ColorUIResource color2 = (ColorUIResource) parseColorOrFunction( resolver.apply( color2Str ), resolver, reportError );
|
||||||
ColorUIResource color2 = (ColorUIResource) parseColorOrFunction( resolvedColor2Str, resolver, reportError );
|
|
||||||
if( color2 == null )
|
if( color2 == null )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@@ -822,6 +1018,61 @@ class UIDefaultsLoader
|
|||||||
return parseFunctionBaseColor( color1Str, function, false, resolver, reportError );
|
return parseFunctionBaseColor( color1Str, function, false, resolver, reportError );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Syntax: contrast(color,dark,light[,threshold])
|
||||||
|
* - color: a color to compare against
|
||||||
|
* - dark: a designated dark color (e.g. #000) or a color function
|
||||||
|
* - light: a designated light color (e.g. #fff) or a color function
|
||||||
|
* - threshold: the threshold (in range 0-100%) to specify where the transition
|
||||||
|
* from "dark" to "light" is (default is 43%)
|
||||||
|
*/
|
||||||
|
private static Object parseColorContrast( List<String> params, Function<String, String> resolver, boolean reportError ) {
|
||||||
|
String colorStr = params.get( 0 );
|
||||||
|
String darkStr = params.get( 1 );
|
||||||
|
String lightStr = params.get( 2 );
|
||||||
|
int threshold = (params.size() > 3) ? parsePercentage( params.get( 3 ) ) : 43;
|
||||||
|
|
||||||
|
// parse color to compare against
|
||||||
|
ColorUIResource color = (ColorUIResource) parseColorOrFunction( resolver.apply( colorStr ), resolver, reportError );
|
||||||
|
if( color == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// check luma and determine whether to use dark or light color
|
||||||
|
String darkOrLightColor = (ColorFunctions.luma( color ) * 100 < threshold)
|
||||||
|
? lightStr
|
||||||
|
: darkStr;
|
||||||
|
|
||||||
|
// parse dark or light color
|
||||||
|
return parseColorOrFunction( resolver.apply( darkOrLightColor ), resolver, reportError );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Syntax: over(foreground,background)
|
||||||
|
* - foreground: a foreground color (e.g. #f00) or a color function;
|
||||||
|
* the alpha of this color is used as weight to mix the two colors
|
||||||
|
* - background: a background color (e.g. #f00) or a color function
|
||||||
|
*/
|
||||||
|
private static Object parseColorOver( List<String> params, Function<String, String> resolver, boolean reportError ) {
|
||||||
|
String foregroundStr = params.get( 0 );
|
||||||
|
String backgroundStr = params.get( 1 );
|
||||||
|
|
||||||
|
// parse foreground color
|
||||||
|
ColorUIResource foreground = (ColorUIResource) parseColorOrFunction( resolver.apply( foregroundStr ), resolver, reportError );
|
||||||
|
if( foreground == null || foreground.getAlpha() == 255 )
|
||||||
|
return foreground;
|
||||||
|
|
||||||
|
Color foreground2 = new Color( foreground.getRGB() );
|
||||||
|
|
||||||
|
// parse background color
|
||||||
|
ColorUIResource background = (ColorUIResource) parseColorOrFunction( resolver.apply( backgroundStr ), resolver, reportError );
|
||||||
|
if( background == null )
|
||||||
|
return foreground2;
|
||||||
|
|
||||||
|
// create new color
|
||||||
|
float weight = foreground.getAlpha() / 255f;
|
||||||
|
return new ColorUIResource( ColorFunctions.mix( foreground2, background, weight ) );
|
||||||
|
}
|
||||||
|
|
||||||
private static Object parseFunctionBaseColor( String colorStr, ColorFunction function,
|
private static Object parseFunctionBaseColor( String colorStr, ColorFunction function,
|
||||||
boolean derived, Function<String, String> resolver, boolean reportError )
|
boolean derived, Function<String, String> resolver, boolean reportError )
|
||||||
{
|
{
|
||||||
@@ -852,6 +1103,107 @@ class UIDefaultsLoader
|
|||||||
return new ColorUIResource( newColor );
|
return new ColorUIResource( newColor );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Syntax: [normal] [bold|+bold|-bold] [italic|+italic|-italic] [<size>|+<incr>|-<decr>|<percent>%] [family[, family]] [$baseFontKey]
|
||||||
|
*/
|
||||||
|
private static Object parseFont( String value ) {
|
||||||
|
Object font = fontCache.get( value );
|
||||||
|
if( font != null )
|
||||||
|
return font;
|
||||||
|
|
||||||
|
int style = -1;
|
||||||
|
int styleChange = 0;
|
||||||
|
int absoluteSize = 0;
|
||||||
|
int relativeSize = 0;
|
||||||
|
float scaleSize = 0;
|
||||||
|
List<String> families = null;
|
||||||
|
String baseFontKey = null;
|
||||||
|
|
||||||
|
// use StreamTokenizer to split string because it supports quoted strings
|
||||||
|
StreamTokenizer st = new StreamTokenizer( new StringReader( value ) );
|
||||||
|
st.resetSyntax();
|
||||||
|
st.wordChars( ' ' + 1, 255 );
|
||||||
|
st.whitespaceChars( 0, ' ' );
|
||||||
|
st.whitespaceChars( ',', ',' ); // ignore ','
|
||||||
|
st.quoteChar( '"' );
|
||||||
|
st.quoteChar( '\'' );
|
||||||
|
|
||||||
|
try {
|
||||||
|
while( st.nextToken() != StreamTokenizer.TT_EOF ) {
|
||||||
|
String param = st.sval;
|
||||||
|
switch( param ) {
|
||||||
|
// font style
|
||||||
|
case "normal":
|
||||||
|
style = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "bold":
|
||||||
|
if( style == -1 )
|
||||||
|
style = 0;
|
||||||
|
style |= Font.BOLD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "italic":
|
||||||
|
if( style == -1 )
|
||||||
|
style = 0;
|
||||||
|
style |= Font.ITALIC;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "+bold": styleChange |= Font.BOLD; break;
|
||||||
|
case "-bold": styleChange |= Font.BOLD << 16; break;
|
||||||
|
case "+italic": styleChange |= Font.ITALIC; break;
|
||||||
|
case "-italic": styleChange |= Font.ITALIC << 16; break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
char firstChar = param.charAt( 0 );
|
||||||
|
if( Character.isDigit( firstChar ) || firstChar == '+' || firstChar == '-' ) {
|
||||||
|
// font size
|
||||||
|
if( absoluteSize != 0 || relativeSize != 0 || scaleSize != 0 )
|
||||||
|
throw new IllegalArgumentException( "size specified more than once in '" + value + "'" );
|
||||||
|
|
||||||
|
if( firstChar == '+' || firstChar == '-' )
|
||||||
|
relativeSize = parseInteger( param, true );
|
||||||
|
else if( param.endsWith( "%" ) )
|
||||||
|
scaleSize = parseInteger( param.substring( 0, param.length() - 1 ), true ) / 100f;
|
||||||
|
else
|
||||||
|
absoluteSize = parseInteger( param, true );
|
||||||
|
} else if( firstChar == '$' ) {
|
||||||
|
// reference to base font
|
||||||
|
if( baseFontKey != null )
|
||||||
|
throw new IllegalArgumentException( "baseFontKey specified more than once in '" + value + "'" );
|
||||||
|
|
||||||
|
baseFontKey = param.substring( 1 );
|
||||||
|
} else {
|
||||||
|
// font family
|
||||||
|
if( families == null )
|
||||||
|
families = Collections.singletonList( param );
|
||||||
|
else {
|
||||||
|
if( families.size() == 1 )
|
||||||
|
families = new ArrayList<>( families );
|
||||||
|
families.add( param );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch( IOException ex ) {
|
||||||
|
throw new IllegalArgumentException( ex );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( style != -1 && styleChange != 0 )
|
||||||
|
throw new IllegalArgumentException( "can not mix absolute style (e.g. 'bold') with derived style (e.g. '+italic') in '" + value + "'" );
|
||||||
|
if( styleChange != 0 ) {
|
||||||
|
if( (styleChange & Font.BOLD) != 0 && (styleChange & (Font.BOLD << 16)) != 0 )
|
||||||
|
throw new IllegalArgumentException( "can not use '+bold' and '-bold' in '" + value + "'" );
|
||||||
|
if( (styleChange & Font.ITALIC) != 0 && (styleChange & (Font.ITALIC << 16)) != 0 )
|
||||||
|
throw new IllegalArgumentException( "can not use '+italic' and '-italic' in '" + value + "'" );
|
||||||
|
}
|
||||||
|
|
||||||
|
font = new FlatLaf.ActiveFont( baseFontKey, families, style, styleChange, absoluteSize, relativeSize, scaleSize );
|
||||||
|
fontCache.put( value, font );
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
|
||||||
private static int parsePercentage( String value ) {
|
private static int parsePercentage( String value ) {
|
||||||
if( !value.endsWith( "%" ) )
|
if( !value.endsWith( "%" ) )
|
||||||
throw new NumberFormatException( "invalid percentage '" + value + "'" );
|
throw new NumberFormatException( "invalid percentage '" + value + "'" );
|
||||||
@@ -868,6 +1220,14 @@ class UIDefaultsLoader
|
|||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Boolean parseBoolean( String value ) {
|
||||||
|
switch( value ) {
|
||||||
|
case "false": return false;
|
||||||
|
case "true": return true;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException( "invalid boolean '" + value + "'" );
|
||||||
|
}
|
||||||
|
|
||||||
private static Character parseCharacter( String value ) {
|
private static Character parseCharacter( String value ) {
|
||||||
if( value.length() != 1 )
|
if( value.length() != 1 )
|
||||||
throw new IllegalArgumentException( "invalid character '" + value + "'" );
|
throw new IllegalArgumentException( "invalid character '" + value + "'" );
|
||||||
@@ -881,7 +1241,7 @@ class UIDefaultsLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
Integer integer = parseInteger( value, true );
|
Integer integer = parseInteger( value, true );
|
||||||
if( integer.intValue() < min || integer.intValue() > max )
|
if( integer < min || integer > max )
|
||||||
throw new NumberFormatException( "integer '" + value + "' out of range (" + min + '-' + max + ')' );
|
throw new NumberFormatException( "integer '" + value + "' out of range (" + min + '-' + max + ')' );
|
||||||
return integer;
|
return integer;
|
||||||
}
|
}
|
||||||
@@ -896,6 +1256,20 @@ class UIDefaultsLoader
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Number parseIntegerOrFloat( String value, boolean reportError ) {
|
||||||
|
try {
|
||||||
|
return Integer.parseInt( value );
|
||||||
|
} catch( NumberFormatException ex ) {
|
||||||
|
try {
|
||||||
|
return Float.parseFloat( value );
|
||||||
|
} catch( NumberFormatException ex2 ) {
|
||||||
|
if( reportError )
|
||||||
|
throw new NumberFormatException( "invalid integer or float '" + value + "'" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private static Float parseFloat( String value, boolean reportError ) {
|
private static Float parseFloat( String value, boolean reportError ) {
|
||||||
try {
|
try {
|
||||||
return Float.parseFloat( value );
|
return Float.parseFloat( value );
|
||||||
@@ -908,34 +1282,34 @@ class UIDefaultsLoader
|
|||||||
|
|
||||||
private static ActiveValue parseScaledInteger( String value ) {
|
private static ActiveValue parseScaledInteger( String value ) {
|
||||||
int val = parseInteger( value, true );
|
int val = parseInteger( value, true );
|
||||||
return (ActiveValue) t -> {
|
return t -> {
|
||||||
return UIScale.scale( val );
|
return UIScale.scale( val );
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ActiveValue parseScaledFloat( String value ) {
|
private static ActiveValue parseScaledFloat( String value ) {
|
||||||
float val = parseFloat( value, true );
|
float val = parseFloat( value, true );
|
||||||
return (ActiveValue) t -> {
|
return t -> {
|
||||||
return UIScale.scale( val );
|
return UIScale.scale( val );
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ActiveValue parseScaledInsets( String value ) {
|
private static ActiveValue parseScaledInsets( String value ) {
|
||||||
Insets insets = parseInsets( value );
|
Insets insets = parseInsets( value );
|
||||||
return (ActiveValue) t -> {
|
return t -> {
|
||||||
return UIScale.scale( insets );
|
return UIScale.scale( insets );
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ActiveValue parseScaledDimension( String value ) {
|
private static ActiveValue parseScaledDimension( String value ) {
|
||||||
Dimension dimension = parseDimension( value );
|
Dimension dimension = parseDimension( value );
|
||||||
return (ActiveValue) t -> {
|
return t -> {
|
||||||
return UIScale.scale( dimension );
|
return UIScale.scale( dimension );
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Object parseGrayFilter( String value ) {
|
private static Object parseGrayFilter( String value ) {
|
||||||
List<String> numbers = split( value, ',' );
|
List<String> numbers = StringUtils.split( value, ',', true, false );
|
||||||
try {
|
try {
|
||||||
int brightness = Integer.parseInt( numbers.get( 0 ) );
|
int brightness = Integer.parseInt( numbers.get( 0 ) );
|
||||||
int contrast = Integer.parseInt( numbers.get( 1 ) );
|
int contrast = Integer.parseInt( numbers.get( 1 ) );
|
||||||
@@ -949,20 +1323,6 @@ class UIDefaultsLoader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Split string and trim parts.
|
|
||||||
*/
|
|
||||||
private static List<String> split( String str, char delim ) {
|
|
||||||
List<String> result = StringUtils.split( str, delim );
|
|
||||||
|
|
||||||
// trim strings
|
|
||||||
int size = result.size();
|
|
||||||
for( int i = 0; i < size; i++ )
|
|
||||||
result.set( i, result.get( i ).trim() );
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Splits function parameters and allows using functions as parameters.
|
* Splits function parameters and allows using functions as parameters.
|
||||||
* In other words: Delimiters surrounded by '(' and ')' are ignored.
|
* In other words: Delimiters surrounded by '(' and ')' are ignored.
|
||||||
@@ -979,11 +1339,11 @@ class UIDefaultsLoader
|
|||||||
else if( ch == ')' )
|
else if( ch == ')' )
|
||||||
nestLevel--;
|
nestLevel--;
|
||||||
else if( nestLevel == 0 && ch == delim ) {
|
else if( nestLevel == 0 && ch == delim ) {
|
||||||
strs.add( str.substring( start, i ).trim() );
|
strs.add( StringUtils.substringTrimmed( str, start, i ) );
|
||||||
start = i + 1;
|
start = i + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
strs.add( str.substring( start ).trim() );
|
strs.add( StringUtils.substringTrimmed( str, start ) );
|
||||||
|
|
||||||
return strs;
|
return strs;
|
||||||
}
|
}
|
||||||
@@ -992,7 +1352,7 @@ class UIDefaultsLoader
|
|||||||
* For use in LazyValue to get value for given key from UIManager and report error
|
* For use in LazyValue to get value for given key from UIManager and report error
|
||||||
* if not found. If key is prefixed by '?', then no error is reported.
|
* if not found. If key is prefixed by '?', then no error is reported.
|
||||||
*/
|
*/
|
||||||
private static Object lazyUIManagerGet( String uiKey ) {
|
static Object lazyUIManagerGet( String uiKey ) {
|
||||||
boolean optional = false;
|
boolean optional = false;
|
||||||
if( uiKey.startsWith( OPTIONAL_PREFIX ) ) {
|
if( uiKey.startsWith( OPTIONAL_PREFIX ) ) {
|
||||||
uiKey = uiKey.substring( OPTIONAL_PREFIX.length() );
|
uiKey = uiKey.substring( OPTIONAL_PREFIX.length() );
|
||||||
@@ -1004,4 +1364,8 @@ class UIDefaultsLoader
|
|||||||
LoggingFacade.INSTANCE.logSevere( "FlatLaf: '" + uiKey + "' not found in UI defaults.", null );
|
LoggingFacade.INSTANCE.logSevere( "FlatLaf: '" + uiKey + "' not found in UI defaults.", null );
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void throwMissingParametersException( String value ) {
|
||||||
|
throw new IllegalArgumentException( "missing parameters in function '" + value + "'" );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public abstract class FlatAbstractIcon
|
|||||||
{
|
{
|
||||||
protected final int width;
|
protected final int width;
|
||||||
protected final int height;
|
protected final int height;
|
||||||
protected final Color color;
|
protected Color color;
|
||||||
|
|
||||||
public FlatAbstractIcon( int width, int height, Color color ) {
|
public FlatAbstractIcon( int width, int height, Color color ) {
|
||||||
this.width = width;
|
this.width = width;
|
||||||
|
|||||||
@@ -23,13 +23,13 @@ import java.awt.Graphics2D;
|
|||||||
import com.formdev.flatlaf.util.AnimatedIcon;
|
import com.formdev.flatlaf.util.AnimatedIcon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for animated icons that scales width and height, creates and initializes
|
* Base class for animated icons that scale width and height, creates and initializes
|
||||||
* a scaled graphics context for icon painting.
|
* a scaled graphics context for icon painting.
|
||||||
* <p>
|
* <p>
|
||||||
* Subclasses do not need to scale icon painting.
|
* Subclasses do not need to scale icon painting.
|
||||||
* <p>
|
* <p>
|
||||||
* This class does not store any state information (needed for animation) in its instance.
|
* This class does not store any state information (needed for animation) in its instance.
|
||||||
* Instead a client property is set on the painted component.
|
* Instead, a client property is set on the painted component.
|
||||||
* This makes it possible to use a share icon instance for multiple components.
|
* This makes it possible to use a share icon instance for multiple components.
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
|
|||||||
@@ -21,7 +21,11 @@ import java.awt.Color;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.plaf.TableHeaderUI;
|
||||||
|
import javax.swing.table.JTableHeader;
|
||||||
|
import com.formdev.flatlaf.ui.FlatTableHeaderUI;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,8 +39,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
public class FlatAscendingSortIcon
|
public class FlatAscendingSortIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
{
|
{
|
||||||
protected final boolean chevron = FlatUIUtils.isChevron( UIManager.getString( "Component.arrowType" ) );
|
protected boolean chevron = FlatUIUtils.isChevron( UIManager.getString( "Component.arrowType" ) );
|
||||||
protected final Color sortIconColor = UIManager.getColor( "Table.sortIconColor" );
|
protected Color sortIconColor = UIManager.getColor( "Table.sortIconColor" );
|
||||||
|
|
||||||
public FlatAscendingSortIcon() {
|
public FlatAscendingSortIcon() {
|
||||||
super( 10, 5, null );
|
super( 10, 5, null );
|
||||||
@@ -44,7 +48,28 @@ public class FlatAscendingSortIcon
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
|
boolean chevron = this.chevron;
|
||||||
|
Color sortIconColor = this.sortIconColor;
|
||||||
|
|
||||||
|
// Because this icon is always shared for all table headers,
|
||||||
|
// get icon specific style from FlatTableHeaderUI.
|
||||||
|
JTableHeader tableHeader = (JTableHeader) SwingUtilities.getAncestorOfClass( JTableHeader.class, c );
|
||||||
|
if( tableHeader != null ) {
|
||||||
|
TableHeaderUI ui = tableHeader.getUI();
|
||||||
|
if( ui instanceof FlatTableHeaderUI ) {
|
||||||
|
FlatTableHeaderUI fui = (FlatTableHeaderUI) ui;
|
||||||
|
if( fui.arrowType != null )
|
||||||
|
chevron = FlatUIUtils.isChevron( fui.arrowType );
|
||||||
|
if( fui.sortIconColor != null )
|
||||||
|
sortIconColor = fui.sortIconColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
g.setColor( sortIconColor );
|
g.setColor( sortIconColor );
|
||||||
|
paintArrow( c, g, chevron );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void paintArrow( Component c, Graphics2D g, boolean chevron ) {
|
||||||
if( chevron ) {
|
if( chevron ) {
|
||||||
// chevron arrow
|
// chevron arrow
|
||||||
Path2D path = FlatUIUtils.createPath( false, 1,4, 5,0, 9,4 );
|
Path2D path = FlatUIUtils.createPath( false, 1,4, 5,0, 9,4 );
|
||||||
|
|||||||
@@ -16,12 +16,14 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.icons;
|
package com.formdev.flatlaf.icons;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.awt.geom.RoundRectangle2D;
|
import java.awt.geom.RoundRectangle2D;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,6 +40,15 @@ public class FlatCapsLockIcon
|
|||||||
super( 16, 16, UIManager.getColor( "PasswordField.capsLockIconColor" ) );
|
super( 16, 16, UIManager.getColor( "PasswordField.capsLockIconColor" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public Object applyStyleProperty( String key, Object value ) {
|
||||||
|
Object oldValue;
|
||||||
|
switch( key ) {
|
||||||
|
case "capsLockIconColor": oldValue = color; color = (Color) value; return oldValue;
|
||||||
|
default: throw new UnknownStyleException( key );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -23,83 +23,115 @@ import java.awt.Component;
|
|||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
import java.awt.geom.RoundRectangle2D;
|
import java.awt.geom.RoundRectangle2D;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import com.formdev.flatlaf.ui.FlatButtonUI;
|
import com.formdev.flatlaf.ui.FlatButtonUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Icon for {@link javax.swing.JCheckBox}.
|
* Icon for {@link javax.swing.JCheckBox}.
|
||||||
*
|
* <p>
|
||||||
* Note: If Component.focusWidth is greater than zero, then the outline focus border
|
* <strong>Note</strong>:
|
||||||
|
* If Component.focusWidth is greater than zero, then the outer focus border
|
||||||
* is painted outside of the icon bounds. Make sure that the checkbox
|
* is painted outside of the icon bounds. Make sure that the checkbox
|
||||||
* has margins, which are equal or greater than focusWidth.
|
* has margins, which are equal or greater than focusWidth.
|
||||||
*
|
*
|
||||||
* @uiDefault CheckBox.icon.style String optional; "outline"/null (default) or "filled"
|
* @uiDefault CheckBox.icon.style String optional; "outlined"/null (default) or "filled"
|
||||||
* @uiDefault Component.focusWidth int
|
* @uiDefault Component.focusWidth int
|
||||||
|
* @uiDefault Component.borderWidth int
|
||||||
* @uiDefault Component.focusColor Color
|
* @uiDefault Component.focusColor Color
|
||||||
* @uiDefault CheckBox.icon.focusWidth int optional; defaults to Component.focusWidth
|
* @uiDefault CheckBox.icon.focusWidth int or float optional; defaults to Component.focusWidth
|
||||||
|
* @uiDefault CheckBox.icon.borderWidth int or float optional; defaults to Component.borderWidth
|
||||||
|
* @uiDefault CheckBox.icon.selectedBorderWidth int or float optional; defaults to CheckBox.icon.borderWidth
|
||||||
|
* @uiDefault CheckBox.icon.disabledSelectedBorderWidth int or float optional; defaults to CheckBox.icon.selectedBorderWidth
|
||||||
|
* @uiDefault CheckBox.arc int
|
||||||
|
*
|
||||||
* @uiDefault CheckBox.icon.focusColor Color optional; defaults to Component.focusColor
|
* @uiDefault CheckBox.icon.focusColor Color optional; defaults to Component.focusColor
|
||||||
* @uiDefault CheckBox.icon.borderColor Color
|
* @uiDefault CheckBox.icon.borderColor Color
|
||||||
* @uiDefault CheckBox.icon.background Color
|
* @uiDefault CheckBox.icon.background Color
|
||||||
* @uiDefault CheckBox.icon.selectedBorderColor Color
|
* @uiDefault CheckBox.icon.selectedBorderColor Color
|
||||||
* @uiDefault CheckBox.icon.selectedBackground Color
|
* @uiDefault CheckBox.icon.selectedBackground Color
|
||||||
* @uiDefault CheckBox.icon.checkmarkColor Color
|
* @uiDefault CheckBox.icon.checkmarkColor Color
|
||||||
|
*
|
||||||
* @uiDefault CheckBox.icon.disabledBorderColor Color
|
* @uiDefault CheckBox.icon.disabledBorderColor Color
|
||||||
* @uiDefault CheckBox.icon.disabledBackground Color
|
* @uiDefault CheckBox.icon.disabledBackground Color
|
||||||
|
* @uiDefault CheckBox.icon.disabledSelectedBorderColor Color optional; CheckBox.icon.disabledBorderColor is used if not specified
|
||||||
|
* @uiDefault CheckBox.icon.disabledSelectedBackground Color optional; CheckBox.icon.disabledBackground is used if not specified
|
||||||
* @uiDefault CheckBox.icon.disabledCheckmarkColor Color
|
* @uiDefault CheckBox.icon.disabledCheckmarkColor Color
|
||||||
|
*
|
||||||
* @uiDefault CheckBox.icon.focusedBorderColor Color optional
|
* @uiDefault CheckBox.icon.focusedBorderColor Color optional
|
||||||
* @uiDefault CheckBox.icon.focusedBackground Color optional
|
* @uiDefault CheckBox.icon.focusedBackground Color optional
|
||||||
* @uiDefault CheckBox.icon.selectedFocusedBorderColor Color optional; CheckBox.icon.focusedBorderColor is used if not specified
|
* @uiDefault CheckBox.icon.focusedSelectedBorderColor Color optional; CheckBox.icon.focusedBorderColor is used if not specified
|
||||||
* @uiDefault CheckBox.icon.selectedFocusedBackground Color optional; CheckBox.icon.focusedBackground is used if not specified
|
* @uiDefault CheckBox.icon.focusedSelectedBackground Color optional; CheckBox.icon.focusedBackground is used if not specified
|
||||||
* @uiDefault CheckBox.icon.selectedFocusedCheckmarkColor Color optional; CheckBox.icon.checkmarkColor is used if not specified
|
* @uiDefault CheckBox.icon.focusedCheckmarkColor Color optional; CheckBox.icon.checkmarkColor is used if not specified
|
||||||
|
*
|
||||||
* @uiDefault CheckBox.icon.hoverBorderColor Color optional
|
* @uiDefault CheckBox.icon.hoverBorderColor Color optional
|
||||||
* @uiDefault CheckBox.icon.hoverBackground Color optional
|
* @uiDefault CheckBox.icon.hoverBackground Color optional
|
||||||
* @uiDefault CheckBox.icon.selectedHoverBackground Color optional; CheckBox.icon.hoverBackground is used if not specified
|
* @uiDefault CheckBox.icon.hoverSelectedBorderColor Color optional; CheckBox.icon.hoverBorderColor is used if not specified
|
||||||
|
* @uiDefault CheckBox.icon.hoverSelectedBackground Color optional; CheckBox.icon.hoverBackground is used if not specified
|
||||||
|
* @uiDefault CheckBox.icon.hoverCheckmarkColor Color optional; CheckBox.icon.checkmarkColor is used if not specified
|
||||||
|
*
|
||||||
|
* @uiDefault CheckBox.icon.pressedBorderColor Color optional
|
||||||
* @uiDefault CheckBox.icon.pressedBackground Color optional
|
* @uiDefault CheckBox.icon.pressedBackground Color optional
|
||||||
* @uiDefault CheckBox.icon.selectedPressedBackground Color optional; CheckBox.icon.pressedBackground is used if not specified
|
* @uiDefault CheckBox.icon.pressedSelectedBorderColor Color optional; CheckBox.icon.pressedBorderColor is used if not specified
|
||||||
* @uiDefault CheckBox.arc int
|
* @uiDefault CheckBox.icon.pressedSelectedBackground Color optional; CheckBox.icon.pressedBackground is used if not specified
|
||||||
|
* @uiDefault CheckBox.icon.pressedCheckmarkColor Color optional; CheckBox.icon.checkmarkColor is used if not specified
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatCheckBoxIcon
|
public class FlatCheckBoxIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
{
|
{
|
||||||
protected final String style = UIManager.getString( "CheckBox.icon.style" );
|
protected final String style = UIManager.getString( getPropertyPrefix() + "icon.style" );
|
||||||
public final int focusWidth = getUIInt( "CheckBox.icon.focusWidth",
|
@Styleable protected float focusWidth = getUIFloat( "CheckBox.icon.focusWidth", UIManager.getInt( "Component.focusWidth" ), style );
|
||||||
UIManager.getInt( "Component.focusWidth" ), style );
|
@Styleable protected Color focusColor = FlatUIUtils.getUIColor( "CheckBox.icon.focusColor", UIManager.getColor( "Component.focusColor" ) );
|
||||||
protected final Color focusColor = FlatUIUtils.getUIColor( "CheckBox.icon.focusColor",
|
/** @since 2 */ @Styleable protected float borderWidth = getUIFloat( "CheckBox.icon.borderWidth", FlatUIUtils.getUIFloat( "Component.borderWidth", 1 ), style );
|
||||||
UIManager.getColor( "Component.focusColor" ) );
|
/** @since 2 */ @Styleable protected float selectedBorderWidth = getUIFloat( "CheckBox.icon.selectedBorderWidth", Float.MIN_VALUE, style );
|
||||||
protected final int arc = FlatUIUtils.getUIInt( "CheckBox.arc", 2 );
|
/** @since 2 */ @Styleable protected float disabledSelectedBorderWidth = getUIFloat( "CheckBox.icon.disabledSelectedBorderWidth", Float.MIN_VALUE, style );
|
||||||
|
@Styleable protected int arc = FlatUIUtils.getUIInt( "CheckBox.arc", 2 );
|
||||||
|
|
||||||
// enabled
|
// enabled
|
||||||
protected final Color borderColor = getUIColor( "CheckBox.icon.borderColor", style );
|
@Styleable protected Color borderColor = getUIColor( "CheckBox.icon.borderColor", style );
|
||||||
protected final Color background = getUIColor( "CheckBox.icon.background", style );
|
@Styleable protected Color background = getUIColor( "CheckBox.icon.background", style );
|
||||||
protected final Color selectedBorderColor = getUIColor( "CheckBox.icon.selectedBorderColor", style );
|
@Styleable protected Color selectedBorderColor = getUIColor( "CheckBox.icon.selectedBorderColor", style );
|
||||||
protected final Color selectedBackground = getUIColor( "CheckBox.icon.selectedBackground", style );
|
@Styleable protected Color selectedBackground = getUIColor( "CheckBox.icon.selectedBackground", style );
|
||||||
protected final Color checkmarkColor = getUIColor( "CheckBox.icon.checkmarkColor", style );
|
@Styleable protected Color checkmarkColor = getUIColor( "CheckBox.icon.checkmarkColor", style );
|
||||||
|
|
||||||
// disabled
|
// disabled
|
||||||
protected final Color disabledBorderColor = getUIColor( "CheckBox.icon.disabledBorderColor", style );
|
@Styleable protected Color disabledBorderColor = getUIColor( "CheckBox.icon.disabledBorderColor", style );
|
||||||
protected final Color disabledBackground = getUIColor( "CheckBox.icon.disabledBackground", style );
|
@Styleable protected Color disabledBackground = getUIColor( "CheckBox.icon.disabledBackground", style );
|
||||||
protected final Color disabledCheckmarkColor = getUIColor( "CheckBox.icon.disabledCheckmarkColor", style );
|
/** @since 2 */ @Styleable protected Color disabledSelectedBorderColor = getUIColor( "CheckBox.icon.disabledSelectedBorderColor", style );
|
||||||
|
/** @since 2 */ @Styleable protected Color disabledSelectedBackground = getUIColor( "CheckBox.icon.disabledSelectedBackground", style );
|
||||||
|
@Styleable protected Color disabledCheckmarkColor = getUIColor( "CheckBox.icon.disabledCheckmarkColor", style );
|
||||||
|
|
||||||
// focused
|
// focused
|
||||||
protected final Color focusedBorderColor = getUIColor( "CheckBox.icon.focusedBorderColor", style );
|
@Styleable protected Color focusedBorderColor = getUIColor( "CheckBox.icon.focusedBorderColor", style );
|
||||||
protected final Color focusedBackground = getUIColor( "CheckBox.icon.focusedBackground", style );
|
@Styleable protected Color focusedBackground = getUIColor( "CheckBox.icon.focusedBackground", style );
|
||||||
protected final Color selectedFocusedBorderColor = getUIColor( "CheckBox.icon.selectedFocusedBorderColor", style );
|
/** @since 2 */ @Styleable protected Color focusedSelectedBorderColor = getUIColor( "CheckBox.icon.focusedSelectedBorderColor", style );
|
||||||
protected final Color selectedFocusedBackground = getUIColor( "CheckBox.icon.selectedFocusedBackground", style );
|
/** @since 2 */ @Styleable protected Color focusedSelectedBackground = getUIColor( "CheckBox.icon.focusedSelectedBackground", style );
|
||||||
protected final Color selectedFocusedCheckmarkColor = getUIColor( "CheckBox.icon.selectedFocusedCheckmarkColor", style );
|
/** @since 2 */ @Styleable protected Color focusedCheckmarkColor = getUIColor( "CheckBox.icon.focusedCheckmarkColor", style );
|
||||||
|
|
||||||
// hover
|
// hover
|
||||||
protected final Color hoverBorderColor = getUIColor( "CheckBox.icon.hoverBorderColor", style );
|
@Styleable protected Color hoverBorderColor = getUIColor( "CheckBox.icon.hoverBorderColor", style );
|
||||||
protected final Color hoverBackground = getUIColor( "CheckBox.icon.hoverBackground", style );
|
@Styleable protected Color hoverBackground = getUIColor( "CheckBox.icon.hoverBackground", style );
|
||||||
protected final Color selectedHoverBackground = getUIColor( "CheckBox.icon.selectedHoverBackground", style );
|
/** @since 2 */ @Styleable protected Color hoverSelectedBorderColor = getUIColor( "CheckBox.icon.hoverSelectedBorderColor", style );
|
||||||
|
/** @since 2 */ @Styleable protected Color hoverSelectedBackground = getUIColor( "CheckBox.icon.hoverSelectedBackground", style );
|
||||||
|
/** @since 2 */ @Styleable protected Color hoverCheckmarkColor = getUIColor( "CheckBox.icon.hoverCheckmarkColor", style );
|
||||||
|
|
||||||
// pressed
|
// pressed
|
||||||
protected final Color pressedBackground = getUIColor( "CheckBox.icon.pressedBackground", style );
|
/** @since 2 */ @Styleable protected Color pressedBorderColor = getUIColor( "CheckBox.icon.pressedBorderColor", style );
|
||||||
protected final Color selectedPressedBackground = getUIColor( "CheckBox.icon.selectedPressedBackground", style );
|
@Styleable protected Color pressedBackground = getUIColor( "CheckBox.icon.pressedBackground", style );
|
||||||
|
/** @since 2 */ @Styleable protected Color pressedSelectedBorderColor = getUIColor( "CheckBox.icon.pressedSelectedBorderColor", style );
|
||||||
|
/** @since 2 */ @Styleable protected Color pressedSelectedBackground = getUIColor( "CheckBox.icon.pressedSelectedBackground", style );
|
||||||
|
/** @since 2 */ @Styleable protected Color pressedCheckmarkColor = getUIColor( "CheckBox.icon.pressedCheckmarkColor", style );
|
||||||
|
|
||||||
|
protected String getPropertyPrefix() {
|
||||||
|
return "CheckBox.";
|
||||||
|
}
|
||||||
|
|
||||||
protected static Color getUIColor( String key, String style ) {
|
protected static Color getUIColor( String key, String style ) {
|
||||||
if( style != null ) {
|
if( style != null ) {
|
||||||
@@ -110,13 +142,14 @@ public class FlatCheckBoxIcon
|
|||||||
return UIManager.getColor( key );
|
return UIManager.getColor( key );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static int getUIInt( String key, int defaultValue, String style ) {
|
/** @since 2 */
|
||||||
|
protected static float getUIFloat( String key, float defaultValue, String style ) {
|
||||||
if( style != null ) {
|
if( style != null ) {
|
||||||
Object value = UIManager.get( styleKey( key, style ) );
|
float value = FlatUIUtils.getUIFloat( styleKey( key, style ), Float.MIN_VALUE );
|
||||||
if( value instanceof Integer )
|
if( value != Float.MIN_VALUE )
|
||||||
return (Integer) value;
|
return value;
|
||||||
}
|
}
|
||||||
return FlatUIUtils.getUIInt( key, defaultValue );
|
return FlatUIUtils.getUIFloat( key, defaultValue );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String styleKey( String key, String style ) {
|
private static String styleKey( String key, String style ) {
|
||||||
@@ -129,11 +162,26 @@ public class FlatCheckBoxIcon
|
|||||||
super( ICON_SIZE, ICON_SIZE, null );
|
super( ICON_SIZE, ICON_SIZE, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public Object applyStyleProperty( String key, Object value ) {
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public Map<String, Class<?>> getStyleableInfos() {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
boolean indeterminate = isIndeterminate( c );
|
boolean indeterminate = isIndeterminate( c );
|
||||||
boolean selected = indeterminate || isSelected( c );
|
boolean selected = indeterminate || isSelected( c );
|
||||||
boolean isFocused = FlatUIUtils.isPermanentFocusOwner( c );
|
boolean isFocused = FlatUIUtils.isPermanentFocusOwner( c );
|
||||||
|
float bw = selected
|
||||||
|
? (disabledSelectedBorderWidth != Float.MIN_VALUE && !c.isEnabled()
|
||||||
|
? disabledSelectedBorderWidth
|
||||||
|
: (selectedBorderWidth != Float.MIN_VALUE ? selectedBorderWidth : borderWidth))
|
||||||
|
: borderWidth;
|
||||||
|
|
||||||
// paint focused border
|
// paint focused border
|
||||||
if( isFocused && focusWidth > 0 && FlatButtonUI.isFocusPainted( c ) ) {
|
if( isFocused && focusWidth > 0 && FlatButtonUI.isFocusPainted( c ) ) {
|
||||||
@@ -143,7 +191,7 @@ public class FlatCheckBoxIcon
|
|||||||
|
|
||||||
// paint border
|
// paint border
|
||||||
g.setColor( getBorderColor( c, selected ) );
|
g.setColor( getBorderColor( c, selected ) );
|
||||||
paintBorder( c, g );
|
paintBorder( c, g, bw );
|
||||||
|
|
||||||
// paint background
|
// paint background
|
||||||
Color bg = FlatUIUtils.deriveColor( getBackground( c, selected ),
|
Color bg = FlatUIUtils.deriveColor( getBackground( c, selected ),
|
||||||
@@ -151,14 +199,14 @@ public class FlatCheckBoxIcon
|
|||||||
if( bg.getAlpha() < 255 ) {
|
if( bg.getAlpha() < 255 ) {
|
||||||
// fill background with default color before filling with non-opaque background
|
// fill background with default color before filling with non-opaque background
|
||||||
g.setColor( selected ? selectedBackground : background );
|
g.setColor( selected ? selectedBackground : background );
|
||||||
paintBackground( c, g );
|
paintBackground( c, g, bw );
|
||||||
}
|
}
|
||||||
g.setColor( bg );
|
g.setColor( bg );
|
||||||
paintBackground( c, g );
|
paintBackground( c, g, bw );
|
||||||
|
|
||||||
// paint checkmark
|
// paint checkmark
|
||||||
if( selected || indeterminate ) {
|
if( selected ) {
|
||||||
g.setColor( getCheckmarkColor( c, selected, isFocused ) );
|
g.setColor( getCheckmarkColor( c ) );
|
||||||
if( indeterminate )
|
if( indeterminate )
|
||||||
paintIndeterminate( c, g );
|
paintIndeterminate( c, g );
|
||||||
else
|
else
|
||||||
@@ -167,20 +215,25 @@ public class FlatCheckBoxIcon
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void paintFocusBorder( Component c, Graphics2D g ) {
|
protected void paintFocusBorder( Component c, Graphics2D g ) {
|
||||||
// the outline focus border is painted outside of the icon
|
// the outer focus border is painted outside of the icon
|
||||||
int wh = ICON_SIZE - 1 + (focusWidth * 2);
|
float wh = ICON_SIZE - 1 + (focusWidth * 2);
|
||||||
int arcwh = arc + (focusWidth * 2);
|
float arcwh = arc + (focusWidth * 2);
|
||||||
g.fillRoundRect( -focusWidth + 1, -focusWidth, wh, wh, arcwh, arcwh );
|
g.fill( new RoundRectangle2D.Float( -focusWidth + 1, -focusWidth, wh, wh, arcwh, arcwh ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void paintBorder( Component c, Graphics2D g ) {
|
protected void paintBorder( Component c, Graphics2D g, float borderWidth ) {
|
||||||
|
if( borderWidth == 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
int arcwh = arc;
|
int arcwh = arc;
|
||||||
g.fillRoundRect( 1, 0, 14, 14, arcwh, arcwh );
|
g.fillRoundRect( 1, 0, 14, 14, arcwh, arcwh );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void paintBackground( Component c, Graphics2D g ) {
|
protected void paintBackground( Component c, Graphics2D g, float borderWidth ) {
|
||||||
int arcwh = arc - 1;
|
float xy = borderWidth;
|
||||||
g.fillRoundRect( 2, 1, 12, 12, arcwh, arcwh );
|
float wh = 14 - (borderWidth * 2);
|
||||||
|
float arcwh = arc - borderWidth;
|
||||||
|
g.fill( new RoundRectangle2D.Float( 1 + xy, xy, wh, wh, arcwh, arcwh ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void paintCheckmark( Component c, Graphics2D g ) {
|
protected void paintCheckmark( Component c, Graphics2D g ) {
|
||||||
@@ -205,6 +258,11 @@ public class FlatCheckBoxIcon
|
|||||||
return c instanceof AbstractButton && ((AbstractButton)c).isSelected();
|
return c instanceof AbstractButton && ((AbstractButton)c).isSelected();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public float getFocusWidth() {
|
||||||
|
return focusWidth;
|
||||||
|
}
|
||||||
|
|
||||||
protected Color getFocusColor( Component c ) {
|
protected Color getFocusColor( Component c ) {
|
||||||
return focusColor;
|
return focusColor;
|
||||||
}
|
}
|
||||||
@@ -212,26 +270,27 @@ public class FlatCheckBoxIcon
|
|||||||
protected Color getBorderColor( Component c, boolean selected ) {
|
protected Color getBorderColor( Component c, boolean selected ) {
|
||||||
return FlatButtonUI.buttonStateColor( c,
|
return FlatButtonUI.buttonStateColor( c,
|
||||||
selected ? selectedBorderColor : borderColor,
|
selected ? selectedBorderColor : borderColor,
|
||||||
disabledBorderColor,
|
(selected && disabledSelectedBorderColor != null) ? disabledSelectedBorderColor : disabledBorderColor,
|
||||||
selected && selectedFocusedBorderColor != null ? selectedFocusedBorderColor : focusedBorderColor,
|
(selected && focusedSelectedBorderColor != null) ? focusedSelectedBorderColor : focusedBorderColor,
|
||||||
hoverBorderColor,
|
(selected && hoverSelectedBorderColor != null) ? hoverSelectedBorderColor : hoverBorderColor,
|
||||||
null );
|
(selected && pressedSelectedBorderColor != null) ? pressedSelectedBorderColor : pressedBorderColor );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Color getBackground( Component c, boolean selected ) {
|
protected Color getBackground( Component c, boolean selected ) {
|
||||||
return FlatButtonUI.buttonStateColor( c,
|
return FlatButtonUI.buttonStateColor( c,
|
||||||
selected ? selectedBackground : background,
|
selected ? selectedBackground : background,
|
||||||
disabledBackground,
|
(selected && disabledSelectedBackground != null) ? disabledSelectedBackground : disabledBackground,
|
||||||
(selected && selectedFocusedBackground != null) ? selectedFocusedBackground : focusedBackground,
|
(selected && focusedSelectedBackground != null) ? focusedSelectedBackground : focusedBackground,
|
||||||
(selected && selectedHoverBackground != null) ? selectedHoverBackground : hoverBackground,
|
(selected && hoverSelectedBackground != null) ? hoverSelectedBackground : hoverBackground,
|
||||||
(selected && selectedPressedBackground != null) ? selectedPressedBackground : pressedBackground );
|
(selected && pressedSelectedBackground != null) ? pressedSelectedBackground : pressedBackground );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Color getCheckmarkColor( Component c, boolean selected, boolean isFocused ) {
|
protected Color getCheckmarkColor( Component c ) {
|
||||||
return c.isEnabled()
|
return FlatButtonUI.buttonStateColor( c,
|
||||||
? ((selected && isFocused && selectedFocusedCheckmarkColor != null)
|
checkmarkColor,
|
||||||
? selectedFocusedCheckmarkColor
|
disabledCheckmarkColor,
|
||||||
: checkmarkColor)
|
focusedCheckmarkColor,
|
||||||
: disabledCheckmarkColor;
|
hoverCheckmarkColor,
|
||||||
|
pressedCheckmarkColor );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,15 +21,18 @@ import java.awt.Color;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
import javax.swing.JMenuItem;
|
import javax.swing.JMenuItem;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Icon for {@link javax.swing.JCheckBoxMenuItem}.
|
* Icon for {@link javax.swing.JCheckBoxMenuItem}.
|
||||||
*
|
*
|
||||||
* @uiDefault MenuItemCheckBox.icon.checkmarkColor Color
|
* @uiDefault CheckBoxMenuItem.icon.checkmarkColor Color
|
||||||
* @uiDefault MenuItemCheckBox.icon.disabledCheckmarkColor Color
|
* @uiDefault CheckBoxMenuItem.icon.disabledCheckmarkColor Color
|
||||||
* @uiDefault MenuItem.selectionForeground Color
|
* @uiDefault MenuItem.selectionForeground Color
|
||||||
* @uiDefault MenuItem.selectionType String
|
* @uiDefault MenuItem.selectionType String
|
||||||
*
|
*
|
||||||
@@ -38,14 +41,24 @@ import javax.swing.UIManager;
|
|||||||
public class FlatCheckBoxMenuItemIcon
|
public class FlatCheckBoxMenuItemIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
{
|
{
|
||||||
protected final Color checkmarkColor = UIManager.getColor( "MenuItemCheckBox.icon.checkmarkColor" );
|
@Styleable protected Color checkmarkColor = UIManager.getColor( "CheckBoxMenuItem.icon.checkmarkColor" );
|
||||||
protected final Color disabledCheckmarkColor = UIManager.getColor( "MenuItemCheckBox.icon.disabledCheckmarkColor" );
|
@Styleable protected Color disabledCheckmarkColor = UIManager.getColor( "CheckBoxMenuItem.icon.disabledCheckmarkColor" );
|
||||||
protected final Color selectionForeground = UIManager.getColor( "MenuItem.selectionForeground" );
|
@Styleable protected Color selectionForeground = UIManager.getColor( "MenuItem.selectionForeground" );
|
||||||
|
|
||||||
public FlatCheckBoxMenuItemIcon() {
|
public FlatCheckBoxMenuItemIcon() {
|
||||||
super( 15, 15, null );
|
super( 15, 15, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public Object applyStyleProperty( String key, Object value ) {
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public Map<String, Class<?>> getStyleableInfos() {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g2 ) {
|
protected void paintIcon( Component c, Graphics2D g2 ) {
|
||||||
boolean selected = (c instanceof AbstractButton) && ((AbstractButton)c).isSelected();
|
boolean selected = (c instanceof AbstractButton) && ((AbstractButton)c).isSelected();
|
||||||
|
|||||||
@@ -22,9 +22,12 @@ import java.awt.Graphics2D;
|
|||||||
import java.awt.geom.Ellipse2D;
|
import java.awt.geom.Ellipse2D;
|
||||||
import java.awt.geom.Line2D;
|
import java.awt.geom.Line2D;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
import javax.swing.ButtonModel;
|
import javax.swing.ButtonModel;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,17 +43,35 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
public class FlatClearIcon
|
public class FlatClearIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
{
|
{
|
||||||
protected Color clearIconColor = UIManager.getColor( "SearchField.clearIconColor" );
|
@Styleable protected Color clearIconColor = UIManager.getColor( "SearchField.clearIconColor" );
|
||||||
protected Color clearIconHoverColor = UIManager.getColor( "SearchField.clearIconHoverColor" );
|
@Styleable protected Color clearIconHoverColor = UIManager.getColor( "SearchField.clearIconHoverColor" );
|
||||||
protected Color clearIconPressedColor = UIManager.getColor( "SearchField.clearIconPressedColor" );
|
@Styleable protected Color clearIconPressedColor = UIManager.getColor( "SearchField.clearIconPressedColor" );
|
||||||
|
|
||||||
|
private final boolean ignoreButtonState;
|
||||||
|
|
||||||
public FlatClearIcon() {
|
public FlatClearIcon() {
|
||||||
|
this( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public FlatClearIcon( boolean ignoreButtonState ) {
|
||||||
super( 16, 16, null );
|
super( 16, 16, null );
|
||||||
|
this.ignoreButtonState = ignoreButtonState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public Object applyStyleProperty( String key, Object value ) {
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public Map<String, Class<?>> getStyleableInfos() {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
if( c instanceof AbstractButton ) {
|
if( !ignoreButtonState && c instanceof AbstractButton ) {
|
||||||
ButtonModel model = ((AbstractButton)c).getModel();
|
ButtonModel model = ((AbstractButton)c).getModel();
|
||||||
if( model.isPressed() || model.isRollover() ) {
|
if( model.isPressed() || model.isRollover() ) {
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -17,11 +17,9 @@
|
|||||||
package com.formdev.flatlaf.icons;
|
package com.formdev.flatlaf.icons;
|
||||||
|
|
||||||
import java.awt.BasicStroke;
|
import java.awt.BasicStroke;
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
import javax.swing.UIManager;
|
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,18 +31,14 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatDescendingSortIcon
|
public class FlatDescendingSortIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAscendingSortIcon
|
||||||
{
|
{
|
||||||
protected final boolean chevron = FlatUIUtils.isChevron( UIManager.getString( "Component.arrowType" ) );
|
|
||||||
protected final Color sortIconColor = UIManager.getColor( "Table.sortIconColor" );
|
|
||||||
|
|
||||||
public FlatDescendingSortIcon() {
|
public FlatDescendingSortIcon() {
|
||||||
super( 10, 5, null );
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintArrow( Component c, Graphics2D g, boolean chevron ) {
|
||||||
g.setColor( sortIconColor );
|
|
||||||
if( chevron ) {
|
if( chevron ) {
|
||||||
// chevron arrow
|
// chevron arrow
|
||||||
Path2D path = FlatUIUtils.createPath( false, 1,0, 5,4, 9,0 );
|
Path2D path = FlatUIUtils.createPath( false, 1,0, 5,4, 9,0 );
|
||||||
|
|||||||
@@ -22,8 +22,11 @@ import java.awt.Component;
|
|||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Ellipse2D;
|
import java.awt.geom.Ellipse2D;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import com.formdev.flatlaf.ui.FlatButtonUI;
|
import com.formdev.flatlaf.ui.FlatButtonUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,29 +53,37 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
public class FlatHelpButtonIcon
|
public class FlatHelpButtonIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
{
|
{
|
||||||
protected final int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
@Styleable protected int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
||||||
protected final Color focusColor = UIManager.getColor( "Component.focusColor" );
|
@Styleable protected Color focusColor = UIManager.getColor( "Component.focusColor" );
|
||||||
protected final float innerFocusWidth = FlatUIUtils.getUIFloat( "HelpButton.innerFocusWidth", FlatUIUtils.getUIFloat( "Component.innerFocusWidth", 0 ) );
|
@Styleable protected float innerFocusWidth = FlatUIUtils.getUIFloat( "HelpButton.innerFocusWidth", FlatUIUtils.getUIFloat( "Component.innerFocusWidth", 0 ) );
|
||||||
protected final int borderWidth = FlatUIUtils.getUIInt( "HelpButton.borderWidth", 1 );
|
@Styleable protected int borderWidth = FlatUIUtils.getUIInt( "HelpButton.borderWidth", 1 );
|
||||||
|
|
||||||
protected final Color borderColor = UIManager.getColor( "HelpButton.borderColor" );
|
@Styleable protected Color borderColor = UIManager.getColor( "HelpButton.borderColor" );
|
||||||
protected final Color disabledBorderColor = UIManager.getColor( "HelpButton.disabledBorderColor" );
|
@Styleable protected Color disabledBorderColor = UIManager.getColor( "HelpButton.disabledBorderColor" );
|
||||||
protected final Color focusedBorderColor = UIManager.getColor( "HelpButton.focusedBorderColor" );
|
@Styleable protected Color focusedBorderColor = UIManager.getColor( "HelpButton.focusedBorderColor" );
|
||||||
protected final Color hoverBorderColor = UIManager.getColor( "HelpButton.hoverBorderColor" );
|
@Styleable protected Color hoverBorderColor = UIManager.getColor( "HelpButton.hoverBorderColor" );
|
||||||
protected final Color background = UIManager.getColor( "HelpButton.background" );
|
@Styleable protected Color background = UIManager.getColor( "HelpButton.background" );
|
||||||
protected final Color disabledBackground = UIManager.getColor( "HelpButton.disabledBackground" );
|
@Styleable protected Color disabledBackground = UIManager.getColor( "HelpButton.disabledBackground" );
|
||||||
protected final Color focusedBackground = UIManager.getColor( "HelpButton.focusedBackground" );
|
@Styleable protected Color focusedBackground = UIManager.getColor( "HelpButton.focusedBackground" );
|
||||||
protected final Color hoverBackground = UIManager.getColor( "HelpButton.hoverBackground" );
|
@Styleable protected Color hoverBackground = UIManager.getColor( "HelpButton.hoverBackground" );
|
||||||
protected final Color pressedBackground = UIManager.getColor( "HelpButton.pressedBackground" );
|
@Styleable protected Color pressedBackground = UIManager.getColor( "HelpButton.pressedBackground" );
|
||||||
protected final Color questionMarkColor = UIManager.getColor( "HelpButton.questionMarkColor" );
|
@Styleable protected Color questionMarkColor = UIManager.getColor( "HelpButton.questionMarkColor" );
|
||||||
protected final Color disabledQuestionMarkColor = UIManager.getColor( "HelpButton.disabledQuestionMarkColor" );
|
@Styleable protected Color disabledQuestionMarkColor = UIManager.getColor( "HelpButton.disabledQuestionMarkColor" );
|
||||||
|
|
||||||
protected final int iconSize = 22 + (focusWidth * 2);
|
|
||||||
|
|
||||||
public FlatHelpButtonIcon() {
|
public FlatHelpButtonIcon() {
|
||||||
super( 0, 0, null );
|
super( 0, 0, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public Object applyStyleProperty( String key, Object value ) {
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public Map<String, Class<?>> getStyleableInfos() {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g2 ) {
|
protected void paintIcon( Component c, Graphics2D g2 ) {
|
||||||
/*
|
/*
|
||||||
@@ -85,11 +96,11 @@ public class FlatHelpButtonIcon
|
|||||||
</svg>
|
</svg>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
boolean enabled = c.isEnabled();
|
boolean enabled = c == null || c.isEnabled();
|
||||||
boolean focused = FlatUIUtils.isPermanentFocusOwner( c );
|
boolean focused = c != null && FlatUIUtils.isPermanentFocusOwner( c );
|
||||||
|
|
||||||
float xy = 0.5f;
|
float xy = 0.5f;
|
||||||
float wh = iconSize - 1;
|
float wh = iconSize() - 1;
|
||||||
|
|
||||||
// paint outer focus border
|
// paint outer focus border
|
||||||
if( focused && FlatButtonUI.isFocusPainted( c ) ) {
|
if( focused && FlatButtonUI.isFocusPainted( c ) ) {
|
||||||
@@ -151,11 +162,15 @@ public class FlatHelpButtonIcon
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getIconWidth() {
|
public int getIconWidth() {
|
||||||
return scale( iconSize );
|
return scale( iconSize() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getIconHeight() {
|
public int getIconHeight() {
|
||||||
return scale( iconSize );
|
return scale( iconSize() );
|
||||||
|
}
|
||||||
|
|
||||||
|
private int iconSize() {
|
||||||
|
return 22 + (focusWidth * 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,12 @@ import java.awt.Color;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.JMenu;
|
import javax.swing.JMenu;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "arrow" icon for {@link javax.swing.JMenu}.
|
* "arrow" icon for {@link javax.swing.JMenu}.
|
||||||
@@ -39,22 +42,32 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
public class FlatMenuArrowIcon
|
public class FlatMenuArrowIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
{
|
{
|
||||||
protected final boolean chevron = FlatUIUtils.isChevron( UIManager.getString( "Component.arrowType" ) );
|
@Styleable protected String arrowType = UIManager.getString( "Component.arrowType" );
|
||||||
protected final Color arrowColor = UIManager.getColor( "Menu.icon.arrowColor" );
|
@Styleable protected Color arrowColor = UIManager.getColor( "Menu.icon.arrowColor" );
|
||||||
protected final Color disabledArrowColor = UIManager.getColor( "Menu.icon.disabledArrowColor" );
|
@Styleable protected Color disabledArrowColor = UIManager.getColor( "Menu.icon.disabledArrowColor" );
|
||||||
protected final Color selectionForeground = UIManager.getColor( "Menu.selectionForeground" );
|
@Styleable protected Color selectionForeground = UIManager.getColor( "Menu.selectionForeground" );
|
||||||
|
|
||||||
public FlatMenuArrowIcon() {
|
public FlatMenuArrowIcon() {
|
||||||
super( 6, 10, null );
|
super( 6, 10, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public Object applyStyleProperty( String key, Object value ) {
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public Map<String, Class<?>> getStyleableInfos() {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
if( !c.getComponentOrientation().isLeftToRight() )
|
if( c != null && !c.getComponentOrientation().isLeftToRight() )
|
||||||
g.rotate( Math.toRadians( 180 ), width / 2., height / 2. );
|
g.rotate( Math.toRadians( 180 ), width / 2., height / 2. );
|
||||||
|
|
||||||
g.setColor( getArrowColor( c ) );
|
g.setColor( getArrowColor( c ) );
|
||||||
if( chevron ) {
|
if( FlatUIUtils.isChevron( arrowType ) ) {
|
||||||
// chevron arrow
|
// chevron arrow
|
||||||
Path2D path = FlatUIUtils.createPath( false, 1,1, 5,5, 1,9 );
|
Path2D path = FlatUIUtils.createPath( false, 1,1, 5,5, 1,9 );
|
||||||
g.setStroke( new BasicStroke( 1f ) );
|
g.setStroke( new BasicStroke( 1f ) );
|
||||||
@@ -69,7 +82,7 @@ public class FlatMenuArrowIcon
|
|||||||
if( c instanceof JMenu && ((JMenu)c).isSelected() && !isUnderlineSelection() )
|
if( c instanceof JMenu && ((JMenu)c).isSelected() && !isUnderlineSelection() )
|
||||||
return selectionForeground;
|
return selectionForeground;
|
||||||
|
|
||||||
return c.isEnabled() ? arrowColor : disabledArrowColor;
|
return c == null || c.isEnabled() ? arrowColor : disabledArrowColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isUnderlineSelection() {
|
protected boolean isUnderlineSelection() {
|
||||||
|
|||||||
@@ -21,14 +21,16 @@ import java.awt.Graphics;
|
|||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "arrow" icon for {@link javax.swing.JMenuItem}.
|
* "arrow" icon for {@link javax.swing.JMenuItem}, {@link javax.swing.JCheckBoxMenuItem}
|
||||||
|
* and {@link javax.swing.JRadioButtonMenuItem}.
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatMenuItemArrowIcon
|
public class FlatMenuItemArrowIcon
|
||||||
extends FlatMenuArrowIcon
|
extends FlatAbstractIcon
|
||||||
{
|
{
|
||||||
public FlatMenuItemArrowIcon() {
|
public FlatMenuItemArrowIcon() {
|
||||||
|
super( 6, 10, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -19,38 +19,51 @@ package com.formdev.flatlaf.icons;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Ellipse2D;
|
import java.awt.geom.Ellipse2D;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Icon for {@link javax.swing.JRadioButton}.
|
* Icon for {@link javax.swing.JRadioButton}.
|
||||||
*
|
* <p>
|
||||||
* Note: If Component.focusWidth is greater than zero, then the outline focus border
|
* <strong>Note</strong>:
|
||||||
|
* If Component.focusWidth is greater than zero, then the outer focus border
|
||||||
* is painted outside of the icon bounds. Make sure that the radiobutton
|
* is painted outside of the icon bounds. Make sure that the radiobutton
|
||||||
* has margins, which are equal or greater than focusWidth.
|
* has margins, which are equal or greater than focusWidth.
|
||||||
*
|
*
|
||||||
* @uiDefault RadioButton.icon.centerDiameter int
|
* @uiDefault RadioButton.icon.style String optional; "outlined"/null (default) or "filled"
|
||||||
|
* @uiDefault RadioButton.icon.centerDiameter int or float
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatRadioButtonIcon
|
public class FlatRadioButtonIcon
|
||||||
extends FlatCheckBoxIcon
|
extends FlatCheckBoxIcon
|
||||||
{
|
{
|
||||||
protected final int centerDiameter = getUIInt( "RadioButton.icon.centerDiameter", 8, style );
|
@Styleable protected float centerDiameter = getUIFloat( "RadioButton.icon.centerDiameter", 8, style );
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintFocusBorder( Component c, Graphics2D g ) {
|
protected String getPropertyPrefix() {
|
||||||
// the outline focus border is painted outside of the icon
|
return "RadioButton.";
|
||||||
int wh = ICON_SIZE + (focusWidth * 2);
|
|
||||||
g.fillOval( -focusWidth, -focusWidth, wh, wh );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintBorder( Component c, Graphics2D g ) {
|
protected void paintFocusBorder( Component c, Graphics2D g ) {
|
||||||
|
// the outer focus border is painted outside of the icon
|
||||||
|
float wh = ICON_SIZE + (focusWidth * 2);
|
||||||
|
g.fill( new Ellipse2D.Float( -focusWidth, -focusWidth, wh, wh ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintBorder( Component c, Graphics2D g, float borderWidth ) {
|
||||||
|
if( borderWidth == 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
g.fillOval( 0, 0, 15, 15 );
|
g.fillOval( 0, 0, 15, 15 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintBackground( Component c, Graphics2D g ) {
|
protected void paintBackground( Component c, Graphics2D g, float borderWidth ) {
|
||||||
g.fillOval( 1, 1, 13, 13 );
|
float xy = borderWidth;
|
||||||
|
float wh = 15 - (borderWidth * 2);
|
||||||
|
g.fill( new Ellipse2D.Float( xy, xy, wh, wh ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.icons;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.geom.Area;
|
||||||
|
import java.awt.geom.Ellipse2D;
|
||||||
|
import java.awt.geom.Path2D;
|
||||||
|
import java.awt.geom.Rectangle2D;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "eye" icon for {@link javax.swing.JPasswordField}.
|
||||||
|
*
|
||||||
|
* @uiDefault PasswordField.revealIconColor Color
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
public class FlatRevealIcon
|
||||||
|
extends FlatAbstractIcon
|
||||||
|
{
|
||||||
|
public FlatRevealIcon() {
|
||||||
|
super( 16, 16, UIManager.getColor( "PasswordField.revealIconColor" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
|
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||||
|
path.append( new Ellipse2D.Float( 5.15f, 6.15f, 5.7f, 5.7f ), false );
|
||||||
|
path.append( new Ellipse2D.Float( 6, 7, 4, 4 ), false );
|
||||||
|
g.fill( path );
|
||||||
|
|
||||||
|
Path2D path2 = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||||
|
path2.append( new Ellipse2D.Float( 2.15f, 4.15f, 11.7f, 11.7f ), false );
|
||||||
|
path2.append( new Ellipse2D.Float( 3, 5, 10, 10 ), false );
|
||||||
|
Area area = new Area( path2 );
|
||||||
|
area.subtract( new Area( new Rectangle2D.Float( 0, 9.5f, 16, 16 ) ) );
|
||||||
|
g.fill( area );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,8 +21,11 @@ import java.awt.Component;
|
|||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Area;
|
import java.awt.geom.Area;
|
||||||
import java.awt.geom.Ellipse2D;
|
import java.awt.geom.Ellipse2D;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import com.formdev.flatlaf.ui.FlatButtonUI;
|
import com.formdev.flatlaf.ui.FlatButtonUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,12 +41,30 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
public class FlatSearchIcon
|
public class FlatSearchIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
{
|
{
|
||||||
protected Color searchIconColor = UIManager.getColor( "SearchField.searchIconColor" );
|
@Styleable protected Color searchIconColor = UIManager.getColor( "SearchField.searchIconColor" );
|
||||||
protected Color searchIconHoverColor = UIManager.getColor( "SearchField.searchIconHoverColor" );
|
@Styleable protected Color searchIconHoverColor = UIManager.getColor( "SearchField.searchIconHoverColor" );
|
||||||
protected Color searchIconPressedColor = UIManager.getColor( "SearchField.searchIconPressedColor" );
|
@Styleable protected Color searchIconPressedColor = UIManager.getColor( "SearchField.searchIconPressedColor" );
|
||||||
|
|
||||||
|
private final boolean ignoreButtonState;
|
||||||
|
|
||||||
public FlatSearchIcon() {
|
public FlatSearchIcon() {
|
||||||
|
this( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public FlatSearchIcon( boolean ignoreButtonState ) {
|
||||||
super( 16, 16, null );
|
super( 16, 16, null );
|
||||||
|
this.ignoreButtonState = ignoreButtonState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public Object applyStyleProperty( String key, Object value ) {
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public Map<String, Class<?>> getStyleableInfos() {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -57,8 +78,10 @@ public class FlatSearchIcon
|
|||||||
</svg>
|
</svg>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
g.setColor( FlatButtonUI.buttonStateColor( c, searchIconColor, searchIconColor,
|
g.setColor( ignoreButtonState
|
||||||
null, searchIconHoverColor, searchIconPressedColor ) );
|
? searchIconColor
|
||||||
|
: FlatButtonUI.buttonStateColor( c, searchIconColor, searchIconColor,
|
||||||
|
null, searchIconHoverColor, searchIconPressedColor ) );
|
||||||
|
|
||||||
// paint magnifier
|
// paint magnifier
|
||||||
Area area = new Area( new Ellipse2D.Float( 2, 2, 10, 10 ) );
|
Area area = new Area( new Ellipse2D.Float( 2, 2, 10, 10 ) );
|
||||||
|
|||||||
@@ -30,6 +30,12 @@ public class FlatSearchWithHistoryIcon
|
|||||||
extends FlatSearchIcon
|
extends FlatSearchIcon
|
||||||
{
|
{
|
||||||
public FlatSearchWithHistoryIcon() {
|
public FlatSearchWithHistoryIcon() {
|
||||||
|
this( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public FlatSearchWithHistoryIcon( boolean ignoreButtonState ) {
|
||||||
|
super( ignoreButtonState );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -23,8 +23,11 @@ import java.awt.Dimension;
|
|||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Line2D;
|
import java.awt.geom.Line2D;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import com.formdev.flatlaf.ui.FlatButtonUI;
|
import com.formdev.flatlaf.ui.FlatButtonUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,39 +50,49 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
public class FlatTabbedPaneCloseIcon
|
public class FlatTabbedPaneCloseIcon
|
||||||
extends FlatAbstractIcon
|
extends FlatAbstractIcon
|
||||||
{
|
{
|
||||||
protected final Dimension size = UIManager.getDimension( "TabbedPane.closeSize" );
|
@Styleable protected Dimension closeSize = UIManager.getDimension( "TabbedPane.closeSize" );
|
||||||
protected final int arc = UIManager.getInt( "TabbedPane.closeArc" );
|
@Styleable protected int closeArc = UIManager.getInt( "TabbedPane.closeArc" );
|
||||||
protected final float crossPlainSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossPlainSize", 7.5f );
|
@Styleable protected float closeCrossPlainSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossPlainSize", 7.5f );
|
||||||
protected final float crossFilledSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossFilledSize", crossPlainSize );
|
@Styleable protected float closeCrossFilledSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossFilledSize", closeCrossPlainSize );
|
||||||
protected final float closeCrossLineWidth = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossLineWidth", 1f );
|
@Styleable protected float closeCrossLineWidth = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossLineWidth", 1f );
|
||||||
protected final Color background = UIManager.getColor( "TabbedPane.closeBackground" );
|
@Styleable protected Color closeBackground = UIManager.getColor( "TabbedPane.closeBackground" );
|
||||||
protected final Color foreground = UIManager.getColor( "TabbedPane.closeForeground" );
|
@Styleable protected Color closeForeground = UIManager.getColor( "TabbedPane.closeForeground" );
|
||||||
protected final Color hoverBackground = UIManager.getColor( "TabbedPane.closeHoverBackground" );
|
@Styleable protected Color closeHoverBackground = UIManager.getColor( "TabbedPane.closeHoverBackground" );
|
||||||
protected final Color hoverForeground = UIManager.getColor( "TabbedPane.closeHoverForeground" );
|
@Styleable protected Color closeHoverForeground = UIManager.getColor( "TabbedPane.closeHoverForeground" );
|
||||||
protected final Color pressedBackground = UIManager.getColor( "TabbedPane.closePressedBackground" );
|
@Styleable protected Color closePressedBackground = UIManager.getColor( "TabbedPane.closePressedBackground" );
|
||||||
protected final Color pressedForeground = UIManager.getColor( "TabbedPane.closePressedForeground" );
|
@Styleable protected Color closePressedForeground = UIManager.getColor( "TabbedPane.closePressedForeground" );
|
||||||
|
|
||||||
public FlatTabbedPaneCloseIcon() {
|
public FlatTabbedPaneCloseIcon() {
|
||||||
super( 16, 16, null );
|
super( 16, 16, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public Object applyStyleProperty( String key, Object value ) {
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public Map<String, Class<?>> getStyleableInfos() {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
// paint background
|
// paint background
|
||||||
Color bg = FlatButtonUI.buttonStateColor( c, background, null, null, hoverBackground, pressedBackground );
|
Color bg = FlatButtonUI.buttonStateColor( c, closeBackground, null, null, closeHoverBackground, closePressedBackground );
|
||||||
if( bg != null ) {
|
if( bg != null ) {
|
||||||
g.setColor( FlatUIUtils.deriveColor( bg, c.getBackground() ) );
|
g.setColor( FlatUIUtils.deriveColor( bg, c.getBackground() ) );
|
||||||
g.fillRoundRect( (width - size.width) / 2, (height - size.height) / 2,
|
g.fillRoundRect( (width - closeSize.width) / 2, (height - closeSize.height) / 2,
|
||||||
size.width, size.height, arc, arc );
|
closeSize.width, closeSize.height, closeArc, closeArc );
|
||||||
}
|
}
|
||||||
|
|
||||||
// set cross color
|
// set cross color
|
||||||
Color fg = FlatButtonUI.buttonStateColor( c, foreground, null, null, hoverForeground, pressedForeground );
|
Color fg = FlatButtonUI.buttonStateColor( c, closeForeground, null, null, closeHoverForeground, closePressedForeground );
|
||||||
g.setColor( FlatUIUtils.deriveColor( fg, c.getForeground() ) );
|
g.setColor( FlatUIUtils.deriveColor( fg, c.getForeground() ) );
|
||||||
|
|
||||||
float mx = width / 2;
|
float mx = width / 2;
|
||||||
float my = height / 2;
|
float my = height / 2;
|
||||||
float r = ((bg != null) ? crossFilledSize : crossPlainSize) / 2;
|
float r = ((bg != null) ? closeCrossFilledSize : closeCrossPlainSize) / 2;
|
||||||
|
|
||||||
// paint cross
|
// paint cross
|
||||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ public class FlatTreeClosedIcon
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
|
FlatTreeCollapsedIcon.setStyleColorFromTreeUI( c, g, ui -> ui.iconClosedColor );
|
||||||
|
|
||||||
/*
|
/*
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
<polygon fill="#6E6E6E" fill-rule="evenodd" points="1 2 6 2 8 4 15 4 15 13 1 13"/>
|
<polygon fill="#6E6E6E" fill-rule="evenodd" points="1 2 6 2 8 4 15 4 15 13 1 13"/>
|
||||||
|
|||||||
@@ -19,7 +19,12 @@ package com.formdev.flatlaf.icons;
|
|||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import javax.swing.JTree;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.plaf.TreeUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatTreeUI;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,8 +51,12 @@ public class FlatTreeCollapsedIcon
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
|
setStyleColorFromTreeUI( c, g );
|
||||||
rotate( c, g );
|
rotate( c, g );
|
||||||
|
|
||||||
|
String arrowType = getStyleFromTreeUI( c, ui -> ui.iconArrowType );
|
||||||
|
boolean chevron = (arrowType != null) ? FlatUIUtils.isChevron( arrowType ) : this.chevron;
|
||||||
|
|
||||||
if( chevron ) {
|
if( chevron ) {
|
||||||
// chevron arrow
|
// chevron arrow
|
||||||
g.fill( FlatUIUtils.createPath( 3,1, 3,2.5, 6,5.5, 3,8.5, 3,10, 4.5,10, 9,5.5, 4.5,1 ) );
|
g.fill( FlatUIUtils.createPath( 3,1, 3,2.5, 6,5.5, 3,8.5, 3,10, 4.5,10, 9,5.5, 4.5,1 ) );
|
||||||
@@ -57,8 +66,34 @@ public class FlatTreeCollapsedIcon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setStyleColorFromTreeUI( Component c, Graphics2D g ) {
|
||||||
|
setStyleColorFromTreeUI( c, g, ui -> ui.iconCollapsedColor );
|
||||||
|
}
|
||||||
|
|
||||||
void rotate( Component c, Graphics2D g ) {
|
void rotate( Component c, Graphics2D g ) {
|
||||||
if( !c.getComponentOrientation().isLeftToRight() )
|
if( !c.getComponentOrientation().isLeftToRight() )
|
||||||
g.rotate( Math.toRadians( 180 ), width / 2., height / 2. );
|
g.rotate( Math.toRadians( 180 ), width / 2., height / 2. );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Because this icon is always shared for all trees,
|
||||||
|
* get icon specific style from FlatTreeUI.
|
||||||
|
*/
|
||||||
|
static <T> T getStyleFromTreeUI( Component c, Function<FlatTreeUI, T> f ) {
|
||||||
|
JTree tree = (c instanceof JTree)
|
||||||
|
? (JTree) c
|
||||||
|
: (JTree) SwingUtilities.getAncestorOfClass( JTree.class, c );
|
||||||
|
if( tree != null ) {
|
||||||
|
TreeUI ui = tree.getUI();
|
||||||
|
if( ui instanceof FlatTreeUI )
|
||||||
|
return f.apply( (FlatTreeUI) ui );
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setStyleColorFromTreeUI( Component c, Graphics2D g, Function<FlatTreeUI, Color> f ) {
|
||||||
|
Color color = getStyleFromTreeUI( c, f );
|
||||||
|
if( color != null )
|
||||||
|
g.setColor( color );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,11 @@ public class FlatTreeExpandedIcon
|
|||||||
super( UIManager.getColor( "Tree.icon.expandedColor" ) );
|
super( UIManager.getColor( "Tree.icon.expandedColor" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void setStyleColorFromTreeUI( Component c, Graphics2D g ) {
|
||||||
|
setStyleColorFromTreeUI( c, g, ui -> ui.iconExpandedColor );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void rotate( Component c, Graphics2D g ) {
|
void rotate( Component c, Graphics2D g ) {
|
||||||
g.rotate( Math.toRadians( 90 ), width / 2., height / 2. );
|
g.rotate( Math.toRadians( 90 ), width / 2., height / 2. );
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ public class FlatTreeLeafIcon
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
|
FlatTreeCollapsedIcon.setStyleColorFromTreeUI( c, g, ui -> ui.iconLeafColor );
|
||||||
|
|
||||||
/*
|
/*
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
<g fill="none" fill-rule="evenodd">
|
<g fill="none" fill-rule="evenodd">
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ public class FlatTreeOpenIcon
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
|
FlatTreeCollapsedIcon.setStyleColorFromTreeUI( c, g, ui -> ui.iconOpenColor );
|
||||||
|
|
||||||
/*
|
/*
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
<g fill="none" fill-rule="evenodd">
|
<g fill="none" fill-rule="evenodd">
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import java.awt.Color;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.RenderingHints;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import com.formdev.flatlaf.ui.FlatButtonUI;
|
import com.formdev.flatlaf.ui.FlatButtonUI;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
@@ -65,8 +66,14 @@ public abstract class FlatWindowAbstractIcon
|
|||||||
protected void paintBackground( Component c, Graphics2D g ) {
|
protected void paintBackground( Component c, Graphics2D g ) {
|
||||||
Color background = FlatButtonUI.buttonStateColor( c, null, null, null, hoverBackground, pressedBackground );
|
Color background = FlatButtonUI.buttonStateColor( c, null, null, null, hoverBackground, pressedBackground );
|
||||||
if( background != null ) {
|
if( background != null ) {
|
||||||
|
// disable antialiasing for background rectangle painting to avoid blury edges when scaled (e.g. at 125% or 175%)
|
||||||
|
Object oldHint = g.getRenderingHint( RenderingHints.KEY_ANTIALIASING );
|
||||||
|
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
|
||||||
|
|
||||||
g.setColor( FlatUIUtils.deriveColor( background, c.getBackground() ) );
|
g.setColor( FlatUIUtils.deriveColor( background, c.getBackground() ) );
|
||||||
g.fillRect( 0, 0, width, height );
|
g.fillRect( 0, 0, width, height );
|
||||||
|
|
||||||
|
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, oldHint );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import java.awt.geom.Line2D;
|
|||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import com.formdev.flatlaf.ui.FlatButtonUI;
|
import com.formdev.flatlaf.ui.FlatButtonUI;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "close" icon for windows (frames and dialogs).
|
* "close" icon for windows (frames and dialogs).
|
||||||
@@ -54,7 +55,7 @@ public class FlatWindowCloseIcon
|
|||||||
int iy = y + ((height - iwh) / 2);
|
int iy = y + ((height - iwh) / 2);
|
||||||
int ix2 = ix + iwh - 1;
|
int ix2 = ix + iwh - 1;
|
||||||
int iy2 = iy + iwh - 1;
|
int iy2 = iy + iwh - 1;
|
||||||
int thickness = (int) scaleFactor;
|
float thickness = SystemInfo.isWindows_11_orLater ? (float) scaleFactor : (int) scaleFactor;
|
||||||
|
|
||||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||||
path.append( new Line2D.Float( ix, iy, ix2, iy2 ), false );
|
path.append( new Line2D.Float( ix, iy, ix2, iy2 ), false );
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.formdev.flatlaf.icons;
|
|||||||
|
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "maximize" icon for windows (frames and dialogs).
|
* "maximize" icon for windows (frames and dialogs).
|
||||||
@@ -35,8 +36,11 @@ public class FlatWindowMaximizeIcon
|
|||||||
int iwh = (int) (10 * scaleFactor);
|
int iwh = (int) (10 * scaleFactor);
|
||||||
int ix = x + ((width - iwh) / 2);
|
int ix = x + ((width - iwh) / 2);
|
||||||
int iy = y + ((height - iwh) / 2);
|
int iy = y + ((height - iwh) / 2);
|
||||||
int thickness = (int) scaleFactor;
|
float thickness = SystemInfo.isWindows_11_orLater ? (float) scaleFactor : (int) scaleFactor;
|
||||||
|
int arc = Math.max( (int) (1.5 * scaleFactor), 2 );
|
||||||
|
|
||||||
g.fill( FlatUIUtils.createRectangle( ix, iy, iwh, iwh, thickness ) );
|
g.fill( SystemInfo.isWindows_11_orLater
|
||||||
|
? FlatUIUtils.createRoundRectangle( ix, iy, iwh, iwh, thickness, arc, arc, arc, arc )
|
||||||
|
: FlatUIUtils.createRectangle( ix, iy, iwh, iwh, thickness ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import java.awt.geom.Area;
|
|||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "restore" icon for windows (frames and dialogs).
|
* "restore" icon for windows (frames and dialogs).
|
||||||
@@ -38,18 +39,33 @@ public class FlatWindowRestoreIcon
|
|||||||
int iwh = (int) (10 * scaleFactor);
|
int iwh = (int) (10 * scaleFactor);
|
||||||
int ix = x + ((width - iwh) / 2);
|
int ix = x + ((width - iwh) / 2);
|
||||||
int iy = y + ((height - iwh) / 2);
|
int iy = y + ((height - iwh) / 2);
|
||||||
int thickness = (int) scaleFactor;
|
float thickness = SystemInfo.isWindows_11_orLater ? (float) scaleFactor : (int) scaleFactor;
|
||||||
|
int arc = Math.max( (int) (1.5 * scaleFactor), 2 );
|
||||||
|
int arcOuter = (int) (arc + (1.5 * scaleFactor));
|
||||||
|
|
||||||
int rwh = (int) (8 * scaleFactor);
|
int rwh = (int) (8 * scaleFactor);
|
||||||
int ro2 = iwh - rwh;
|
int ro2 = iwh - rwh;
|
||||||
|
|
||||||
Path2D r1 = FlatUIUtils.createRectangle( ix + ro2, iy, rwh, rwh, thickness );
|
// upper-right rectangle
|
||||||
Path2D r2 = FlatUIUtils.createRectangle( ix, iy + ro2, rwh, rwh, thickness );
|
Path2D r1 = SystemInfo.isWindows_11_orLater
|
||||||
|
? FlatUIUtils.createRoundRectangle( ix + ro2, iy, rwh, rwh, thickness, arc, arcOuter, arc, arc )
|
||||||
|
: FlatUIUtils.createRectangle( ix + ro2, iy, rwh, rwh, thickness );
|
||||||
|
|
||||||
|
// lower-left rectangle
|
||||||
|
Path2D r2 = SystemInfo.isWindows_11_orLater
|
||||||
|
? FlatUIUtils.createRoundRectangle( ix, iy + ro2, rwh, rwh, thickness, arc, arc, arc, arc )
|
||||||
|
: FlatUIUtils.createRectangle( ix, iy + ro2, rwh, rwh, thickness );
|
||||||
|
|
||||||
|
// paint upper-right rectangle
|
||||||
Area area = new Area( r1 );
|
Area area = new Area( r1 );
|
||||||
area.subtract( new Area( new Rectangle2D.Float( ix, iy + ro2, rwh, rwh ) ) );
|
if( SystemInfo.isWindows_11_orLater ) {
|
||||||
|
area.subtract( new Area( new Rectangle2D.Float( ix, (float) (iy + scaleFactor), rwh, rwh ) ) );
|
||||||
|
area.subtract( new Area( new Rectangle2D.Float( (float) (ix + scaleFactor), iy + ro2, rwh, rwh ) ) );
|
||||||
|
} else
|
||||||
|
area.subtract( new Area( new Rectangle2D.Float( ix, iy + ro2, rwh, rwh ) ) );
|
||||||
g.fill( area );
|
g.fill( area );
|
||||||
|
|
||||||
|
// paint lower-left rectangle
|
||||||
g.fill( r2 );
|
g.fill( r2 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ package com.formdev.flatlaf.resources;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The only purpose of this file is to add a .class file to this package to make it non-empty.
|
* The only purpose of this file is to add a .class file to this package to make it non-empty.
|
||||||
* Otherwise the compiler outputs a warning because this package is opend in module-info.java.
|
* Otherwise, the compiler outputs a warning because this package is opened in module-info.java.
|
||||||
* Also when using --patch-module (e.g. from an IDE), an error would occur for empty packages.
|
* Also, when using --patch-module (e.g. from an IDE), an error would occur for empty packages.
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -37,15 +37,15 @@ public class FlatArrowButton
|
|||||||
extends BasicArrowButton
|
extends BasicArrowButton
|
||||||
implements UIResource
|
implements UIResource
|
||||||
{
|
{
|
||||||
public static final int DEFAULT_ARROW_WIDTH = 8;
|
public static final int DEFAULT_ARROW_WIDTH = 9;
|
||||||
|
|
||||||
protected final boolean chevron;
|
protected boolean chevron;
|
||||||
protected final Color foreground;
|
protected Color foreground;
|
||||||
protected final Color disabledForeground;
|
protected Color disabledForeground;
|
||||||
protected final Color hoverForeground;
|
protected Color hoverForeground;
|
||||||
protected final Color hoverBackground;
|
protected Color hoverBackground;
|
||||||
protected final Color pressedForeground;
|
protected Color pressedForeground;
|
||||||
protected final Color pressedBackground;
|
protected Color pressedBackground;
|
||||||
|
|
||||||
private int arrowWidth = DEFAULT_ARROW_WIDTH;
|
private int arrowWidth = DEFAULT_ARROW_WIDTH;
|
||||||
private float xOffset = 0;
|
private float xOffset = 0;
|
||||||
@@ -58,14 +58,8 @@ public class FlatArrowButton
|
|||||||
Color hoverForeground, Color hoverBackground, Color pressedForeground, Color pressedBackground )
|
Color hoverForeground, Color hoverBackground, Color pressedForeground, Color pressedBackground )
|
||||||
{
|
{
|
||||||
super( direction, Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE );
|
super( direction, Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE );
|
||||||
|
updateStyle( type, foreground, disabledForeground, hoverForeground, hoverBackground,
|
||||||
this.chevron = FlatUIUtils.isChevron( type );
|
pressedForeground, pressedBackground );
|
||||||
this.foreground = foreground;
|
|
||||||
this.disabledForeground = disabledForeground;
|
|
||||||
this.hoverForeground = hoverForeground;
|
|
||||||
this.hoverBackground = hoverBackground;
|
|
||||||
this.pressedForeground = pressedForeground;
|
|
||||||
this.pressedBackground = pressedBackground;
|
|
||||||
|
|
||||||
setOpaque( false );
|
setOpaque( false );
|
||||||
setBorder( null );
|
setBorder( null );
|
||||||
@@ -101,6 +95,19 @@ public class FlatArrowButton
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public void updateStyle( String type, Color foreground, Color disabledForeground,
|
||||||
|
Color hoverForeground, Color hoverBackground, Color pressedForeground, Color pressedBackground )
|
||||||
|
{
|
||||||
|
this.chevron = FlatUIUtils.isChevron( type );
|
||||||
|
this.foreground = foreground;
|
||||||
|
this.disabledForeground = disabledForeground;
|
||||||
|
this.hoverForeground = hoverForeground;
|
||||||
|
this.hoverBackground = hoverBackground;
|
||||||
|
this.pressedForeground = pressedForeground;
|
||||||
|
this.pressedBackground = pressedBackground;
|
||||||
|
}
|
||||||
|
|
||||||
public int getArrowWidth() {
|
public int getArrowWidth() {
|
||||||
return arrowWidth;
|
return arrowWidth;
|
||||||
}
|
}
|
||||||
@@ -204,6 +211,6 @@ public class FlatArrowButton
|
|||||||
if( vert && parent instanceof JComponent && FlatUIUtils.hasRoundBorder( (JComponent) parent ) )
|
if( vert && parent instanceof JComponent && FlatUIUtils.hasRoundBorder( (JComponent) parent ) )
|
||||||
x -= scale( parent.getComponentOrientation().isLeftToRight() ? 1 : -1 );
|
x -= scale( parent.getComponentOrientation().isLeftToRight() ? 1 : -1 );
|
||||||
|
|
||||||
FlatUIUtils.paintArrow( g, x, 0, getWidth(), getHeight(), getDirection(), chevron, arrowWidth, xOffset, yOffset );
|
FlatUIUtils.paintArrow( g, x, 0, getWidth(), getHeight(), getDirection(), chevron, getArrowWidth(), getXOffset(), getYOffset() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import java.awt.Graphics;
|
|||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.Paint;
|
import java.awt.Paint;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.JComboBox;
|
import javax.swing.JComboBox;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
@@ -31,21 +32,25 @@ import javax.swing.JViewport;
|
|||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.basic.BasicBorders;
|
import javax.swing.plaf.basic.BasicBorders;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableBorder;
|
||||||
import com.formdev.flatlaf.util.DerivedColor;
|
import com.formdev.flatlaf.util.DerivedColor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Border for various components (e.g. {@link javax.swing.JTextField}).
|
* Border for various components (e.g. {@link javax.swing.JTextField}).
|
||||||
*
|
* <p>
|
||||||
* There is empty space around the component border, if Component.focusWidth is greater than zero,
|
* There is empty space around the component border, if Component.focusWidth is greater than zero,
|
||||||
* which is used to paint outer focus border.
|
* which is used to paint outer focus border.
|
||||||
*
|
* <p>
|
||||||
* Because there is empty space (if outer focus border is not painted),
|
* Because there is empty space (if outer focus border is not painted),
|
||||||
* UI delegates that use this border (or subclasses) must invoke
|
* UI delegates that use this border (or subclasses) must invoke
|
||||||
* {@link FlatUIUtils#paintParentBackground} to paint the empty space correctly.
|
* {@link FlatUIUtils#paintParentBackground} to fill the empty space correctly.
|
||||||
*
|
*
|
||||||
* @uiDefault Component.focusWidth int
|
* @uiDefault Component.focusWidth int
|
||||||
* @uiDefault Component.innerFocusWidth int or float
|
* @uiDefault Component.innerFocusWidth int or float
|
||||||
* @uiDefault Component.innerOutlineWidth int or float
|
* @uiDefault Component.innerOutlineWidth int or float
|
||||||
|
* @uiDefault Component.borderWidth int or float
|
||||||
|
*
|
||||||
* @uiDefault Component.focusColor Color
|
* @uiDefault Component.focusColor Color
|
||||||
* @uiDefault Component.borderColor Color
|
* @uiDefault Component.borderColor Color
|
||||||
* @uiDefault Component.disabledBorderColor Color
|
* @uiDefault Component.disabledBorderColor Color
|
||||||
@@ -61,20 +66,40 @@ import com.formdev.flatlaf.util.DerivedColor;
|
|||||||
*/
|
*/
|
||||||
public class FlatBorder
|
public class FlatBorder
|
||||||
extends BasicBorders.MarginBorder
|
extends BasicBorders.MarginBorder
|
||||||
|
implements StyleableBorder
|
||||||
{
|
{
|
||||||
protected final int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
@Styleable protected int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
||||||
protected final float innerFocusWidth = FlatUIUtils.getUIFloat( "Component.innerFocusWidth", 0 );
|
@Styleable protected float innerFocusWidth = FlatUIUtils.getUIFloat( "Component.innerFocusWidth", 0 );
|
||||||
protected final float innerOutlineWidth = FlatUIUtils.getUIFloat( "Component.innerOutlineWidth", 0 );
|
@Styleable protected float innerOutlineWidth = FlatUIUtils.getUIFloat( "Component.innerOutlineWidth", 0 );
|
||||||
protected final Color focusColor = UIManager.getColor( "Component.focusColor" );
|
/** @since 2 */ @Styleable protected float borderWidth = FlatUIUtils.getUIFloat( "Component.borderWidth", 1 );
|
||||||
protected final Color borderColor = UIManager.getColor( "Component.borderColor" );
|
|
||||||
protected final Color disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
|
|
||||||
protected final Color focusedBorderColor = UIManager.getColor( "Component.focusedBorderColor" );
|
|
||||||
|
|
||||||
protected final Color errorBorderColor = UIManager.getColor( "Component.error.borderColor" );
|
@Styleable protected Color focusColor = UIManager.getColor( "Component.focusColor" );
|
||||||
protected final Color errorFocusedBorderColor = UIManager.getColor( "Component.error.focusedBorderColor" );
|
@Styleable protected Color borderColor = UIManager.getColor( "Component.borderColor" );
|
||||||
protected final Color warningBorderColor = UIManager.getColor( "Component.warning.borderColor" );
|
@Styleable protected Color disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
|
||||||
protected final Color warningFocusedBorderColor = UIManager.getColor( "Component.warning.focusedBorderColor" );
|
@Styleable protected Color focusedBorderColor = UIManager.getColor( "Component.focusedBorderColor" );
|
||||||
protected final Color customBorderColor = UIManager.getColor( "Component.custom.borderColor" );
|
|
||||||
|
@Styleable(dot=true) protected Color errorBorderColor = UIManager.getColor( "Component.error.borderColor" );
|
||||||
|
@Styleable(dot=true) protected Color errorFocusedBorderColor = UIManager.getColor( "Component.error.focusedBorderColor" );
|
||||||
|
@Styleable(dot=true) protected Color warningBorderColor = UIManager.getColor( "Component.warning.borderColor" );
|
||||||
|
@Styleable(dot=true) protected Color warningFocusedBorderColor = UIManager.getColor( "Component.warning.focusedBorderColor" );
|
||||||
|
@Styleable(dot=true) protected Color customBorderColor = UIManager.getColor( "Component.custom.borderColor" );
|
||||||
|
|
||||||
|
// only used via styling (not in UI defaults, but has likewise client properties)
|
||||||
|
/** @since 2 */ @Styleable protected String outline;
|
||||||
|
/** @since 2 */ @Styleable protected Color outlineColor;
|
||||||
|
/** @since 2 */ @Styleable protected Color outlineFocusedColor;
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Object applyStyleProperty( String key, Object value ) {
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos() {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
|
}
|
||||||
|
|
||||||
@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 ) {
|
||||||
@@ -83,9 +108,11 @@ public class FlatBorder
|
|||||||
FlatUIUtils.setRenderingHints( g2 );
|
FlatUIUtils.setRenderingHints( g2 );
|
||||||
|
|
||||||
float focusWidth = scale( (float) getFocusWidth( c ) );
|
float focusWidth = scale( (float) getFocusWidth( c ) );
|
||||||
float borderWidth = scale( (float) getBorderWidth( c ) );
|
float focusInnerWidth = 0;
|
||||||
|
float borderWidth = scale( getBorderWidth( c ) );
|
||||||
float arc = scale( (float) getArc( c ) );
|
float arc = scale( (float) getArc( c ) );
|
||||||
Color outlineColor = getOutlineColor( c );
|
Color outlineColor = getOutlineColor( c );
|
||||||
|
Color focusColor = null;
|
||||||
|
|
||||||
// paint outer border
|
// paint outer border
|
||||||
if( outlineColor != null || isFocused( c ) ) {
|
if( outlineColor != null || isFocused( c ) ) {
|
||||||
@@ -94,15 +121,16 @@ public class FlatBorder
|
|||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
if( focusWidth > 0 || innerWidth > 0 ) {
|
if( focusWidth > 0 || innerWidth > 0 ) {
|
||||||
g2.setColor( (outlineColor != null) ? outlineColor : getFocusColor( c ) );
|
focusColor = (outlineColor != null) ? outlineColor : getFocusColor( c );
|
||||||
FlatUIUtils.paintComponentOuterBorder( g2, x, y, width, height,
|
focusInnerWidth = borderWidth + scale( innerWidth );
|
||||||
focusWidth, borderWidth + scale( innerWidth ), arc );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// paint border
|
// paint border
|
||||||
g2.setPaint( (outlineColor != null) ? outlineColor : getBorderColor( c ) );
|
Paint borderColor = (outlineColor != null) ? outlineColor : getBorderColor( c );
|
||||||
FlatUIUtils.paintComponentBorder( g2, x, y, width, height, focusWidth, borderWidth, arc );
|
FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height,
|
||||||
|
focusWidth, 1, focusInnerWidth, borderWidth, arc,
|
||||||
|
focusColor, borderColor, null );
|
||||||
} finally {
|
} finally {
|
||||||
g2.dispose();
|
g2.dispose();
|
||||||
}
|
}
|
||||||
@@ -117,6 +145,17 @@ public class FlatBorder
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
Object outline = ((JComponent)c).getClientProperty( FlatClientProperties.OUTLINE );
|
Object outline = ((JComponent)c).getClientProperty( FlatClientProperties.OUTLINE );
|
||||||
|
if( outline == null )
|
||||||
|
outline = this.outline;
|
||||||
|
if( outline == null ) {
|
||||||
|
if( outlineColor != null && outlineFocusedColor != null )
|
||||||
|
outline = new Color[] { outlineFocusedColor, outlineColor };
|
||||||
|
else if( outlineColor != null )
|
||||||
|
outline = outlineColor;
|
||||||
|
else if( outlineFocusedColor != null )
|
||||||
|
outline = outlineFocusedColor;
|
||||||
|
}
|
||||||
|
|
||||||
if( outline instanceof String ) {
|
if( outline instanceof String ) {
|
||||||
switch( (String) outline ) {
|
switch( (String) outline ) {
|
||||||
case FlatClientProperties.OUTLINE_ERROR:
|
case FlatClientProperties.OUTLINE_ERROR:
|
||||||
@@ -229,8 +268,8 @@ public class FlatBorder
|
|||||||
* Returns the (unscaled) line thickness used to paint the border.
|
* Returns the (unscaled) line thickness used to paint the border.
|
||||||
* This may be different to {@link #getLineWidth}.
|
* This may be different to {@link #getLineWidth}.
|
||||||
*/
|
*/
|
||||||
protected int getBorderWidth( Component c ) {
|
protected float getBorderWidth( Component c ) {
|
||||||
return getLineWidth( c );
|
return borderWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -26,57 +26,66 @@ import java.awt.Paint;
|
|||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Border for {@link javax.swing.JButton}.
|
* Border for {@link javax.swing.JButton}.
|
||||||
*
|
*
|
||||||
|
* @uiDefault Button.arc int
|
||||||
|
* @uiDefault Button.innerFocusWidth int or float optional; defaults to Component.innerFocusWidth
|
||||||
|
* @uiDefault Button.borderWidth int or float optional; defaults to Component.borderWidth
|
||||||
|
*
|
||||||
* @uiDefault Button.borderColor Color
|
* @uiDefault Button.borderColor Color
|
||||||
* @uiDefault Button.startBorderColor Color optional; if set, a gradient paint is used and Button.borderColor is ignored
|
* @uiDefault Button.startBorderColor Color optional; if set, a gradient paint is used and Button.borderColor is ignored
|
||||||
* @uiDefault Button.endBorderColor Color optional; if set, a gradient paint is used
|
* @uiDefault Button.endBorderColor Color optional; if set, a gradient paint is used
|
||||||
* @uiDefault Button.disabledBorderColor Color
|
* @uiDefault Button.disabledBorderColor Color
|
||||||
* @uiDefault Button.focusedBorderColor Color
|
* @uiDefault Button.focusedBorderColor Color
|
||||||
* @uiDefault Button.hoverBorderColor Color optional
|
* @uiDefault Button.hoverBorderColor Color optional
|
||||||
|
*
|
||||||
|
* @uiDefault Button.default.borderWidth int or float
|
||||||
* @uiDefault Button.default.borderColor Color
|
* @uiDefault Button.default.borderColor Color
|
||||||
* @uiDefault Button.default.startBorderColor Color optional; if set, a gradient paint is used and Button.default.borderColor is ignored
|
* @uiDefault Button.default.startBorderColor Color optional; if set, a gradient paint is used and Button.default.borderColor is ignored
|
||||||
* @uiDefault Button.default.endBorderColor Color optional; if set, a gradient paint is used
|
* @uiDefault Button.default.endBorderColor Color optional; if set, a gradient paint is used
|
||||||
* @uiDefault Button.default.hoverBorderColor Color optional
|
|
||||||
* @uiDefault Button.default.focusedBorderColor Color
|
* @uiDefault Button.default.focusedBorderColor Color
|
||||||
* @uiDefault Button.default.focusColor Color
|
* @uiDefault Button.default.focusColor Color
|
||||||
|
* @uiDefault Button.default.hoverBorderColor Color optional
|
||||||
|
*
|
||||||
|
* @uiDefault Button.toolbar.focusWidth int or float optional; default is 1.5
|
||||||
* @uiDefault Button.toolbar.focusColor Color optional; defaults to Component.focusColor
|
* @uiDefault Button.toolbar.focusColor Color optional; defaults to Component.focusColor
|
||||||
* @uiDefault Button.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
|
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatButtonBorder
|
public class FlatButtonBorder
|
||||||
extends FlatBorder
|
extends FlatBorder
|
||||||
{
|
{
|
||||||
protected final Color borderColor = FlatUIUtils.getUIColor( "Button.startBorderColor", "Button.borderColor" );
|
@Styleable protected int arc = UIManager.getInt( "Button.arc" );
|
||||||
protected final Color endBorderColor = UIManager.getColor( "Button.endBorderColor" );
|
|
||||||
protected final Color disabledBorderColor = UIManager.getColor( "Button.disabledBorderColor" );
|
protected Color endBorderColor = UIManager.getColor( "Button.endBorderColor" );
|
||||||
protected final Color focusedBorderColor = UIManager.getColor( "Button.focusedBorderColor" );
|
@Styleable protected Color hoverBorderColor = UIManager.getColor( "Button.hoverBorderColor" );
|
||||||
protected final Color hoverBorderColor = UIManager.getColor( "Button.hoverBorderColor" );
|
|
||||||
protected final Color defaultBorderColor = FlatUIUtils.getUIColor( "Button.default.startBorderColor", "Button.default.borderColor" );
|
@Styleable(dot=true) protected float defaultBorderWidth = FlatUIUtils.getUIFloat( "Button.default.borderWidth", 1 );
|
||||||
protected final Color defaultEndBorderColor = UIManager.getColor( "Button.default.endBorderColor" );
|
@Styleable(dot=true) protected Color defaultBorderColor = FlatUIUtils.getUIColor( "Button.default.startBorderColor", "Button.default.borderColor" );
|
||||||
protected final Color defaultHoverBorderColor = UIManager.getColor( "Button.default.hoverBorderColor" );
|
protected Color defaultEndBorderColor = UIManager.getColor( "Button.default.endBorderColor" );
|
||||||
protected final Color defaultFocusedBorderColor = UIManager.getColor( "Button.default.focusedBorderColor" );
|
@Styleable(dot=true) protected Color defaultFocusedBorderColor = UIManager.getColor( "Button.default.focusedBorderColor" );
|
||||||
protected final Color defaultFocusColor = UIManager.getColor( "Button.default.focusColor" );
|
@Styleable(dot=true) protected Color defaultFocusColor = UIManager.getColor( "Button.default.focusColor" );
|
||||||
/** @since 1.4 */
|
@Styleable(dot=true) protected Color defaultHoverBorderColor = UIManager.getColor( "Button.default.hoverBorderColor" );
|
||||||
protected final Color toolbarFocusColor = UIManager.getColor( "Button.toolbar.focusColor" );
|
|
||||||
protected final int borderWidth = UIManager.getInt( "Button.borderWidth" );
|
/** @since 1.4 */ @Styleable(dot=true) protected float toolbarFocusWidth = FlatUIUtils.getUIFloat( "Button.toolbar.focusWidth", 1.5f );
|
||||||
protected final int defaultBorderWidth = UIManager.getInt( "Button.default.borderWidth" );
|
/** @since 1.4 */ @Styleable(dot=true) protected Color toolbarFocusColor = UIManager.getColor( "Button.toolbar.focusColor" );
|
||||||
protected final float buttonInnerFocusWidth = FlatUIUtils.getUIFloat( "Button.innerFocusWidth", innerFocusWidth );
|
@Styleable(dot=true) protected Insets toolbarMargin = UIManager.getInsets( "Button.toolbar.margin" );
|
||||||
protected final Insets toolbarMargin = UIManager.getInsets( "Button.toolbar.margin" );
|
@Styleable(dot=true) protected Insets toolbarSpacingInsets = UIManager.getInsets( "Button.toolbar.spacingInsets" );
|
||||||
protected final Insets toolbarSpacingInsets = UIManager.getInsets( "Button.toolbar.spacingInsets" );
|
|
||||||
/** @since 1.4 */
|
public FlatButtonBorder() {
|
||||||
protected final float toolbarFocusWidth = FlatUIUtils.getUIFloat( "Button.toolbar.focusWidth", 1.5f );
|
innerFocusWidth = FlatUIUtils.getUIFloat( "Button.innerFocusWidth", innerFocusWidth );
|
||||||
protected final int arc = UIManager.getInt( "Button.arc" );
|
borderWidth = FlatUIUtils.getUIFloat( "Button.borderWidth", borderWidth );
|
||||||
|
|
||||||
|
borderColor = FlatUIUtils.getUIColor( "Button.startBorderColor", "Button.borderColor" );
|
||||||
|
disabledBorderColor = UIManager.getColor( "Button.disabledBorderColor" );
|
||||||
|
focusedBorderColor = UIManager.getColor( "Button.focusedBorderColor" );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
@@ -90,9 +99,7 @@ public class FlatButtonBorder
|
|||||||
paintToolBarFocus( c, g, x, y, width, height );
|
paintToolBarFocus( c, g, x, y, width, height );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @since 1.4 */
|
||||||
* @since 1.4
|
|
||||||
*/
|
|
||||||
protected void paintToolBarFocus( Component c, Graphics g, int x, int y, int width, int height ) {
|
protected void paintToolBarFocus( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
Graphics2D g2 = (Graphics2D) g.create();
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
try {
|
try {
|
||||||
@@ -108,9 +115,10 @@ public class FlatButtonBorder
|
|||||||
width -= spacing.left + spacing.right;
|
width -= spacing.left + spacing.right;
|
||||||
height -= spacing.top + spacing.bottom;
|
height -= spacing.top + spacing.bottom;
|
||||||
|
|
||||||
g2.setColor( (outlineColor != null) ? outlineColor : getFocusColor( c ) );
|
Color color = (outlineColor != null) ? outlineColor : getFocusColor( c );
|
||||||
// not using paintComponentOuterBorder() here because its round edges look too "thick"
|
// not using focus border painting of paintOutlinedComponent() here
|
||||||
FlatUIUtils.paintComponentBorder( g2, x, y, width, height, 0, focusWidth, arc );
|
// because its round edges look too "thick"
|
||||||
|
FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height, 0, 0, 0, focusWidth, arc, null, color, null );
|
||||||
} finally {
|
} finally {
|
||||||
g2.dispose();
|
g2.dispose();
|
||||||
}
|
}
|
||||||
@@ -151,7 +159,7 @@ public class FlatButtonBorder
|
|||||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
if( FlatButtonUI.isToolBarButton( c ) ) {
|
if( FlatButtonUI.isToolBarButton( c ) ) {
|
||||||
// In toolbars, use button margin only if explicitly set.
|
// In toolbars, use button margin only if explicitly set.
|
||||||
// Otherwise use toolbar margin specified in UI defaults.
|
// Otherwise, use toolbar margin specified in UI defaults.
|
||||||
Insets margin = (c instanceof AbstractButton)
|
Insets margin = (c instanceof AbstractButton)
|
||||||
? ((AbstractButton)c).getMargin()
|
? ((AbstractButton)c).getMargin()
|
||||||
: null;
|
: null;
|
||||||
@@ -175,12 +183,7 @@ public class FlatButtonBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected float getInnerFocusWidth( Component c ) {
|
protected float getBorderWidth( Component c ) {
|
||||||
return buttonInnerFocusWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int getBorderWidth( Component c ) {
|
|
||||||
return FlatButtonUI.isDefaultButton( c ) ? defaultBorderWidth : borderWidth;
|
return FlatButtonUI.isDefaultButton( c ) ? defaultBorderWidth : borderWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,21 +30,33 @@ import java.awt.Insets;
|
|||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.geom.RoundRectangle2D;
|
import java.awt.geom.RoundRectangle2D;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
import javax.swing.ButtonModel;
|
import javax.swing.ButtonModel;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JTextField;
|
||||||
import javax.swing.JToggleButton;
|
import javax.swing.JToggleButton;
|
||||||
import javax.swing.JToolBar;
|
import javax.swing.JToolBar;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.plaf.ButtonUI;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicButtonListener;
|
import javax.swing.plaf.basic.BasicButtonListener;
|
||||||
import javax.swing.plaf.basic.BasicButtonUI;
|
import javax.swing.plaf.basic.BasicButtonUI;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
|
import com.formdev.flatlaf.icons.FlatHelpButtonIcon;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,20 +78,27 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault Button.startBackground Color optional; if set, a gradient paint is used and Button.background is ignored
|
* @uiDefault Button.startBackground Color optional; if set, a gradient paint is used and Button.background is ignored
|
||||||
* @uiDefault Button.endBackground Color optional; if set, a gradient paint is used
|
* @uiDefault Button.endBackground Color optional; if set, a gradient paint is used
|
||||||
* @uiDefault Button.focusedBackground Color optional
|
* @uiDefault Button.focusedBackground Color optional
|
||||||
|
* @uiDefault Button.focusedForeground Color optional
|
||||||
* @uiDefault Button.hoverBackground Color optional
|
* @uiDefault Button.hoverBackground Color optional
|
||||||
|
* @uiDefault Button.hoverForeground Color optional
|
||||||
* @uiDefault Button.pressedBackground Color optional
|
* @uiDefault Button.pressedBackground Color optional
|
||||||
|
* @uiDefault Button.pressedForeground Color optional
|
||||||
* @uiDefault Button.selectedBackground Color
|
* @uiDefault Button.selectedBackground Color
|
||||||
* @uiDefault Button.selectedForeground Color
|
* @uiDefault Button.selectedForeground Color
|
||||||
* @uiDefault Button.disabledBackground Color optional
|
* @uiDefault Button.disabledBackground Color optional
|
||||||
* @uiDefault Button.disabledText Color
|
* @uiDefault Button.disabledText Color
|
||||||
* @uiDefault Button.disabledSelectedBackground Color
|
* @uiDefault Button.disabledSelectedBackground Color
|
||||||
|
* @uiDefault Button.disabledSelectedForeground Color optional
|
||||||
* @uiDefault Button.default.background Color
|
* @uiDefault Button.default.background Color
|
||||||
* @uiDefault Button.default.startBackground Color optional; if set, a gradient paint is used and Button.default.background is ignored
|
* @uiDefault Button.default.startBackground Color optional; if set, a gradient paint is used and Button.default.background is ignored
|
||||||
* @uiDefault Button.default.endBackground Color optional; if set, a gradient paint is used
|
* @uiDefault Button.default.endBackground Color optional; if set, a gradient paint is used
|
||||||
* @uiDefault Button.default.foreground Color
|
* @uiDefault Button.default.foreground Color
|
||||||
* @uiDefault Button.default.focusedBackground Color optional
|
* @uiDefault Button.default.focusedBackground Color optional
|
||||||
|
* @uiDefault Button.default.focusedForeground Color optional
|
||||||
* @uiDefault Button.default.hoverBackground Color optional
|
* @uiDefault Button.default.hoverBackground Color optional
|
||||||
|
* @uiDefault Button.default.hoverForeground Color optional
|
||||||
* @uiDefault Button.default.pressedBackground Color optional
|
* @uiDefault Button.default.pressedBackground Color optional
|
||||||
|
* @uiDefault Button.default.pressedForeground Color optional
|
||||||
* @uiDefault Button.default.boldText boolean
|
* @uiDefault Button.default.boldText boolean
|
||||||
* @uiDefault Button.paintShadow boolean default is false
|
* @uiDefault Button.paintShadow boolean default is false
|
||||||
* @uiDefault Button.shadowWidth int default is 2
|
* @uiDefault Button.shadowWidth int default is 2
|
||||||
@@ -87,15 +106,21 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault Button.default.shadowColor Color optional
|
* @uiDefault Button.default.shadowColor Color optional
|
||||||
* @uiDefault Button.toolbar.spacingInsets Insets
|
* @uiDefault Button.toolbar.spacingInsets Insets
|
||||||
* @uiDefault Button.toolbar.hoverBackground Color
|
* @uiDefault Button.toolbar.hoverBackground Color
|
||||||
|
* @uiDefault Button.toolbar.hoverForeground Color optional
|
||||||
* @uiDefault Button.toolbar.pressedBackground Color
|
* @uiDefault Button.toolbar.pressedBackground Color
|
||||||
|
* @uiDefault Button.toolbar.pressedForeground Color optional
|
||||||
* @uiDefault Button.toolbar.selectedBackground Color
|
* @uiDefault Button.toolbar.selectedBackground Color
|
||||||
|
* @uiDefault Button.toolbar.selectedForeground Color optional
|
||||||
|
* @uiDefault Button.toolbar.disabledSelectedBackground Color optional
|
||||||
|
* @uiDefault Button.toolbar.disabledSelectedForeground Color optional
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatButtonUI
|
public class FlatButtonUI
|
||||||
extends BasicButtonUI
|
extends BasicButtonUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
protected int minimumWidth;
|
@Styleable protected int minimumWidth;
|
||||||
protected int iconTextGap;
|
protected int iconTextGap;
|
||||||
|
|
||||||
protected Color background;
|
protected Color background;
|
||||||
@@ -103,39 +128,74 @@ public class FlatButtonUI
|
|||||||
|
|
||||||
protected Color startBackground;
|
protected Color startBackground;
|
||||||
protected Color endBackground;
|
protected Color endBackground;
|
||||||
protected Color focusedBackground;
|
@Styleable protected Color focusedBackground;
|
||||||
protected Color hoverBackground;
|
/** @since 2.3 */ @Styleable protected Color focusedForeground;
|
||||||
protected Color pressedBackground;
|
@Styleable protected Color hoverBackground;
|
||||||
protected Color selectedBackground;
|
/** @since 2.3 */ @Styleable protected Color hoverForeground;
|
||||||
protected Color selectedForeground;
|
@Styleable protected Color pressedBackground;
|
||||||
protected Color disabledBackground;
|
/** @since 2.3 */ @Styleable protected Color pressedForeground;
|
||||||
protected Color disabledText;
|
@Styleable protected Color selectedBackground;
|
||||||
protected Color disabledSelectedBackground;
|
@Styleable protected Color selectedForeground;
|
||||||
|
@Styleable protected Color disabledBackground;
|
||||||
|
@Styleable protected Color disabledText;
|
||||||
|
@Styleable protected Color disabledSelectedBackground;
|
||||||
|
/** @since 2.3 */ @Styleable protected Color disabledSelectedForeground;
|
||||||
|
|
||||||
protected Color defaultBackground;
|
@Styleable(dot=true) protected Color defaultBackground;
|
||||||
protected Color defaultEndBackground;
|
protected Color defaultEndBackground;
|
||||||
protected Color defaultForeground;
|
@Styleable(dot=true) protected Color defaultForeground;
|
||||||
protected Color defaultFocusedBackground;
|
@Styleable(dot=true) protected Color defaultFocusedBackground;
|
||||||
protected Color defaultHoverBackground;
|
/** @since 2.3 */ @Styleable(dot=true) protected Color defaultFocusedForeground;
|
||||||
protected Color defaultPressedBackground;
|
@Styleable(dot=true) protected Color defaultHoverBackground;
|
||||||
protected boolean defaultBoldText;
|
/** @since 2.3 */ @Styleable(dot=true) protected Color defaultHoverForeground;
|
||||||
|
@Styleable(dot=true) protected Color defaultPressedBackground;
|
||||||
|
/** @since 2.3 */ @Styleable(dot=true) protected Color defaultPressedForeground;
|
||||||
|
@Styleable(dot=true) protected boolean defaultBoldText;
|
||||||
|
|
||||||
protected int shadowWidth;
|
@Styleable protected boolean paintShadow;
|
||||||
protected Color shadowColor;
|
@Styleable protected int shadowWidth;
|
||||||
protected Color defaultShadowColor;
|
@Styleable protected Color shadowColor;
|
||||||
|
@Styleable(dot=true) protected Color defaultShadowColor;
|
||||||
|
|
||||||
protected Insets toolbarSpacingInsets;
|
@Styleable(dot=true) protected Color toolbarHoverBackground;
|
||||||
protected Color toolbarHoverBackground;
|
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarHoverForeground;
|
||||||
protected Color toolbarPressedBackground;
|
@Styleable(dot=true) protected Color toolbarPressedBackground;
|
||||||
protected Color toolbarSelectedBackground;
|
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarPressedForeground;
|
||||||
|
@Styleable(dot=true) protected Color toolbarSelectedBackground;
|
||||||
|
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarSelectedForeground;
|
||||||
|
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarDisabledSelectedBackground;
|
||||||
|
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarDisabledSelectedForeground;
|
||||||
|
|
||||||
|
// only used via styling (not in UI defaults, but has likewise client properties)
|
||||||
|
/** @since 2 */ @Styleable protected String buttonType;
|
||||||
|
/** @since 2 */ @Styleable protected boolean squareSize;
|
||||||
|
/** @since 2 */ @Styleable protected int minimumHeight;
|
||||||
|
|
||||||
private Icon helpButtonIcon;
|
private Icon helpButtonIcon;
|
||||||
private Insets defaultMargin;
|
private Insets defaultMargin;
|
||||||
|
|
||||||
|
private final boolean shared;
|
||||||
|
private boolean helpButtonIconShared = true;
|
||||||
private boolean defaults_initialized = false;
|
private boolean defaults_initialized = false;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
private AtomicBoolean borderShared;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return FlatUIUtils.createSharedUI( FlatButtonUI.class, FlatButtonUI::new );
|
return FlatUIUtils.canUseSharedUI( c )
|
||||||
|
? FlatUIUtils.createSharedUI( FlatButtonUI.class, () -> new FlatButtonUI( true ) )
|
||||||
|
: new FlatButtonUI( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected FlatButtonUI( boolean shared ) {
|
||||||
|
this.shared = shared;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle( (AbstractButton) c );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -154,40 +214,47 @@ public class FlatButtonUI
|
|||||||
startBackground = UIManager.getColor( prefix + "startBackground" );
|
startBackground = UIManager.getColor( prefix + "startBackground" );
|
||||||
endBackground = UIManager.getColor( prefix + "endBackground" );
|
endBackground = UIManager.getColor( prefix + "endBackground" );
|
||||||
focusedBackground = UIManager.getColor( prefix + "focusedBackground" );
|
focusedBackground = UIManager.getColor( prefix + "focusedBackground" );
|
||||||
|
focusedForeground = UIManager.getColor( prefix + "focusedForeground" );
|
||||||
hoverBackground = UIManager.getColor( prefix + "hoverBackground" );
|
hoverBackground = UIManager.getColor( prefix + "hoverBackground" );
|
||||||
|
hoverForeground = UIManager.getColor( prefix + "hoverForeground" );
|
||||||
pressedBackground = UIManager.getColor( prefix + "pressedBackground" );
|
pressedBackground = UIManager.getColor( prefix + "pressedBackground" );
|
||||||
|
pressedForeground = UIManager.getColor( prefix + "pressedForeground" );
|
||||||
selectedBackground = UIManager.getColor( prefix + "selectedBackground" );
|
selectedBackground = UIManager.getColor( prefix + "selectedBackground" );
|
||||||
selectedForeground = UIManager.getColor( prefix + "selectedForeground" );
|
selectedForeground = UIManager.getColor( prefix + "selectedForeground" );
|
||||||
disabledBackground = UIManager.getColor( prefix + "disabledBackground" );
|
disabledBackground = UIManager.getColor( prefix + "disabledBackground" );
|
||||||
disabledText = UIManager.getColor( prefix + "disabledText" );
|
disabledText = UIManager.getColor( prefix + "disabledText" );
|
||||||
disabledSelectedBackground = UIManager.getColor( prefix + "disabledSelectedBackground" );
|
disabledSelectedBackground = UIManager.getColor( prefix + "disabledSelectedBackground" );
|
||||||
|
disabledSelectedForeground = UIManager.getColor( prefix + "disabledSelectedForeground" );
|
||||||
if( UIManager.getBoolean( "Button.paintShadow" ) ) {
|
|
||||||
shadowWidth = FlatUIUtils.getUIInt( "Button.shadowWidth", 2 );
|
|
||||||
shadowColor = UIManager.getColor( "Button.shadowColor" );
|
|
||||||
defaultShadowColor = UIManager.getColor( "Button.default.shadowColor" );
|
|
||||||
} else {
|
|
||||||
shadowWidth = 0;
|
|
||||||
shadowColor = null;
|
|
||||||
defaultShadowColor = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultBackground = FlatUIUtils.getUIColor( "Button.default.startBackground", "Button.default.background" );
|
defaultBackground = FlatUIUtils.getUIColor( "Button.default.startBackground", "Button.default.background" );
|
||||||
defaultEndBackground = UIManager.getColor( "Button.default.endBackground" );
|
defaultEndBackground = UIManager.getColor( "Button.default.endBackground" );
|
||||||
defaultForeground = UIManager.getColor( "Button.default.foreground" );
|
defaultForeground = UIManager.getColor( "Button.default.foreground" );
|
||||||
defaultFocusedBackground = UIManager.getColor( "Button.default.focusedBackground" );
|
defaultFocusedBackground = UIManager.getColor( "Button.default.focusedBackground" );
|
||||||
|
defaultFocusedForeground = UIManager.getColor( "Button.default.focusedForeground" );
|
||||||
defaultHoverBackground = UIManager.getColor( "Button.default.hoverBackground" );
|
defaultHoverBackground = UIManager.getColor( "Button.default.hoverBackground" );
|
||||||
|
defaultHoverForeground = UIManager.getColor( "Button.default.hoverForeground" );
|
||||||
defaultPressedBackground = UIManager.getColor( "Button.default.pressedBackground" );
|
defaultPressedBackground = UIManager.getColor( "Button.default.pressedBackground" );
|
||||||
|
defaultPressedForeground = UIManager.getColor( "Button.default.pressedForeground" );
|
||||||
defaultBoldText = UIManager.getBoolean( "Button.default.boldText" );
|
defaultBoldText = UIManager.getBoolean( "Button.default.boldText" );
|
||||||
|
|
||||||
toolbarSpacingInsets = UIManager.getInsets( "Button.toolbar.spacingInsets" );
|
paintShadow = UIManager.getBoolean( "Button.paintShadow" );
|
||||||
|
shadowWidth = FlatUIUtils.getUIInt( "Button.shadowWidth", 2 );
|
||||||
|
shadowColor = UIManager.getColor( "Button.shadowColor" );
|
||||||
|
defaultShadowColor = UIManager.getColor( "Button.default.shadowColor" );
|
||||||
|
|
||||||
toolbarHoverBackground = UIManager.getColor( prefix + "toolbar.hoverBackground" );
|
toolbarHoverBackground = UIManager.getColor( prefix + "toolbar.hoverBackground" );
|
||||||
|
toolbarHoverForeground = UIManager.getColor( prefix + "toolbar.hoverForeground" );
|
||||||
toolbarPressedBackground = UIManager.getColor( prefix + "toolbar.pressedBackground" );
|
toolbarPressedBackground = UIManager.getColor( prefix + "toolbar.pressedBackground" );
|
||||||
|
toolbarPressedForeground = UIManager.getColor( prefix + "toolbar.pressedForeground" );
|
||||||
toolbarSelectedBackground = UIManager.getColor( prefix + "toolbar.selectedBackground" );
|
toolbarSelectedBackground = UIManager.getColor( prefix + "toolbar.selectedBackground" );
|
||||||
|
toolbarSelectedForeground = UIManager.getColor( prefix + "toolbar.selectedForeground" );
|
||||||
|
toolbarDisabledSelectedBackground = UIManager.getColor( prefix + "toolbar.disabledSelectedBackground" );
|
||||||
|
toolbarDisabledSelectedForeground = UIManager.getColor( prefix + "toolbar.disabledSelectedForeground" );
|
||||||
|
|
||||||
helpButtonIcon = UIManager.getIcon( "HelpButton.icon" );
|
helpButtonIcon = UIManager.getIcon( "HelpButton.icon" );
|
||||||
defaultMargin = UIManager.getInsets( prefix + "margin" );
|
defaultMargin = UIManager.getInsets( prefix + "margin" );
|
||||||
|
|
||||||
|
helpButtonIconShared = true;
|
||||||
defaults_initialized = true;
|
defaults_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,6 +274,9 @@ public class FlatButtonUI
|
|||||||
protected void uninstallDefaults( AbstractButton b ) {
|
protected void uninstallDefaults( AbstractButton b ) {
|
||||||
super.uninstallDefaults( b );
|
super.uninstallDefaults( b );
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
borderShared = null;
|
||||||
|
|
||||||
MigLayoutVisualPadding.uninstall( b );
|
MigLayoutVisualPadding.uninstall( b );
|
||||||
defaults_initialized = false;
|
defaults_initialized = false;
|
||||||
}
|
}
|
||||||
@@ -228,9 +298,74 @@ public class FlatButtonUI
|
|||||||
b.revalidate();
|
b.revalidate();
|
||||||
b.repaint();
|
b.repaint();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OUTLINE:
|
||||||
|
b.repaint();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STYLE:
|
||||||
|
case STYLE_CLASS:
|
||||||
|
if( shared && FlatStylingSupport.hasStyleProperty( b ) ) {
|
||||||
|
// unshare component UI if necessary
|
||||||
|
// updateUI() invokes installStyle() from installUI()
|
||||||
|
b.updateUI();
|
||||||
|
} else
|
||||||
|
installStyle( b );
|
||||||
|
b.revalidate();
|
||||||
|
b.repaint();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle( AbstractButton b ) {
|
||||||
|
try {
|
||||||
|
applyStyle( b, FlatStylingSupport.getResolvedStyle( b, getStyleType() ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
String getStyleType() {
|
||||||
|
return "Button";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( AbstractButton b, Object style ) {
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style,
|
||||||
|
(key, value) -> applyStyleProperty( b, key, value ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( AbstractButton b, String key, Object value ) {
|
||||||
|
if( key.startsWith( "help." ) ) {
|
||||||
|
if( !(helpButtonIcon instanceof FlatHelpButtonIcon) )
|
||||||
|
return new UnknownStyleException( key );
|
||||||
|
|
||||||
|
if( helpButtonIconShared ) {
|
||||||
|
helpButtonIcon = FlatStylingSupport.cloneIcon( helpButtonIcon );
|
||||||
|
helpButtonIconShared = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
key = key.substring( "help.".length() );
|
||||||
|
return ((FlatHelpButtonIcon)helpButtonIcon).applyStyleProperty( key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( borderShared == null )
|
||||||
|
borderShared = new AtomicBoolean( true );
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, b, borderShared );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
Map<String, Class<?>> infos = FlatStylingSupport.getAnnotatedStyleableInfos( this, c.getBorder() );
|
||||||
|
if( helpButtonIcon instanceof FlatHelpButtonIcon )
|
||||||
|
FlatStylingSupport.putAllPrefixKey( infos, "help.", ((FlatHelpButtonIcon)helpButtonIcon).getStyleableInfos() );
|
||||||
|
return infos;
|
||||||
|
}
|
||||||
|
|
||||||
static boolean isContentAreaFilled( Component c ) {
|
static boolean isContentAreaFilled( Component c ) {
|
||||||
return !(c instanceof AbstractButton) || ((AbstractButton)c).isContentAreaFilled();
|
return !(c instanceof AbstractButton) || ((AbstractButton)c).isContentAreaFilled();
|
||||||
}
|
}
|
||||||
@@ -245,7 +380,7 @@ public class FlatButtonUI
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the button has an icon but no text,
|
* Returns true if the button has an icon but no text,
|
||||||
* or it it does not have an icon and the text is either "..." or one character.
|
* or it does not have an icon and the text is either "..." or one character.
|
||||||
*/
|
*/
|
||||||
static boolean isIconOnlyOrSingleCharacterButton( Component c ) {
|
static boolean isIconOnlyOrSingleCharacterButton( Component c ) {
|
||||||
if( !(c instanceof JButton) && !(c instanceof JToggleButton) )
|
if( !(c instanceof JButton) && !(c instanceof JToggleButton) )
|
||||||
@@ -268,11 +403,11 @@ public class FlatButtonUI
|
|||||||
if( !(c instanceof AbstractButton) )
|
if( !(c instanceof AbstractButton) )
|
||||||
return TYPE_OTHER;
|
return TYPE_OTHER;
|
||||||
|
|
||||||
Object value = ((AbstractButton)c).getClientProperty( BUTTON_TYPE );
|
String value = getButtonTypeStr( (AbstractButton) c );
|
||||||
if( !(value instanceof String) )
|
if( value == null )
|
||||||
return TYPE_OTHER;
|
return TYPE_OTHER;
|
||||||
|
|
||||||
switch( (String) value ) {
|
switch( value ) {
|
||||||
case BUTTON_TYPE_SQUARE: return TYPE_SQUARE;
|
case BUTTON_TYPE_SQUARE: return TYPE_SQUARE;
|
||||||
case BUTTON_TYPE_ROUND_RECT: return TYPE_ROUND_RECT;
|
case BUTTON_TYPE_ROUND_RECT: return TYPE_ROUND_RECT;
|
||||||
default: return TYPE_OTHER;
|
default: return TYPE_OTHER;
|
||||||
@@ -280,16 +415,27 @@ public class FlatButtonUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
static boolean isHelpButton( Component c ) {
|
static boolean isHelpButton( Component c ) {
|
||||||
return c instanceof JButton && clientPropertyEquals( (JButton) c, BUTTON_TYPE, BUTTON_TYPE_HELP );
|
return c instanceof JButton && BUTTON_TYPE_HELP.equals( getButtonTypeStr( (JButton) c ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isToolBarButton( Component c ) {
|
static boolean isToolBarButton( Component c ) {
|
||||||
return c.getParent() instanceof JToolBar ||
|
return c.getParent() instanceof JToolBar ||
|
||||||
(c instanceof AbstractButton && clientPropertyEquals( (AbstractButton) c, BUTTON_TYPE, BUTTON_TYPE_TOOLBAR_BUTTON ));
|
(c instanceof AbstractButton && BUTTON_TYPE_TOOLBAR_BUTTON.equals( getButtonTypeStr( (AbstractButton) c ) ));
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isBorderlessButton( Component c ) {
|
static boolean isBorderlessButton( Component c ) {
|
||||||
return c instanceof AbstractButton && clientPropertyEquals( (AbstractButton) c, BUTTON_TYPE, BUTTON_TYPE_BORDERLESS );
|
return c instanceof AbstractButton && BUTTON_TYPE_BORDERLESS.equals( getButtonTypeStr( (AbstractButton) c ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getButtonTypeStr( AbstractButton c ) {
|
||||||
|
// get from client property
|
||||||
|
Object value = c.getClientProperty( BUTTON_TYPE );
|
||||||
|
if( value instanceof String )
|
||||||
|
return (String) value;
|
||||||
|
|
||||||
|
// get from styling property
|
||||||
|
ButtonUI ui = c.getUI();
|
||||||
|
return (ui instanceof FlatButtonUI) ? ((FlatButtonUI)ui).buttonType : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -318,28 +464,46 @@ public class FlatButtonUI
|
|||||||
try {
|
try {
|
||||||
FlatUIUtils.setRenderingHints( g2 );
|
FlatUIUtils.setRenderingHints( g2 );
|
||||||
|
|
||||||
|
boolean def = isDefaultButton( c );
|
||||||
boolean isToolBarButton = isToolBarButton( c );
|
boolean isToolBarButton = isToolBarButton( c );
|
||||||
float focusWidth = isToolBarButton ? 0 : FlatUIUtils.getBorderFocusWidth( c );
|
float focusWidth = isToolBarButton ? 0 : FlatUIUtils.getBorderFocusWidth( c );
|
||||||
float arc = FlatUIUtils.getBorderArc( c );
|
float arc = FlatUIUtils.getBorderArc( c );
|
||||||
|
float textFieldArc = 0;
|
||||||
|
|
||||||
boolean def = isDefaultButton( c );
|
// if toolbar button is in leading/trailing component of a text field,
|
||||||
|
// increase toolbar button arc to match text field arc (if necessary)
|
||||||
|
if( isToolBarButton &&
|
||||||
|
FlatClientProperties.clientProperty( c, STYLE_CLASS, "", String.class ).contains( "inTextField" ) )
|
||||||
|
{
|
||||||
|
JTextField textField = (JTextField) SwingUtilities.getAncestorOfClass( JTextField.class, c );
|
||||||
|
if( textField != null )
|
||||||
|
textFieldArc = FlatUIUtils.getBorderArc( textField );
|
||||||
|
}
|
||||||
|
|
||||||
int x = 0;
|
int x = 0;
|
||||||
int y = 0;
|
int y = 0;
|
||||||
int width = c.getWidth();
|
int width = c.getWidth();
|
||||||
int height = c.getHeight();
|
int height = c.getHeight();
|
||||||
|
|
||||||
if( isToolBarButton ) {
|
if( isToolBarButton && c.getBorder() instanceof FlatButtonBorder ) {
|
||||||
Insets spacing = UIScale.scale( toolbarSpacingInsets );
|
Insets spacing = UIScale.scale( ((FlatButtonBorder)c.getBorder()).toolbarSpacingInsets );
|
||||||
x += spacing.left;
|
x += spacing.left;
|
||||||
y += spacing.top;
|
y += spacing.top;
|
||||||
width -= spacing.left + spacing.right;
|
width -= spacing.left + spacing.right;
|
||||||
height -= spacing.top + spacing.bottom;
|
height -= spacing.top + spacing.bottom;
|
||||||
|
|
||||||
|
// reduce text field arc
|
||||||
|
textFieldArc -= spacing.top + spacing.bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// increase toolbar button arc to match text field arc (if necessary)
|
||||||
|
if( arc < textFieldArc )
|
||||||
|
arc = textFieldArc;
|
||||||
|
|
||||||
// paint shadow
|
// paint shadow
|
||||||
Color shadowColor = def ? defaultShadowColor : this.shadowColor;
|
Color shadowColor = def ? defaultShadowColor : this.shadowColor;
|
||||||
if( shadowColor != null && shadowWidth > 0 && focusWidth > 0 && c.isEnabled() &&
|
if( paintShadow &&
|
||||||
|
shadowColor != null && shadowWidth > 0 && focusWidth > 0 && c.isEnabled() &&
|
||||||
!isToolBarButton && !isBorderlessButton( c ) &&
|
!isToolBarButton && !isBorderlessButton( c ) &&
|
||||||
!(isFocusPainted( c ) && FlatUIUtils.isPermanentFocusOwner( c )) )
|
!(isFocusPainted( c ) && FlatUIUtils.isPermanentFocusOwner( c )) )
|
||||||
{
|
{
|
||||||
@@ -367,6 +531,23 @@ public class FlatButtonUI
|
|||||||
super.paint( FlatLabelUI.createGraphicsHTMLTextYCorrection( g, c ), c );
|
super.paint( FlatLabelUI.createGraphicsHTMLTextYCorrection( g, c ), c );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintIcon( Graphics g, JComponent c, Rectangle iconRect ) {
|
||||||
|
// correct icon location when using bold font for default button
|
||||||
|
int xOffset = defaultBoldPlainWidthDiff( c ) / 2;
|
||||||
|
if( xOffset > 0 ) {
|
||||||
|
boolean ltr = c.getComponentOrientation().isLeftToRight();
|
||||||
|
switch( ((AbstractButton)c).getHorizontalTextPosition() ) {
|
||||||
|
case SwingConstants.RIGHT: iconRect.x -= xOffset; break;
|
||||||
|
case SwingConstants.LEFT: iconRect.x += xOffset; break;
|
||||||
|
case SwingConstants.TRAILING: iconRect.x -= ltr ? xOffset : -xOffset; break;
|
||||||
|
case SwingConstants.LEADING: iconRect.x += ltr ? xOffset : -xOffset; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.paintIcon( g, c, iconRect );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text ) {
|
protected void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text ) {
|
||||||
if( isHelpButton( b ) )
|
if( isHelpButton( b ) )
|
||||||
@@ -387,6 +568,8 @@ public class FlatButtonUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text, Color foreground ) {
|
public static void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text, Color foreground ) {
|
||||||
|
if(foreground == null)
|
||||||
|
foreground=Color.red;
|
||||||
FontMetrics fm = b.getFontMetrics( b.getFont() );
|
FontMetrics fm = b.getFontMetrics( b.getFont() );
|
||||||
int mnemonicIndex = FlatLaf.isShowMnemonics() ? b.getDisplayedMnemonicIndex() : -1;
|
int mnemonicIndex = FlatLaf.isShowMnemonics() ? b.getDisplayedMnemonicIndex() : -1;
|
||||||
|
|
||||||
@@ -400,11 +583,14 @@ public class FlatButtonUI
|
|||||||
|
|
||||||
// selected state
|
// selected state
|
||||||
if( ((AbstractButton)c).isSelected() ) {
|
if( ((AbstractButton)c).isSelected() ) {
|
||||||
// in toolbar use same background colors for disabled and enabled because
|
// in toolbar, if toolbarDisabledSelectedBackground is null,
|
||||||
|
// use same background colors for disabled and enabled because
|
||||||
// we assume that toolbar icon is shown disabled
|
// we assume that toolbar icon is shown disabled
|
||||||
return buttonStateColor( c,
|
return buttonStateColor( c,
|
||||||
toolBarButton ? toolbarSelectedBackground : selectedBackground,
|
toolBarButton ? toolbarSelectedBackground : selectedBackground,
|
||||||
toolBarButton ? toolbarSelectedBackground : disabledSelectedBackground,
|
toolBarButton
|
||||||
|
? (toolbarDisabledSelectedBackground != null ? toolbarDisabledSelectedBackground : toolbarSelectedBackground)
|
||||||
|
: disabledSelectedBackground,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
toolBarButton ? toolbarPressedBackground : pressedBackground );
|
toolBarButton ? toolbarPressedBackground : pressedBackground );
|
||||||
@@ -446,6 +632,9 @@ public class FlatButtonUI
|
|||||||
public static Color buttonStateColor( Component c, Color enabledColor, Color disabledColor,
|
public static Color buttonStateColor( Component c, Color enabledColor, Color disabledColor,
|
||||||
Color focusedColor, Color hoverColor, Color pressedColor )
|
Color focusedColor, Color hoverColor, Color pressedColor )
|
||||||
{
|
{
|
||||||
|
if( c == null )
|
||||||
|
return enabledColor;
|
||||||
|
|
||||||
if( !c.isEnabled() )
|
if( !c.isEnabled() )
|
||||||
return disabledColor;
|
return disabledColor;
|
||||||
|
|
||||||
@@ -466,18 +655,48 @@ public class FlatButtonUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Color getForeground( JComponent c ) {
|
protected Color getForeground( JComponent c ) {
|
||||||
if( !c.isEnabled() )
|
boolean toolBarButton = isToolBarButton( c ) || isBorderlessButton( c );
|
||||||
return disabledText;
|
|
||||||
|
|
||||||
if( ((AbstractButton)c).isSelected() && !(isToolBarButton( c ) || isBorderlessButton( c )) )
|
// selected state
|
||||||
return selectedForeground;
|
if( ((AbstractButton)c).isSelected() ) {
|
||||||
|
return buttonStateColor( c,
|
||||||
|
toolBarButton
|
||||||
|
? (toolbarSelectedForeground != null ? toolbarSelectedForeground : c.getForeground())
|
||||||
|
: selectedForeground,
|
||||||
|
toolBarButton
|
||||||
|
? (toolbarDisabledSelectedForeground != null ? toolbarDisabledSelectedForeground : disabledText)
|
||||||
|
: (disabledSelectedForeground != null ? disabledSelectedForeground : disabledText),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
toolBarButton ? toolbarPressedForeground : pressedForeground );
|
||||||
|
}
|
||||||
|
|
||||||
|
// toolbar button
|
||||||
|
if( toolBarButton ) {
|
||||||
|
return buttonStateColor( c,
|
||||||
|
c.getForeground(),
|
||||||
|
disabledText,
|
||||||
|
null,
|
||||||
|
toolbarHoverForeground,
|
||||||
|
toolbarPressedForeground );
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean def = isDefaultButton( c );
|
||||||
|
return buttonStateColor( c,
|
||||||
|
getForegroundBase( c, def ),
|
||||||
|
disabledText,
|
||||||
|
isCustomForeground( c.getForeground() ) ? null : (def ? defaultFocusedForeground : focusedForeground),
|
||||||
|
def ? defaultHoverForeground : hoverForeground,
|
||||||
|
def ? defaultPressedForeground : pressedForeground );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.3 */
|
||||||
|
protected Color getForegroundBase( JComponent c, boolean def ) {
|
||||||
// use component foreground if explicitly set
|
// use component foreground if explicitly set
|
||||||
Color fg = c.getForeground();
|
Color fg = c.getForeground();
|
||||||
if( isCustomForeground( fg ) )
|
if( isCustomForeground( fg ) )
|
||||||
return fg;
|
return fg;
|
||||||
|
|
||||||
boolean def = isDefaultButton( c );
|
|
||||||
return def ? defaultForeground : fg;
|
return def ? defaultForeground : fg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -494,9 +713,12 @@ public class FlatButtonUI
|
|||||||
if( prefSize == null )
|
if( prefSize == null )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
// increase width when using bold font for default button
|
||||||
|
prefSize.width += defaultBoldPlainWidthDiff( c );
|
||||||
|
|
||||||
// make square or apply minimum width/height
|
// make square or apply minimum width/height
|
||||||
boolean isIconOnlyOrSingleCharacter = isIconOnlyOrSingleCharacterButton( c );
|
boolean isIconOnlyOrSingleCharacter = isIconOnlyOrSingleCharacterButton( c );
|
||||||
if( clientPropertyBoolean( c, SQUARE_SIZE, false ) ) {
|
if( clientPropertyBoolean( c, SQUARE_SIZE, squareSize ) ) {
|
||||||
// make button square (increase width or height so that they are equal)
|
// make button square (increase width or height so that they are equal)
|
||||||
prefSize.width = prefSize.height = Math.max( prefSize.width, prefSize.height );
|
prefSize.width = prefSize.height = Math.max( prefSize.width, prefSize.height );
|
||||||
} else if( isIconOnlyOrSingleCharacter && ((AbstractButton)c).getIcon() == null ) {
|
} else if( isIconOnlyOrSingleCharacter && ((AbstractButton)c).getIcon() == null ) {
|
||||||
@@ -508,12 +730,29 @@ public class FlatButtonUI
|
|||||||
// apply minimum width/height
|
// apply minimum width/height
|
||||||
int fw = Math.round( FlatUIUtils.getBorderFocusWidth( c ) * 2 );
|
int fw = Math.round( FlatUIUtils.getBorderFocusWidth( c ) * 2 );
|
||||||
prefSize.width = Math.max( prefSize.width, scale( FlatUIUtils.minimumWidth( c, minimumWidth ) ) + fw );
|
prefSize.width = Math.max( prefSize.width, scale( FlatUIUtils.minimumWidth( c, minimumWidth ) ) + fw );
|
||||||
prefSize.height = Math.max( prefSize.height, scale( FlatUIUtils.minimumHeight( c, 0 ) ) + fw );
|
prefSize.height = Math.max( prefSize.height, scale( FlatUIUtils.minimumHeight( c, minimumHeight ) ) + fw );
|
||||||
}
|
}
|
||||||
|
|
||||||
return prefSize;
|
return prefSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int defaultBoldPlainWidthDiff( JComponent c ) {
|
||||||
|
if( defaultBoldText && isDefaultButton( c ) && c.getFont() instanceof UIResource ) {
|
||||||
|
String text = ((AbstractButton)c).getText();
|
||||||
|
if( text == null || text.isEmpty() )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Font font = c.getFont();
|
||||||
|
Font boldFont = font.deriveFont( Font.BOLD );
|
||||||
|
int boldWidth = c.getFontMetrics( boldFont ).stringWidth( text );
|
||||||
|
int plainWidth = c.getFontMetrics( font ).stringWidth( text );
|
||||||
|
if( boldWidth > plainWidth )
|
||||||
|
return boldWidth - plainWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean hasDefaultMargins( JComponent c ) {
|
private boolean hasDefaultMargins( JComponent c ) {
|
||||||
Insets margin = ((AbstractButton)c).getMargin();
|
Insets margin = ((AbstractButton)c).getMargin();
|
||||||
return margin instanceof UIResource && Objects.equals( margin, defaultMargin );
|
return margin instanceof UIResource && Objects.equals( margin, defaultMargin );
|
||||||
|
|||||||
@@ -18,19 +18,27 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import static com.formdev.flatlaf.FlatClientProperties.*;
|
import static com.formdev.flatlaf.FlatClientProperties.*;
|
||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
import java.awt.Insets;
|
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.FocusEvent;
|
import java.awt.event.FocusEvent;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
|
import javax.swing.Action;
|
||||||
|
import javax.swing.ActionMap;
|
||||||
import javax.swing.JFormattedTextField;
|
import javax.swing.JFormattedTextField;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.text.BadLocationException;
|
import javax.swing.text.BadLocationException;
|
||||||
import javax.swing.text.DefaultCaret;
|
import javax.swing.text.DefaultCaret;
|
||||||
|
import javax.swing.text.DefaultEditorKit;
|
||||||
import javax.swing.text.Document;
|
import javax.swing.text.Document;
|
||||||
import javax.swing.text.JTextComponent;
|
import javax.swing.text.JTextComponent;
|
||||||
|
import javax.swing.text.Utilities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Caret that can select all text on focus gained.
|
* Caret that can select all text on focus gained.
|
||||||
|
* Also fixes Swing's double-click-and-drag behavior so that dragging after
|
||||||
|
* a double-click extends selection by whole words.
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
@@ -38,12 +46,19 @@ public class FlatCaret
|
|||||||
extends DefaultCaret
|
extends DefaultCaret
|
||||||
implements UIResource
|
implements UIResource
|
||||||
{
|
{
|
||||||
|
private static final String KEY_CARET_INFO = "FlatLaf.internal.caretInfo";
|
||||||
|
|
||||||
private final String selectAllOnFocusPolicy;
|
private final String selectAllOnFocusPolicy;
|
||||||
private final boolean selectAllOnMouseClick;
|
private final boolean selectAllOnMouseClick;
|
||||||
|
|
||||||
|
private boolean inInstall;
|
||||||
private boolean wasFocused;
|
private boolean wasFocused;
|
||||||
private boolean wasTemporaryLost;
|
private boolean wasTemporaryLost;
|
||||||
private boolean isMousePressed;
|
private boolean isMousePressed;
|
||||||
|
private boolean isWordSelection;
|
||||||
|
private boolean isLineSelection;
|
||||||
|
private int dragSelectionStart;
|
||||||
|
private int dragSelectionEnd;
|
||||||
|
|
||||||
public FlatCaret( String selectAllOnFocusPolicy, boolean selectAllOnMouseClick ) {
|
public FlatCaret( String selectAllOnFocusPolicy, boolean selectAllOnMouseClick ) {
|
||||||
this.selectAllOnFocusPolicy = selectAllOnFocusPolicy;
|
this.selectAllOnFocusPolicy = selectAllOnFocusPolicy;
|
||||||
@@ -52,34 +67,82 @@ public class FlatCaret
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void install( JTextComponent c ) {
|
public void install( JTextComponent c ) {
|
||||||
super.install( c );
|
// get caret info if switched theme
|
||||||
|
long[] ci = (long[]) c.getClientProperty( KEY_CARET_INFO );
|
||||||
|
if( ci != null ) {
|
||||||
|
c.putClientProperty( KEY_CARET_INFO, null );
|
||||||
|
|
||||||
// the dot and mark are lost when switching LaF
|
// if caret info is too old assume that switched from FlatLaf
|
||||||
// --> move dot to end of text so that all text may be selected when it gains focus
|
// to another Laf and back to FlatLaf
|
||||||
Document doc = c.getDocument();
|
if( System.currentTimeMillis() - 500 > ci[3] )
|
||||||
if( doc != null && getDot() == 0 && getMark() == 0 ) {
|
ci = null;
|
||||||
int length = doc.getLength();
|
|
||||||
if( length > 0 )
|
|
||||||
setDot( length );
|
|
||||||
}
|
}
|
||||||
|
if( ci != null ) {
|
||||||
|
// when switching theme, it is necessary to set blink rate before
|
||||||
|
// invoking super.install() otherwise the caret does not blink
|
||||||
|
setBlinkRate( (int) ci[2] );
|
||||||
|
}
|
||||||
|
|
||||||
|
inInstall = true;
|
||||||
|
try {
|
||||||
|
super.install( c );
|
||||||
|
} finally {
|
||||||
|
inInstall = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ci != null ) {
|
||||||
|
// restore selection
|
||||||
|
select( (int) ci[1], (int) ci[0] );
|
||||||
|
|
||||||
|
// if text component is focused, then caret and selection are visible,
|
||||||
|
// but when switching theme, the component does not yet have
|
||||||
|
// a highlighter and the selection is not painted
|
||||||
|
// --> make selection temporary invisible later, then the caret
|
||||||
|
// adds selection highlights to the text component highlighter
|
||||||
|
if( isSelectionVisible() ) {
|
||||||
|
EventQueue.invokeLater( () -> {
|
||||||
|
if( getComponent() == null )
|
||||||
|
return; // was deinstalled
|
||||||
|
|
||||||
|
if( isSelectionVisible() ) {
|
||||||
|
setSelectionVisible( false );
|
||||||
|
setSelectionVisible( true );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deinstall( JTextComponent c ) {
|
||||||
|
// remember dot and mark (the selection) when switching theme
|
||||||
|
c.putClientProperty( KEY_CARET_INFO, new long[] {
|
||||||
|
getDot(),
|
||||||
|
getMark(),
|
||||||
|
getBlinkRate(),
|
||||||
|
System.currentTimeMillis(),
|
||||||
|
} );
|
||||||
|
|
||||||
|
super.deinstall( c );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void adjustVisibility( Rectangle nloc ) {
|
protected void adjustVisibility( Rectangle nloc ) {
|
||||||
JTextComponent c = getComponent();
|
JTextComponent c = getComponent();
|
||||||
if( c != null && c.getUI() instanceof FlatTextFieldUI ) {
|
if( c != null && c.getUI() instanceof FlatTextFieldUI ) {
|
||||||
Insets padding = ((FlatTextFieldUI)c.getUI()).getPadding();
|
// need to fix x location because JTextField.scrollRectToVisible() uses insets.left
|
||||||
if( padding != null ) {
|
// (as BasicTextUI.getVisibleEditorRect() does),
|
||||||
nloc.x -= padding.left;
|
// but FlatTextFieldUI.getVisibleEditorRect() may add some padding
|
||||||
nloc.y -= padding.top;
|
Rectangle r = ((FlatTextFieldUI)c.getUI()).getVisibleEditorRect();
|
||||||
}
|
if( r != null )
|
||||||
|
nloc.x -= r.x - c.getInsets().left;
|
||||||
}
|
}
|
||||||
super.adjustVisibility( nloc );
|
super.adjustVisibility( nloc );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void focusGained( FocusEvent e ) {
|
public void focusGained( FocusEvent e ) {
|
||||||
if( !wasTemporaryLost && (!isMousePressed || selectAllOnMouseClick) )
|
if( !inInstall && !wasTemporaryLost && (!isMousePressed || selectAllOnMouseClick) )
|
||||||
selectAllOnFocusGained();
|
selectAllOnFocusGained();
|
||||||
wasTemporaryLost = false;
|
wasTemporaryLost = false;
|
||||||
wasFocused = true;
|
wasFocused = true;
|
||||||
@@ -97,25 +160,83 @@ public class FlatCaret
|
|||||||
public void mousePressed( MouseEvent e ) {
|
public void mousePressed( MouseEvent e ) {
|
||||||
isMousePressed = true;
|
isMousePressed = true;
|
||||||
super.mousePressed( e );
|
super.mousePressed( e );
|
||||||
|
|
||||||
|
JTextComponent c = getComponent();
|
||||||
|
|
||||||
|
// left double-click starts word selection
|
||||||
|
isWordSelection = e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton( e ) && !e.isConsumed();
|
||||||
|
|
||||||
|
// left triple-click starts line selection
|
||||||
|
isLineSelection = e.getClickCount() == 3 && SwingUtilities.isLeftMouseButton( e ) && (!e.isConsumed() || c.getDragEnabled());
|
||||||
|
|
||||||
|
// select line
|
||||||
|
// (this is also done in DefaultCaret.mouseClicked(), but this event is
|
||||||
|
// sent when the mouse is released, which is too late for triple-click-and-drag)
|
||||||
|
if( isLineSelection ) {
|
||||||
|
ActionMap actionMap = c.getActionMap();
|
||||||
|
Action selectLineAction = (actionMap != null)
|
||||||
|
? actionMap.get( DefaultEditorKit.selectLineAction )
|
||||||
|
: null;
|
||||||
|
if( selectLineAction != null ) {
|
||||||
|
selectLineAction.actionPerformed( new ActionEvent( c,
|
||||||
|
ActionEvent.ACTION_PERFORMED, null, e.getWhen(), e.getModifiers() ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remember selection where word/line selection starts to keep it always selected while dragging
|
||||||
|
if( isWordSelection || isLineSelection ) {
|
||||||
|
int mark = getMark();
|
||||||
|
int dot = getDot();
|
||||||
|
dragSelectionStart = Math.min( dot, mark );
|
||||||
|
dragSelectionEnd = Math.max( dot, mark );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseReleased( MouseEvent e ) {
|
public void mouseReleased( MouseEvent e ) {
|
||||||
isMousePressed = false;
|
isMousePressed = false;
|
||||||
|
isWordSelection = false;
|
||||||
|
isLineSelection = false;
|
||||||
super.mouseReleased( e );
|
super.mouseReleased( e );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseDragged( MouseEvent e ) {
|
||||||
|
if( (isWordSelection || isLineSelection) &&
|
||||||
|
!e.isConsumed() && SwingUtilities.isLeftMouseButton( e ) )
|
||||||
|
{
|
||||||
|
// fix Swing's double/triple-click-and-drag behavior so that dragging after
|
||||||
|
// a double/triple-click extends selection by whole words/lines
|
||||||
|
JTextComponent c = getComponent();
|
||||||
|
int pos = c.viewToModel( e.getPoint() );
|
||||||
|
if( pos < 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if( pos > dragSelectionEnd )
|
||||||
|
select( dragSelectionStart, isWordSelection ? Utilities.getWordEnd( c, pos ) : Utilities.getRowEnd( c, pos ) );
|
||||||
|
else if( pos < dragSelectionStart )
|
||||||
|
select( dragSelectionEnd, isWordSelection ? Utilities.getWordStart( c, pos ) : Utilities.getRowStart( c, pos ) );
|
||||||
|
else
|
||||||
|
select( dragSelectionStart, dragSelectionEnd );
|
||||||
|
} catch( BadLocationException ex ) {
|
||||||
|
UIManager.getLookAndFeel().provideErrorFeedback( c );
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
super.mouseDragged( e );
|
||||||
|
}
|
||||||
|
|
||||||
protected void selectAllOnFocusGained() {
|
protected void selectAllOnFocusGained() {
|
||||||
JTextComponent c = getComponent();
|
JTextComponent c = getComponent();
|
||||||
Document doc = c.getDocument();
|
Document doc = c.getDocument();
|
||||||
if( doc == null || !c.isEnabled() || !c.isEditable() )
|
if( doc == null || !c.isEnabled() || !c.isEditable() || FlatUIUtils.isCellEditor( c ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Object selectAllOnFocusPolicy = c.getClientProperty( SELECT_ALL_ON_FOCUS_POLICY );
|
Object selectAllOnFocusPolicy = c.getClientProperty( SELECT_ALL_ON_FOCUS_POLICY );
|
||||||
if( selectAllOnFocusPolicy == null )
|
if( selectAllOnFocusPolicy == null )
|
||||||
selectAllOnFocusPolicy = this.selectAllOnFocusPolicy;
|
selectAllOnFocusPolicy = this.selectAllOnFocusPolicy;
|
||||||
|
|
||||||
if( SELECT_ALL_ON_FOCUS_POLICY_NEVER.equals( selectAllOnFocusPolicy ) )
|
if( selectAllOnFocusPolicy == null || SELECT_ALL_ON_FOCUS_POLICY_NEVER.equals( selectAllOnFocusPolicy ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if( !SELECT_ALL_ON_FOCUS_POLICY_ALWAYS.equals( selectAllOnFocusPolicy ) ) {
|
if( !SELECT_ALL_ON_FOCUS_POLICY_ALWAYS.equals( selectAllOnFocusPolicy ) ) {
|
||||||
@@ -135,18 +256,24 @@ public class FlatCaret
|
|||||||
// select all
|
// select all
|
||||||
if( c instanceof JFormattedTextField ) {
|
if( c instanceof JFormattedTextField ) {
|
||||||
EventQueue.invokeLater( () -> {
|
EventQueue.invokeLater( () -> {
|
||||||
setDot( 0 );
|
if( getComponent() == null )
|
||||||
moveDot( doc.getLength() );
|
return; // was deinstalled
|
||||||
|
|
||||||
|
select( 0, doc.getLength() );
|
||||||
} );
|
} );
|
||||||
} else {
|
} else {
|
||||||
setDot( 0 );
|
select( 0, doc.getLength() );
|
||||||
moveDot( doc.getLength() );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void select( int mark, int dot ) {
|
||||||
* @since 1.4
|
if( mark != getMark() )
|
||||||
*/
|
setDot( mark );
|
||||||
|
if( dot != getDot() )
|
||||||
|
moveDot( dot );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 1.4 */
|
||||||
public void scrollCaretToVisible() {
|
public void scrollCaretToVisible() {
|
||||||
JTextComponent c = getComponent();
|
JTextComponent c = getComponent();
|
||||||
if( c == null || c.getUI() == null )
|
if( c == null || c.getUI() == null )
|
||||||
|
|||||||
@@ -16,13 +16,19 @@
|
|||||||
|
|
||||||
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.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
|
import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JCheckBoxMenuItem}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JCheckBoxMenuItem}.
|
||||||
@@ -54,13 +60,22 @@ import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
|
|||||||
*/
|
*/
|
||||||
public class FlatCheckBoxMenuItemUI
|
public class FlatCheckBoxMenuItemUI
|
||||||
extends BasicCheckBoxMenuItemUI
|
extends BasicCheckBoxMenuItemUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
private FlatMenuItemRenderer renderer;
|
private FlatMenuItemRenderer renderer;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatCheckBoxMenuItemUI();
|
return new FlatCheckBoxMenuItemUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
@@ -74,13 +89,61 @@ public class FlatCheckBoxMenuItemUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
FlatMenuItemRenderer.clearClientProperties( menuItem.getParent() );
|
||||||
renderer = null;
|
renderer = null;
|
||||||
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected FlatMenuItemRenderer createRenderer() {
|
protected FlatMenuItemRenderer createRenderer() {
|
||||||
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
||||||
|
return FlatStylingSupport.createPropertyChangeListener( c, this::installStyle, super.createPropertyChangeListener( c ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle() {
|
||||||
|
try {
|
||||||
|
applyStyle( FlatStylingSupport.getResolvedStyle( menuItem, "CheckBoxMenuItem" ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( Object style ) {
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
try {
|
||||||
|
return renderer.applyStyleProperty( key, value );
|
||||||
|
} catch ( UnknownStyleException ex ) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
Object oldValue;
|
||||||
|
switch( key ) {
|
||||||
|
// BasicMenuItemUI
|
||||||
|
case "selectionBackground": oldValue = selectionBackground; selectionBackground = (Color) value; return oldValue;
|
||||||
|
case "selectionForeground": oldValue = selectionForeground; selectionForeground = (Color) value; return oldValue;
|
||||||
|
case "disabledForeground": oldValue = disabledForeground; disabledForeground = (Color) value; return oldValue;
|
||||||
|
case "acceleratorForeground": oldValue = acceleratorForeground; acceleratorForeground = (Color) value; return oldValue;
|
||||||
|
case "acceleratorSelectionForeground": oldValue = acceleratorSelectionForeground; acceleratorSelectionForeground = (Color) value; return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, menuItem, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return FlatMenuItemUI.getStyleableInfos( renderer );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
|
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
|
||||||
return renderer.getPreferredMenuItemSize();
|
return renderer.getPreferredMenuItemSize();
|
||||||
|
|||||||
@@ -43,11 +43,24 @@ public class FlatCheckBoxUI
|
|||||||
extends FlatRadioButtonUI
|
extends FlatRadioButtonUI
|
||||||
{
|
{
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return FlatUIUtils.createSharedUI( FlatCheckBoxUI.class, FlatCheckBoxUI::new );
|
return FlatUIUtils.canUseSharedUI( c )
|
||||||
|
? FlatUIUtils.createSharedUI( FlatCheckBoxUI.class, () -> new FlatCheckBoxUI( true ) )
|
||||||
|
: new FlatCheckBoxUI( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected FlatCheckBoxUI( boolean shared ) {
|
||||||
|
super( shared );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPropertyPrefix() {
|
public String getPropertyPrefix() {
|
||||||
return "CheckBox.";
|
return "CheckBox.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
String getStyleType() {
|
||||||
|
return "CheckBox";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import static com.formdev.flatlaf.FlatClientProperties.*;
|
||||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
import static com.formdev.flatlaf.util.UIScale.scale;
|
||||||
import static com.formdev.flatlaf.util.UIScale.unscale;
|
import static com.formdev.flatlaf.util.UIScale.unscale;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
@@ -41,6 +42,8 @@ import java.awt.event.MouseEvent;
|
|||||||
import java.awt.event.MouseListener;
|
import java.awt.event.MouseListener;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
import javax.swing.CellRendererPane;
|
import javax.swing.CellRendererPane;
|
||||||
@@ -66,7 +69,9 @@ import javax.swing.plaf.basic.BasicComboBoxUI;
|
|||||||
import javax.swing.plaf.basic.BasicComboPopup;
|
import javax.swing.plaf.basic.BasicComboPopup;
|
||||||
import javax.swing.plaf.basic.ComboPopup;
|
import javax.swing.plaf.basic.ComboPopup;
|
||||||
import javax.swing.text.JTextComponent;
|
import javax.swing.text.JTextComponent;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,6 +86,11 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
* @uiDefault ComboBox.padding Insets
|
* @uiDefault ComboBox.padding Insets
|
||||||
* @uiDefault ComboBox.squareButton boolean default is true
|
* @uiDefault ComboBox.squareButton boolean default is true
|
||||||
*
|
*
|
||||||
|
* <!-- BasicComboPopup -->
|
||||||
|
*
|
||||||
|
* @uiDefault ComboBox.selectionBackground Color
|
||||||
|
* @uiDefault ComboBox.selectionForeground Color
|
||||||
|
*
|
||||||
* <!-- FlatComboBoxUI -->
|
* <!-- FlatComboBoxUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault ComboBox.minimumWidth int
|
* @uiDefault ComboBox.minimumWidth int
|
||||||
@@ -89,15 +99,16 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
* @uiDefault ComboBox.buttonStyle String auto (default), button or none
|
* @uiDefault ComboBox.buttonStyle String auto (default), button or none
|
||||||
* @uiDefault Component.arrowType String chevron (default) or triangle
|
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
* @uiDefault Component.isIntelliJTheme boolean
|
||||||
* @uiDefault Component.borderColor Color
|
|
||||||
* @uiDefault Component.disabledBorderColor Color
|
|
||||||
* @uiDefault ComboBox.editableBackground Color optional; defaults to ComboBox.background
|
* @uiDefault ComboBox.editableBackground Color optional; defaults to ComboBox.background
|
||||||
* @uiDefault ComboBox.focusedBackground Color optional
|
* @uiDefault ComboBox.focusedBackground Color optional
|
||||||
* @uiDefault ComboBox.disabledBackground Color
|
* @uiDefault ComboBox.disabledBackground Color
|
||||||
* @uiDefault ComboBox.disabledForeground Color
|
* @uiDefault ComboBox.disabledForeground Color
|
||||||
* @uiDefault ComboBox.buttonBackground Color
|
* @uiDefault ComboBox.buttonBackground Color optional
|
||||||
* @uiDefault ComboBox.buttonEditableBackground Color
|
* @uiDefault ComboBox.buttonEditableBackground Color optional
|
||||||
* @uiDefault ComboBox.buttonFocusedBackground Color optional; defaults to ComboBox.focusedBackground
|
* @uiDefault ComboBox.buttonFocusedBackground Color optional; defaults to ComboBox.focusedBackground
|
||||||
|
* @uiDefault ComboBox.buttonSeparatorWidth int or float optional; defaults to Component.borderWidth
|
||||||
|
* @uiDefault ComboBox.buttonSeparatorColor Color optional
|
||||||
|
* @uiDefault ComboBox.buttonDisabledSeparatorColor Color optional
|
||||||
* @uiDefault ComboBox.buttonArrowColor Color
|
* @uiDefault ComboBox.buttonArrowColor Color
|
||||||
* @uiDefault ComboBox.buttonDisabledArrowColor Color
|
* @uiDefault ComboBox.buttonDisabledArrowColor Color
|
||||||
* @uiDefault ComboBox.buttonHoverArrowColor Color
|
* @uiDefault ComboBox.buttonHoverArrowColor Color
|
||||||
@@ -108,29 +119,31 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
*/
|
*/
|
||||||
public class FlatComboBoxUI
|
public class FlatComboBoxUI
|
||||||
extends BasicComboBoxUI
|
extends BasicComboBoxUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
protected int minimumWidth;
|
@Styleable protected int minimumWidth;
|
||||||
protected int editorColumns;
|
@Styleable protected int editorColumns;
|
||||||
protected String buttonStyle;
|
@Styleable protected String buttonStyle;
|
||||||
protected String arrowType;
|
@Styleable protected String arrowType;
|
||||||
protected boolean isIntelliJTheme;
|
protected boolean isIntelliJTheme;
|
||||||
protected Color borderColor;
|
|
||||||
protected Color disabledBorderColor;
|
|
||||||
|
|
||||||
protected Color editableBackground;
|
@Styleable protected Color editableBackground;
|
||||||
protected Color focusedBackground;
|
@Styleable protected Color focusedBackground;
|
||||||
protected Color disabledBackground;
|
@Styleable protected Color disabledBackground;
|
||||||
protected Color disabledForeground;
|
@Styleable protected Color disabledForeground;
|
||||||
|
|
||||||
protected Color buttonBackground;
|
@Styleable protected Color buttonBackground;
|
||||||
protected Color buttonEditableBackground;
|
@Styleable protected Color buttonEditableBackground;
|
||||||
protected Color buttonFocusedBackground;
|
@Styleable protected Color buttonFocusedBackground;
|
||||||
protected Color buttonArrowColor;
|
/** @since 2 */ @Styleable protected float buttonSeparatorWidth;
|
||||||
protected Color buttonDisabledArrowColor;
|
/** @since 2 */ @Styleable protected Color buttonSeparatorColor;
|
||||||
protected Color buttonHoverArrowColor;
|
/** @since 2 */ @Styleable protected Color buttonDisabledSeparatorColor;
|
||||||
protected Color buttonPressedArrowColor;
|
@Styleable protected Color buttonArrowColor;
|
||||||
|
@Styleable protected Color buttonDisabledArrowColor;
|
||||||
|
@Styleable protected Color buttonHoverArrowColor;
|
||||||
|
@Styleable protected Color buttonPressedArrowColor;
|
||||||
|
|
||||||
protected Color popupBackground;
|
@Styleable protected Color popupBackground;
|
||||||
|
|
||||||
private MouseListener hoverListener;
|
private MouseListener hoverListener;
|
||||||
protected boolean hover;
|
protected boolean hover;
|
||||||
@@ -138,10 +151,20 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
private CellPaddingBorder paddingBorder;
|
private CellPaddingBorder paddingBorder;
|
||||||
|
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
private AtomicBoolean borderShared;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatComboBoxUI();
|
return new FlatComboBoxUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installListeners() {
|
protected void installListeners() {
|
||||||
super.installListeners();
|
super.installListeners();
|
||||||
@@ -198,8 +221,6 @@ public class FlatComboBoxUI
|
|||||||
buttonStyle = UIManager.getString( "ComboBox.buttonStyle" );
|
buttonStyle = UIManager.getString( "ComboBox.buttonStyle" );
|
||||||
arrowType = UIManager.getString( "Component.arrowType" );
|
arrowType = UIManager.getString( "Component.arrowType" );
|
||||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
||||||
borderColor = UIManager.getColor( "Component.borderColor" );
|
|
||||||
disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
|
|
||||||
|
|
||||||
editableBackground = UIManager.getColor( "ComboBox.editableBackground" );
|
editableBackground = UIManager.getColor( "ComboBox.editableBackground" );
|
||||||
focusedBackground = UIManager.getColor( "ComboBox.focusedBackground" );
|
focusedBackground = UIManager.getColor( "ComboBox.focusedBackground" );
|
||||||
@@ -209,6 +230,9 @@ public class FlatComboBoxUI
|
|||||||
buttonBackground = UIManager.getColor( "ComboBox.buttonBackground" );
|
buttonBackground = UIManager.getColor( "ComboBox.buttonBackground" );
|
||||||
buttonFocusedBackground = UIManager.getColor( "ComboBox.buttonFocusedBackground" );
|
buttonFocusedBackground = UIManager.getColor( "ComboBox.buttonFocusedBackground" );
|
||||||
buttonEditableBackground = UIManager.getColor( "ComboBox.buttonEditableBackground" );
|
buttonEditableBackground = UIManager.getColor( "ComboBox.buttonEditableBackground" );
|
||||||
|
buttonSeparatorWidth = FlatUIUtils.getUIFloat( "ComboBox.buttonSeparatorWidth", FlatUIUtils.getUIFloat( "Component.borderWidth", 1 ) );
|
||||||
|
buttonSeparatorColor = UIManager.getColor( "ComboBox.buttonSeparatorColor" );
|
||||||
|
buttonDisabledSeparatorColor = UIManager.getColor( "ComboBox.buttonDisabledSeparatorColor" );
|
||||||
buttonArrowColor = UIManager.getColor( "ComboBox.buttonArrowColor" );
|
buttonArrowColor = UIManager.getColor( "ComboBox.buttonArrowColor" );
|
||||||
buttonDisabledArrowColor = UIManager.getColor( "ComboBox.buttonDisabledArrowColor" );
|
buttonDisabledArrowColor = UIManager.getColor( "ComboBox.buttonDisabledArrowColor" );
|
||||||
buttonHoverArrowColor = UIManager.getColor( "ComboBox.buttonHoverArrowColor" );
|
buttonHoverArrowColor = UIManager.getColor( "ComboBox.buttonHoverArrowColor" );
|
||||||
@@ -230,9 +254,6 @@ public class FlatComboBoxUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
borderColor = null;
|
|
||||||
disabledBorderColor = null;
|
|
||||||
|
|
||||||
editableBackground = null;
|
editableBackground = null;
|
||||||
focusedBackground = null;
|
focusedBackground = null;
|
||||||
disabledBackground = null;
|
disabledBackground = null;
|
||||||
@@ -241,6 +262,8 @@ public class FlatComboBoxUI
|
|||||||
buttonBackground = null;
|
buttonBackground = null;
|
||||||
buttonEditableBackground = null;
|
buttonEditableBackground = null;
|
||||||
buttonFocusedBackground = null;
|
buttonFocusedBackground = null;
|
||||||
|
buttonSeparatorColor = null;
|
||||||
|
buttonDisabledSeparatorColor = null;
|
||||||
buttonArrowColor = null;
|
buttonArrowColor = null;
|
||||||
buttonDisabledArrowColor = null;
|
buttonDisabledArrowColor = null;
|
||||||
buttonHoverArrowColor = null;
|
buttonHoverArrowColor = null;
|
||||||
@@ -250,6 +273,9 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
paddingBorder.uninstall();
|
paddingBorder.uninstall();
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
borderShared = null;
|
||||||
|
|
||||||
MigLayoutVisualPadding.uninstall( comboBox );
|
MigLayoutVisualPadding.uninstall( comboBox );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,7 +286,10 @@ public class FlatComboBoxUI
|
|||||||
public void layoutContainer( Container parent ) {
|
public void layoutContainer( Container parent ) {
|
||||||
super.layoutContainer( parent );
|
super.layoutContainer( parent );
|
||||||
|
|
||||||
if( arrowButton != null ) {
|
// on macOS, a Swing combo box is used for AWT component java.awt.Choice
|
||||||
|
// and the font may be (temporary) null
|
||||||
|
|
||||||
|
if( arrowButton != null && comboBox.getFont() != null ) {
|
||||||
// limit button width to height of a raw combobox (without insets)
|
// limit button width to height of a raw combobox (without insets)
|
||||||
FontMetrics fm = comboBox.getFontMetrics( comboBox.getFont() );
|
FontMetrics fm = comboBox.getFontMetrics( comboBox.getFont() );
|
||||||
int maxButtonWidth = fm.getHeight() + scale( padding.top ) + scale( padding.bottom );
|
int maxButtonWidth = fm.getHeight() + scale( padding.top ) + scale( padding.bottom );
|
||||||
@@ -322,12 +351,30 @@ public class FlatComboBoxUI
|
|||||||
} else if( editor != null && source == comboBox && propertyName == "componentOrientation" ) {
|
} else if( editor != null && source == comboBox && propertyName == "componentOrientation" ) {
|
||||||
ComponentOrientation o = (ComponentOrientation) e.getNewValue();
|
ComponentOrientation o = (ComponentOrientation) e.getNewValue();
|
||||||
editor.applyComponentOrientation( o );
|
editor.applyComponentOrientation( o );
|
||||||
} else if( editor != null && FlatClientProperties.PLACEHOLDER_TEXT.equals( propertyName ) )
|
} else {
|
||||||
editor.repaint();
|
switch( propertyName ) {
|
||||||
else if( FlatClientProperties.COMPONENT_ROUND_RECT.equals( propertyName ) )
|
case PLACEHOLDER_TEXT:
|
||||||
comboBox.repaint();
|
if( editor != null )
|
||||||
else if( FlatClientProperties.MINIMUM_WIDTH.equals( propertyName ) )
|
editor.repaint();
|
||||||
comboBox.revalidate();
|
break;
|
||||||
|
|
||||||
|
case COMPONENT_ROUND_RECT:
|
||||||
|
case OUTLINE:
|
||||||
|
comboBox.repaint();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MINIMUM_WIDTH:
|
||||||
|
comboBox.revalidate();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STYLE:
|
||||||
|
case STYLE_CLASS:
|
||||||
|
installStyle();
|
||||||
|
comboBox.revalidate();
|
||||||
|
comboBox.repaint();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,7 +442,7 @@ public class FlatComboBoxUI
|
|||||||
unscale( Math.max( scale( padding.right ) - insets.right, 0 ) )
|
unscale( Math.max( scale( padding.right ) - insets.right, 0 ) )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
textField.putClientProperty( FlatClientProperties.TEXT_FIELD_PADDING, pad );
|
textField.putClientProperty( TEXT_FIELD_PADDING, pad );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateEditorColors() {
|
private void updateEditorColors() {
|
||||||
@@ -414,6 +461,58 @@ public class FlatComboBoxUI
|
|||||||
return new FlatComboBoxButton();
|
return new FlatComboBoxButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle() {
|
||||||
|
try {
|
||||||
|
applyStyle( FlatStylingSupport.getResolvedStyle( comboBox, "ComboBox" ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( Object style ) {
|
||||||
|
Insets oldPadding = padding;
|
||||||
|
int oldEditorColumns = editorColumns;
|
||||||
|
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||||
|
|
||||||
|
if( !padding.equals( oldPadding ) ) {
|
||||||
|
paddingBorder.padding = padding;
|
||||||
|
updateEditorPadding();
|
||||||
|
}
|
||||||
|
if( arrowButton instanceof FlatComboBoxButton )
|
||||||
|
((FlatComboBoxButton)arrowButton).updateStyle();
|
||||||
|
if( popup instanceof FlatComboPopup )
|
||||||
|
((FlatComboPopup)popup).updateStyle();
|
||||||
|
if( editorColumns != oldEditorColumns && editor instanceof JTextField )
|
||||||
|
((JTextField)editor).setColumns( editorColumns );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
// BasicComboBoxUI
|
||||||
|
if( key.equals( "padding" ) ) {
|
||||||
|
Object oldValue = padding;
|
||||||
|
padding = (Insets) value;
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( borderShared == null )
|
||||||
|
borderShared = new AtomicBoolean( true );
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, comboBox, borderShared );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
Map<String, Class<?>> infos = new FlatStylingSupport.StyleableInfosMap<>();
|
||||||
|
infos.put( "padding", Insets.class );
|
||||||
|
FlatStylingSupport.collectAnnotatedStyleableInfos( this, infos );
|
||||||
|
FlatStylingSupport.collectStyleableInfos( comboBox.getBorder(), infos );
|
||||||
|
return infos;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update( Graphics g, JComponent c ) {
|
public void update( Graphics g, JComponent c ) {
|
||||||
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
|
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
|
||||||
@@ -450,26 +549,32 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
// paint arrow button background
|
// paint arrow button background
|
||||||
if( enabled && !isCellRenderer ) {
|
if( enabled && !isCellRenderer ) {
|
||||||
g2.setColor( paintButton
|
Color buttonColor = paintButton
|
||||||
? buttonEditableBackground
|
? buttonEditableBackground
|
||||||
: (buttonFocusedBackground != null || focusedBackground != null) && isPermanentFocusOwner( comboBox )
|
: (buttonFocusedBackground != null || focusedBackground != null) && isPermanentFocusOwner( comboBox )
|
||||||
? (buttonFocusedBackground != null ? buttonFocusedBackground : focusedBackground)
|
? (buttonFocusedBackground != null ? buttonFocusedBackground : focusedBackground)
|
||||||
: buttonBackground );
|
: buttonBackground;
|
||||||
Shape oldClip = g2.getClip();
|
if( buttonColor != null ) {
|
||||||
if( isLeftToRight )
|
g2.setColor( buttonColor );
|
||||||
g2.clipRect( arrowX, 0, width - arrowX, height );
|
Shape oldClip = g2.getClip();
|
||||||
else
|
if( isLeftToRight )
|
||||||
g2.clipRect( 0, 0, arrowX + arrowWidth, height );
|
g2.clipRect( arrowX, 0, width - arrowX, height );
|
||||||
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
|
else
|
||||||
g2.setClip( oldClip );
|
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
|
// paint vertical line between value and arrow button
|
||||||
if( paintButton ) {
|
if( paintButton ) {
|
||||||
g2.setColor( enabled ? borderColor : disabledBorderColor );
|
Color separatorColor = enabled ? buttonSeparatorColor : buttonDisabledSeparatorColor;
|
||||||
float lw = scale( 1f );
|
if( separatorColor != null && buttonSeparatorWidth > 0 ) {
|
||||||
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
|
g2.setColor( separatorColor );
|
||||||
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2)) );
|
float lw = scale( buttonSeparatorWidth );
|
||||||
|
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
|
||||||
|
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2)) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -495,11 +600,20 @@ public class FlatComboBoxUI
|
|||||||
c.setBackground( getBackground( enabled ) );
|
c.setBackground( getBackground( enabled ) );
|
||||||
c.setForeground( getForeground( enabled ) );
|
c.setForeground( getForeground( enabled ) );
|
||||||
|
|
||||||
|
// make renderer component temporary non-opaque to avoid that renderer paints
|
||||||
|
// background outside of border if combobox uses larger arc for edges
|
||||||
|
// (e.g. FlatClientProperties.COMPONENT_ROUND_RECT is true)
|
||||||
|
if( c instanceof JComponent )
|
||||||
|
((JComponent)c).setOpaque( false );
|
||||||
|
|
||||||
boolean shouldValidate = (c instanceof JPanel);
|
boolean shouldValidate = (c instanceof JPanel);
|
||||||
|
|
||||||
paddingBorder.install( c );
|
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();
|
paddingBorder.uninstall();
|
||||||
|
|
||||||
|
if( c instanceof JComponent )
|
||||||
|
((JComponent)c).setOpaque( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -584,13 +698,14 @@ public class FlatComboBoxUI
|
|||||||
return parentParent != null && !comboBox.getBackground().equals( parentParent.getBackground() );
|
return parentParent != null && !comboBox.getBackground().equals( parentParent.getBackground() );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @since 1.3 */
|
||||||
* @since 1.3
|
|
||||||
*/
|
|
||||||
public static boolean isPermanentFocusOwner( JComboBox<?> comboBox ) {
|
public static boolean isPermanentFocusOwner( JComboBox<?> comboBox ) {
|
||||||
if( comboBox.isEditable() ) {
|
if( comboBox.isEditable() ) {
|
||||||
|
if( FlatUIUtils.isPermanentFocusOwner( comboBox ) )
|
||||||
|
return true;
|
||||||
|
|
||||||
Component editorComponent = comboBox.getEditor().getEditorComponent();
|
Component editorComponent = comboBox.getEditor().getEditorComponent();
|
||||||
return (editorComponent != null) ? FlatUIUtils.isPermanentFocusOwner( editorComponent ) : false;
|
return editorComponent != null && FlatUIUtils.isPermanentFocusOwner( editorComponent );
|
||||||
} else
|
} else
|
||||||
return FlatUIUtils.isPermanentFocusOwner( comboBox );
|
return FlatUIUtils.isPermanentFocusOwner( comboBox );
|
||||||
}
|
}
|
||||||
@@ -612,6 +727,11 @@ public class FlatComboBoxUI
|
|||||||
hoverForeground, hoverBackground, pressedForeground, pressedBackground );
|
hoverForeground, hoverBackground, pressedForeground, pressedBackground );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void updateStyle() {
|
||||||
|
updateStyle( arrowType, buttonArrowColor, buttonDisabledArrowColor,
|
||||||
|
buttonHoverArrowColor, null, buttonPressedArrowColor, null );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isHover() {
|
protected boolean isHover() {
|
||||||
return super.isHover() || (!comboBox.isEditable() ? hover : false);
|
return super.isHover() || (!comboBox.isEditable() ? hover : false);
|
||||||
@@ -698,9 +818,12 @@ public class FlatComboBoxUI
|
|||||||
// make opaque to avoid that background shines thru border (e.g. at 150% scaling)
|
// make opaque to avoid that background shines thru border (e.g. at 150% scaling)
|
||||||
setOpaque( true );
|
setOpaque( true );
|
||||||
|
|
||||||
|
// set popup border
|
||||||
|
// use non-UIResource to avoid that it is overwritten when making
|
||||||
|
// popup visible (see JPopupMenu.setInvoker()) in theme editor preview
|
||||||
Border border = UIManager.getBorder( "PopupMenu.border" );
|
Border border = UIManager.getBorder( "PopupMenu.border" );
|
||||||
if( border != null )
|
if( border != null )
|
||||||
setBorder( border );
|
setBorder( FlatUIUtils.nonUIResource( border ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -708,8 +831,17 @@ public class FlatComboBoxUI
|
|||||||
super.configureList();
|
super.configureList();
|
||||||
|
|
||||||
list.setCellRenderer( new PopupListCellRenderer() );
|
list.setCellRenderer( new PopupListCellRenderer() );
|
||||||
|
updateStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateStyle() {
|
||||||
if( popupBackground != null )
|
if( popupBackground != null )
|
||||||
list.setBackground( popupBackground );
|
list.setBackground( popupBackground );
|
||||||
|
|
||||||
|
// set popup background because it may shine thru when scaled (e.g. at 150%)
|
||||||
|
// use non-UIResource to avoid that it is overwritten when making
|
||||||
|
// popup visible (see JPopupMenu.setInvoker()) in theme editor preview
|
||||||
|
setBackground( FlatUIUtils.nonUIResource( list.getBackground() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -788,7 +920,7 @@ public class FlatComboBoxUI
|
|||||||
private static class CellPaddingBorder
|
private static class CellPaddingBorder
|
||||||
extends AbstractBorder
|
extends AbstractBorder
|
||||||
{
|
{
|
||||||
private final Insets padding;
|
private Insets padding;
|
||||||
private JComponent rendererComponent;
|
private JComponent rendererComponent;
|
||||||
private Border rendererBorder;
|
private Border rendererBorder;
|
||||||
|
|
||||||
@@ -796,7 +928,9 @@ public class FlatComboBoxUI
|
|||||||
this.padding = padding;
|
this.padding = padding;
|
||||||
}
|
}
|
||||||
|
|
||||||
void install( Component c ) {
|
// using synchronized to avoid problems with code that modifies combo box
|
||||||
|
// (model, selection, etc) not on AWT thread (which should be not done)
|
||||||
|
synchronized void install( Component c ) {
|
||||||
if( !(c instanceof JComponent) )
|
if( !(c instanceof JComponent) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -819,7 +953,7 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
// remember old border and replace it
|
// remember old border and replace it
|
||||||
rendererBorder = jc.getBorder();
|
rendererBorder = jc.getBorder();
|
||||||
rendererComponent.setBorder( this );
|
jc.setBorder( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -828,7 +962,7 @@ public class FlatComboBoxUI
|
|||||||
* there is no single place to uninstall it.
|
* there is no single place to uninstall it.
|
||||||
* This is the reason why this method is called from various places.
|
* This is the reason why this method is called from various places.
|
||||||
*/
|
*/
|
||||||
void uninstall() {
|
synchronized void uninstall() {
|
||||||
if( rendererComponent == null )
|
if( rendererComponent == null )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -839,9 +973,9 @@ public class FlatComboBoxUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
synchronized public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
Insets padding = scale( this.padding );
|
Insets padding = scale( this.padding );
|
||||||
if( rendererBorder != null ) {
|
if( rendererBorder != null && !(rendererBorder instanceof CellPaddingBorder) ) {
|
||||||
Insets insideInsets = rendererBorder.getBorderInsets( c );
|
Insets insideInsets = rendererBorder.getBorderInsets( c );
|
||||||
insets.top = Math.max( padding.top, insideInsets.top );
|
insets.top = Math.max( padding.top, insideInsets.top );
|
||||||
insets.left = Math.max( padding.left, insideInsets.left );
|
insets.left = Math.max( padding.left, insideInsets.left );
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ import java.awt.Image;
|
|||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.RadialGradientPaint;
|
import java.awt.RadialGradientPaint;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.util.Map;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableBorder;
|
||||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -40,14 +43,17 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*/
|
*/
|
||||||
public class FlatDropShadowBorder
|
public class FlatDropShadowBorder
|
||||||
extends FlatEmptyBorder
|
extends FlatEmptyBorder
|
||||||
|
implements StyleableBorder
|
||||||
{
|
{
|
||||||
private final Color shadowColor;
|
@Styleable protected Color shadowColor;
|
||||||
private final Insets shadowInsets;
|
@Styleable protected Insets shadowInsets;
|
||||||
private final float shadowOpacity;
|
@Styleable protected float shadowOpacity;
|
||||||
|
|
||||||
private final int shadowSize;
|
private int shadowSize;
|
||||||
private Image shadowImage;
|
private Image shadowImage;
|
||||||
private Color lastShadowColor;
|
private Color lastShadowColor;
|
||||||
|
private float lastShadowOpacity;
|
||||||
|
private int lastShadowSize;
|
||||||
private double lastSystemScaleFactor;
|
private double lastSystemScaleFactor;
|
||||||
private float lastUserScaleFactor;
|
private float lastUserScaleFactor;
|
||||||
|
|
||||||
@@ -64,17 +70,43 @@ public class FlatDropShadowBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
public FlatDropShadowBorder( Color shadowColor, Insets shadowInsets, float shadowOpacity ) {
|
public FlatDropShadowBorder( Color shadowColor, Insets shadowInsets, float shadowOpacity ) {
|
||||||
super( Math.max( shadowInsets.top, 0 ), Math.max( shadowInsets.left, 0 ),
|
super( nonNegativeInsets( shadowInsets ) );
|
||||||
Math.max( shadowInsets.bottom, 0 ), Math.max( shadowInsets.right, 0 ) );
|
|
||||||
this.shadowColor = shadowColor;
|
this.shadowColor = shadowColor;
|
||||||
this.shadowInsets = shadowInsets;
|
this.shadowInsets = shadowInsets;
|
||||||
this.shadowOpacity = shadowOpacity;
|
this.shadowOpacity = shadowOpacity;
|
||||||
|
|
||||||
shadowSize = Math.max(
|
shadowSize = maxInset( shadowInsets );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Insets nonNegativeInsets( Insets shadowInsets ) {
|
||||||
|
return new Insets( Math.max( shadowInsets.top, 0 ), Math.max( shadowInsets.left, 0 ),
|
||||||
|
Math.max( shadowInsets.bottom, 0 ), Math.max( shadowInsets.right, 0 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private int maxInset( Insets shadowInsets ) {
|
||||||
|
return Math.max(
|
||||||
Math.max( shadowInsets.left, shadowInsets.right ),
|
Math.max( shadowInsets.left, shadowInsets.right ),
|
||||||
Math.max( shadowInsets.top, shadowInsets.bottom ) );
|
Math.max( shadowInsets.top, shadowInsets.bottom ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Object applyStyleProperty( String key, Object value ) {
|
||||||
|
Object oldValue = FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||||
|
if( key.equals( "shadowInsets" ) ) {
|
||||||
|
applyStyleProperty( nonNegativeInsets( shadowInsets ) );
|
||||||
|
shadowSize = maxInset( shadowInsets );
|
||||||
|
}
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos() {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
if( shadowSize <= 0 )
|
if( shadowSize <= 0 )
|
||||||
@@ -91,12 +123,16 @@ public class FlatDropShadowBorder
|
|||||||
float userScaleFactor = UIScale.getUserScaleFactor();
|
float userScaleFactor = UIScale.getUserScaleFactor();
|
||||||
if( shadowImage == null ||
|
if( shadowImage == null ||
|
||||||
!shadowColor.equals( lastShadowColor ) ||
|
!shadowColor.equals( lastShadowColor ) ||
|
||||||
|
lastShadowOpacity != shadowOpacity ||
|
||||||
|
lastShadowSize != shadowSize ||
|
||||||
lastSystemScaleFactor != scaleFactor ||
|
lastSystemScaleFactor != scaleFactor ||
|
||||||
lastUserScaleFactor != userScaleFactor )
|
lastUserScaleFactor != userScaleFactor )
|
||||||
{
|
{
|
||||||
shadowImage = createShadowImage( shadowColor, shadowSize, shadowOpacity,
|
shadowImage = createShadowImage( shadowColor, shadowSize, shadowOpacity,
|
||||||
(float) (scaleFactor * userScaleFactor) );
|
(float) (scaleFactor * userScaleFactor) );
|
||||||
lastShadowColor = shadowColor;
|
lastShadowColor = shadowColor;
|
||||||
|
lastShadowOpacity = shadowOpacity;
|
||||||
|
lastShadowSize = shadowSize;
|
||||||
lastSystemScaleFactor = scaleFactor;
|
lastSystemScaleFactor = scaleFactor;
|
||||||
lastUserScaleFactor = userScaleFactor;
|
lastUserScaleFactor = userScaleFactor;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,14 +24,19 @@ import java.awt.Graphics2D;
|
|||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.event.FocusListener;
|
import java.awt.event.FocusListener;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JEditorPane;
|
import javax.swing.JEditorPane;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicEditorPaneUI;
|
import javax.swing.plaf.basic.BasicEditorPaneUI;
|
||||||
|
import javax.swing.text.Caret;
|
||||||
import javax.swing.text.JTextComponent;
|
import javax.swing.text.JTextComponent;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JEditorPane}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JEditorPane}.
|
||||||
@@ -39,8 +44,8 @@ import com.formdev.flatlaf.util.HiDPIUtils;
|
|||||||
* <!-- BasicEditorPaneUI -->
|
* <!-- BasicEditorPaneUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault EditorPane.font Font
|
* @uiDefault EditorPane.font Font
|
||||||
* @uiDefault EditorPane.background Color also used if not editable
|
* @uiDefault EditorPane.background Color
|
||||||
* @uiDefault EditorPane.foreground Color
|
* @uiDefault EditorPane.foreground Color also used if not editable
|
||||||
* @uiDefault EditorPane.caretForeground Color
|
* @uiDefault EditorPane.caretForeground Color
|
||||||
* @uiDefault EditorPane.selectionBackground Color
|
* @uiDefault EditorPane.selectionBackground Color
|
||||||
* @uiDefault EditorPane.selectionForeground Color
|
* @uiDefault EditorPane.selectionForeground Color
|
||||||
@@ -61,20 +66,35 @@ import com.formdev.flatlaf.util.HiDPIUtils;
|
|||||||
*/
|
*/
|
||||||
public class FlatEditorPaneUI
|
public class FlatEditorPaneUI
|
||||||
extends BasicEditorPaneUI
|
extends BasicEditorPaneUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
protected int minimumWidth;
|
@Styleable protected int minimumWidth;
|
||||||
protected boolean isIntelliJTheme;
|
protected boolean isIntelliJTheme;
|
||||||
protected Color focusedBackground;
|
private Color background;
|
||||||
|
@Styleable protected Color disabledBackground;
|
||||||
|
@Styleable protected Color inactiveBackground;
|
||||||
|
@Styleable protected Color focusedBackground;
|
||||||
|
|
||||||
|
private Color oldDisabledBackground;
|
||||||
|
private Color oldInactiveBackground;
|
||||||
|
|
||||||
private Insets defaultMargin;
|
private Insets defaultMargin;
|
||||||
|
|
||||||
private Object oldHonorDisplayProperties;
|
private Object oldHonorDisplayProperties;
|
||||||
private FocusListener focusListener;
|
private FocusListener focusListener;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatEditorPaneUI();
|
return new FlatEditorPaneUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
@@ -82,6 +102,9 @@ public class FlatEditorPaneUI
|
|||||||
String prefix = getPropertyPrefix();
|
String prefix = getPropertyPrefix();
|
||||||
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
||||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
||||||
|
background = UIManager.getColor( prefix + ".background" );
|
||||||
|
disabledBackground = UIManager.getColor( prefix + ".disabledBackground" );
|
||||||
|
inactiveBackground = UIManager.getColor( prefix + ".inactiveBackground" );
|
||||||
focusedBackground = UIManager.getColor( prefix + ".focusedBackground" );
|
focusedBackground = UIManager.getColor( prefix + ".focusedBackground" );
|
||||||
|
|
||||||
defaultMargin = UIManager.getInsets( prefix + ".margin" );
|
defaultMargin = UIManager.getInsets( prefix + ".margin" );
|
||||||
@@ -95,8 +118,16 @@ public class FlatEditorPaneUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
background = null;
|
||||||
|
disabledBackground = null;
|
||||||
|
inactiveBackground = null;
|
||||||
focusedBackground = null;
|
focusedBackground = null;
|
||||||
|
|
||||||
|
oldDisabledBackground = null;
|
||||||
|
oldInactiveBackground = null;
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
|
||||||
getComponent().putClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES, oldHonorDisplayProperties );
|
getComponent().putClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES, oldHonorDisplayProperties );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,19 +149,72 @@ public class FlatEditorPaneUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void propertyChange( PropertyChangeEvent e ) {
|
protected Caret createCaret() {
|
||||||
super.propertyChange( e );
|
return new FlatCaret( null, false );
|
||||||
propertyChange( getComponent(), e );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void propertyChange( JTextComponent c, PropertyChangeEvent e ) {
|
@Override
|
||||||
|
protected void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
// invoke updateBackground() before super.propertyChange()
|
||||||
|
String propertyName = e.getPropertyName();
|
||||||
|
if( "editable".equals( propertyName ) || "enabled".equals( propertyName ) )
|
||||||
|
updateBackground();
|
||||||
|
|
||||||
|
super.propertyChange( e );
|
||||||
|
propertyChange( getComponent(), e, this::installStyle );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void propertyChange( JTextComponent c, PropertyChangeEvent e, Runnable installStyle ) {
|
||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
case FlatClientProperties.MINIMUM_WIDTH:
|
case FlatClientProperties.MINIMUM_WIDTH:
|
||||||
c.revalidate();
|
c.revalidate();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.STYLE:
|
||||||
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
|
installStyle.run();
|
||||||
|
c.revalidate();
|
||||||
|
c.repaint();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle() {
|
||||||
|
try {
|
||||||
|
applyStyle( FlatStylingSupport.getResolvedStyle( getComponent(), "EditorPane" ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( Object style ) {
|
||||||
|
oldDisabledBackground = disabledBackground;
|
||||||
|
oldInactiveBackground = inactiveBackground;
|
||||||
|
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||||
|
|
||||||
|
updateBackground();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, getComponent(), key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateBackground() {
|
||||||
|
FlatTextFieldUI.updateBackground( getComponent(), background,
|
||||||
|
disabledBackground, inactiveBackground,
|
||||||
|
oldDisabledBackground, oldInactiveBackground );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
return applyMinimumWidth( c, super.getPreferredSize( c ), minimumWidth, defaultMargin );
|
return applyMinimumWidth( c, super.getPreferredSize( c ), minimumWidth, defaultMargin );
|
||||||
|
|||||||
@@ -50,6 +50,12 @@ public class FlatEmptyBorder
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
|
return scaleInsets( c, insets, top, left, bottom, right );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static Insets scaleInsets( Component c, Insets insets,
|
||||||
|
int top, int left, int bottom, int right )
|
||||||
|
{
|
||||||
boolean leftToRight = left == right || c.getComponentOrientation().isLeftToRight();
|
boolean leftToRight = left == right || c.getComponentOrientation().isLeftToRight();
|
||||||
insets.left = scale( leftToRight ? left : right );
|
insets.left = scale( leftToRight ? left : right );
|
||||||
insets.top = scale( top );
|
insets.top = scale( top );
|
||||||
@@ -61,4 +67,13 @@ public class FlatEmptyBorder
|
|||||||
public Insets getUnscaledBorderInsets() {
|
public Insets getUnscaledBorderInsets() {
|
||||||
return super.getBorderInsets();
|
return super.getBorderInsets();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object applyStyleProperty( Insets insets ) {
|
||||||
|
Insets oldInsets = getUnscaledBorderInsets();
|
||||||
|
top = insets.top;
|
||||||
|
left = insets.left;
|
||||||
|
bottom = insets.bottom;
|
||||||
|
right = insets.right;
|
||||||
|
return oldInsets;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,11 +19,21 @@ package com.formdev.flatlaf.ui;
|
|||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Image;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
|
import java.awt.LayoutManager;
|
||||||
|
import java.awt.RenderingHints;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.function.Function;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
|
import javax.swing.ButtonGroup;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
@@ -34,12 +44,16 @@ import javax.swing.JPanel;
|
|||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
import javax.swing.JToggleButton;
|
import javax.swing.JToggleButton;
|
||||||
|
import javax.swing.JToolBar;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.filechooser.FileSystemView;
|
||||||
import javax.swing.filechooser.FileView;
|
import javax.swing.filechooser.FileView;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.metal.MetalFileChooserUI;
|
import javax.swing.plaf.metal.MetalFileChooserUI;
|
||||||
import javax.swing.table.TableCellRenderer;
|
import javax.swing.table.TableCellRenderer;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.ScaledImageIcon;
|
import com.formdev.flatlaf.util.ScaledImageIcon;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
@@ -133,12 +147,21 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault FileChooser.listViewActionLabelText String
|
* @uiDefault FileChooser.listViewActionLabelText String
|
||||||
* @uiDefault FileChooser.detailsViewActionLabelText String
|
* @uiDefault FileChooser.detailsViewActionLabelText String
|
||||||
*
|
*
|
||||||
|
* <!-- FlatFileChooserUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault FileChooser.shortcuts.buttonSize Dimension optional; default is 84,64
|
||||||
|
* @uiDefault FileChooser.shortcuts.iconSize Dimension optional; default is 32,32
|
||||||
|
* @uiDefault FileChooser.shortcuts.filesFunction Function<File[], File[]>
|
||||||
|
* @uiDefault FileChooser.shortcuts.displayNameFunction Function<File, String>
|
||||||
|
* @uiDefault FileChooser.shortcuts.iconFunction Function<File, Icon>
|
||||||
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatFileChooserUI
|
public class FlatFileChooserUI
|
||||||
extends MetalFileChooserUI
|
extends MetalFileChooserUI
|
||||||
{
|
{
|
||||||
private final FlatFileView fileView = new FlatFileView();
|
private final FlatFileView fileView = new FlatFileView();
|
||||||
|
private FlatShortcutsPanel shortcutsPanel;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatFileChooserUI( (JFileChooser) c );
|
return new FlatFileChooserUI( (JFileChooser) c );
|
||||||
@@ -153,6 +176,25 @@ public class FlatFileChooserUI
|
|||||||
super.installComponents( fc );
|
super.installComponents( fc );
|
||||||
|
|
||||||
patchUI( fc );
|
patchUI( fc );
|
||||||
|
|
||||||
|
if( !UIManager.getBoolean( "FileChooser.noPlacesBar" ) ) { // same as in Windows L&F
|
||||||
|
FlatShortcutsPanel panel = createShortcutsPanel( fc );
|
||||||
|
if( panel.getComponentCount() > 0 ) {
|
||||||
|
shortcutsPanel = panel;
|
||||||
|
fc.add( shortcutsPanel, BorderLayout.LINE_START );
|
||||||
|
fc.addPropertyChangeListener( shortcutsPanel );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uninstallComponents( JFileChooser fc ) {
|
||||||
|
super.uninstallComponents( fc );
|
||||||
|
|
||||||
|
if( shortcutsPanel != null ) {
|
||||||
|
fc.removePropertyChangeListener( shortcutsPanel );
|
||||||
|
shortcutsPanel = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void patchUI( JFileChooser fc ) {
|
private void patchUI( JFileChooser fc ) {
|
||||||
@@ -192,6 +234,25 @@ public class FlatFileChooserUI
|
|||||||
} catch( ArrayIndexOutOfBoundsException ex ) {
|
} catch( ArrayIndexOutOfBoundsException ex ) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// put north, center and south components into a new panel so that
|
||||||
|
// the shortcuts panel (at west) gets full height
|
||||||
|
LayoutManager layout = fc.getLayout();
|
||||||
|
if( layout instanceof BorderLayout ) {
|
||||||
|
BorderLayout borderLayout = (BorderLayout) layout;
|
||||||
|
borderLayout.setHgap( 8 );
|
||||||
|
|
||||||
|
Component north = borderLayout.getLayoutComponent( BorderLayout.NORTH );
|
||||||
|
Component center = borderLayout.getLayoutComponent( BorderLayout.CENTER );
|
||||||
|
Component south = borderLayout.getLayoutComponent( BorderLayout.SOUTH );
|
||||||
|
if( north != null && center != null && south != null ) {
|
||||||
|
JPanel p = new JPanel( new BorderLayout( 0, 11 ) );
|
||||||
|
p.add( north, BorderLayout.NORTH );
|
||||||
|
p.add( center, BorderLayout.CENTER );
|
||||||
|
p.add( south, BorderLayout.SOUTH );
|
||||||
|
fc.add( p, BorderLayout.CENTER );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -250,9 +311,19 @@ public class FlatFileChooserUI
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.3 */
|
||||||
|
protected FlatShortcutsPanel createShortcutsPanel( JFileChooser fc ) {
|
||||||
|
return new FlatShortcutsPanel( fc );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
return UIScale.scale( super.getPreferredSize( c ) );
|
Dimension prefSize = super.getPreferredSize( c );
|
||||||
|
Dimension minSize = getMinimumSize( c );
|
||||||
|
int shortcutsPanelWidth = (shortcutsPanel != null) ? shortcutsPanel.getPreferredSize().width : 0;
|
||||||
|
return new Dimension(
|
||||||
|
Math.max( prefSize.width, minSize.width + shortcutsPanelWidth ),
|
||||||
|
Math.max( prefSize.height, minSize.height ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -262,12 +333,23 @@ public class FlatFileChooserUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileView getFileView( JFileChooser fc ) {
|
public FileView getFileView( JFileChooser fc ) {
|
||||||
return fileView;
|
return doNotUseSystemIcons() ? super.getFileView( fc ) : fileView;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clearIconCache() {
|
public void clearIconCache() {
|
||||||
fileView.clearIconCache();
|
if( doNotUseSystemIcons() )
|
||||||
|
super.clearIconCache();
|
||||||
|
else
|
||||||
|
fileView.clearIconCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean doNotUseSystemIcons() {
|
||||||
|
// Java 17 32bit craches on Windows when using system icons
|
||||||
|
// fixed in Java 18+ (see https://bugs.openjdk.java.net/browse/JDK-8277299)
|
||||||
|
return SystemInfo.isWindows &&
|
||||||
|
SystemInfo.isX86 &&
|
||||||
|
(SystemInfo.isJava_17_orLater && !SystemInfo.isJava_18_orLater);
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class FlatFileView -------------------------------------------------
|
//---- class FlatFileView -------------------------------------------------
|
||||||
@@ -305,4 +387,234 @@ public class FlatFileChooserUI
|
|||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- class FlatShortcutsPanel -------------------------------------------
|
||||||
|
|
||||||
|
/** @since 2.3 */
|
||||||
|
public static class FlatShortcutsPanel
|
||||||
|
extends JToolBar
|
||||||
|
implements PropertyChangeListener
|
||||||
|
{
|
||||||
|
private final JFileChooser fc;
|
||||||
|
|
||||||
|
private final Dimension buttonSize;
|
||||||
|
private final Dimension iconSize;
|
||||||
|
private final Function<File[], File[]> filesFunction;
|
||||||
|
private final Function<File, String> displayNameFunction;
|
||||||
|
private final Function<File, Icon> iconFunction;
|
||||||
|
|
||||||
|
protected final File[] files;
|
||||||
|
protected final JToggleButton[] buttons;
|
||||||
|
protected final ButtonGroup buttonGroup;
|
||||||
|
|
||||||
|
@SuppressWarnings( "unchecked" )
|
||||||
|
public FlatShortcutsPanel( JFileChooser fc ) {
|
||||||
|
super( JToolBar.VERTICAL );
|
||||||
|
this.fc = fc;
|
||||||
|
setFloatable( false );
|
||||||
|
|
||||||
|
buttonSize = UIScale.scale( getUIDimension( "FileChooser.shortcuts.buttonSize", 84, 64 ) );
|
||||||
|
iconSize = getUIDimension( "FileChooser.shortcuts.iconSize", 32, 32 );
|
||||||
|
|
||||||
|
filesFunction = (Function<File[], File[]>) UIManager.get( "FileChooser.shortcuts.filesFunction" );
|
||||||
|
displayNameFunction = (Function<File, String>) UIManager.get( "FileChooser.shortcuts.displayNameFunction" );
|
||||||
|
iconFunction = (Function<File, Icon>) UIManager.get( "FileChooser.shortcuts.iconFunction" );
|
||||||
|
|
||||||
|
FileSystemView fsv = fc.getFileSystemView();
|
||||||
|
File[] files = getChooserShortcutPanelFiles( fsv );
|
||||||
|
if( filesFunction != null )
|
||||||
|
files = filesFunction.apply( files );
|
||||||
|
this.files = files;
|
||||||
|
|
||||||
|
// create toolbar buttons
|
||||||
|
buttons = new JToggleButton[files.length];
|
||||||
|
buttonGroup = new ButtonGroup();
|
||||||
|
for( int i = 0; i < files.length; i++ ) {
|
||||||
|
// wrap drive path
|
||||||
|
if( fsv.isFileSystemRoot( files[i] ) )
|
||||||
|
files[i] = fsv.createFileObject( files[i].getAbsolutePath() );
|
||||||
|
|
||||||
|
File file = files[i];
|
||||||
|
String name = getDisplayName( fsv, file );
|
||||||
|
Icon icon = getIcon( fsv, file );
|
||||||
|
|
||||||
|
// remove path from name
|
||||||
|
int lastSepIndex = name.lastIndexOf( File.separatorChar );
|
||||||
|
if( lastSepIndex >= 0 && lastSepIndex < name.length() - 1 )
|
||||||
|
name = name.substring( lastSepIndex + 1 );
|
||||||
|
|
||||||
|
// scale icon (if necessary)
|
||||||
|
if( icon instanceof ImageIcon )
|
||||||
|
icon = new ScaledImageIcon( (ImageIcon) icon, iconSize.width, iconSize.height );
|
||||||
|
else if( icon != null )
|
||||||
|
icon = new ShortcutIcon( icon, iconSize.width, iconSize.height );
|
||||||
|
|
||||||
|
// create button
|
||||||
|
JToggleButton button = createButton( name, icon );
|
||||||
|
button.addActionListener( e -> {
|
||||||
|
fc.setCurrentDirectory( file );
|
||||||
|
} );
|
||||||
|
|
||||||
|
add( button );
|
||||||
|
buttonGroup.add( button );
|
||||||
|
buttons[i] = button;
|
||||||
|
}
|
||||||
|
|
||||||
|
directoryChanged( fc.getCurrentDirectory() );
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dimension getUIDimension( String key, int defaultWidth, int defaultHeight ) {
|
||||||
|
Dimension size = UIManager.getDimension( key );
|
||||||
|
if( size == null )
|
||||||
|
size = new Dimension( defaultWidth, defaultHeight );
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected JToggleButton createButton( String name, Icon icon ) {
|
||||||
|
JToggleButton button = new JToggleButton( name, icon );
|
||||||
|
button.setVerticalTextPosition( SwingConstants.BOTTOM );
|
||||||
|
button.setHorizontalTextPosition( SwingConstants.CENTER );
|
||||||
|
button.setAlignmentX( Component.CENTER_ALIGNMENT );
|
||||||
|
button.setIconTextGap( 0 );
|
||||||
|
button.setPreferredSize( buttonSize );
|
||||||
|
button.setMaximumSize( buttonSize );
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected File[] getChooserShortcutPanelFiles( FileSystemView fsv ) {
|
||||||
|
try {
|
||||||
|
if( SystemInfo.isJava_12_orLater ) {
|
||||||
|
Method m = fsv.getClass().getMethod( "getChooserShortcutPanelFiles" );
|
||||||
|
File[] files = (File[]) m.invoke( fsv );
|
||||||
|
|
||||||
|
// on macOS and Linux, files consists only of the user home directory
|
||||||
|
if( files.length == 1 && files[0].equals( new File( System.getProperty( "user.home" ) ) ) )
|
||||||
|
files = new File[0];
|
||||||
|
|
||||||
|
return files;
|
||||||
|
} else if( SystemInfo.isWindows ) {
|
||||||
|
Class<?> cls = Class.forName( "sun.awt.shell.ShellFolder" );
|
||||||
|
Method m = cls.getMethod( "get", String.class );
|
||||||
|
return (File[]) m.invoke( null, "fileChooserShortcutPanelFolders" );
|
||||||
|
}
|
||||||
|
} catch( IllegalAccessException ex ) {
|
||||||
|
// do not log because access may be denied via VM option '--illegal-access=deny'
|
||||||
|
} catch( Exception ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback
|
||||||
|
return new File[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getDisplayName( FileSystemView fsv, File file ) {
|
||||||
|
if( displayNameFunction != null ) {
|
||||||
|
String name = displayNameFunction.apply( file );
|
||||||
|
if( name != null )
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fsv.getSystemDisplayName( file );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Icon getIcon( FileSystemView fsv, File file ) {
|
||||||
|
if( iconFunction != null ) {
|
||||||
|
Icon icon = iconFunction.apply( file );
|
||||||
|
if( icon != null )
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Java 17+ supports getting larger system icons
|
||||||
|
try {
|
||||||
|
if( SystemInfo.isJava_17_orLater ) {
|
||||||
|
Method m = fsv.getClass().getMethod( "getSystemIcon", File.class, int.class, int.class );
|
||||||
|
return (Icon) m.invoke( fsv, file, iconSize.width, iconSize.height );
|
||||||
|
} else if( iconSize.width > 16 || iconSize.height > 16 ) {
|
||||||
|
Class<?> cls = Class.forName( "sun.awt.shell.ShellFolder" );
|
||||||
|
if( cls.isInstance( file ) ) {
|
||||||
|
Method m = file.getClass().getMethod( "getIcon", boolean.class );
|
||||||
|
m.setAccessible( true );
|
||||||
|
Image image = (Image) m.invoke( file, true );
|
||||||
|
if( image != null )
|
||||||
|
return new ImageIcon( image );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch( IllegalAccessException ex ) {
|
||||||
|
// do not log because access may be denied via VM option '--illegal-access=deny'
|
||||||
|
} catch( Exception ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
|
||||||
|
// get system icon in default size 16x16
|
||||||
|
return fsv.getSystemIcon( file );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void directoryChanged( File file ) {
|
||||||
|
if( file != null ) {
|
||||||
|
String absolutePath = file.getAbsolutePath();
|
||||||
|
for( int i = 0; i < files.length; i++ ) {
|
||||||
|
// also compare path because otherwise selecting "Documents"
|
||||||
|
// in "Look in" combobox would not select "Documents" shortcut item
|
||||||
|
if( files[i].equals( file ) || files[i].getAbsolutePath().equals( absolutePath ) ) {
|
||||||
|
buttons[i].setSelected( true );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buttonGroup.clearSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
switch( e.getPropertyName() ) {
|
||||||
|
case JFileChooser.DIRECTORY_CHANGED_PROPERTY:
|
||||||
|
directoryChanged( fc.getCurrentDirectory() );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class ShortcutIcon -------------------------------------------------
|
||||||
|
|
||||||
|
private static class ShortcutIcon
|
||||||
|
implements Icon
|
||||||
|
{
|
||||||
|
private final Icon icon;
|
||||||
|
private final int iconWidth;
|
||||||
|
private final int iconHeight;
|
||||||
|
|
||||||
|
ShortcutIcon( Icon icon, int iconWidth, int iconHeight ) {
|
||||||
|
this.icon = icon;
|
||||||
|
this.iconWidth = iconWidth;
|
||||||
|
this.iconHeight = iconHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paintIcon( Component c, Graphics g, int x, int y ) {
|
||||||
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
|
try {
|
||||||
|
// set rendering hint for the case that the icon is a bitmap (not used for vector icons)
|
||||||
|
g2.setRenderingHint( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC );
|
||||||
|
|
||||||
|
double scale = (double) getIconWidth() / (double) icon.getIconWidth();
|
||||||
|
g2.translate( x, y );
|
||||||
|
g2.scale( scale, scale );
|
||||||
|
|
||||||
|
icon.paintIcon( c, g2, 0, 0 );
|
||||||
|
} finally {
|
||||||
|
g2.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getIconWidth() {
|
||||||
|
return UIScale.scale( iconWidth );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getIconHeight() {
|
||||||
|
return UIScale.scale( iconHeight );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import javax.swing.plaf.ComponentUI;
|
|||||||
* @uiDefault Component.isIntelliJTheme boolean
|
* @uiDefault Component.isIntelliJTheme boolean
|
||||||
* @uiDefault FormattedTextField.placeholderForeground Color
|
* @uiDefault FormattedTextField.placeholderForeground Color
|
||||||
* @uiDefault FormattedTextField.focusedBackground Color optional
|
* @uiDefault FormattedTextField.focusedBackground Color optional
|
||||||
|
* @uiDefault FormattedTextField.iconTextGap int optional, default is 4
|
||||||
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
|
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
|
||||||
* @uiDefault TextComponent.selectAllOnMouseClick boolean
|
* @uiDefault TextComponent.selectAllOnMouseClick boolean
|
||||||
*
|
*
|
||||||
@@ -59,4 +60,10 @@ public class FlatFormattedTextFieldUI
|
|||||||
protected String getPropertyPrefix() {
|
protected String getPropertyPrefix() {
|
||||||
return "FormattedTextField";
|
return "FormattedTextField";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
String getStyleType() {
|
||||||
|
return "FormattedTextField";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,6 +213,13 @@ public class FlatInternalFrameTitlePane
|
|||||||
case "componentOrientation":
|
case "componentOrientation":
|
||||||
applyComponentOrientation( frame.getComponentOrientation() );
|
applyComponentOrientation( frame.getComponentOrientation() );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "opaque":
|
||||||
|
// Do not invoke super.propertyChange() here because it always
|
||||||
|
// invokes repaint(), which would cause endless repainting.
|
||||||
|
// The opaque flag is temporary changed in FlatUIUtils.hasOpaqueBeenExplicitlySet(),
|
||||||
|
// invoked from FlatInternalFrameUI.update().
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
super.propertyChange( e );
|
super.propertyChange( e );
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ import java.awt.Graphics2D;
|
|||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JInternalFrame;
|
import javax.swing.JInternalFrame;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
@@ -31,6 +34,10 @@ import javax.swing.UIManager;
|
|||||||
import javax.swing.event.MouseInputAdapter;
|
import javax.swing.event.MouseInputAdapter;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicInternalFrameUI;
|
import javax.swing.plaf.basic.BasicInternalFrameUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableBorder;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JInternalFrame}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JInternalFrame}.
|
||||||
@@ -86,9 +93,13 @@ import javax.swing.plaf.basic.BasicInternalFrameUI;
|
|||||||
*/
|
*/
|
||||||
public class FlatInternalFrameUI
|
public class FlatInternalFrameUI
|
||||||
extends BasicInternalFrameUI
|
extends BasicInternalFrameUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
protected FlatWindowResizer windowResizer;
|
protected FlatWindowResizer windowResizer;
|
||||||
|
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
private AtomicBoolean borderShared;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatInternalFrameUI( (JInternalFrame) c );
|
return new FlatInternalFrameUI( (JInternalFrame) c );
|
||||||
}
|
}
|
||||||
@@ -104,6 +115,8 @@ public class FlatInternalFrameUI
|
|||||||
LookAndFeel.installProperty( frame, "opaque", false );
|
LookAndFeel.installProperty( frame, "opaque", false );
|
||||||
|
|
||||||
windowResizer = createWindowResizer();
|
windowResizer = createWindowResizer();
|
||||||
|
|
||||||
|
installStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -114,6 +127,9 @@ public class FlatInternalFrameUI
|
|||||||
windowResizer.uninstall();
|
windowResizer.uninstall();
|
||||||
windowResizer = null;
|
windowResizer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
borderShared = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -130,15 +146,69 @@ public class FlatInternalFrameUI
|
|||||||
return new FlatBorderListener();
|
return new FlatBorderListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PropertyChangeListener createPropertyChangeListener() {
|
||||||
|
return FlatStylingSupport.createPropertyChangeListener( frame, this::installStyle,
|
||||||
|
super.createPropertyChangeListener() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle() {
|
||||||
|
try {
|
||||||
|
applyStyle( FlatStylingSupport.getResolvedStyle( frame, "InternalFrame" ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( Object style ) {
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
if( borderShared == null )
|
||||||
|
borderShared = new AtomicBoolean( true );
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, frame, borderShared );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this, frame.getBorder() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update( Graphics g, JComponent c ) {
|
||||||
|
// The internal frame actually should be opaque and fill its background,
|
||||||
|
// but it must be non-opaque to allow translucent resize handles (outside of visual bounds).
|
||||||
|
// To avoid that parent may shine through internal frame (e.g. if menu bar is non-opaque),
|
||||||
|
// fill background excluding insets (translucent resize handles),
|
||||||
|
// but only if opaque was not set explicitly by application to false.
|
||||||
|
// If applications has set internal frame opacity to false, do not fill background (for compatibility).
|
||||||
|
if( !c.isOpaque() && !FlatUIUtils.hasOpaqueBeenExplicitlySet( c ) ) {
|
||||||
|
Insets insets = c.getInsets();
|
||||||
|
|
||||||
|
g.setColor( c.getBackground() );
|
||||||
|
g.fillRect( insets.left, insets.top,
|
||||||
|
c.getWidth() - insets.left - insets.right,
|
||||||
|
c.getHeight() - insets.top - insets.bottom );
|
||||||
|
}
|
||||||
|
|
||||||
|
super.update( g, c );
|
||||||
|
}
|
||||||
|
|
||||||
//---- class FlatInternalFrameBorder --------------------------------------
|
//---- class FlatInternalFrameBorder --------------------------------------
|
||||||
|
|
||||||
public static class FlatInternalFrameBorder
|
public static class FlatInternalFrameBorder
|
||||||
extends FlatEmptyBorder
|
extends FlatEmptyBorder
|
||||||
|
implements StyleableBorder
|
||||||
{
|
{
|
||||||
private final Color activeBorderColor = UIManager.getColor( "InternalFrame.activeBorderColor" );
|
@Styleable protected Color activeBorderColor = UIManager.getColor( "InternalFrame.activeBorderColor" );
|
||||||
private final Color inactiveBorderColor = UIManager.getColor( "InternalFrame.inactiveBorderColor" );
|
@Styleable protected Color inactiveBorderColor = UIManager.getColor( "InternalFrame.inactiveBorderColor" );
|
||||||
private final int borderLineWidth = FlatUIUtils.getUIInt( "InternalFrame.borderLineWidth", 1 );
|
@Styleable protected int borderLineWidth = FlatUIUtils.getUIInt( "InternalFrame.borderLineWidth", 1 );
|
||||||
private final boolean dropShadowPainted = UIManager.getBoolean( "InternalFrame.dropShadowPainted" );
|
@Styleable protected boolean dropShadowPainted = UIManager.getBoolean( "InternalFrame.dropShadowPainted" );
|
||||||
|
|
||||||
private final FlatDropShadowBorder activeDropShadowBorder = new FlatDropShadowBorder(
|
private final FlatDropShadowBorder activeDropShadowBorder = new FlatDropShadowBorder(
|
||||||
UIManager.getColor( "InternalFrame.activeDropShadowColor" ),
|
UIManager.getColor( "InternalFrame.activeDropShadowColor" ),
|
||||||
@@ -153,6 +223,36 @@ public class FlatInternalFrameUI
|
|||||||
super( UIManager.getInsets( "InternalFrame.borderMargins" ) );
|
super( UIManager.getInsets( "InternalFrame.borderMargins" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object applyStyleProperty( String key, Object value ) {
|
||||||
|
switch( key ) {
|
||||||
|
case "borderMargins": return applyStyleProperty( (Insets) value );
|
||||||
|
|
||||||
|
case "activeDropShadowColor": return activeDropShadowBorder.applyStyleProperty( "shadowColor", value );
|
||||||
|
case "activeDropShadowInsets": return activeDropShadowBorder.applyStyleProperty( "shadowInsets", value );
|
||||||
|
case "activeDropShadowOpacity": return activeDropShadowBorder.applyStyleProperty( "shadowOpacity", value );
|
||||||
|
case "inactiveDropShadowColor": return inactiveDropShadowBorder.applyStyleProperty( "shadowColor", value );
|
||||||
|
case "inactiveDropShadowInsets": return inactiveDropShadowBorder.applyStyleProperty( "shadowInsets", value );
|
||||||
|
case "inactiveDropShadowOpacity": return inactiveDropShadowBorder.applyStyleProperty( "shadowOpacity", value );
|
||||||
|
}
|
||||||
|
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos() {
|
||||||
|
Map<String, Class<?>> infos = new FlatStylingSupport.StyleableInfosMap<>();
|
||||||
|
FlatStylingSupport.collectAnnotatedStyleableInfos( this, infos );
|
||||||
|
infos.put( "borderMargins", Insets.class );
|
||||||
|
infos.put( "activeDropShadowColor", Color.class );
|
||||||
|
infos.put( "activeDropShadowInsets", Insets.class );
|
||||||
|
infos.put( "activeDropShadowOpacity", float.class );
|
||||||
|
infos.put( "inactiveDropShadowColor", Color.class );
|
||||||
|
infos.put( "inactiveDropShadowInsets", Insets.class );
|
||||||
|
infos.put( "inactiveDropShadowOpacity", float.class );
|
||||||
|
return infos;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
if( c instanceof JInternalFrame && ((JInternalFrame)c).isMaximum() ) {
|
if( c instanceof JInternalFrame && ((JInternalFrame)c).isMaximum() ) {
|
||||||
@@ -206,9 +306,7 @@ public class FlatInternalFrameUI
|
|||||||
|
|
||||||
//---- class FlatBorderListener -------------------------------------------
|
//---- class FlatBorderListener -------------------------------------------
|
||||||
|
|
||||||
/**
|
/** @since 1.6 */
|
||||||
* @since 1.6
|
|
||||||
*/
|
|
||||||
protected class FlatBorderListener
|
protected class FlatBorderListener
|
||||||
extends BorderListener
|
extends BorderListener
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import java.awt.Rectangle;
|
|||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
@@ -33,8 +34,12 @@ import javax.swing.UIManager;
|
|||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicHTML;
|
import javax.swing.plaf.basic.BasicHTML;
|
||||||
import javax.swing.plaf.basic.BasicLabelUI;
|
import javax.swing.plaf.basic.BasicLabelUI;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,13 +59,30 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*/
|
*/
|
||||||
public class FlatLabelUI
|
public class FlatLabelUI
|
||||||
extends BasicLabelUI
|
extends BasicLabelUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
private Color disabledForeground;
|
@Styleable protected Color disabledForeground;
|
||||||
|
|
||||||
|
private final boolean shared;
|
||||||
private boolean defaults_initialized = false;
|
private boolean defaults_initialized = false;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return FlatUIUtils.createSharedUI( FlatLabelUI.class, FlatLabelUI::new );
|
return FlatUIUtils.canUseSharedUI( c )
|
||||||
|
? FlatUIUtils.createSharedUI( FlatLabelUI.class, () -> new FlatLabelUI( true ) )
|
||||||
|
: new FlatLabelUI( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected FlatLabelUI( boolean shared ) {
|
||||||
|
this.shared = shared;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle( (JLabel) c );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -77,7 +99,9 @@ public class FlatLabelUI
|
|||||||
@Override
|
@Override
|
||||||
protected void uninstallDefaults( JLabel c ) {
|
protected void uninstallDefaults( JLabel c ) {
|
||||||
super.uninstallDefaults( c );
|
super.uninstallDefaults( c );
|
||||||
|
|
||||||
defaults_initialized = false;
|
defaults_initialized = false;
|
||||||
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -94,10 +118,46 @@ public class FlatLabelUI
|
|||||||
if( name == "text" || name == "font" || name == "foreground" ) {
|
if( name == "text" || name == "font" || name == "foreground" ) {
|
||||||
JLabel label = (JLabel) e.getSource();
|
JLabel label = (JLabel) e.getSource();
|
||||||
updateHTMLRenderer( label, label.getText(), true );
|
updateHTMLRenderer( label, label.getText(), true );
|
||||||
|
} else if( name.equals( FlatClientProperties.STYLE ) || name.equals( FlatClientProperties.STYLE_CLASS ) ) {
|
||||||
|
JLabel label = (JLabel) e.getSource();
|
||||||
|
if( shared && FlatStylingSupport.hasStyleProperty( label ) ) {
|
||||||
|
// unshare component UI if necessary
|
||||||
|
// updateUI() invokes installStyle() from installUI()
|
||||||
|
label.updateUI();
|
||||||
|
} else
|
||||||
|
installStyle( label );
|
||||||
|
label.revalidate();
|
||||||
|
label.repaint();
|
||||||
} else
|
} else
|
||||||
super.propertyChange( e );
|
super.propertyChange( e );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle( JLabel c ) {
|
||||||
|
try {
|
||||||
|
applyStyle( c, FlatStylingSupport.getResolvedStyle( c, "Label" ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( JLabel c, Object style ) {
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style,
|
||||||
|
(key, value) -> applyStyleProperty( c, key, value ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( JLabel c, String key, Object value ) {
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, c, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether text contains HTML tags that use "absolute-size" keywords
|
* Checks whether text contains HTML tags that use "absolute-size" keywords
|
||||||
* (e.g. "x-large") for font-size in default style sheet
|
* (e.g. "x-large") for font-size in default style sheet
|
||||||
|
|||||||
@@ -37,15 +37,18 @@ public class FlatLineBorder
|
|||||||
{
|
{
|
||||||
private final Color lineColor;
|
private final Color lineColor;
|
||||||
private final float lineThickness;
|
private final float lineThickness;
|
||||||
|
/** @since 2 */ private final int arc;
|
||||||
|
|
||||||
public FlatLineBorder( Insets insets, Color lineColor ) {
|
public FlatLineBorder( Insets insets, Color lineColor ) {
|
||||||
this( insets, lineColor, 1f );
|
this( insets, lineColor, 1f, 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
public FlatLineBorder( Insets insets, Color lineColor, float lineThickness ) {
|
/** @since 2 */
|
||||||
|
public FlatLineBorder( Insets insets, Color lineColor, float lineThickness, int arc ) {
|
||||||
super( insets );
|
super( insets );
|
||||||
this.lineColor = lineColor;
|
this.lineColor = lineColor;
|
||||||
this.lineThickness = lineThickness;
|
this.lineThickness = lineThickness;
|
||||||
|
this.arc = arc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Color getLineColor() {
|
public Color getLineColor() {
|
||||||
@@ -56,13 +59,18 @@ public class FlatLineBorder
|
|||||||
return lineThickness;
|
return lineThickness;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public int getArc() {
|
||||||
|
return arc;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
Graphics2D g2 = (Graphics2D) g.create();
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
try {
|
try {
|
||||||
FlatUIUtils.setRenderingHints( g2 );
|
FlatUIUtils.setRenderingHints( g2 );
|
||||||
g2.setColor( lineColor );
|
FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height,
|
||||||
FlatUIUtils.paintComponentBorder( g2, x, y, width, height, 0f, scale( lineThickness ), 0f );
|
0, 0, 0, scale( getLineThickness() ), scale( getArc() ), null, getLineColor(), null );
|
||||||
} finally {
|
} finally {
|
||||||
g2.dispose();
|
g2.dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,11 +16,15 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Insets;
|
||||||
|
import java.util.function.Function;
|
||||||
import javax.swing.JList;
|
import javax.swing.JList;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.plaf.ListUI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cell border for {@link javax.swing.DefaultListCellRenderer}
|
* Cell border for {@link javax.swing.DefaultListCellRenderer}
|
||||||
@@ -33,12 +37,54 @@ import javax.swing.UIManager;
|
|||||||
public class FlatListCellBorder
|
public class FlatListCellBorder
|
||||||
extends FlatLineBorder
|
extends FlatLineBorder
|
||||||
{
|
{
|
||||||
final boolean showCellFocusIndicator = UIManager.getBoolean( "List.showCellFocusIndicator" );
|
/** @since 2 */ protected boolean showCellFocusIndicator = UIManager.getBoolean( "List.showCellFocusIndicator" );
|
||||||
|
|
||||||
|
private Component c;
|
||||||
|
|
||||||
protected FlatListCellBorder() {
|
protected FlatListCellBorder() {
|
||||||
super( UIManager.getInsets( "List.cellMargins" ), UIManager.getColor( "List.cellFocusColor" ) );
|
super( UIManager.getInsets( "List.cellMargins" ), UIManager.getColor( "List.cellFocusColor" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
|
Insets m = getStyleFromListUI( c, ui -> ui.cellMargins );
|
||||||
|
if( m != null )
|
||||||
|
return scaleInsets( c, insets, m.top, m.left, m.bottom, m.right );
|
||||||
|
|
||||||
|
return super.getBorderInsets( c, insets );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color getLineColor() {
|
||||||
|
if( c != null ) {
|
||||||
|
Color color = getStyleFromListUI( c, ui -> ui.cellFocusColor );
|
||||||
|
if( color != null )
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
return super.getLineColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
|
this.c = c;
|
||||||
|
super.paintBorder( c, g, x, y, width, height );
|
||||||
|
this.c = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Because this border is always shared for all lists,
|
||||||
|
* get border specific style from FlatListUI.
|
||||||
|
*/
|
||||||
|
static <T> T getStyleFromListUI( Component c, Function<FlatListUI, T> f ) {
|
||||||
|
JList<?> list = (JList<?>) SwingUtilities.getAncestorOfClass( JList.class, c );
|
||||||
|
if( list != null ) {
|
||||||
|
ListUI ui = list.getUI();
|
||||||
|
if( ui instanceof FlatListUI )
|
||||||
|
return f.apply( (FlatListUI) ui );
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
//---- class Default ------------------------------------------------------
|
//---- class Default ------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,17 +113,19 @@ public class FlatListCellBorder
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Border for selected cell that uses margins and paints focus indicator border
|
* Border for selected cell that uses margins and paints focus indicator border
|
||||||
* if enabled (List.showCellFocusIndicator=true) and exactly one item is selected.
|
* if enabled (List.showCellFocusIndicator=true) and multiple items are selected.
|
||||||
*/
|
*/
|
||||||
public static class Selected
|
public static class Selected
|
||||||
extends FlatListCellBorder
|
extends FlatListCellBorder
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
|
Boolean b = getStyleFromListUI( c, ui -> ui.showCellFocusIndicator );
|
||||||
|
boolean showCellFocusIndicator = (b != null) ? b : this.showCellFocusIndicator;
|
||||||
if( !showCellFocusIndicator )
|
if( !showCellFocusIndicator )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// paint focus indicator border only if exactly one item is selected
|
// paint focus indicator border only if multiple items are selected
|
||||||
JList<?> list = (JList<?>) SwingUtilities.getAncestorOfClass( JList.class, c );
|
JList<?> list = (JList<?>) SwingUtilities.getAncestorOfClass( JList.class, c );
|
||||||
if( list != null && list.getMinSelectionIndex() == list.getMaxSelectionIndex() )
|
if( list != null && list.getMinSelectionIndex() == list.getMaxSelectionIndex() )
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -18,14 +18,19 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
|
import java.awt.Insets;
|
||||||
import java.awt.event.FocusEvent;
|
import java.awt.event.FocusEvent;
|
||||||
import java.awt.event.FocusListener;
|
import java.awt.event.FocusListener;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicListUI;
|
import javax.swing.plaf.basic.BasicListUI;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JList}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JList}.
|
||||||
@@ -65,16 +70,31 @@ import com.formdev.flatlaf.FlatClientProperties;
|
|||||||
*/
|
*/
|
||||||
public class FlatListUI
|
public class FlatListUI
|
||||||
extends BasicListUI
|
extends BasicListUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
protected Color selectionBackground;
|
@Styleable protected Color selectionBackground;
|
||||||
protected Color selectionForeground;
|
@Styleable protected Color selectionForeground;
|
||||||
protected Color selectionInactiveBackground;
|
@Styleable protected Color selectionInactiveBackground;
|
||||||
protected Color selectionInactiveForeground;
|
@Styleable protected Color selectionInactiveForeground;
|
||||||
|
|
||||||
|
// for FlatListCellBorder
|
||||||
|
/** @since 2 */ @Styleable protected Insets cellMargins;
|
||||||
|
/** @since 2 */ @Styleable protected Color cellFocusColor;
|
||||||
|
/** @since 2 */ @Styleable protected Boolean showCellFocusIndicator;
|
||||||
|
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatListUI();
|
return new FlatListUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
@@ -95,17 +115,8 @@ public class FlatListUI
|
|||||||
selectionForeground = null;
|
selectionForeground = null;
|
||||||
selectionInactiveBackground = null;
|
selectionInactiveBackground = null;
|
||||||
selectionInactiveForeground = null;
|
selectionInactiveForeground = null;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
oldStyleValues = null;
|
||||||
protected PropertyChangeListener createPropertyChangeListener() {
|
|
||||||
PropertyChangeListener superListener = super.createPropertyChangeListener();
|
|
||||||
return e -> {
|
|
||||||
superListener.propertyChange( e );
|
|
||||||
|
|
||||||
if( FlatClientProperties.COMPONENT_FOCUS_OWNER.equals( e.getPropertyName() ) )
|
|
||||||
toggleSelectionColors();
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -129,10 +140,79 @@ public class FlatListUI
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PropertyChangeListener createPropertyChangeListener() {
|
||||||
|
PropertyChangeListener superListener = super.createPropertyChangeListener();
|
||||||
|
return e -> {
|
||||||
|
superListener.propertyChange( e );
|
||||||
|
|
||||||
|
switch( e.getPropertyName() ) {
|
||||||
|
case FlatClientProperties.COMPONENT_FOCUS_OWNER:
|
||||||
|
toggleSelectionColors();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.STYLE:
|
||||||
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
|
installStyle();
|
||||||
|
list.revalidate();
|
||||||
|
list.repaint();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle() {
|
||||||
|
try {
|
||||||
|
applyStyle( FlatStylingSupport.getResolvedStyle( list, "List" ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( Object style ) {
|
||||||
|
Color oldSelectionBackground = selectionBackground;
|
||||||
|
Color oldSelectionForeground = selectionForeground;
|
||||||
|
Color oldSelectionInactiveBackground = selectionInactiveBackground;
|
||||||
|
Color oldSelectionInactiveForeground = selectionInactiveForeground;
|
||||||
|
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||||
|
|
||||||
|
// update selection background
|
||||||
|
if( selectionBackground != oldSelectionBackground ) {
|
||||||
|
Color selBg = list.getSelectionBackground();
|
||||||
|
if( selBg == oldSelectionBackground )
|
||||||
|
list.setSelectionBackground( selectionBackground );
|
||||||
|
else if( selBg == oldSelectionInactiveBackground )
|
||||||
|
list.setSelectionBackground( selectionInactiveBackground );
|
||||||
|
}
|
||||||
|
|
||||||
|
// update selection foreground
|
||||||
|
if( selectionForeground != oldSelectionForeground ) {
|
||||||
|
Color selFg = list.getSelectionForeground();
|
||||||
|
if( selFg == oldSelectionForeground )
|
||||||
|
list.setSelectionForeground( selectionForeground );
|
||||||
|
else if( selFg == oldSelectionInactiveForeground )
|
||||||
|
list.setSelectionForeground( selectionInactiveForeground );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, list, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle selection colors from focused to inactive and vice versa.
|
* Toggle selection colors from focused to inactive and vice versa.
|
||||||
*
|
*
|
||||||
* This is not a optimal solution but much easier than rewriting the whole paint methods.
|
* This is not an optimal solution but much easier than rewriting the whole paint methods.
|
||||||
*
|
*
|
||||||
* Using a LaF specific renderer was avoided because often a custom renderer is
|
* Using a LaF specific renderer was avoided because often a custom renderer is
|
||||||
* already used in applications. Then either the inactive colors are not used,
|
* already used in applications. Then either the inactive colors are not used,
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import javax.swing.plaf.basic.BasicBorders;
|
|||||||
public class FlatMarginBorder
|
public class FlatMarginBorder
|
||||||
extends BasicBorders.MarginBorder
|
extends BasicBorders.MarginBorder
|
||||||
{
|
{
|
||||||
private final int left, right, top, bottom;
|
protected int left, right, top, bottom;
|
||||||
|
|
||||||
public FlatMarginBorder() {
|
public FlatMarginBorder() {
|
||||||
left = right = top = bottom = 0;
|
left = right = top = bottom = 0;
|
||||||
|
|||||||
@@ -21,8 +21,11 @@ import java.awt.Color;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.JMenuBar;
|
import javax.swing.JMenuBar;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableBorder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Border for {@link javax.swing.JMenuBar}.
|
* Border for {@link javax.swing.JMenuBar}.
|
||||||
@@ -33,11 +36,26 @@ import javax.swing.UIManager;
|
|||||||
*/
|
*/
|
||||||
public class FlatMenuBarBorder
|
public class FlatMenuBarBorder
|
||||||
extends FlatMarginBorder
|
extends FlatMarginBorder
|
||||||
|
implements StyleableBorder
|
||||||
{
|
{
|
||||||
private final Color borderColor = UIManager.getColor( "MenuBar.borderColor" );
|
@Styleable protected Color borderColor = UIManager.getColor( "MenuBar.borderColor" );
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Object applyStyleProperty( String key, Object value ) {
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos() {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
|
if( !showBottomSeparator( c ) )
|
||||||
|
return;
|
||||||
|
|
||||||
float lineHeight = scale( (float) 1 );
|
float lineHeight = scale( (float) 1 );
|
||||||
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight );
|
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight );
|
||||||
}
|
}
|
||||||
@@ -53,4 +71,9 @@ public class FlatMenuBarBorder
|
|||||||
insets.right = scale( margin.right );
|
insets.right = scale( margin.right );
|
||||||
return insets;
|
return insets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected boolean showBottomSeparator( Component c ) {
|
||||||
|
return !FlatMenuBarUI.useUnifiedBackground( c );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,9 +17,14 @@
|
|||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Insets;
|
||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.ActionMap;
|
import javax.swing.ActionMap;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
@@ -36,6 +41,9 @@ import javax.swing.plaf.ComponentUI;
|
|||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicMenuBarUI;
|
import javax.swing.plaf.basic.BasicMenuBarUI;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,13 +55,30 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
* @uiDefault MenuBar.background Color
|
* @uiDefault MenuBar.background Color
|
||||||
* @uiDefault MenuBar.foreground Color
|
* @uiDefault MenuBar.foreground Color
|
||||||
* @uiDefault MenuBar.border Border
|
* @uiDefault MenuBar.border Border
|
||||||
|
*
|
||||||
|
* <!-- FlatMenuBarUI -->
|
||||||
|
*
|
||||||
* @uiDefault TitlePane.unifiedBackground boolean
|
* @uiDefault TitlePane.unifiedBackground boolean
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatMenuBarUI
|
public class FlatMenuBarUI
|
||||||
extends BasicMenuBarUI
|
extends BasicMenuBarUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
|
// used in FlatMenuItemBorder
|
||||||
|
/** @since 2 */ @Styleable protected Insets itemMargins;
|
||||||
|
|
||||||
|
// used in FlatMenuUI
|
||||||
|
/** @since 2 */ @Styleable protected Color hoverBackground;
|
||||||
|
/** @since 2 */ @Styleable protected Color underlineSelectionBackground;
|
||||||
|
/** @since 2 */ @Styleable protected Color underlineSelectionColor;
|
||||||
|
/** @since 2 */ @Styleable protected int underlineSelectionHeight = -1;
|
||||||
|
|
||||||
|
private PropertyChangeListener propertyChangeListener;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
private AtomicBoolean borderShared;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatMenuBarUI();
|
return new FlatMenuBarUI();
|
||||||
}
|
}
|
||||||
@@ -63,6 +88,13 @@ public class FlatMenuBarUI
|
|||||||
* Do not add any functionality here.
|
* Do not add any functionality here.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
@@ -70,6 +102,30 @@ public class FlatMenuBarUI
|
|||||||
LookAndFeel.installProperty( menuBar, "opaque", false );
|
LookAndFeel.installProperty( menuBar, "opaque", false );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallDefaults() {
|
||||||
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
borderShared = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installListeners() {
|
||||||
|
super.installListeners();
|
||||||
|
|
||||||
|
propertyChangeListener = FlatStylingSupport.createPropertyChangeListener( menuBar, this::installStyle, null );
|
||||||
|
menuBar.addPropertyChangeListener( propertyChangeListener );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallListeners() {
|
||||||
|
super.uninstallListeners();
|
||||||
|
|
||||||
|
menuBar.removePropertyChangeListener( propertyChangeListener );
|
||||||
|
propertyChangeListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installKeyboardActions() {
|
protected void installKeyboardActions() {
|
||||||
super.installKeyboardActions();
|
super.installKeyboardActions();
|
||||||
@@ -82,6 +138,33 @@ public class FlatMenuBarUI
|
|||||||
map.put( "takeFocus", new TakeFocus() );
|
map.put( "takeFocus", new TakeFocus() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle() {
|
||||||
|
try {
|
||||||
|
applyStyle( FlatStylingSupport.getResolvedStyle( menuBar, "MenuBar" ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( Object style ) {
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
if( borderShared == null )
|
||||||
|
borderShared = new AtomicBoolean( true );
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, menuBar, borderShared );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this, menuBar.getBorder() );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update( Graphics g, JComponent c ) {
|
public void update( Graphics g, JComponent c ) {
|
||||||
// paint background
|
// paint background
|
||||||
@@ -97,20 +180,22 @@ public class FlatMenuBarUI
|
|||||||
protected Color getBackground( JComponent c ) {
|
protected Color getBackground( JComponent c ) {
|
||||||
Color background = c.getBackground();
|
Color background = c.getBackground();
|
||||||
|
|
||||||
// paint background if opaque or if having custom background color
|
// paint background if opaque
|
||||||
if( c.isOpaque() || !(background instanceof UIResource) )
|
if( c.isOpaque() )
|
||||||
return background;
|
return background;
|
||||||
|
|
||||||
// paint background if menu bar is not the "main" menu bar
|
// do not paint background if non-opaque and having custom background color
|
||||||
|
if( !(background instanceof UIResource) )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// paint background if menu bar is not the "main" menu bar (e.g. in internal frame)
|
||||||
JRootPane rootPane = SwingUtilities.getRootPane( c );
|
JRootPane rootPane = SwingUtilities.getRootPane( c );
|
||||||
if( rootPane == null || !(rootPane.getParent() instanceof Window) || rootPane.getJMenuBar() != c )
|
if( rootPane == null || !(rootPane.getParent() instanceof Window) || rootPane.getJMenuBar() != c )
|
||||||
return background;
|
return background;
|
||||||
|
|
||||||
// use parent background for unified title pane
|
// use parent background for unified title pane
|
||||||
// (not storing value of "TitlePane.unifiedBackground" in class to allow changing at runtime)
|
if( useUnifiedBackground( c ) )
|
||||||
if( UIManager.getBoolean( "TitlePane.unifiedBackground" ) &&
|
background = FlatUIUtils.getParentBackground( c );
|
||||||
FlatNativeWindowBorder.hasCustomDecoration( (Window) rootPane.getParent() ) )
|
|
||||||
background = FlatUIUtils.getParentBackground( c );
|
|
||||||
|
|
||||||
// paint background in full screen mode
|
// paint background in full screen mode
|
||||||
if( FlatUIUtils.isFullScreen( rootPane ) )
|
if( FlatUIUtils.isFullScreen( rootPane ) )
|
||||||
@@ -120,6 +205,22 @@ public class FlatMenuBarUI
|
|||||||
return FlatRootPaneUI.isMenuBarEmbedded( rootPane ) ? null : background;
|
return FlatRootPaneUI.isMenuBarEmbedded( rootPane ) ? null : background;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**@since 2 */
|
||||||
|
static boolean useUnifiedBackground( Component c ) {
|
||||||
|
// check whether:
|
||||||
|
// - TitlePane.unifiedBackground is true and
|
||||||
|
// - menu bar is the "main" menu bar and
|
||||||
|
// - window root pane has custom decoration style
|
||||||
|
|
||||||
|
JRootPane rootPane;
|
||||||
|
// (not storing value of "TitlePane.unifiedBackground" in class to allow changing at runtime)
|
||||||
|
return UIManager.getBoolean( "TitlePane.unifiedBackground" ) &&
|
||||||
|
(rootPane = SwingUtilities.getRootPane( c )) != null &&
|
||||||
|
rootPane.getParent() instanceof Window &&
|
||||||
|
rootPane.getJMenuBar() == c &&
|
||||||
|
rootPane.getWindowDecorationStyle() != JRootPane.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
//---- class TakeFocus ----------------------------------------------------
|
//---- class TakeFocus ----------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -18,9 +18,11 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
import static com.formdev.flatlaf.util.UIScale.scale;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
|
import java.awt.Container;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import javax.swing.JMenuBar;
|
import javax.swing.JMenuBar;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.plaf.MenuBarUI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Border for {@link javax.swing.JMenu}, {@link javax.swing.JMenuItem},
|
* Border for {@link javax.swing.JMenu}, {@link javax.swing.JMenuItem},
|
||||||
@@ -33,15 +35,22 @@ import javax.swing.UIManager;
|
|||||||
public class FlatMenuItemBorder
|
public class FlatMenuItemBorder
|
||||||
extends FlatMarginBorder
|
extends FlatMarginBorder
|
||||||
{
|
{
|
||||||
|
// only used if parent menubar is not an instance of FlatMenuBarUI
|
||||||
private final Insets menuBarItemMargins = UIManager.getInsets( "MenuBar.itemMargins" );
|
private final Insets menuBarItemMargins = UIManager.getInsets( "MenuBar.itemMargins" );
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
if( c.getParent() instanceof JMenuBar ) {
|
Container parent = c.getParent();
|
||||||
insets.top = scale( menuBarItemMargins.top );
|
if( parent instanceof JMenuBar ) {
|
||||||
insets.left = scale( menuBarItemMargins.left );
|
// get margins from FlatMenuBarUI to allow styling
|
||||||
insets.bottom = scale( menuBarItemMargins.bottom );
|
MenuBarUI ui = ((JMenuBar)parent).getUI();
|
||||||
insets.right = scale( menuBarItemMargins.right );
|
Insets margins = (ui instanceof FlatMenuBarUI && ((FlatMenuBarUI)ui).itemMargins != null)
|
||||||
|
? ((FlatMenuBarUI)ui).itemMargins
|
||||||
|
: this.menuBarItemMargins;
|
||||||
|
insets.top = scale( margins.top );
|
||||||
|
insets.left = scale( margins.left );
|
||||||
|
insets.bottom = scale( margins.bottom );
|
||||||
|
insets.right = scale( margins.right );
|
||||||
return insets;
|
return insets;
|
||||||
} else
|
} else
|
||||||
return super.getBorderInsets( c, insets );
|
return super.getBorderInsets( c, insets );
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package com.formdev.flatlaf.ui;
|
|||||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
import static com.formdev.flatlaf.util.UIScale.scale;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
|
import java.awt.Container;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
import java.awt.FontMetrics;
|
import java.awt.FontMetrics;
|
||||||
@@ -30,7 +31,9 @@ import java.awt.Rectangle;
|
|||||||
import java.awt.event.InputEvent;
|
import java.awt.event.InputEvent;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
import java.text.AttributedCharacterIterator;
|
import java.text.AttributedCharacterIterator;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JMenu;
|
import javax.swing.JMenu;
|
||||||
import javax.swing.JMenuItem;
|
import javax.swing.JMenuItem;
|
||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
@@ -38,7 +41,12 @@ import javax.swing.SwingUtilities;
|
|||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.basic.BasicHTML;
|
import javax.swing.plaf.basic.BasicHTML;
|
||||||
import javax.swing.text.View;
|
import javax.swing.text.View;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
|
import com.formdev.flatlaf.icons.FlatCheckBoxMenuItemIcon;
|
||||||
|
import com.formdev.flatlaf.icons.FlatMenuArrowIcon;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||||
import com.formdev.flatlaf.util.DerivedColor;
|
import com.formdev.flatlaf.util.DerivedColor;
|
||||||
import com.formdev.flatlaf.util.Graphics2DProxy;
|
import com.formdev.flatlaf.util.Graphics2DProxy;
|
||||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
@@ -47,43 +55,48 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
/**
|
/**
|
||||||
* Renderer for menu items.
|
* Renderer for menu items.
|
||||||
*
|
*
|
||||||
|
* @uiDefault MenuItem.verticallyAlignText boolean
|
||||||
* @uiDefault MenuItem.minimumWidth int
|
* @uiDefault MenuItem.minimumWidth int
|
||||||
* @uiDefault MenuItem.minimumIconSize Dimension
|
* @uiDefault MenuItem.minimumIconSize Dimension
|
||||||
* @uiDefault MenuItem.textAcceleratorGap int
|
* @uiDefault MenuItem.textAcceleratorGap int
|
||||||
* @uiDefault MenuItem.textNoAcceleratorGap int
|
* @uiDefault MenuItem.textNoAcceleratorGap int
|
||||||
* @uiDefault MenuItem.acceleratorArrowGap int
|
* @uiDefault MenuItem.acceleratorArrowGap int
|
||||||
* @uiDefault MenuItem.checkBackground Color
|
* @uiDefault MenuItem.checkBackground Color
|
||||||
|
* @uiDefault MenuItem.checkMargins Insets
|
||||||
|
* @uiDefault MenuItem.selectionType String null (default) or underline
|
||||||
* @uiDefault MenuItem.underlineSelectionBackground Color
|
* @uiDefault MenuItem.underlineSelectionBackground Color
|
||||||
* @uiDefault MenuItem.underlineSelectionCheckBackground Color
|
* @uiDefault MenuItem.underlineSelectionCheckBackground Color
|
||||||
* @uiDefault MenuItem.underlineSelectionColor Color
|
* @uiDefault MenuItem.underlineSelectionColor Color
|
||||||
* @uiDefault MenuItem.underlineSelectionHeight int
|
* @uiDefault MenuItem.underlineSelectionHeight int
|
||||||
* @uiDefault MenuItem.selectionBackground Color
|
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatMenuItemRenderer
|
public class FlatMenuItemRenderer
|
||||||
{
|
{
|
||||||
|
private static final String KEY_MAX_ICONS_WIDTH = "FlatLaf.internal.FlatMenuItemRenderer.maxIconWidth";
|
||||||
|
|
||||||
protected final JMenuItem menuItem;
|
protected final JMenuItem menuItem;
|
||||||
protected final Icon checkIcon;
|
protected Icon checkIcon;
|
||||||
protected final Icon arrowIcon;
|
protected Icon arrowIcon;
|
||||||
protected final Font acceleratorFont;
|
@Styleable protected Font acceleratorFont;
|
||||||
protected final String acceleratorDelimiter;
|
protected final String acceleratorDelimiter;
|
||||||
|
|
||||||
protected final int minimumWidth = UIManager.getInt( "MenuItem.minimumWidth" );
|
/** @since 2 */ @Styleable protected boolean verticallyAlignText = FlatUIUtils.getUIBoolean( "MenuItem.verticallyAlignText", true );
|
||||||
protected final Dimension minimumIconSize;
|
@Styleable protected int minimumWidth = UIManager.getInt( "MenuItem.minimumWidth" );
|
||||||
protected final int textAcceleratorGap = FlatUIUtils.getUIInt( "MenuItem.textAcceleratorGap", 28 );
|
@Styleable protected Dimension minimumIconSize;
|
||||||
protected final int textNoAcceleratorGap = FlatUIUtils.getUIInt( "MenuItem.textNoAcceleratorGap", 6 );
|
@Styleable protected int textAcceleratorGap = FlatUIUtils.getUIInt( "MenuItem.textAcceleratorGap", 28 );
|
||||||
protected final int acceleratorArrowGap = FlatUIUtils.getUIInt( "MenuItem.acceleratorArrowGap", 2 );
|
@Styleable protected int textNoAcceleratorGap = FlatUIUtils.getUIInt( "MenuItem.textNoAcceleratorGap", 6 );
|
||||||
|
@Styleable protected int acceleratorArrowGap = FlatUIUtils.getUIInt( "MenuItem.acceleratorArrowGap", 2 );
|
||||||
|
|
||||||
protected final Color checkBackground = UIManager.getColor( "MenuItem.checkBackground" );
|
@Styleable protected Color checkBackground = UIManager.getColor( "MenuItem.checkBackground" );
|
||||||
protected final Insets checkMargins = UIManager.getInsets( "MenuItem.checkMargins" );
|
@Styleable protected Insets checkMargins = UIManager.getInsets( "MenuItem.checkMargins" );
|
||||||
|
|
||||||
protected final Color underlineSelectionBackground = UIManager.getColor( "MenuItem.underlineSelectionBackground" );
|
@Styleable protected Color underlineSelectionBackground = UIManager.getColor( "MenuItem.underlineSelectionBackground" );
|
||||||
protected final Color underlineSelectionCheckBackground = UIManager.getColor( "MenuItem.underlineSelectionCheckBackground" );
|
@Styleable protected Color underlineSelectionCheckBackground = UIManager.getColor( "MenuItem.underlineSelectionCheckBackground" );
|
||||||
protected final Color underlineSelectionColor = UIManager.getColor( "MenuItem.underlineSelectionColor" );
|
@Styleable protected Color underlineSelectionColor = UIManager.getColor( "MenuItem.underlineSelectionColor" );
|
||||||
protected final int underlineSelectionHeight = UIManager.getInt( "MenuItem.underlineSelectionHeight" );
|
@Styleable protected int underlineSelectionHeight = UIManager.getInt( "MenuItem.underlineSelectionHeight" );
|
||||||
|
|
||||||
protected final Color selectionBackground = UIManager.getColor( "MenuItem.selectionBackground" );
|
private boolean iconsShared = true;
|
||||||
|
|
||||||
protected FlatMenuItemRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
|
protected FlatMenuItemRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
|
||||||
Font acceleratorFont, String acceleratorDelimiter )
|
Font acceleratorFont, String acceleratorDelimiter )
|
||||||
@@ -98,6 +111,64 @@ public class FlatMenuItemRenderer
|
|||||||
this.minimumIconSize = (minimumIconSize != null) ? minimumIconSize : new Dimension( 16, 16 );
|
this.minimumIconSize = (minimumIconSize != null) ? minimumIconSize : new Dimension( 16, 16 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
// style icon
|
||||||
|
if( key.startsWith( "icon." ) || key.equals( "selectionForeground" ) ) {
|
||||||
|
if( iconsShared ) {
|
||||||
|
if( checkIcon instanceof FlatCheckBoxMenuItemIcon )
|
||||||
|
checkIcon = FlatStylingSupport.cloneIcon( checkIcon );
|
||||||
|
if( arrowIcon instanceof FlatMenuArrowIcon )
|
||||||
|
arrowIcon = FlatStylingSupport.cloneIcon( arrowIcon );
|
||||||
|
iconsShared = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( key.startsWith( "icon." ) ) {
|
||||||
|
String key2 = key.substring( "icon.".length() );
|
||||||
|
|
||||||
|
try {
|
||||||
|
if( checkIcon instanceof FlatCheckBoxMenuItemIcon )
|
||||||
|
return ((FlatCheckBoxMenuItemIcon)checkIcon).applyStyleProperty( key2, value );
|
||||||
|
} catch ( UnknownStyleException ex ) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if( arrowIcon instanceof FlatMenuArrowIcon )
|
||||||
|
return ((FlatMenuArrowIcon)arrowIcon).applyStyleProperty( key2, value );
|
||||||
|
} catch ( UnknownStyleException ex ) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
// keys with prefix "icon." are only for icons
|
||||||
|
throw new UnknownStyleException( key );
|
||||||
|
} else if( key.equals( "selectionForeground" ) ) {
|
||||||
|
// special case: same key is used in icons and in menuitem
|
||||||
|
if( checkIcon instanceof FlatCheckBoxMenuItemIcon )
|
||||||
|
((FlatCheckBoxMenuItemIcon)checkIcon).applyStyleProperty( key, value );
|
||||||
|
if( arrowIcon instanceof FlatMenuArrowIcon )
|
||||||
|
((FlatMenuArrowIcon)arrowIcon).applyStyleProperty( key, value );
|
||||||
|
|
||||||
|
// throw exception because the caller should also apply this key
|
||||||
|
throw new UnknownStyleException( key );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public Map<String, Class<?>> getStyleableInfos() {
|
||||||
|
Map<String, Class<?>> infos = FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
|
if( checkIcon instanceof FlatCheckBoxMenuItemIcon )
|
||||||
|
FlatStylingSupport.putAllPrefixKey( infos, "icon.", ((FlatCheckBoxMenuItemIcon)checkIcon).getStyleableInfos() );
|
||||||
|
infos.remove( "icon.selectionForeground" );
|
||||||
|
if( arrowIcon instanceof FlatMenuArrowIcon )
|
||||||
|
FlatStylingSupport.putAllPrefixKey( infos, "icon.", ((FlatMenuArrowIcon)arrowIcon).getStyleableInfos() );
|
||||||
|
infos.remove( "icon.selectionForeground" );
|
||||||
|
return infos;
|
||||||
|
}
|
||||||
|
|
||||||
protected Dimension getPreferredMenuItemSize() {
|
protected Dimension getPreferredMenuItemSize() {
|
||||||
int width = 0;
|
int width = 0;
|
||||||
int height = 0;
|
int height = 0;
|
||||||
@@ -254,7 +325,7 @@ debug*/
|
|||||||
paintBackground( g, underlineSelection ? underlineSelectionBackground : selectionBackground );
|
paintBackground( g, underlineSelection ? underlineSelectionBackground : selectionBackground );
|
||||||
if( underlineSelection && isArmedOrSelected( menuItem ) )
|
if( underlineSelection && isArmedOrSelected( menuItem ) )
|
||||||
paintUnderlineSelection( g, underlineSelectionColor, underlineSelectionHeight );
|
paintUnderlineSelection( g, underlineSelectionColor, underlineSelectionHeight );
|
||||||
paintIcon( g, iconRect, getIconForPainting(), underlineSelection ? underlineSelectionCheckBackground : checkBackground );
|
paintIcon( g, iconRect, getIconForPainting(), underlineSelection ? underlineSelectionCheckBackground : checkBackground, selectionBackground );
|
||||||
paintText( g, textRect, menuItem.getText(), selectionForeground, disabledForeground );
|
paintText( g, textRect, menuItem.getText(), selectionForeground, disabledForeground );
|
||||||
paintAccelerator( g, accelRect, getAcceleratorText(), acceleratorForeground, acceleratorSelectionForeground, disabledForeground );
|
paintAccelerator( g, accelRect, getAcceleratorText(), acceleratorForeground, acceleratorSelectionForeground, disabledForeground );
|
||||||
if( !isTopLevelMenu( menuItem ) )
|
if( !isTopLevelMenu( menuItem ) )
|
||||||
@@ -301,7 +372,7 @@ debug*/
|
|||||||
return FlatUIUtils.deriveColor( background, baseColor );
|
return FlatUIUtils.deriveColor( background, baseColor );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void paintIcon( Graphics g, Rectangle iconRect, Icon icon, Color checkBackground ) {
|
protected void paintIcon( Graphics g, Rectangle iconRect, Icon icon, Color checkBackground, Color selectionBackground ) {
|
||||||
// if checkbox/radiobutton menu item is selected and also has a custom icon,
|
// if checkbox/radiobutton menu item is selected and also has a custom icon,
|
||||||
// then use filled icon background to indicate selection (instead of using checkIcon)
|
// then use filled icon background to indicate selection (instead of using checkIcon)
|
||||||
if( menuItem.isSelected() && checkIcon != null && icon != checkIcon ) {
|
if( menuItem.isSelected() && checkIcon != null && icon != checkIcon ) {
|
||||||
@@ -343,11 +414,10 @@ debug*/
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// center because the real icon may be smaller than dimension in iconRect
|
// center because the real icon may be smaller than dimension in iconRect
|
||||||
int x = iconRect.x + centerOffset( iconRect.width, icon.getIconWidth() );
|
|
||||||
int y = iconRect.y + centerOffset( iconRect.height, icon.getIconHeight() );
|
int y = iconRect.y + centerOffset( iconRect.height, icon.getIconHeight() );
|
||||||
|
|
||||||
// paint
|
// paint
|
||||||
icon.paintIcon( menuItem, g, x, y );
|
icon.paintIcon( menuItem, g, iconRect.x, y );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void paintText( Graphics g, JMenuItem menuItem,
|
protected static void paintText( Graphics g, JMenuItem menuItem,
|
||||||
@@ -382,6 +452,10 @@ debug*/
|
|||||||
htmlView.paint( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ), textRect );
|
htmlView.paint( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ), textRect );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if either the menu item is armed (mouse over item)
|
||||||
|
* or it is a {@code JMenu} and selected (shows submenu).
|
||||||
|
*/
|
||||||
protected static boolean isArmedOrSelected( JMenuItem menuItem ) {
|
protected static boolean isArmedOrSelected( JMenuItem menuItem ) {
|
||||||
return menuItem.isArmed() || (menuItem instanceof JMenu && menuItem.isSelected());
|
return menuItem.isArmed() || (menuItem instanceof JMenu && menuItem.isSelected());
|
||||||
}
|
}
|
||||||
@@ -412,6 +486,12 @@ debug*/
|
|||||||
return pressedIcon;
|
return pressedIcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( isArmedOrSelected( menuItem ) ) {
|
||||||
|
Icon selectedIcon = menuItem.getSelectedIcon();
|
||||||
|
if( selectedIcon != null )
|
||||||
|
return selectedIcon;
|
||||||
|
}
|
||||||
|
|
||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -498,6 +578,44 @@ debug*/
|
|||||||
shiftGlyph = 0x21E7,
|
shiftGlyph = 0x21E7,
|
||||||
commandGlyph = 0x2318;
|
commandGlyph = 0x2318;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the maximum width of all menu item icons in the popup.
|
||||||
|
*/
|
||||||
|
private int getMaxIconsWidth() {
|
||||||
|
if( !verticallyAlignText )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Container parent = menuItem.getParent();
|
||||||
|
if( !(parent instanceof JComponent) )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int maxWidth = FlatClientProperties.clientPropertyInt( (JComponent) parent, KEY_MAX_ICONS_WIDTH, -1 );
|
||||||
|
if( maxWidth >= 0 )
|
||||||
|
return maxWidth;
|
||||||
|
|
||||||
|
maxWidth = 0;
|
||||||
|
|
||||||
|
for( Component c : parent.getComponents() ) {
|
||||||
|
if( !(c instanceof JMenuItem) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Icon icon = ((JMenuItem)c).getIcon();
|
||||||
|
if( icon != null )
|
||||||
|
maxWidth = Math.max( maxWidth, icon.getIconWidth() );
|
||||||
|
}
|
||||||
|
|
||||||
|
((JComponent)parent).putClientProperty( KEY_MAX_ICONS_WIDTH, maxWidth );
|
||||||
|
return maxWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clearClientProperties( Component c ) {
|
||||||
|
if( !(c instanceof JComponent) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
JComponent jc = (JComponent) c;
|
||||||
|
jc.putClientProperty( FlatMenuItemRenderer.KEY_MAX_ICONS_WIDTH, null );
|
||||||
|
}
|
||||||
|
|
||||||
//---- class MinSizeIcon --------------------------------------------------
|
//---- class MinSizeIcon --------------------------------------------------
|
||||||
|
|
||||||
private class MinSizeIcon
|
private class MinSizeIcon
|
||||||
@@ -512,6 +630,7 @@ debug*/
|
|||||||
@Override
|
@Override
|
||||||
public int getIconWidth() {
|
public int getIconWidth() {
|
||||||
int iconWidth = (delegate != null) ? delegate.getIconWidth() : 0;
|
int iconWidth = (delegate != null) ? delegate.getIconWidth() : 0;
|
||||||
|
iconWidth = Math.max( iconWidth, getMaxIconsWidth() );
|
||||||
return Math.max( iconWidth, scale( minimumIconSize.width ) );
|
return Math.max( iconWidth, scale( minimumIconSize.width ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,13 +16,19 @@
|
|||||||
|
|
||||||
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.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicMenuItemUI;
|
import javax.swing.plaf.basic.BasicMenuItemUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenuItem}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenuItem}.
|
||||||
@@ -54,13 +60,22 @@ import javax.swing.plaf.basic.BasicMenuItemUI;
|
|||||||
*/
|
*/
|
||||||
public class FlatMenuItemUI
|
public class FlatMenuItemUI
|
||||||
extends BasicMenuItemUI
|
extends BasicMenuItemUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
private FlatMenuItemRenderer renderer;
|
private FlatMenuItemRenderer renderer;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatMenuItemUI();
|
return new FlatMenuItemUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
@@ -74,13 +89,72 @@ public class FlatMenuItemUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
FlatMenuItemRenderer.clearClientProperties( menuItem.getParent() );
|
||||||
renderer = null;
|
renderer = null;
|
||||||
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected FlatMenuItemRenderer createRenderer() {
|
protected FlatMenuItemRenderer createRenderer() {
|
||||||
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
||||||
|
return FlatStylingSupport.createPropertyChangeListener( c, this::installStyle, super.createPropertyChangeListener( c ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle() {
|
||||||
|
try {
|
||||||
|
applyStyle( FlatStylingSupport.getResolvedStyle( menuItem, "MenuItem" ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( Object style ) {
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
try {
|
||||||
|
return renderer.applyStyleProperty( key, value );
|
||||||
|
} catch ( UnknownStyleException ex ) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
Object oldValue;
|
||||||
|
switch( key ) {
|
||||||
|
// BasicMenuItemUI
|
||||||
|
case "selectionBackground": oldValue = selectionBackground; selectionBackground = (Color) value; return oldValue;
|
||||||
|
case "selectionForeground": oldValue = selectionForeground; selectionForeground = (Color) value; return oldValue;
|
||||||
|
case "disabledForeground": oldValue = disabledForeground; disabledForeground = (Color) value; return oldValue;
|
||||||
|
case "acceleratorForeground": oldValue = acceleratorForeground; acceleratorForeground = (Color) value; return oldValue;
|
||||||
|
case "acceleratorSelectionForeground": oldValue = acceleratorSelectionForeground; acceleratorSelectionForeground = (Color) value; return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, menuItem, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return getStyleableInfos( renderer );
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, Class<?>> getStyleableInfos( FlatMenuItemRenderer renderer ) {
|
||||||
|
Map<String, Class<?>> infos = new FlatStylingSupport.StyleableInfosMap<>();
|
||||||
|
infos.put( "selectionBackground", Color.class );
|
||||||
|
infos.put( "selectionForeground", Color.class );
|
||||||
|
infos.put( "disabledForeground", Color.class );
|
||||||
|
infos.put( "acceleratorForeground", Color.class );
|
||||||
|
infos.put( "acceleratorSelectionForeground", Color.class );
|
||||||
|
infos.putAll( renderer.getStyleableInfos() );
|
||||||
|
return infos;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
|
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
|
||||||
return renderer.getPreferredMenuItemSize();
|
return renderer.getPreferredMenuItemSize();
|
||||||
|
|||||||
@@ -21,16 +21,24 @@ import java.awt.Dimension;
|
|||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
import javax.swing.ButtonModel;
|
import javax.swing.ButtonModel;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JMenu;
|
import javax.swing.JMenu;
|
||||||
|
import javax.swing.JMenuBar;
|
||||||
import javax.swing.JMenuItem;
|
import javax.swing.JMenuItem;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.event.MouseInputListener;
|
import javax.swing.event.MouseInputListener;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import javax.swing.plaf.MenuBarUI;
|
||||||
import javax.swing.plaf.basic.BasicMenuUI;
|
import javax.swing.plaf.basic.BasicMenuUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenu}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenu}.
|
||||||
@@ -60,10 +68,10 @@ import javax.swing.plaf.basic.BasicMenuUI;
|
|||||||
* <!-- FlatMenuUI -->
|
* <!-- FlatMenuUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault MenuItem.iconTextGap int
|
* @uiDefault MenuItem.iconTextGap int
|
||||||
* @uiDefault MenuBar.hoverBackground Color
|
|
||||||
*
|
*
|
||||||
* <!-- FlatMenuRenderer -->
|
* <!-- FlatMenuRenderer -->
|
||||||
*
|
*
|
||||||
|
* @uiDefault MenuBar.hoverBackground Color
|
||||||
* @uiDefault MenuBar.underlineSelectionBackground Color
|
* @uiDefault MenuBar.underlineSelectionBackground Color
|
||||||
* @uiDefault MenuBar.underlineSelectionColor Color
|
* @uiDefault MenuBar.underlineSelectionColor Color
|
||||||
* @uiDefault MenuBar.underlineSelectionHeight int
|
* @uiDefault MenuBar.underlineSelectionHeight int
|
||||||
@@ -72,14 +80,22 @@ import javax.swing.plaf.basic.BasicMenuUI;
|
|||||||
*/
|
*/
|
||||||
public class FlatMenuUI
|
public class FlatMenuUI
|
||||||
extends BasicMenuUI
|
extends BasicMenuUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
private Color hoverBackground;
|
|
||||||
private FlatMenuItemRenderer renderer;
|
private FlatMenuItemRenderer renderer;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatMenuUI();
|
return new FlatMenuUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
@@ -88,7 +104,6 @@ public class FlatMenuUI
|
|||||||
|
|
||||||
menuItem.setRolloverEnabled( true );
|
menuItem.setRolloverEnabled( true );
|
||||||
|
|
||||||
hoverBackground = UIManager.getColor( "MenuBar.hoverBackground" );
|
|
||||||
renderer = createRenderer();
|
renderer = createRenderer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,8 +111,9 @@ public class FlatMenuUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
hoverBackground = null;
|
FlatMenuItemRenderer.clearClientProperties( menuItem.getParent() );
|
||||||
renderer = null;
|
renderer = null;
|
||||||
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected FlatMenuItemRenderer createRenderer() {
|
protected FlatMenuItemRenderer createRenderer() {
|
||||||
@@ -129,6 +145,52 @@ public class FlatMenuUI
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
||||||
|
return FlatStylingSupport.createPropertyChangeListener( c, this::installStyle, super.createPropertyChangeListener( c ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle() {
|
||||||
|
try {
|
||||||
|
applyStyle( FlatStylingSupport.getResolvedStyle( menuItem, "Menu" ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( Object style ) {
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
try {
|
||||||
|
return renderer.applyStyleProperty( key, value );
|
||||||
|
} catch ( UnknownStyleException ex ) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
Object oldValue;
|
||||||
|
switch( key ) {
|
||||||
|
// BasicMenuItemUI
|
||||||
|
case "selectionBackground": oldValue = selectionBackground; selectionBackground = (Color) value; return oldValue;
|
||||||
|
case "selectionForeground": oldValue = selectionForeground; selectionForeground = (Color) value; return oldValue;
|
||||||
|
case "disabledForeground": oldValue = disabledForeground; disabledForeground = (Color) value; return oldValue;
|
||||||
|
case "acceleratorForeground": oldValue = acceleratorForeground; acceleratorForeground = (Color) value; return oldValue;
|
||||||
|
case "acceleratorSelectionForeground": oldValue = acceleratorSelectionForeground; acceleratorSelectionForeground = (Color) value; return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, menuItem, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return FlatMenuItemUI.getStyleableInfos( renderer );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getMinimumSize( JComponent c ) {
|
public Dimension getMinimumSize( JComponent c ) {
|
||||||
// avoid that top-level menus (in menu bar) are made smaller if horizontal space is rare
|
// avoid that top-level menus (in menu bar) are made smaller if horizontal space is rare
|
||||||
@@ -153,9 +215,10 @@ public class FlatMenuUI
|
|||||||
protected class FlatMenuRenderer
|
protected class FlatMenuRenderer
|
||||||
extends FlatMenuItemRenderer
|
extends FlatMenuItemRenderer
|
||||||
{
|
{
|
||||||
protected final Color menuBarUnderlineSelectionBackground = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionBackground", underlineSelectionBackground );
|
protected Color hoverBackground = UIManager.getColor( "MenuBar.hoverBackground" );
|
||||||
protected final Color menuBarUnderlineSelectionColor = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionColor", underlineSelectionColor );
|
protected Color menuBarUnderlineSelectionBackground = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionBackground", underlineSelectionBackground );
|
||||||
protected final int menuBarUnderlineSelectionHeight = FlatUIUtils.getUIInt( "MenuBar.underlineSelectionHeight", underlineSelectionHeight );
|
protected Color menuBarUnderlineSelectionColor = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionColor", underlineSelectionColor );
|
||||||
|
protected int menuBarUnderlineSelectionHeight = FlatUIUtils.getUIInt( "MenuBar.underlineSelectionHeight", underlineSelectionHeight );
|
||||||
|
|
||||||
protected FlatMenuRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
|
protected FlatMenuRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
|
||||||
Font acceleratorFont, String acceleratorDelimiter )
|
Font acceleratorFont, String acceleratorDelimiter )
|
||||||
@@ -165,27 +228,39 @@ public class FlatMenuUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintBackground( Graphics g, Color selectionBackground ) {
|
protected void paintBackground( Graphics g, Color selectionBackground ) {
|
||||||
if( isUnderlineSelection() && ((JMenu)menuItem).isTopLevelMenu() )
|
if( ((JMenu)menuItem).isTopLevelMenu() ) {
|
||||||
selectionBackground = menuBarUnderlineSelectionBackground;
|
if( isUnderlineSelection() )
|
||||||
|
selectionBackground = getStyleFromMenuBarUI( ui -> ui.underlineSelectionBackground, menuBarUnderlineSelectionBackground );
|
||||||
|
|
||||||
ButtonModel model = menuItem.getModel();
|
ButtonModel model = menuItem.getModel();
|
||||||
if( model.isRollover() && !model.isArmed() && !model.isSelected() &&
|
if( model.isRollover() && !model.isArmed() && !model.isSelected() && model.isEnabled() ) {
|
||||||
model.isEnabled() && ((JMenu)menuItem).isTopLevelMenu() )
|
g.setColor( deriveBackground( getStyleFromMenuBarUI( ui -> ui.hoverBackground, hoverBackground ) ) );
|
||||||
{
|
g.fillRect( 0, 0, menuItem.getWidth(), menuItem.getHeight() );
|
||||||
g.setColor( deriveBackground( hoverBackground ) );
|
return;
|
||||||
g.fillRect( 0, 0, menuItem.getWidth(), menuItem.getHeight() );
|
}
|
||||||
} else
|
}
|
||||||
super.paintBackground( g, selectionBackground );
|
|
||||||
|
super.paintBackground( g, selectionBackground );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintUnderlineSelection( Graphics g, Color underlineSelectionColor, int underlineSelectionHeight ) {
|
protected void paintUnderlineSelection( Graphics g, Color underlineSelectionColor, int underlineSelectionHeight ) {
|
||||||
if( ((JMenu)menuItem).isTopLevelMenu() ) {
|
if( ((JMenu)menuItem).isTopLevelMenu() ) {
|
||||||
underlineSelectionColor = menuBarUnderlineSelectionColor;
|
underlineSelectionColor = getStyleFromMenuBarUI( ui -> ui.underlineSelectionColor, menuBarUnderlineSelectionColor );
|
||||||
underlineSelectionHeight = menuBarUnderlineSelectionHeight;
|
underlineSelectionHeight = getStyleFromMenuBarUI( ui -> (ui.underlineSelectionHeight != -1)
|
||||||
|
? ui.underlineSelectionHeight : null, menuBarUnderlineSelectionHeight );
|
||||||
}
|
}
|
||||||
|
|
||||||
super.paintUnderlineSelection( g, underlineSelectionColor, underlineSelectionHeight );
|
super.paintUnderlineSelection( g, underlineSelectionColor, underlineSelectionHeight );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private <T> T getStyleFromMenuBarUI( Function<FlatMenuBarUI, T> f, T defaultValue ) {
|
||||||
|
MenuBarUI ui = ((JMenuBar)menuItem.getParent()).getUI();
|
||||||
|
if( !(ui instanceof FlatMenuBarUI) )
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
T value = f.apply( (FlatMenuBarUI) ui );
|
||||||
|
return (value != null) ? value : defaultValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import com.formdev.flatlaf.FlatSystemProperties;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
import com.formdev.flatlaf.util.NativeLibrary;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to load FlatLaf native library (.dll, .so or .dylib),
|
||||||
|
* if available for current operating system and CPU architecture.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
* @since 2.3
|
||||||
|
*/
|
||||||
|
class FlatNativeLibrary
|
||||||
|
{
|
||||||
|
private static NativeLibrary nativeLibrary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads native library (if available) and returns whether loaded successfully.
|
||||||
|
* Returns {@code false} if no native library is available.
|
||||||
|
*/
|
||||||
|
static synchronized boolean isLoaded() {
|
||||||
|
initialize();
|
||||||
|
return (nativeLibrary != null) ? nativeLibrary.isLoaded() : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void initialize() {
|
||||||
|
if( nativeLibrary != null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
String libraryName;
|
||||||
|
if( SystemInfo.isWindows_10_orLater && (SystemInfo.isX86 || SystemInfo.isX86_64) ) {
|
||||||
|
// Windows: requires Windows 10 (x86 or x86_64)
|
||||||
|
|
||||||
|
libraryName = "flatlaf-windows-x86";
|
||||||
|
if( SystemInfo.isX86_64 )
|
||||||
|
libraryName += "_64";
|
||||||
|
|
||||||
|
// load jawt native library
|
||||||
|
loadJAWT();
|
||||||
|
} else
|
||||||
|
return; // no native library available for current OS or CPU architecture
|
||||||
|
|
||||||
|
// load native library
|
||||||
|
nativeLibrary = createNativeLibrary( libraryName );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NativeLibrary createNativeLibrary( String libraryName ) {
|
||||||
|
String libraryPath = System.getProperty( FlatSystemProperties.NATIVE_LIBRARY_PATH );
|
||||||
|
if( libraryPath != null ) {
|
||||||
|
File libraryFile = new File( libraryPath, System.mapLibraryName( libraryName ) );
|
||||||
|
if( libraryFile.exists() )
|
||||||
|
return new NativeLibrary( libraryFile, true );
|
||||||
|
else
|
||||||
|
LoggingFacade.INSTANCE.logSevere( "Did not find external library " + libraryFile + ", using extracted library instead", null );
|
||||||
|
}
|
||||||
|
|
||||||
|
return new NativeLibrary( "com/formdev/flatlaf/natives/" + libraryName, null, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loadJAWT() {
|
||||||
|
if( SystemInfo.isJava_9_orLater )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// In Java 8, load jawt.dll (part of JRE) explicitly because it
|
||||||
|
// is not found when running application with <jdk>/bin/java.exe.
|
||||||
|
// When using <jdk>/jre/bin/java.exe, it is found.
|
||||||
|
// jawt.dll is located in <jdk>/jre/bin/.
|
||||||
|
// Java 9 and later do not have this problem.
|
||||||
|
try {
|
||||||
|
System.loadLibrary( "jawt" );
|
||||||
|
} catch( UnsatisfiedLinkError ex ) {
|
||||||
|
// log error only if native library jawt.dll not already loaded
|
||||||
|
String message = ex.getMessage();
|
||||||
|
if( message == null || !message.contains( "already loaded in another classloader" ) )
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
} catch( Exception ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -56,7 +56,7 @@ public class FlatNativeWindowBorder
|
|||||||
private static final boolean canUseJBRCustomDecorations =
|
private static final boolean canUseJBRCustomDecorations =
|
||||||
canUseWindowDecorations &&
|
canUseWindowDecorations &&
|
||||||
SystemInfo.isJetBrainsJVM_11_orLater &&
|
SystemInfo.isJetBrainsJVM_11_orLater &&
|
||||||
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, true );
|
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, false );
|
||||||
|
|
||||||
private static Boolean supported;
|
private static Boolean supported;
|
||||||
private static Provider nativeProvider;
|
private static Provider nativeProvider;
|
||||||
@@ -235,7 +235,8 @@ public class FlatNativeWindowBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight,
|
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight,
|
||||||
List<Rectangle> hitTestSpots, Rectangle appIconBounds )
|
List<Rectangle> hitTestSpots, Rectangle appIconBounds, Rectangle minimizeButtonBounds,
|
||||||
|
Rectangle maximizeButtonBounds, Rectangle closeButtonBounds )
|
||||||
{
|
{
|
||||||
if( canUseJBRCustomDecorations ) {
|
if( canUseJBRCustomDecorations ) {
|
||||||
JBRCustomDecorations.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots );
|
JBRCustomDecorations.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots );
|
||||||
@@ -245,9 +246,8 @@ public class FlatNativeWindowBorder
|
|||||||
if( !isSupported() )
|
if( !isSupported() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
nativeProvider.setTitleBarHeight( window, titleBarHeight );
|
nativeProvider.updateTitleBarInfo( window, titleBarHeight, hitTestSpots,
|
||||||
nativeProvider.setTitleBarHitTestSpots( window, hitTestSpots );
|
appIconBounds, minimizeButtonBounds, maximizeButtonBounds, closeButtonBounds );
|
||||||
nativeProvider.setTitleBarAppIconBounds( window, appIconBounds );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean showWindow( Window window, int cmd ) {
|
static boolean showWindow( Window window, int cmd ) {
|
||||||
@@ -268,7 +268,7 @@ public class FlatNativeWindowBorder
|
|||||||
try {
|
try {
|
||||||
/*
|
/*
|
||||||
Class<?> cls = Class.forName( "com.formdev.flatlaf.natives.jna.windows.FlatWindowsNativeWindowBorder" );
|
Class<?> cls = Class.forName( "com.formdev.flatlaf.natives.jna.windows.FlatWindowsNativeWindowBorder" );
|
||||||
Method m = cls.getMethod( "getInstance" );
|
java.lang.reflect.Method m = cls.getMethod( "getInstance" );
|
||||||
setNativeProvider( (Provider) m.invoke( null ) );
|
setNativeProvider( (Provider) m.invoke( null ) );
|
||||||
*/
|
*/
|
||||||
setNativeProvider( FlatWindowsNativeWindowBorder.getInstance() );
|
setNativeProvider( FlatWindowsNativeWindowBorder.getInstance() );
|
||||||
@@ -277,9 +277,7 @@ public class FlatNativeWindowBorder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @since 1.1.1 */
|
||||||
* @since 1.1.1
|
|
||||||
*/
|
|
||||||
public static void setNativeProvider( Provider provider ) {
|
public static void setNativeProvider( Provider provider ) {
|
||||||
if( nativeProvider != null )
|
if( nativeProvider != null )
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
@@ -294,9 +292,9 @@ public class FlatNativeWindowBorder
|
|||||||
{
|
{
|
||||||
boolean hasCustomDecoration( Window window );
|
boolean hasCustomDecoration( Window window );
|
||||||
void setHasCustomDecoration( Window window, boolean hasCustomDecoration );
|
void setHasCustomDecoration( Window window, boolean hasCustomDecoration );
|
||||||
void setTitleBarHeight( Window window, int titleBarHeight );
|
void updateTitleBarInfo( Window window, int titleBarHeight, List<Rectangle> hitTestSpots,
|
||||||
void setTitleBarHitTestSpots( Window window, List<Rectangle> hitTestSpots );
|
Rectangle appIconBounds, Rectangle minimizeButtonBounds, Rectangle maximizeButtonBounds,
|
||||||
void setTitleBarAppIconBounds( Window window, Rectangle appIconBounds );
|
Rectangle closeButtonBounds );
|
||||||
|
|
||||||
// commands for showWindow(); values must match Win32 API
|
// commands for showWindow(); values must match Win32 API
|
||||||
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
|
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
|
||||||
@@ -315,6 +313,10 @@ public class FlatNativeWindowBorder
|
|||||||
|
|
||||||
//---- class WindowTopBorder -------------------------------------------
|
//---- class WindowTopBorder -------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Window top border used on Windows 10.
|
||||||
|
* No longer needed since Windows 11.
|
||||||
|
*/
|
||||||
static class WindowTopBorder
|
static class WindowTopBorder
|
||||||
extends JBRCustomDecorations.JBRWindowTopBorder
|
extends JBRCustomDecorations.JBRWindowTopBorder
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -19,21 +19,24 @@ package com.formdev.flatlaf.ui;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
|
||||||
import java.awt.GridBagConstraints;
|
import java.awt.GridBagConstraints;
|
||||||
import java.awt.Insets;
|
import java.beans.PropertyChangeListener;
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
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.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JRootPane;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicHTML;
|
import javax.swing.plaf.basic.BasicHTML;
|
||||||
import javax.swing.plaf.basic.BasicOptionPaneUI;
|
import javax.swing.plaf.basic.BasicOptionPaneUI;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.util.SwingUtils;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -79,6 +82,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*
|
*
|
||||||
* <!-- FlatOptionPaneUI -->
|
* <!-- FlatOptionPaneUI -->
|
||||||
*
|
*
|
||||||
|
* @uiDefault OptionPane.showIcon boolean
|
||||||
* @uiDefault OptionPane.iconMessageGap int
|
* @uiDefault OptionPane.iconMessageGap int
|
||||||
* @uiDefault OptionPane.messagePadding int
|
* @uiDefault OptionPane.messagePadding int
|
||||||
* @uiDefault OptionPane.maxCharactersPerLine int
|
* @uiDefault OptionPane.maxCharactersPerLine int
|
||||||
@@ -88,6 +92,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
public class FlatOptionPaneUI
|
public class FlatOptionPaneUI
|
||||||
extends BasicOptionPaneUI
|
extends BasicOptionPaneUI
|
||||||
{
|
{
|
||||||
|
/** @since 2 */ protected boolean showIcon;
|
||||||
protected int iconMessageGap;
|
protected int iconMessageGap;
|
||||||
protected int messagePadding;
|
protected int messagePadding;
|
||||||
protected int maxCharactersPerLine;
|
protected int maxCharactersPerLine;
|
||||||
@@ -102,6 +107,7 @@ public class FlatOptionPaneUI
|
|||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
|
showIcon = UIManager.getBoolean( "OptionPane.showIcon" );
|
||||||
iconMessageGap = UIManager.getInt( "OptionPane.iconMessageGap" );
|
iconMessageGap = UIManager.getInt( "OptionPane.iconMessageGap" );
|
||||||
messagePadding = UIManager.getInt( "OptionPane.messagePadding" );
|
messagePadding = UIManager.getInt( "OptionPane.messagePadding" );
|
||||||
maxCharactersPerLine = UIManager.getInt( "OptionPane.maxCharactersPerLine" );
|
maxCharactersPerLine = UIManager.getInt( "OptionPane.maxCharactersPerLine" );
|
||||||
@@ -116,6 +122,24 @@ public class FlatOptionPaneUI
|
|||||||
updateChildPanels( optionPane );
|
updateChildPanels( optionPane );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PropertyChangeListener createPropertyChangeListener() {
|
||||||
|
PropertyChangeListener superListener = super.createPropertyChangeListener();
|
||||||
|
return e -> {
|
||||||
|
superListener.propertyChange( e );
|
||||||
|
|
||||||
|
// hide window title bar icon
|
||||||
|
// (only if showIcon is false, otherwise the default behavior is used)
|
||||||
|
if( !showIcon && "ancestor".equals( e.getPropertyName() ) && e.getNewValue() != null ) {
|
||||||
|
JRootPane rootPane = SwingUtilities.getRootPane( optionPane );
|
||||||
|
if( rootPane != null &&
|
||||||
|
rootPane.getContentPane().getComponentCount() > 0 &&
|
||||||
|
rootPane.getContentPane().getComponent( 0 ) == optionPane )
|
||||||
|
rootPane.putClientProperty( FlatClientProperties.TITLE_BAR_SHOW_ICON, false );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getMinimumOptionPaneSize() {
|
public Dimension getMinimumOptionPaneSize() {
|
||||||
return UIScale.scale( super.getMinimumOptionPaneSize() );
|
return UIScale.scale( super.getMinimumOptionPaneSize() );
|
||||||
@@ -133,7 +157,7 @@ public class FlatOptionPaneUI
|
|||||||
|
|
||||||
// set icon-message gap
|
// set icon-message gap
|
||||||
if( iconMessageGap > 0 ) {
|
if( iconMessageGap > 0 ) {
|
||||||
Component iconMessageSeparator = findByName( messageArea, "OptionPane.separator" );
|
Component iconMessageSeparator = SwingUtils.getComponentByName( messageArea, "OptionPane.separator" );
|
||||||
if( iconMessageSeparator != null )
|
if( iconMessageSeparator != null )
|
||||||
iconMessageSeparator.setPreferredSize( new Dimension( UIScale.scale( iconMessageGap ), 1 ) );
|
iconMessageSeparator.setPreferredSize( new Dimension( UIScale.scale( iconMessageGap ), 1 ) );
|
||||||
}
|
}
|
||||||
@@ -205,7 +229,7 @@ public class FlatOptionPaneUI
|
|||||||
// use non-UIResource borders to avoid that they are replaced when switching LaF
|
// use non-UIResource borders to avoid that they are replaced when switching LaF
|
||||||
Border border = panel.getBorder();
|
Border border = panel.getBorder();
|
||||||
if( border instanceof UIResource )
|
if( border instanceof UIResource )
|
||||||
panel.setBorder( new NonUIResourceBorder( border ) );
|
panel.setBorder( FlatUIUtils.nonUIResource( border ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( child instanceof Container )
|
if( child instanceof Container )
|
||||||
@@ -213,49 +237,8 @@ public class FlatOptionPaneUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Component findByName( Container c, String name ) {
|
|
||||||
for( Component child : c.getComponents() ) {
|
|
||||||
if( name.equals( child.getName() ) )
|
|
||||||
return child;
|
|
||||||
|
|
||||||
if( child instanceof Container ) {
|
|
||||||
Component c2 = findByName( (Container) child, name );
|
|
||||||
if( c2 != null )
|
|
||||||
return c2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean getSizeButtonsToSameWidth() {
|
protected boolean getSizeButtonsToSameWidth() {
|
||||||
return sameSizeButtons;
|
return sameSizeButtons;
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class NonUIResourceBorder ------------------------------------------
|
|
||||||
|
|
||||||
private static class NonUIResourceBorder
|
|
||||||
implements Border
|
|
||||||
{
|
|
||||||
private final Border delegate;
|
|
||||||
|
|
||||||
NonUIResourceBorder( Border delegate ) {
|
|
||||||
this.delegate = delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
|
||||||
delegate.paintBorder( c, g, x, y, width, height );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Insets getBorderInsets( Component c ) {
|
|
||||||
return delegate.getBorderInsets( c );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isBorderOpaque() {
|
|
||||||
return delegate.isBorderOpaque();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,20 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JPanel;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicPanelUI;
|
import javax.swing.plaf.basic.BasicPanelUI;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JPanel}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JPanel}.
|
||||||
@@ -27,15 +38,122 @@ import javax.swing.plaf.basic.BasicPanelUI;
|
|||||||
*
|
*
|
||||||
* @uiDefault Panel.font Font unused
|
* @uiDefault Panel.font Font unused
|
||||||
* @uiDefault Panel.background Color only used if opaque
|
* @uiDefault Panel.background Color only used if opaque
|
||||||
* @uiDefault Panel.foreground Color
|
* @uiDefault Panel.foreground Color unused
|
||||||
* @uiDefault Panel.border Border
|
* @uiDefault Panel.border Border
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatPanelUI
|
public class FlatPanelUI
|
||||||
extends BasicPanelUI
|
extends BasicPanelUI
|
||||||
|
implements StyleableUI, PropertyChangeListener
|
||||||
{
|
{
|
||||||
|
// only used via styling (not in UI defaults)
|
||||||
|
/** @since 2 */ @Styleable protected int arc = -1;
|
||||||
|
|
||||||
|
private final boolean shared;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return FlatUIUtils.createSharedUI( FlatPanelUI.class, FlatPanelUI::new );
|
return FlatUIUtils.canUseSharedUI( c )
|
||||||
|
? FlatUIUtils.createSharedUI( FlatPanelUI.class, () -> new FlatPanelUI( true ) )
|
||||||
|
: new FlatPanelUI( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected FlatPanelUI( boolean shared ) {
|
||||||
|
this.shared = shared;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
c.addPropertyChangeListener( this );
|
||||||
|
|
||||||
|
installStyle( (JPanel) c );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uninstallUI( JComponent c ) {
|
||||||
|
super.uninstallUI( c );
|
||||||
|
|
||||||
|
c.removePropertyChangeListener( this );
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.0.1 */
|
||||||
|
@Override
|
||||||
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
switch( e.getPropertyName() ) {
|
||||||
|
case FlatClientProperties.STYLE:
|
||||||
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
|
JPanel c = (JPanel) e.getSource();
|
||||||
|
if( shared && FlatStylingSupport.hasStyleProperty( c ) ) {
|
||||||
|
// unshare component UI if necessary
|
||||||
|
// updateUI() invokes installStyle() from installUI()
|
||||||
|
c.updateUI();
|
||||||
|
} else
|
||||||
|
installStyle( c );
|
||||||
|
c.revalidate();
|
||||||
|
c.repaint();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle( JPanel c ) {
|
||||||
|
try {
|
||||||
|
applyStyle( c, FlatStylingSupport.getResolvedStyle( c, "Panel" ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( JPanel c, Object style ) {
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style,
|
||||||
|
(key, value) -> applyStyleProperty( c, key, value ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( JPanel c, String key, Object value ) {
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, c, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update( Graphics g, JComponent c ) {
|
||||||
|
// fill background
|
||||||
|
if( c.isOpaque() ) {
|
||||||
|
int width = c.getWidth();
|
||||||
|
int height = c.getHeight();
|
||||||
|
int arc = (this.arc >= 0)
|
||||||
|
? this.arc
|
||||||
|
: ((c.getBorder() instanceof FlatLineBorder)
|
||||||
|
? ((FlatLineBorder)c.getBorder()).getArc()
|
||||||
|
: 0);
|
||||||
|
|
||||||
|
// fill background with parent color to avoid garbage in rounded corners
|
||||||
|
if( arc > 0 )
|
||||||
|
FlatUIUtils.paintParentBackground( g, c );
|
||||||
|
|
||||||
|
g.setColor( c.getBackground() );
|
||||||
|
if( arc > 0 ) {
|
||||||
|
// fill rounded rectangle if having rounded corners
|
||||||
|
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
|
||||||
|
FlatUIUtils.paintComponentBackground( (Graphics2D) g, 0, 0, width, height,
|
||||||
|
0, UIScale.scale( arc ) );
|
||||||
|
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||||
|
} else
|
||||||
|
g.fillRect( 0, 0, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
paint( g, c );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,17 +16,20 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Insets;
|
import java.awt.Rectangle;
|
||||||
import java.awt.Shape;
|
|
||||||
import java.awt.Toolkit;
|
import java.awt.Toolkit;
|
||||||
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.util.Map;
|
||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
import javax.swing.ActionMap;
|
import javax.swing.ActionMap;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JPasswordField;
|
||||||
|
import javax.swing.JToggleButton;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
@@ -36,6 +39,10 @@ import javax.swing.text.Element;
|
|||||||
import javax.swing.text.JTextComponent;
|
import javax.swing.text.JTextComponent;
|
||||||
import javax.swing.text.PasswordView;
|
import javax.swing.text.PasswordView;
|
||||||
import javax.swing.text.View;
|
import javax.swing.text.View;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.icons.FlatCapsLockIcon;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JPasswordField}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JPasswordField}.
|
||||||
@@ -61,6 +68,7 @@ import javax.swing.text.View;
|
|||||||
* @uiDefault Component.isIntelliJTheme boolean
|
* @uiDefault Component.isIntelliJTheme boolean
|
||||||
* @uiDefault PasswordField.placeholderForeground Color
|
* @uiDefault PasswordField.placeholderForeground Color
|
||||||
* @uiDefault PasswordField.focusedBackground Color optional
|
* @uiDefault PasswordField.focusedBackground Color optional
|
||||||
|
* @uiDefault PasswordField.iconTextGap int optional, default is 4
|
||||||
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
|
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
|
||||||
* @uiDefault TextComponent.selectAllOnMouseClick boolean
|
* @uiDefault TextComponent.selectAllOnMouseClick boolean
|
||||||
*
|
*
|
||||||
@@ -68,17 +76,29 @@ import javax.swing.text.View;
|
|||||||
*
|
*
|
||||||
* @uiDefault PasswordField.echoChar character
|
* @uiDefault PasswordField.echoChar character
|
||||||
* @uiDefault PasswordField.showCapsLock boolean
|
* @uiDefault PasswordField.showCapsLock boolean
|
||||||
|
* @uiDefault PasswordField.showRevealButton boolean
|
||||||
* @uiDefault PasswordField.capsLockIcon Icon
|
* @uiDefault PasswordField.capsLockIcon Icon
|
||||||
|
* @uiDefault PasswordField.revealIcon Icon
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatPasswordFieldUI
|
public class FlatPasswordFieldUI
|
||||||
extends FlatTextFieldUI
|
extends FlatTextFieldUI
|
||||||
{
|
{
|
||||||
protected boolean showCapsLock;
|
// used to preserve reveal button state when switching theme
|
||||||
|
private static final String KEY_REVEAL_SELECTED = "FlatLaf.internal.FlatPasswordFieldUI.revealSelected";
|
||||||
|
|
||||||
|
private Character echoChar;
|
||||||
|
|
||||||
|
@Styleable protected boolean showCapsLock;
|
||||||
|
/** @since 2 */ @Styleable protected boolean showRevealButton;
|
||||||
protected Icon capsLockIcon;
|
protected Icon capsLockIcon;
|
||||||
|
/** @since 2 */ protected Icon revealIcon;
|
||||||
|
|
||||||
private KeyListener capsLockListener;
|
private KeyListener capsLockListener;
|
||||||
|
private boolean capsLockIconShared = true;
|
||||||
|
private JToggleButton revealButton;
|
||||||
|
private boolean uninstallEchoChar;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatPasswordFieldUI();
|
return new FlatPasswordFieldUI();
|
||||||
@@ -89,17 +109,34 @@ public class FlatPasswordFieldUI
|
|||||||
return "PasswordField";
|
return "PasswordField";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installRevealButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uninstallUI( JComponent c ) {
|
||||||
|
uninstallRevealButton();
|
||||||
|
|
||||||
|
super.uninstallUI( c );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
String prefix = getPropertyPrefix();
|
String prefix = getPropertyPrefix();
|
||||||
Character echoChar = (Character) UIManager.get( prefix + ".echoChar" );
|
echoChar = (Character) UIManager.get( prefix + ".echoChar" );
|
||||||
if( echoChar != null )
|
if( echoChar != null )
|
||||||
LookAndFeel.installProperty( getComponent(), "echoChar", echoChar );
|
LookAndFeel.installProperty( getComponent(), "echoChar", echoChar );
|
||||||
|
|
||||||
showCapsLock = UIManager.getBoolean( "PasswordField.showCapsLock" );
|
showCapsLock = UIManager.getBoolean( "PasswordField.showCapsLock" );
|
||||||
|
showRevealButton = UIManager.getBoolean( "PasswordField.showRevealButton" );
|
||||||
capsLockIcon = UIManager.getIcon( "PasswordField.capsLockIcon" );
|
capsLockIcon = UIManager.getIcon( "PasswordField.capsLockIcon" );
|
||||||
|
revealIcon = UIManager.getIcon( "PasswordField.revealIcon" );
|
||||||
|
capsLockIconShared = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -107,6 +144,7 @@ public class FlatPasswordFieldUI
|
|||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
capsLockIcon = null;
|
capsLockIcon = null;
|
||||||
|
revealIcon = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -155,57 +193,154 @@ public class FlatPasswordFieldUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
String getStyleType() {
|
||||||
|
return "PasswordField";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void applyStyle( Object style ) {
|
||||||
|
boolean oldShowRevealButton = showRevealButton;
|
||||||
|
|
||||||
|
super.applyStyle( style );
|
||||||
|
|
||||||
|
if( showRevealButton != oldShowRevealButton ) {
|
||||||
|
uninstallRevealButton();
|
||||||
|
installRevealButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
if( key.equals( "capsLockIconColor" ) && capsLockIcon instanceof FlatCapsLockIcon ) {
|
||||||
|
if( capsLockIconShared ) {
|
||||||
|
capsLockIcon = FlatStylingSupport.cloneIcon( capsLockIcon );
|
||||||
|
capsLockIconShared = false;
|
||||||
|
}
|
||||||
|
return ((FlatCapsLockIcon)capsLockIcon).applyStyleProperty( key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.applyStyleProperty( key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
Map<String, Class<?>> infos = super.getStyleableInfos( c );
|
||||||
|
infos.put( "capsLockIconColor", Color.class );
|
||||||
|
return infos;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View create( Element elem ) {
|
public View create( Element elem ) {
|
||||||
return new PasswordView( elem );
|
return new PasswordView( elem );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
@Override
|
@Override
|
||||||
protected void paintSafely( Graphics g ) {
|
protected void paintIcons( Graphics g, Rectangle r ) {
|
||||||
// safe and restore clipping area because super.paintSafely() modifies it
|
super.paintIcons( g, r );
|
||||||
// and the caps lock icon would be truncated
|
|
||||||
Shape oldClip = g.getClip();
|
|
||||||
super.paintSafely( g );
|
|
||||||
g.setClip( oldClip );
|
|
||||||
|
|
||||||
paintCapsLock( g );
|
if( isCapsLockVisible() )
|
||||||
|
paintCapsLock( g, r );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void paintCapsLock( Graphics g ) {
|
/** @since 2 */
|
||||||
if( !isCapsLockVisible() )
|
protected void paintCapsLock( Graphics g, Rectangle r ) {
|
||||||
return;
|
|
||||||
|
|
||||||
JTextComponent c = getComponent();
|
JTextComponent c = getComponent();
|
||||||
int y = (c.getHeight() - capsLockIcon.getIconHeight()) / 2;
|
|
||||||
int x = c.getComponentOrientation().isLeftToRight()
|
int x = c.getComponentOrientation().isLeftToRight()
|
||||||
? c.getWidth() - capsLockIcon.getIconWidth() - y
|
? r.x + r.width - capsLockIcon.getIconWidth()
|
||||||
: y;
|
: r.x;
|
||||||
|
int y = r.y + Math.round( (r.height - capsLockIcon.getIconHeight()) / 2f );
|
||||||
capsLockIcon.paintIcon( c, g, x, y );
|
capsLockIcon.paintIcon( c, g, x, y );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @since 2 */
|
||||||
* @since 1.4
|
@Override
|
||||||
*/
|
protected boolean hasTrailingIcon() {
|
||||||
|
return super.hasTrailingIcon() || isCapsLockVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
protected int getTrailingIconWidth() {
|
||||||
|
return super.getTrailingIconWidth()
|
||||||
|
+ (isCapsLockVisible() ? capsLockIcon.getIconWidth() + UIScale.scale( iconTextGap ) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 1.4 */
|
||||||
protected boolean isCapsLockVisible() {
|
protected boolean isCapsLockVisible() {
|
||||||
if( !showCapsLock )
|
if( !showCapsLock )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
JTextComponent c = getComponent();
|
return FlatUIUtils.isPermanentFocusOwner( getComponent() ) &&
|
||||||
return FlatUIUtils.isPermanentFocusOwner( c ) &&
|
|
||||||
Toolkit.getDefaultToolkit().getLockingKeyState( KeyEvent.VK_CAPS_LOCK );
|
Toolkit.getDefaultToolkit().getLockingKeyState( KeyEvent.VK_CAPS_LOCK );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @since 2 */
|
||||||
* @since 1.4
|
protected void installRevealButton() {
|
||||||
*/
|
if( showRevealButton ) {
|
||||||
@Override
|
revealButton = createRevealButton();
|
||||||
protected Insets getPadding() {
|
installLayout();
|
||||||
Insets padding = super.getPadding();
|
getComponent().add( revealButton );
|
||||||
if( !isCapsLockVisible() )
|
}
|
||||||
return padding;
|
}
|
||||||
|
|
||||||
boolean ltr = getComponent().getComponentOrientation().isLeftToRight();
|
/** @since 2 */
|
||||||
int iconWidth = capsLockIcon.getIconWidth();
|
protected JToggleButton createRevealButton() {
|
||||||
return FlatUIUtils.addInsets( padding, new Insets( 0, ltr ? 0 : iconWidth, 0, ltr ? iconWidth : 0 ) );
|
JToggleButton button = new JToggleButton( revealIcon );
|
||||||
|
button.setName( "PasswordField.revealButton" );
|
||||||
|
prepareLeadingOrTrailingComponent( button );
|
||||||
|
button.putClientProperty( FlatClientProperties.STYLE_CLASS, "inTextField revealButton" );
|
||||||
|
if( FlatClientProperties.clientPropertyBoolean( getComponent(), KEY_REVEAL_SELECTED, false ) ) {
|
||||||
|
button.setSelected( true );
|
||||||
|
updateEchoChar( true );
|
||||||
|
}
|
||||||
|
button.addActionListener( e -> {
|
||||||
|
boolean selected = button.isSelected();
|
||||||
|
updateEchoChar( selected );
|
||||||
|
getComponent().putClientProperty( KEY_REVEAL_SELECTED, selected );
|
||||||
|
} );
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateEchoChar( boolean selected ) {
|
||||||
|
char newEchoChar = selected
|
||||||
|
? 0
|
||||||
|
: (echoChar != null ? echoChar : '*');
|
||||||
|
|
||||||
|
JPasswordField c = (JPasswordField) getComponent();
|
||||||
|
LookAndFeel.installProperty( c, "echoChar", newEchoChar );
|
||||||
|
|
||||||
|
// check whether was able to set echo char via LookAndFeel.installProperty()
|
||||||
|
// if not, then echo char was explicitly changed via JPasswordField.setEchoChar()
|
||||||
|
char actualEchoChar = c.getEchoChar();
|
||||||
|
if( actualEchoChar != newEchoChar ) {
|
||||||
|
if( selected && actualEchoChar != 0 ) {
|
||||||
|
// use explicitly set echo char
|
||||||
|
echoChar = actualEchoChar;
|
||||||
|
uninstallEchoChar = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
c.setEchoChar( newEchoChar );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void uninstallRevealButton() {
|
||||||
|
if( revealButton != null ) {
|
||||||
|
if( uninstallEchoChar && revealButton.isSelected() )
|
||||||
|
((JPasswordField)getComponent()).setEchoChar( echoChar );
|
||||||
|
|
||||||
|
getComponent().remove( revealButton );
|
||||||
|
revealButton = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JComponent[] getTrailingComponents() {
|
||||||
|
return new JComponent[] { trailingComponent, revealButton, clearButton };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,10 +16,12 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.AWTEvent;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
|
import java.awt.EventQueue;
|
||||||
import java.awt.GraphicsConfiguration;
|
import java.awt.GraphicsConfiguration;
|
||||||
import java.awt.GraphicsDevice;
|
import java.awt.GraphicsDevice;
|
||||||
import java.awt.GraphicsEnvironment;
|
import java.awt.GraphicsEnvironment;
|
||||||
@@ -33,6 +35,7 @@ import java.awt.Toolkit;
|
|||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
import java.awt.event.ComponentEvent;
|
import java.awt.event.ComponentEvent;
|
||||||
import java.awt.event.ComponentListener;
|
import java.awt.event.ComponentListener;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
@@ -121,6 +124,10 @@ public class FlatPopupFactory
|
|||||||
popupWindow.getGraphicsConfiguration() == owner.getGraphicsConfiguration() )
|
popupWindow.getGraphicsConfiguration() == owner.getGraphicsConfiguration() )
|
||||||
return popup;
|
return popup;
|
||||||
|
|
||||||
|
// avoid endless loop (should newer happen; PopupFactory cache size is 5)
|
||||||
|
if( ++count > 10 )
|
||||||
|
return popup;
|
||||||
|
|
||||||
// remove contents component from popup window
|
// remove contents component from popup window
|
||||||
if( popupWindow instanceof JWindow )
|
if( popupWindow instanceof JWindow )
|
||||||
((JWindow)popupWindow).getContentPane().removeAll();
|
((JWindow)popupWindow).getContentPane().removeAll();
|
||||||
@@ -128,10 +135,6 @@ public class FlatPopupFactory
|
|||||||
// dispose unused popup
|
// dispose unused popup
|
||||||
// (do not invoke popup.hide() because this would cache the popup window)
|
// (do not invoke popup.hide() because this would cache the popup window)
|
||||||
popupWindow.dispose();
|
popupWindow.dispose();
|
||||||
|
|
||||||
// avoid endless loop (should newer happen; PopupFactory cache size is 5)
|
|
||||||
if( ++count > 10 )
|
|
||||||
return popup;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +143,7 @@ public class FlatPopupFactory
|
|||||||
* <p>
|
* <p>
|
||||||
* On a dual screen setup, where screens use different scale factors, it may happen
|
* On a dual screen setup, where screens use different scale factors, it may happen
|
||||||
* that the window location changes when showing a heavy weight popup window.
|
* that the window location changes when showing a heavy weight popup window.
|
||||||
* E.g. when opening an dialog on the secondary screen and making combobox popup visible.
|
* E.g. when opening a dialog on the secondary screen and making combobox popup visible.
|
||||||
* <p>
|
* <p>
|
||||||
* This is a workaround for https://bugs.openjdk.java.net/browse/JDK-8224608
|
* This is a workaround for https://bugs.openjdk.java.net/browse/JDK-8224608
|
||||||
*/
|
*/
|
||||||
@@ -219,7 +222,7 @@ public class FlatPopupFactory
|
|||||||
* and corrects the y-location so that the tooltip is placed above the mouse location.
|
* and corrects the y-location so that the tooltip is placed above the mouse location.
|
||||||
*/
|
*/
|
||||||
private Point fixToolTipLocation( Component owner, Component contents, int x, int y ) {
|
private Point fixToolTipLocation( Component owner, Component contents, int x, int y ) {
|
||||||
if( !(contents instanceof JToolTip) || !wasInvokedFromToolTipManager() )
|
if( !(contents instanceof JToolTip) || !wasInvokedFromToolTipManager() || hasTipLocation( owner ) )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
PointerInfo pointerInfo = MouseInfo.getPointerInfo();
|
PointerInfo pointerInfo = MouseInfo.getPointerInfo();
|
||||||
@@ -264,6 +267,35 @@ public class FlatPopupFactory
|
|||||||
return StackUtils.wasInvokedFrom( ToolTipManager.class.getName(), "showTipWindow", 8 );
|
return StackUtils.wasInvokedFrom( ToolTipManager.class.getName(), "showTipWindow", 8 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the owner component returns a tooltip location in
|
||||||
|
* JComponent.getToolTipLocation(MouseEvent).
|
||||||
|
*/
|
||||||
|
private boolean hasTipLocation( Component owner ) {
|
||||||
|
if( !(owner instanceof JComponent) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
AWTEvent e = EventQueue.getCurrentEvent();
|
||||||
|
MouseEvent me;
|
||||||
|
if( e instanceof MouseEvent )
|
||||||
|
me = (MouseEvent) e;
|
||||||
|
else {
|
||||||
|
// no mouse event available because a timer is used to show the tooltip
|
||||||
|
// --> create mouse event from current mouse location
|
||||||
|
PointerInfo pointerInfo = MouseInfo.getPointerInfo();
|
||||||
|
if( pointerInfo == null )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Point location = new Point( pointerInfo.getLocation());
|
||||||
|
SwingUtilities.convertPointFromScreen( location, owner );
|
||||||
|
me = new MouseEvent( owner, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(),
|
||||||
|
0, location.x, location.y, 0, false );
|
||||||
|
}
|
||||||
|
|
||||||
|
return me.getSource() == owner &&
|
||||||
|
((JComponent)owner).getToolTipLocation( me ) != null;
|
||||||
|
}
|
||||||
|
|
||||||
//---- class NonFlashingPopup ---------------------------------------------
|
//---- class NonFlashingPopup ---------------------------------------------
|
||||||
|
|
||||||
private class NonFlashingPopup
|
private class NonFlashingPopup
|
||||||
|
|||||||
@@ -16,11 +16,15 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableBorder;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,12 +37,40 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*/
|
*/
|
||||||
public class FlatPopupMenuBorder
|
public class FlatPopupMenuBorder
|
||||||
extends FlatLineBorder
|
extends FlatLineBorder
|
||||||
|
implements StyleableBorder
|
||||||
{
|
{
|
||||||
|
private Color borderColor;
|
||||||
|
|
||||||
public FlatPopupMenuBorder() {
|
public FlatPopupMenuBorder() {
|
||||||
super( UIManager.getInsets( "PopupMenu.borderInsets" ),
|
super( UIManager.getInsets( "PopupMenu.borderInsets" ),
|
||||||
UIManager.getColor( "PopupMenu.borderColor" ) );
|
UIManager.getColor( "PopupMenu.borderColor" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Object applyStyleProperty( String key, Object value ) {
|
||||||
|
Object oldValue;
|
||||||
|
switch( key ) {
|
||||||
|
case "borderInsets": return applyStyleProperty( (Insets) value );
|
||||||
|
case "borderColor": oldValue = getLineColor(); borderColor = (Color) value; return oldValue;
|
||||||
|
}
|
||||||
|
throw new UnknownStyleException( key );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos() {
|
||||||
|
Map<String, Class<?>> infos = new FlatStylingSupport.StyleableInfosMap<>();
|
||||||
|
infos.put( "borderInsets", Insets.class );
|
||||||
|
infos.put( "borderColor", Color.class );
|
||||||
|
return infos;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color getLineColor() {
|
||||||
|
return (borderColor != null) ? borderColor : super.getLineColor();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
if( c instanceof Container &&
|
if( c instanceof Container &&
|
||||||
|
|||||||
@@ -39,11 +39,24 @@ public class FlatPopupMenuSeparatorUI
|
|||||||
extends FlatSeparatorUI
|
extends FlatSeparatorUI
|
||||||
{
|
{
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return FlatUIUtils.createSharedUI( FlatPopupMenuSeparatorUI.class, FlatPopupMenuSeparatorUI::new );
|
return FlatUIUtils.canUseSharedUI( c )
|
||||||
|
? FlatUIUtils.createSharedUI( FlatPopupMenuSeparatorUI.class, () -> new FlatPopupMenuSeparatorUI( true ) )
|
||||||
|
: new FlatPopupMenuSeparatorUI( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected FlatPopupMenuSeparatorUI( boolean shared ) {
|
||||||
|
super( shared );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getPropertyPrefix() {
|
protected String getPropertyPrefix() {
|
||||||
return "PopupMenuSeparator";
|
return "PopupMenuSeparator";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
String getStyleType() {
|
||||||
|
return "PopupMenuSeparator";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,60 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Container;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.EventQueue;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.GraphicsConfiguration;
|
||||||
|
import java.awt.GraphicsDevice;
|
||||||
|
import java.awt.GraphicsEnvironment;
|
||||||
|
import java.awt.Insets;
|
||||||
|
import java.awt.LayoutManager;
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.Toolkit;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.event.MouseListener;
|
||||||
|
import java.awt.event.MouseWheelEvent;
|
||||||
|
import java.awt.event.MouseWheelListener;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import javax.swing.BoxLayout;
|
||||||
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JPopupMenu;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JViewport;
|
||||||
|
import javax.swing.MenuElement;
|
||||||
|
import javax.swing.MenuSelectionManager;
|
||||||
|
import javax.swing.Popup;
|
||||||
|
import javax.swing.PopupFactory;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.Timer;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.event.MenuKeyEvent;
|
||||||
|
import javax.swing.event.MenuKeyListener;
|
||||||
|
import javax.swing.event.PopupMenuEvent;
|
||||||
|
import javax.swing.event.PopupMenuListener;
|
||||||
|
import javax.swing.plaf.ButtonUI;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import javax.swing.plaf.UIResource;
|
||||||
|
import javax.swing.plaf.basic.BasicComboPopup;
|
||||||
|
import javax.swing.plaf.basic.BasicMenuItemUI;
|
||||||
import javax.swing.plaf.basic.BasicPopupMenuUI;
|
import javax.swing.plaf.basic.BasicPopupMenuUI;
|
||||||
|
import javax.swing.plaf.basic.DefaultMenuLayout;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JPopupMenu}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JPopupMenu}.
|
||||||
@@ -30,12 +81,371 @@ import javax.swing.plaf.basic.BasicPopupMenuUI;
|
|||||||
* @uiDefault PopupMenu.foreground Color
|
* @uiDefault PopupMenu.foreground Color
|
||||||
* @uiDefault PopupMenu.border Border
|
* @uiDefault PopupMenu.border Border
|
||||||
*
|
*
|
||||||
|
* <!-- FlatPopupMenuUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||||
|
* @uiDefault PopupMenu.scrollArrowColor Color
|
||||||
|
* @uiDefault PopupMenu.hoverScrollArrowBackground Color optional
|
||||||
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatPopupMenuUI
|
public class FlatPopupMenuUI
|
||||||
extends BasicPopupMenuUI
|
extends BasicPopupMenuUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
|
/** @since 2.1 */ @Styleable protected String arrowType;
|
||||||
|
/** @since 2.1 */ @Styleable protected Color scrollArrowColor;
|
||||||
|
/** @since 2.1 */ @Styleable protected Color hoverScrollArrowBackground;
|
||||||
|
|
||||||
|
private PropertyChangeListener propertyChangeListener;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
private AtomicBoolean borderShared;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatPopupMenuUI();
|
return new FlatPopupMenuUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uninstallUI( JComponent c ) {
|
||||||
|
super.uninstallUI( c );
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
borderShared = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installDefaults() {
|
||||||
|
super.installDefaults();
|
||||||
|
|
||||||
|
arrowType = UIManager.getString( "Component.arrowType" );
|
||||||
|
scrollArrowColor = UIManager.getColor( "PopupMenu.scrollArrowColor" );
|
||||||
|
hoverScrollArrowBackground = UIManager.getColor( "PopupMenu.hoverScrollArrowBackground" );
|
||||||
|
|
||||||
|
LayoutManager layout = popupMenu.getLayout();
|
||||||
|
if( layout == null || layout instanceof UIResource )
|
||||||
|
popupMenu.setLayout( new FlatMenuLayout( popupMenu, BoxLayout.Y_AXIS ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallDefaults() {
|
||||||
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
scrollArrowColor = null;
|
||||||
|
hoverScrollArrowBackground = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installListeners() {
|
||||||
|
super.installListeners();
|
||||||
|
|
||||||
|
propertyChangeListener = FlatStylingSupport.createPropertyChangeListener( popupMenu, this::installStyle, null );
|
||||||
|
popupMenu.addPropertyChangeListener( propertyChangeListener );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallListeners() {
|
||||||
|
super.uninstallListeners();
|
||||||
|
|
||||||
|
popupMenu.removePropertyChangeListener( propertyChangeListener );
|
||||||
|
propertyChangeListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle() {
|
||||||
|
try {
|
||||||
|
applyStyle( FlatStylingSupport.getResolvedStyle( popupMenu, "PopupMenu" ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( Object style ) {
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
if( borderShared == null )
|
||||||
|
borderShared = new AtomicBoolean( true );
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, popupMenu, borderShared );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this, popupMenu.getBorder() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Popup getPopup( JPopupMenu popup, int x, int y ) {
|
||||||
|
// do not add scroller to combobox popups or to popups that already have a scroll pane
|
||||||
|
if( popup instanceof BasicComboPopup ||
|
||||||
|
(popup.getComponentCount() > 0 && popup.getComponent( 0 ) instanceof JScrollPane) )
|
||||||
|
return super.getPopup( popup, x, y );
|
||||||
|
|
||||||
|
// do not add scroller if popup fits into screen
|
||||||
|
Dimension prefSize = popup.getPreferredSize();
|
||||||
|
int screenHeight = getScreenHeightAt( x, y );
|
||||||
|
if( prefSize.height <= screenHeight )
|
||||||
|
return super.getPopup( popup, x, y );
|
||||||
|
|
||||||
|
// create scroller
|
||||||
|
FlatPopupScroller scroller = new FlatPopupScroller( popup );
|
||||||
|
scroller.setPreferredSize( new Dimension( prefSize.width, screenHeight ) );
|
||||||
|
|
||||||
|
// create popup
|
||||||
|
PopupFactory popupFactory = PopupFactory.getSharedInstance();
|
||||||
|
return popupFactory.getPopup( popup.getInvoker(), scroller, x, y );
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getScreenHeightAt( int x, int y ) {
|
||||||
|
// find GraphicsConfiguration at popup location (similar to JPopupMenu.getCurrentGraphicsConfiguration())
|
||||||
|
GraphicsConfiguration gc = null;
|
||||||
|
for( GraphicsDevice device : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices() ) {
|
||||||
|
if( device.getType() == GraphicsDevice.TYPE_RASTER_SCREEN ) {
|
||||||
|
GraphicsConfiguration dgc = device.getDefaultConfiguration();
|
||||||
|
if( dgc.getBounds().contains( x, y ) ) {
|
||||||
|
gc = dgc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( gc == null && popupMenu.getInvoker() != null )
|
||||||
|
gc = popupMenu.getInvoker().getGraphicsConfiguration();
|
||||||
|
|
||||||
|
// compute screen height
|
||||||
|
// (always subtract screen insets because there is no API to detect whether
|
||||||
|
// the popup can overlap the taskbar; see JPopupMenu.canPopupOverlapTaskBar())
|
||||||
|
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
||||||
|
Rectangle screenBounds = (gc != null) ? gc.getBounds() : new Rectangle( toolkit.getScreenSize() );
|
||||||
|
Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets( gc );
|
||||||
|
return screenBounds.height - screenInsets.top - screenInsets.bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class FlatMenuLayout -----------------------------------------------
|
||||||
|
|
||||||
|
protected static class FlatMenuLayout
|
||||||
|
extends DefaultMenuLayout
|
||||||
|
{
|
||||||
|
public FlatMenuLayout( Container target, int axis ) {
|
||||||
|
super( target, axis );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension preferredLayoutSize( Container target ) {
|
||||||
|
FlatMenuItemRenderer.clearClientProperties( target );
|
||||||
|
|
||||||
|
return super.preferredLayoutSize( target );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class FlatPopupScroller --------------------------------------------
|
||||||
|
|
||||||
|
private class FlatPopupScroller
|
||||||
|
extends JPanel
|
||||||
|
implements MouseWheelListener, PopupMenuListener, MenuKeyListener
|
||||||
|
{
|
||||||
|
private final JPopupMenu popup;
|
||||||
|
|
||||||
|
private final JScrollPane scrollPane;
|
||||||
|
private final JButton scrollUpButton;
|
||||||
|
private final JButton scrollDownButton;
|
||||||
|
private int unitIncrement;
|
||||||
|
|
||||||
|
FlatPopupScroller( JPopupMenu popup ) {
|
||||||
|
super( new BorderLayout() );
|
||||||
|
this.popup = popup;
|
||||||
|
|
||||||
|
// this panel is required to avoid that JPopupMenu.setLocation() will be invoked
|
||||||
|
// while scrolling, because this would call JPopupMenu.showPopup()
|
||||||
|
JPanel view = new JPanel( new BorderLayout() );
|
||||||
|
view.add( popup, BorderLayout.CENTER );
|
||||||
|
|
||||||
|
scrollPane = new JScrollPane( view, JScrollPane.VERTICAL_SCROLLBAR_NEVER, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER );
|
||||||
|
scrollPane.setBorder( null );
|
||||||
|
|
||||||
|
scrollUpButton = new ArrowButton( SwingConstants.NORTH );
|
||||||
|
scrollDownButton = new ArrowButton( SwingConstants.SOUTH );
|
||||||
|
|
||||||
|
add( scrollPane, BorderLayout.CENTER );
|
||||||
|
add( scrollUpButton, BorderLayout.NORTH );
|
||||||
|
add( scrollDownButton, BorderLayout.SOUTH );
|
||||||
|
|
||||||
|
setBackground( popup.getBackground() );
|
||||||
|
setBorder( popup.getBorder() );
|
||||||
|
popup.setBorder( null );
|
||||||
|
|
||||||
|
popup.addPopupMenuListener( this );
|
||||||
|
popup.addMouseWheelListener( this );
|
||||||
|
popup.addMenuKeyListener( this );
|
||||||
|
|
||||||
|
updateArrowButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
void scroll( int unitsToScroll ) {
|
||||||
|
if( unitIncrement == 0 )
|
||||||
|
unitIncrement = new JMenuItem( "X" ).getPreferredSize().height;
|
||||||
|
|
||||||
|
JViewport viewport = scrollPane.getViewport();
|
||||||
|
Point viewPosition = viewport.getViewPosition();
|
||||||
|
int newY = viewPosition.y + (unitIncrement * unitsToScroll);
|
||||||
|
if( newY < 0 )
|
||||||
|
newY = 0;
|
||||||
|
else
|
||||||
|
newY = Math.min( newY, viewport.getViewSize().height - viewport.getExtentSize().height );
|
||||||
|
viewport.setViewPosition( new Point( viewPosition.x, newY ) );
|
||||||
|
|
||||||
|
updateArrowButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateArrowButtons() {
|
||||||
|
JViewport viewport = scrollPane.getViewport();
|
||||||
|
Point viewPosition = viewport.getViewPosition();
|
||||||
|
|
||||||
|
scrollUpButton.setVisible( viewPosition.y > 0 );
|
||||||
|
scrollDownButton.setVisible( viewPosition.y < viewport.getViewSize().height - viewport.getExtentSize().height );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- interface PopupMenuListener ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void popupMenuWillBecomeInvisible( PopupMenuEvent e ) {
|
||||||
|
// restore popup border
|
||||||
|
popup.setBorder( getBorder() );
|
||||||
|
|
||||||
|
popup.removePopupMenuListener( this );
|
||||||
|
popup.removeMouseWheelListener( this );
|
||||||
|
popup.removeMenuKeyListener( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void popupMenuWillBecomeVisible( PopupMenuEvent e ) {}
|
||||||
|
@Override public void popupMenuCanceled( PopupMenuEvent e ) {}
|
||||||
|
|
||||||
|
//---- interface MouseWheelListener ----
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll when user rotates mouse wheel.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void mouseWheelMoved( MouseWheelEvent e ) {
|
||||||
|
// convert mouse location before scrolling
|
||||||
|
Point mouseLocation = SwingUtilities.convertPoint( (Component) e.getSource(), e.getPoint(), this );
|
||||||
|
|
||||||
|
// scroll
|
||||||
|
scroll( e.getUnitsToScroll() );
|
||||||
|
|
||||||
|
// select menu item at mouse location
|
||||||
|
Component c = SwingUtilities.getDeepestComponentAt( this, mouseLocation.x, mouseLocation.y );
|
||||||
|
if( c instanceof JMenuItem ) {
|
||||||
|
ButtonUI ui = ((JMenuItem)c).getUI();
|
||||||
|
if( ui instanceof BasicMenuItemUI )
|
||||||
|
MenuSelectionManager.defaultManager().setSelectedPath( ((BasicMenuItemUI)ui).getPath() );
|
||||||
|
}
|
||||||
|
|
||||||
|
// this avoids that the popup is closed when running on Java 8
|
||||||
|
// https://bugs.openjdk.java.net/browse/JDK-8075063
|
||||||
|
e.consume();
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- interface MenuKeyListener ----
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll when user presses Up or Down keys.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void menuKeyPressed( MenuKeyEvent e ) {
|
||||||
|
// use invokeLater() because menu selection is not yet updated because
|
||||||
|
// this listener is invoked before another listener that updates the menu selection
|
||||||
|
EventQueue.invokeLater( () -> {
|
||||||
|
if( !isDisplayable() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
MenuElement[] path = MenuSelectionManager.defaultManager().getSelectedPath();
|
||||||
|
if( path.length == 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// scroll selected menu item to visible area
|
||||||
|
Component c = path[path.length - 1].getComponent();
|
||||||
|
JViewport viewport = scrollPane.getViewport();
|
||||||
|
Point pt = SwingUtilities.convertPoint( c, 0, 0, viewport );
|
||||||
|
viewport.scrollRectToVisible( new Rectangle( pt, c.getSize() ) );
|
||||||
|
|
||||||
|
// update arrow buttons
|
||||||
|
boolean upVisible = scrollUpButton.isVisible();
|
||||||
|
updateArrowButtons();
|
||||||
|
if( !upVisible && scrollUpButton.isVisible() ) {
|
||||||
|
// if "up" button becomes visible, make sure that bottom menu item stays visible
|
||||||
|
Point viewPosition = viewport.getViewPosition();
|
||||||
|
int newY = viewPosition.y + scrollUpButton.getPreferredSize().height;
|
||||||
|
viewport.setViewPosition( new Point( viewPosition.x, newY ) );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void menuKeyTyped( MenuKeyEvent e ) {}
|
||||||
|
@Override public void menuKeyReleased( MenuKeyEvent e ) {}
|
||||||
|
|
||||||
|
//---- class ArrowButton ----------------------------------------------
|
||||||
|
|
||||||
|
private class ArrowButton
|
||||||
|
extends FlatArrowButton
|
||||||
|
implements MouseListener, ActionListener
|
||||||
|
{
|
||||||
|
private Timer timer;
|
||||||
|
|
||||||
|
ArrowButton( int direction ) {
|
||||||
|
super( direction, arrowType, scrollArrowColor, null, null, hoverScrollArrowBackground, null, null );
|
||||||
|
|
||||||
|
addMouseListener( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paint( Graphics g ) {
|
||||||
|
// always fill background to paint over border on HiDPI screens
|
||||||
|
g.setColor( popup.getBackground() );
|
||||||
|
g.fillRect( 0, 0, getWidth(), getHeight() );
|
||||||
|
|
||||||
|
super.paint( g );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- interface MouseListener ----
|
||||||
|
|
||||||
|
@Override public void mouseClicked( MouseEvent e ) {}
|
||||||
|
@Override public void mousePressed( MouseEvent e ) {}
|
||||||
|
@Override public void mouseReleased( MouseEvent e ) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseEntered( MouseEvent e ) {
|
||||||
|
if( timer == null )
|
||||||
|
timer = new Timer( 50, this );
|
||||||
|
timer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseExited( MouseEvent e ) {
|
||||||
|
if( timer != null )
|
||||||
|
timer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- interface ActionListener ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed( ActionEvent e ) {
|
||||||
|
if( timer != null && !isDisplayable() ) {
|
||||||
|
timer.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scroll( direction == SwingConstants.NORTH ? -1 : 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,13 +25,17 @@ import java.awt.Insets;
|
|||||||
import java.awt.geom.Area;
|
import java.awt.geom.Area;
|
||||||
import java.awt.geom.RoundRectangle2D;
|
import java.awt.geom.RoundRectangle2D;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JProgressBar;
|
import javax.swing.JProgressBar;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicProgressBarUI;
|
import javax.swing.plaf.basic.BasicProgressBarUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,17 +62,30 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*/
|
*/
|
||||||
public class FlatProgressBarUI
|
public class FlatProgressBarUI
|
||||||
extends BasicProgressBarUI
|
extends BasicProgressBarUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
protected int arc;
|
@Styleable protected int arc;
|
||||||
protected Dimension horizontalSize;
|
@Styleable protected Dimension horizontalSize;
|
||||||
protected Dimension verticalSize;
|
@Styleable protected Dimension verticalSize;
|
||||||
|
|
||||||
|
// only used via styling (not in UI defaults, but has likewise client properties)
|
||||||
|
/** @since 2 */ @Styleable protected boolean largeHeight;
|
||||||
|
/** @since 2 */ @Styleable protected boolean square;
|
||||||
|
|
||||||
private PropertyChangeListener propertyChangeListener;
|
private PropertyChangeListener propertyChangeListener;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatProgressBarUI();
|
return new FlatProgressBarUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
@@ -80,6 +97,13 @@ public class FlatProgressBarUI
|
|||||||
verticalSize = UIManager.getDimension( "ProgressBar.verticalSize" );
|
verticalSize = UIManager.getDimension( "ProgressBar.verticalSize" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallDefaults() {
|
||||||
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installListeners() {
|
protected void installListeners() {
|
||||||
super.installListeners();
|
super.installListeners();
|
||||||
@@ -91,6 +115,13 @@ public class FlatProgressBarUI
|
|||||||
progressBar.revalidate();
|
progressBar.revalidate();
|
||||||
progressBar.repaint();
|
progressBar.repaint();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case STYLE:
|
||||||
|
case STYLE_CLASS:
|
||||||
|
installStyle();
|
||||||
|
progressBar.revalidate();
|
||||||
|
progressBar.repaint();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
progressBar.addPropertyChangeListener( propertyChangeListener );
|
progressBar.addPropertyChangeListener( propertyChangeListener );
|
||||||
@@ -104,11 +135,36 @@ public class FlatProgressBarUI
|
|||||||
propertyChangeListener = null;
|
propertyChangeListener = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle() {
|
||||||
|
try {
|
||||||
|
applyStyle( FlatStylingSupport.getResolvedStyle( progressBar, "ProgressBar" ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( Object style ) {
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, progressBar, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
Dimension size = super.getPreferredSize( c );
|
Dimension size = super.getPreferredSize( c );
|
||||||
|
|
||||||
if( progressBar.isStringPainted() || clientPropertyBoolean( c, PROGRESS_BAR_LARGE_HEIGHT, false ) ) {
|
if( progressBar.isStringPainted() || clientPropertyBoolean( c, PROGRESS_BAR_LARGE_HEIGHT, largeHeight ) ) {
|
||||||
// recalculate progress height/width to make it smaller
|
// recalculate progress height/width to make it smaller
|
||||||
Insets insets = progressBar.getInsets();
|
Insets insets = progressBar.getInsets();
|
||||||
FontMetrics fm = progressBar.getFontMetrics( progressBar.getFont() );
|
FontMetrics fm = progressBar.getFontMetrics( progressBar.getFont() );
|
||||||
@@ -151,7 +207,7 @@ public class FlatProgressBarUI
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
boolean horizontal = (progressBar.getOrientation() == JProgressBar.HORIZONTAL);
|
boolean horizontal = (progressBar.getOrientation() == JProgressBar.HORIZONTAL);
|
||||||
int arc = clientPropertyBoolean( c, PROGRESS_BAR_SQUARE, false )
|
int arc = clientPropertyBoolean( c, PROGRESS_BAR_SQUARE, square )
|
||||||
? 0
|
? 0
|
||||||
: Math.min( UIScale.scale( this.arc ), horizontal ? height : width );
|
: Math.min( UIScale.scale( this.arc ), horizontal ? height : width );
|
||||||
|
|
||||||
|
|||||||
@@ -16,13 +16,19 @@
|
|||||||
|
|
||||||
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.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI;
|
import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JRadioButtonMenuItem}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JRadioButtonMenuItem}.
|
||||||
@@ -54,13 +60,22 @@ import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI;
|
|||||||
*/
|
*/
|
||||||
public class FlatRadioButtonMenuItemUI
|
public class FlatRadioButtonMenuItemUI
|
||||||
extends BasicRadioButtonMenuItemUI
|
extends BasicRadioButtonMenuItemUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
private FlatMenuItemRenderer renderer;
|
private FlatMenuItemRenderer renderer;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatRadioButtonMenuItemUI();
|
return new FlatRadioButtonMenuItemUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
@@ -74,13 +89,61 @@ public class FlatRadioButtonMenuItemUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
FlatMenuItemRenderer.clearClientProperties( menuItem.getParent() );
|
||||||
renderer = null;
|
renderer = null;
|
||||||
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected FlatMenuItemRenderer createRenderer() {
|
protected FlatMenuItemRenderer createRenderer() {
|
||||||
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
||||||
|
return FlatStylingSupport.createPropertyChangeListener( c, this::installStyle, super.createPropertyChangeListener( c ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle() {
|
||||||
|
try {
|
||||||
|
applyStyle( FlatStylingSupport.getResolvedStyle( menuItem, "RadioButtonMenuItem" ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( Object style ) {
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
try {
|
||||||
|
return renderer.applyStyleProperty( key, value );
|
||||||
|
} catch ( UnknownStyleException ex ) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
Object oldValue;
|
||||||
|
switch( key ) {
|
||||||
|
// BasicMenuItemUI
|
||||||
|
case "selectionBackground": oldValue = selectionBackground; selectionBackground = (Color) value; return oldValue;
|
||||||
|
case "selectionForeground": oldValue = selectionForeground; selectionForeground = (Color) value; return oldValue;
|
||||||
|
case "disabledForeground": oldValue = disabledForeground; disabledForeground = (Color) value; return oldValue;
|
||||||
|
case "acceleratorForeground": oldValue = acceleratorForeground; acceleratorForeground = (Color) value; return oldValue;
|
||||||
|
case "acceleratorSelectionForeground": oldValue = acceleratorSelectionForeground; acceleratorSelectionForeground = (Color) value; return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, menuItem, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return FlatMenuItemUI.getStyleableInfos( renderer );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
|
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
|
||||||
return renderer.getPreferredMenuItemSize();
|
return renderer.getPreferredMenuItemSize();
|
||||||
|
|||||||
@@ -23,15 +23,24 @@ 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.beans.PropertyChangeEvent;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
import javax.swing.CellRendererPane;
|
import javax.swing.CellRendererPane;
|
||||||
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import javax.swing.plaf.basic.BasicButtonListener;
|
||||||
import javax.swing.plaf.basic.BasicRadioButtonUI;
|
import javax.swing.plaf.basic.BasicRadioButtonUI;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.icons.FlatCheckBoxIcon;
|
import com.formdev.flatlaf.icons.FlatCheckBoxIcon;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,16 +65,34 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*/
|
*/
|
||||||
public class FlatRadioButtonUI
|
public class FlatRadioButtonUI
|
||||||
extends BasicRadioButtonUI
|
extends BasicRadioButtonUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
protected int iconTextGap;
|
protected int iconTextGap;
|
||||||
protected Color disabledText;
|
@Styleable protected Color disabledText;
|
||||||
|
|
||||||
private Color defaultBackground;
|
private Color defaultBackground;
|
||||||
|
|
||||||
|
private final boolean shared;
|
||||||
|
private boolean iconShared = true;
|
||||||
private boolean defaults_initialized = false;
|
private boolean defaults_initialized = false;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return FlatUIUtils.createSharedUI( FlatRadioButtonUI.class, FlatRadioButtonUI::new );
|
return FlatUIUtils.canUseSharedUI( c )
|
||||||
|
? FlatUIUtils.createSharedUI( FlatRadioButtonUI.class, () -> new FlatRadioButtonUI( true ) )
|
||||||
|
: new FlatRadioButtonUI( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected FlatRadioButtonUI( boolean shared ) {
|
||||||
|
this.shared = shared;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle( (AbstractButton) c );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -80,6 +107,7 @@ public class FlatRadioButtonUI
|
|||||||
|
|
||||||
defaultBackground = UIManager.getColor( prefix + "background" );
|
defaultBackground = UIManager.getColor( prefix + "background" );
|
||||||
|
|
||||||
|
iconShared = true;
|
||||||
defaults_initialized = true;
|
defaults_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,11 +121,85 @@ public class FlatRadioButtonUI
|
|||||||
protected void uninstallDefaults( AbstractButton b ) {
|
protected void uninstallDefaults( AbstractButton b ) {
|
||||||
super.uninstallDefaults( b );
|
super.uninstallDefaults( b );
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
|
||||||
MigLayoutVisualPadding.uninstall( b );
|
MigLayoutVisualPadding.uninstall( b );
|
||||||
defaults_initialized = false;
|
defaults_initialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Insets tempInsets = new Insets( 0, 0, 0, 0 );
|
@Override
|
||||||
|
protected BasicButtonListener createButtonListener( AbstractButton b ) {
|
||||||
|
return new FlatRadioButtonListener( b );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void propertyChange( AbstractButton b, PropertyChangeEvent e ) {
|
||||||
|
switch( e.getPropertyName() ) {
|
||||||
|
case FlatClientProperties.STYLE:
|
||||||
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
|
if( shared && FlatStylingSupport.hasStyleProperty( b ) ) {
|
||||||
|
// unshare component UI if necessary
|
||||||
|
// updateUI() invokes installStyle() from installUI()
|
||||||
|
b.updateUI();
|
||||||
|
} else
|
||||||
|
installStyle( b );
|
||||||
|
b.revalidate();
|
||||||
|
b.repaint();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle( AbstractButton b ) {
|
||||||
|
try {
|
||||||
|
applyStyle( b, FlatStylingSupport.getResolvedStyle( b, getStyleType() ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
String getStyleType() {
|
||||||
|
return "RadioButton";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( AbstractButton b, Object style ) {
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style,
|
||||||
|
(key, value) -> applyStyleProperty( b, key, value ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( AbstractButton b, String key, Object value ) {
|
||||||
|
// style icon
|
||||||
|
if( key.startsWith( "icon." ) ) {
|
||||||
|
if( !(icon instanceof FlatCheckBoxIcon) )
|
||||||
|
return new UnknownStyleException( key );
|
||||||
|
|
||||||
|
if( iconShared ) {
|
||||||
|
icon = FlatStylingSupport.cloneIcon( icon );
|
||||||
|
iconShared = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
key = key.substring( "icon.".length() );
|
||||||
|
return ((FlatCheckBoxIcon)icon).applyStyleProperty( key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, b, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
Map<String, Class<?>> infos = FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
|
if( icon instanceof FlatCheckBoxIcon ) {
|
||||||
|
for( Map.Entry<String, Class<?>> e : ((FlatCheckBoxIcon)icon).getStyleableInfos().entrySet() )
|
||||||
|
infos.put( "icon.".concat( e.getKey() ), e.getValue() );
|
||||||
|
}
|
||||||
|
return infos;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Insets tempInsets = new Insets( 0, 0, 0, 0 );
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
@@ -110,7 +212,7 @@ public class FlatRadioButtonUI
|
|||||||
if( focusWidth > 0 ) {
|
if( focusWidth > 0 ) {
|
||||||
// Increase preferred width and height if insets were explicitly reduced (e.g. with
|
// Increase preferred width and height if insets were explicitly reduced (e.g. with
|
||||||
// an EmptyBorder) and icon has a focus width, which is not included in icon size.
|
// an EmptyBorder) and icon has a focus width, which is not included in icon size.
|
||||||
// Otherwise the component may be too small and outer focus border may be cut off.
|
// Otherwise, the component may be too small and outer focus border may be cut off.
|
||||||
Insets insets = c.getInsets( tempInsets );
|
Insets insets = c.getInsets( tempInsets );
|
||||||
size.width += Math.max( focusWidth - insets.left, 0 ) + Math.max( focusWidth - insets.right, 0 );
|
size.width += Math.max( focusWidth - insets.left, 0 ) + Math.max( focusWidth - insets.right, 0 );
|
||||||
size.height += Math.max( focusWidth - insets.top, 0 ) + Math.max( focusWidth - insets.bottom, 0 );
|
size.height += Math.max( focusWidth - insets.top, 0 ) + Math.max( focusWidth - insets.bottom, 0 );
|
||||||
@@ -178,8 +280,32 @@ public class FlatRadioButtonUI
|
|||||||
|
|
||||||
private int getIconFocusWidth( JComponent c ) {
|
private int getIconFocusWidth( JComponent c ) {
|
||||||
AbstractButton b = (AbstractButton) c;
|
AbstractButton b = (AbstractButton) c;
|
||||||
return (b.getIcon() == null && getDefaultIcon() instanceof FlatCheckBoxIcon)
|
Icon icon = b.getIcon();
|
||||||
? UIScale.scale( ((FlatCheckBoxIcon)getDefaultIcon()).focusWidth )
|
if( icon == null )
|
||||||
|
icon = getDefaultIcon();
|
||||||
|
|
||||||
|
return (icon instanceof FlatCheckBoxIcon)
|
||||||
|
? Math.round( UIScale.scale( ((FlatCheckBoxIcon)icon).getFocusWidth() ) )
|
||||||
: 0;
|
: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- class FlatRadioButtonListener --------------------------------------
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected class FlatRadioButtonListener
|
||||||
|
extends BasicButtonListener
|
||||||
|
{
|
||||||
|
private final AbstractButton b;
|
||||||
|
|
||||||
|
protected FlatRadioButtonListener( AbstractButton b ) {
|
||||||
|
super( b );
|
||||||
|
this.b = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
super.propertyChange( e );
|
||||||
|
FlatRadioButtonUI.this.propertyChange( b, e );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,9 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*
|
*
|
||||||
* <!-- FlatWindowResizer -->
|
* <!-- FlatWindowResizer -->
|
||||||
*
|
*
|
||||||
|
* @uiDefault RootPane.font Font unused
|
||||||
|
* @uiDefault RootPane.background Color
|
||||||
|
* @uiDefault RootPane.foreground Color unused
|
||||||
* @uiDefault RootPane.borderDragThickness int
|
* @uiDefault RootPane.borderDragThickness int
|
||||||
* @uiDefault RootPane.cornerDragWidth int
|
* @uiDefault RootPane.cornerDragWidth int
|
||||||
* @uiDefault RootPane.honorFrameMinimumSizeOnResize boolean
|
* @uiDefault RootPane.honorFrameMinimumSizeOnResize boolean
|
||||||
@@ -126,8 +129,23 @@ public class FlatRootPaneUI
|
|||||||
protected void installDefaults( JRootPane c ) {
|
protected void installDefaults( JRootPane c ) {
|
||||||
super.installDefaults( c );
|
super.installDefaults( c );
|
||||||
|
|
||||||
|
// Give the root pane useful background, foreground and font.
|
||||||
|
// Background is used for title bar and menu bar if native window decorations
|
||||||
|
// and unified background are enabled.
|
||||||
|
// Foreground and font are usually not used, but set for completeness.
|
||||||
|
// Not using LookAndFeel.installColorsAndFont() here because it will not work
|
||||||
|
// because the properties are null by default but inherit non-null values from parent.
|
||||||
|
if( !c.isBackgroundSet() || c.getBackground() instanceof UIResource )
|
||||||
|
c.setBackground( UIManager.getColor( "RootPane.background" ) );
|
||||||
|
if( !c.isForegroundSet() || c.getForeground() instanceof UIResource )
|
||||||
|
c.setForeground( UIManager.getColor( "RootPane.foreground" ) );
|
||||||
|
if( !c.isFontSet() || c.getFont() instanceof UIResource )
|
||||||
|
c.setFont( UIManager.getFont( "RootPane.font" ) );
|
||||||
|
|
||||||
// Update background color of JFrame or JDialog parent to avoid bad border
|
// Update background color of JFrame or JDialog parent to avoid bad border
|
||||||
// on HiDPI screens when switching from light to dark Laf.
|
// on HiDPI screens when switching from light to dark Laf.
|
||||||
|
// Window background color is also used in native window decorations
|
||||||
|
// to fill background when window is initially shown or when resizing window.
|
||||||
// The background of JFrame is initialized in JFrame.frameInit() and
|
// The background of JFrame is initialized in JFrame.frameInit() and
|
||||||
// the background of JDialog in JDialog.dialogInit(),
|
// the background of JDialog in JDialog.dialogInit(),
|
||||||
// but it was not updated when switching Laf.
|
// but it was not updated when switching Laf.
|
||||||
@@ -143,13 +161,26 @@ public class FlatRootPaneUI
|
|||||||
c.putClientProperty( "jetbrains.awt.windowDarkAppearance", FlatLaf.isLafDark() );
|
c.putClientProperty( "jetbrains.awt.windowDarkAppearance", FlatLaf.isLafDark() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallDefaults( JRootPane c ) {
|
||||||
|
super.uninstallDefaults( c );
|
||||||
|
|
||||||
|
// uninstall background, foreground and font because not all Lafs set them
|
||||||
|
if( c.isBackgroundSet() && c.getBackground() instanceof UIResource )
|
||||||
|
c.setBackground( null );
|
||||||
|
if( c.isForegroundSet() && c.getForeground() instanceof UIResource )
|
||||||
|
c.setForeground( null );
|
||||||
|
if( c.isFontSet() && c.getFont() instanceof UIResource )
|
||||||
|
c.setFont( null );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installListeners( JRootPane root ) {
|
protected void installListeners( JRootPane root ) {
|
||||||
super.installListeners( root );
|
super.installListeners( root );
|
||||||
|
|
||||||
if( SystemInfo.isJava_9_orLater ) {
|
if( SystemInfo.isJava_9_orLater ) {
|
||||||
// On HiDPI screens, where scaling is used, there may be white lines at the
|
// On HiDPI screens, where scaling is used, there may be white lines on the
|
||||||
// bottom and at the right side of the window when it is initially shown.
|
// bottom and on the right side of the window when it is initially shown.
|
||||||
// This is very disturbing in dark themes, but hard to notice in light themes.
|
// This is very disturbing in dark themes, but hard to notice in light themes.
|
||||||
// Seems to be a rounding issue when Swing adds dirty region of window
|
// Seems to be a rounding issue when Swing adds dirty region of window
|
||||||
// using RepaintManager.nativeAddDirtyRegion().
|
// using RepaintManager.nativeAddDirtyRegion().
|
||||||
@@ -198,24 +229,18 @@ public class FlatRootPaneUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @since 1.1.2 */
|
||||||
* @since 1.1.2
|
|
||||||
*/
|
|
||||||
protected void installNativeWindowBorder() {
|
protected void installNativeWindowBorder() {
|
||||||
nativeWindowBorderData = FlatNativeWindowBorder.install( rootPane );
|
nativeWindowBorderData = FlatNativeWindowBorder.install( rootPane );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @since 1.1.2 */
|
||||||
* @since 1.1.2
|
|
||||||
*/
|
|
||||||
protected void uninstallNativeWindowBorder() {
|
protected void uninstallNativeWindowBorder() {
|
||||||
FlatNativeWindowBorder.uninstall( rootPane, nativeWindowBorderData );
|
FlatNativeWindowBorder.uninstall( rootPane, nativeWindowBorderData );
|
||||||
nativeWindowBorderData = null;
|
nativeWindowBorderData = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @since 1.1.2 */
|
||||||
* @since 1.1.2
|
|
||||||
*/
|
|
||||||
public static void updateNativeWindowBorder( JRootPane rootPane ) {
|
public static void updateNativeWindowBorder( JRootPane rootPane ) {
|
||||||
RootPaneUI rui = rootPane.getUI();
|
RootPaneUI rui = rootPane.getUI();
|
||||||
if( !(rui instanceof FlatRootPaneUI) )
|
if( !(rui instanceof FlatRootPaneUI) )
|
||||||
@@ -319,6 +344,11 @@ public class FlatRootPaneUI
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.TITLE_BAR_SHOW_ICON:
|
||||||
|
if( titlePane != null )
|
||||||
|
titlePane.updateIcon();
|
||||||
|
break;
|
||||||
|
|
||||||
case FlatClientProperties.TITLE_BAR_BACKGROUND:
|
case FlatClientProperties.TITLE_BAR_BACKGROUND:
|
||||||
case FlatClientProperties.TITLE_BAR_FOREGROUND:
|
case FlatClientProperties.TITLE_BAR_FOREGROUND:
|
||||||
if( titlePane != null )
|
if( titlePane != null )
|
||||||
@@ -480,7 +510,7 @@ public class FlatRootPaneUI
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
Container parent = c.getParent();
|
Container parent = c.getParent();
|
||||||
boolean active = parent instanceof Window ? ((Window)parent).isActive() : false;
|
boolean active = parent instanceof Window && ((Window)parent).isActive();
|
||||||
|
|
||||||
g.setColor( FlatUIUtils.deriveColor( active ? activeBorderColor : inactiveBorderColor, baseBorderColor ) );
|
g.setColor( FlatUIUtils.deriveColor( active ? activeBorderColor : inactiveBorderColor, baseBorderColor ) );
|
||||||
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
|
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
|
||||||
@@ -492,9 +522,7 @@ public class FlatRootPaneUI
|
|||||||
|
|
||||||
protected boolean isWindowMaximized( Component c ) {
|
protected boolean isWindowMaximized( Component c ) {
|
||||||
Container parent = c.getParent();
|
Container parent = c.getParent();
|
||||||
return parent instanceof Frame
|
return parent instanceof Frame && (((Frame)parent).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0;
|
||||||
? (((Frame)parent).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0
|
|
||||||
: false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Border for various components (e.g. {@link javax.swing.JComboBox}).
|
* Border for various components (e.g. {@link javax.swing.JComboBox}).
|
||||||
@@ -29,7 +30,10 @@ import javax.swing.UIManager;
|
|||||||
public class FlatRoundBorder
|
public class FlatRoundBorder
|
||||||
extends FlatBorder
|
extends FlatBorder
|
||||||
{
|
{
|
||||||
protected final int arc = UIManager.getInt( "Component.arc" );
|
@Styleable protected int arc = UIManager.getInt( "Component.arc" );
|
||||||
|
|
||||||
|
// only used via styling (not in UI defaults, but has likewise client properties)
|
||||||
|
/** @since 2 */ @Styleable protected Boolean roundRect;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getArc( Component c ) {
|
protected int getArc( Component c ) {
|
||||||
@@ -37,6 +41,8 @@ public class FlatRoundBorder
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Boolean roundRect = FlatUIUtils.isRoundRect( c );
|
Boolean roundRect = FlatUIUtils.isRoundRect( c );
|
||||||
|
if( roundRect == null )
|
||||||
|
roundRect = this.roundRect;
|
||||||
return roundRect != null ? (roundRect ? Short.MAX_VALUE : 0) : arc;
|
return roundRect != null ? (roundRect ? Short.MAX_VALUE : 0) : arc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import java.awt.Rectangle;
|
|||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import javax.swing.InputMap;
|
import javax.swing.InputMap;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
@@ -35,6 +36,9 @@ import javax.swing.UIManager;
|
|||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicScrollBarUI;
|
import javax.swing.plaf.basic.BasicScrollBarUI;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,7 +47,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* <!-- BasicScrollBarUI -->
|
* <!-- BasicScrollBarUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault ScrollBar.background Color
|
* @uiDefault ScrollBar.background Color
|
||||||
* @uiDefault ScrollBar.foreground Color
|
* @uiDefault ScrollBar.foreground Color unused
|
||||||
* @uiDefault ScrollBar.track Color
|
* @uiDefault ScrollBar.track Color
|
||||||
* @uiDefault ScrollBar.thumb Color
|
* @uiDefault ScrollBar.thumb Color
|
||||||
* @uiDefault ScrollBar.width int
|
* @uiDefault ScrollBar.width int
|
||||||
@@ -53,6 +57,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*
|
*
|
||||||
* <!-- FlatScrollBarUI -->
|
* <!-- FlatScrollBarUI -->
|
||||||
*
|
*
|
||||||
|
* @uiDefault ScrollBar.minimumButtonSize Dimension
|
||||||
* @uiDefault ScrollBar.trackInsets Insets
|
* @uiDefault ScrollBar.trackInsets Insets
|
||||||
* @uiDefault ScrollBar.thumbInsets Insets
|
* @uiDefault ScrollBar.thumbInsets Insets
|
||||||
* @uiDefault ScrollBar.trackArc int
|
* @uiDefault ScrollBar.trackArc int
|
||||||
@@ -74,33 +79,47 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*/
|
*/
|
||||||
public class FlatScrollBarUI
|
public class FlatScrollBarUI
|
||||||
extends BasicScrollBarUI
|
extends BasicScrollBarUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
protected Insets trackInsets;
|
// overrides BasicScrollBarUI.supportsAbsolutePositioning (which is private)
|
||||||
protected Insets thumbInsets;
|
@Styleable protected boolean allowsAbsolutePositioning;
|
||||||
protected int trackArc;
|
|
||||||
protected int thumbArc;
|
|
||||||
protected Color hoverTrackColor;
|
|
||||||
protected Color hoverThumbColor;
|
|
||||||
protected boolean hoverThumbWithTrack;
|
|
||||||
protected Color pressedTrackColor;
|
|
||||||
protected Color pressedThumbColor;
|
|
||||||
protected boolean pressedThumbWithTrack;
|
|
||||||
|
|
||||||
protected boolean showButtons;
|
/** @since 2.1 */ @Styleable protected Dimension minimumButtonSize;
|
||||||
protected String arrowType;
|
@Styleable protected Insets trackInsets;
|
||||||
protected Color buttonArrowColor;
|
@Styleable protected Insets thumbInsets;
|
||||||
protected Color buttonDisabledArrowColor;
|
@Styleable protected int trackArc;
|
||||||
protected Color hoverButtonBackground;
|
@Styleable protected int thumbArc;
|
||||||
protected Color pressedButtonBackground;
|
@Styleable protected Color hoverTrackColor;
|
||||||
|
@Styleable protected Color hoverThumbColor;
|
||||||
|
@Styleable protected boolean hoverThumbWithTrack;
|
||||||
|
@Styleable protected Color pressedTrackColor;
|
||||||
|
@Styleable protected Color pressedThumbColor;
|
||||||
|
@Styleable protected boolean pressedThumbWithTrack;
|
||||||
|
|
||||||
|
@Styleable protected boolean showButtons;
|
||||||
|
@Styleable protected String arrowType;
|
||||||
|
@Styleable protected Color buttonArrowColor;
|
||||||
|
@Styleable protected Color buttonDisabledArrowColor;
|
||||||
|
@Styleable protected Color hoverButtonBackground;
|
||||||
|
@Styleable protected Color pressedButtonBackground;
|
||||||
|
|
||||||
private MouseAdapter hoverListener;
|
private MouseAdapter hoverListener;
|
||||||
protected boolean hoverTrack;
|
protected boolean hoverTrack;
|
||||||
protected boolean hoverThumb;
|
protected boolean hoverThumb;
|
||||||
|
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatScrollBarUI();
|
return new FlatScrollBarUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installListeners() {
|
protected void installListeners() {
|
||||||
super.installListeners();
|
super.installListeners();
|
||||||
@@ -123,6 +142,9 @@ public class FlatScrollBarUI
|
|||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
|
allowsAbsolutePositioning = super.getSupportsAbsolutePositioning();
|
||||||
|
|
||||||
|
minimumButtonSize = UIManager.getDimension( "ScrollBar.minimumButtonSize" );
|
||||||
trackInsets = UIManager.getInsets( "ScrollBar.trackInsets" );
|
trackInsets = UIManager.getInsets( "ScrollBar.trackInsets" );
|
||||||
thumbInsets = UIManager.getInsets( "ScrollBar.thumbInsets" );
|
thumbInsets = UIManager.getInsets( "ScrollBar.thumbInsets" );
|
||||||
trackArc = UIManager.getInt( "ScrollBar.trackArc" );
|
trackArc = UIManager.getInt( "ScrollBar.trackArc" );
|
||||||
@@ -152,6 +174,7 @@ public class FlatScrollBarUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
minimumButtonSize = null;
|
||||||
trackInsets = null;
|
trackInsets = null;
|
||||||
thumbInsets = null;
|
thumbInsets = null;
|
||||||
hoverTrackColor = null;
|
hoverTrackColor = null;
|
||||||
@@ -163,6 +186,8 @@ public class FlatScrollBarUI
|
|||||||
buttonDisabledArrowColor = null;
|
buttonDisabledArrowColor = null;
|
||||||
hoverButtonBackground = null;
|
hoverButtonBackground = null;
|
||||||
pressedButtonBackground = null;
|
pressedButtonBackground = null;
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -177,6 +202,13 @@ public class FlatScrollBarUI
|
|||||||
scrollbar.repaint();
|
scrollbar.repaint();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.STYLE:
|
||||||
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
|
installStyle();
|
||||||
|
scrollbar.revalidate();
|
||||||
|
scrollbar.repaint();
|
||||||
|
break;
|
||||||
|
|
||||||
case "componentOrientation":
|
case "componentOrientation":
|
||||||
// this is missing in BasicScrollBarUI.Handler.propertyChange()
|
// this is missing in BasicScrollBarUI.Handler.propertyChange()
|
||||||
InputMap inputMap = (InputMap) UIManager.get( "ScrollBar.ancestorInputMap" );
|
InputMap inputMap = (InputMap) UIManager.get( "ScrollBar.ancestorInputMap" );
|
||||||
@@ -193,6 +225,53 @@ public class FlatScrollBarUI
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle() {
|
||||||
|
try {
|
||||||
|
applyStyle( FlatStylingSupport.getResolvedStyle( scrollbar, "ScrollBar" ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( Object style ) {
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||||
|
|
||||||
|
if( incrButton instanceof FlatScrollBarButton )
|
||||||
|
((FlatScrollBarButton)incrButton).updateStyle();
|
||||||
|
if( decrButton instanceof FlatScrollBarButton )
|
||||||
|
((FlatScrollBarButton)decrButton).updateStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
Object oldValue;
|
||||||
|
switch( key ) {
|
||||||
|
// BasicScrollBarUI
|
||||||
|
case "track": oldValue = trackColor; trackColor = (Color) value; return oldValue;
|
||||||
|
case "thumb": oldValue = thumbColor; thumbColor = (Color) value; return oldValue;
|
||||||
|
case "width": oldValue = scrollBarWidth; scrollBarWidth = (int) value; return oldValue;
|
||||||
|
case "minimumThumbSize": oldValue = minimumThumbSize; minimumThumbSize = (Dimension) value; return oldValue;
|
||||||
|
case "maximumThumbSize": oldValue = maximumThumbSize; maximumThumbSize = (Dimension) value; return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, scrollbar, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
Map<String, Class<?>> infos = new FlatStylingSupport.StyleableInfosMap<>();
|
||||||
|
infos.put( "track", Color.class );
|
||||||
|
infos.put( "thumb", Color.class );
|
||||||
|
infos.put( "width", int.class );
|
||||||
|
infos.put( "minimumThumbSize", Dimension.class );
|
||||||
|
infos.put( "maximumThumbSize", Dimension.class );
|
||||||
|
FlatStylingSupport.collectAnnotatedStyleableInfos( this, infos );
|
||||||
|
return infos;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
return UIScale.scale( super.getPreferredSize( c ) );
|
return UIScale.scale( super.getPreferredSize( c ) );
|
||||||
@@ -209,9 +288,17 @@ public class FlatScrollBarUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isShowButtons() {
|
protected boolean isShowButtons() {
|
||||||
|
// check client property on scroll bar
|
||||||
Object showButtons = scrollbar.getClientProperty( FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS );
|
Object showButtons = scrollbar.getClientProperty( FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS );
|
||||||
if( showButtons == null && scrollbar.getParent() instanceof JScrollPane )
|
if( showButtons == null && scrollbar.getParent() instanceof JScrollPane ) {
|
||||||
showButtons = ((JScrollPane)scrollbar.getParent()).getClientProperty( FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS );
|
JScrollPane scrollPane = (JScrollPane) scrollbar.getParent();
|
||||||
|
// check client property on scroll pane
|
||||||
|
showButtons = scrollPane.getClientProperty( FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS );
|
||||||
|
if( showButtons == null && scrollPane.getUI() instanceof FlatScrollPaneUI ) {
|
||||||
|
// check styling property on scroll pane
|
||||||
|
showButtons = ((FlatScrollPaneUI)scrollPane.getUI()).showButtons;
|
||||||
|
}
|
||||||
|
}
|
||||||
return (showButtons != null) ? Objects.equals( showButtons, true ) : this.showButtons;
|
return (showButtons != null) ? Objects.equals( showButtons, true ) : this.showButtons;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,6 +382,11 @@ public class FlatScrollBarUI
|
|||||||
return UIScale.scale( FlatUIUtils.addInsets( super.getMaximumThumbSize(), thumbInsets ) );
|
return UIScale.scale( FlatUIUtils.addInsets( super.getMaximumThumbSize(), thumbInsets ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getSupportsAbsolutePositioning() {
|
||||||
|
return allowsAbsolutePositioning;
|
||||||
|
}
|
||||||
|
|
||||||
//---- class ScrollBarHoverListener ---------------------------------------
|
//---- class ScrollBarHoverListener ---------------------------------------
|
||||||
|
|
||||||
// using static field to disabling hover for other scroll bars
|
// using static field to disabling hover for other scroll bars
|
||||||
@@ -363,11 +455,27 @@ public class FlatScrollBarUI
|
|||||||
super( direction, type, foreground, disabledForeground,
|
super( direction, type, foreground, disabledForeground,
|
||||||
hoverForeground, hoverBackground, pressedForeground, pressedBackground );
|
hoverForeground, hoverBackground, pressedForeground, pressedBackground );
|
||||||
|
|
||||||
setArrowWidth( FlatArrowButton.DEFAULT_ARROW_WIDTH - 2 );
|
|
||||||
setFocusable( false );
|
setFocusable( false );
|
||||||
setRequestFocusEnabled( false );
|
setRequestFocusEnabled( false );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void updateStyle() {
|
||||||
|
updateStyle( arrowType, buttonArrowColor, buttonDisabledArrowColor,
|
||||||
|
null, hoverButtonBackground, null, pressedButtonBackground );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getArrowWidth() {
|
||||||
|
// scale arrow size depending on scroll bar width
|
||||||
|
// (6 is default arrow width; 10 is base scroll bar width)
|
||||||
|
int arrowWidth = Math.round( 6 * (scrollBarWidth / 10f) );
|
||||||
|
|
||||||
|
// compute arrow size that leaves equal space on both sides (arrow is centered)
|
||||||
|
arrowWidth = scrollBarWidth - (((scrollBarWidth - arrowWidth) / 2) * 2);
|
||||||
|
|
||||||
|
return arrowWidth;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Color deriveBackground( Color background ) {
|
protected Color deriveBackground( Color background ) {
|
||||||
return FlatUIUtils.deriveColor( background, scrollbar.getBackground() );
|
return FlatUIUtils.deriveColor( background, scrollbar.getBackground() );
|
||||||
@@ -376,8 +484,9 @@ public class FlatScrollBarUI
|
|||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize() {
|
public Dimension getPreferredSize() {
|
||||||
if( isShowButtons() ) {
|
if( isShowButtons() ) {
|
||||||
int w = UIScale.scale( scrollBarWidth );
|
int w = UIScale.scale( Math.max( scrollBarWidth, (minimumButtonSize != null) ? minimumButtonSize.width : 0 ) );
|
||||||
return new Dimension( w, w );
|
int h = UIScale.scale( Math.max( scrollBarWidth, (minimumButtonSize != null) ? minimumButtonSize.height : 0 ) );
|
||||||
|
return new Dimension( w, h );
|
||||||
} else
|
} else
|
||||||
return new Dimension();
|
return new Dimension();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ import java.awt.event.MouseWheelEvent;
|
|||||||
import java.awt.event.MouseWheelListener;
|
import java.awt.event.MouseWheelListener;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
@@ -46,6 +48,9 @@ import javax.swing.UIManager;
|
|||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicScrollPaneUI;
|
import javax.swing.plaf.basic.BasicScrollPaneUI;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JScrollPane}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JScrollPane}.
|
||||||
@@ -66,9 +71,16 @@ import com.formdev.flatlaf.FlatClientProperties;
|
|||||||
*/
|
*/
|
||||||
public class FlatScrollPaneUI
|
public class FlatScrollPaneUI
|
||||||
extends BasicScrollPaneUI
|
extends BasicScrollPaneUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
|
// only used via styling (not in UI defaults, but has likewise client properties)
|
||||||
|
/** @since 2 */ @Styleable protected Boolean showButtons;
|
||||||
|
|
||||||
private Handler handler;
|
private Handler handler;
|
||||||
|
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
private AtomicBoolean borderShared;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatScrollPaneUI();
|
return new FlatScrollPaneUI();
|
||||||
}
|
}
|
||||||
@@ -80,6 +92,8 @@ public class FlatScrollPaneUI
|
|||||||
int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
||||||
LookAndFeel.installProperty( c, "opaque", focusWidth == 0 );
|
LookAndFeel.installProperty( c, "opaque", focusWidth == 0 );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
|
||||||
MigLayoutVisualPadding.install( scrollpane );
|
MigLayoutVisualPadding.install( scrollpane );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,6 +102,9 @@ public class FlatScrollPaneUI
|
|||||||
MigLayoutVisualPadding.uninstall( scrollpane );
|
MigLayoutVisualPadding.uninstall( scrollpane );
|
||||||
|
|
||||||
super.uninstallUI( c );
|
super.uninstallUI( c );
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
borderShared = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -272,7 +289,18 @@ public class FlatScrollPaneUI
|
|||||||
((JButton)corner).setBorder( BorderFactory.createEmptyBorder() );
|
((JButton)corner).setBorder( BorderFactory.createEmptyBorder() );
|
||||||
((JButton)corner).setFocusable( false );
|
((JButton)corner).setFocusable( false );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.OUTLINE:
|
||||||
|
scrollpane.repaint();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.STYLE:
|
||||||
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
|
installStyle();
|
||||||
|
scrollpane.revalidate();
|
||||||
|
scrollpane.repaint();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -283,6 +311,38 @@ public class FlatScrollPaneUI
|
|||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle() {
|
||||||
|
try {
|
||||||
|
applyStyle( FlatStylingSupport.getResolvedStyle( scrollpane, "ScrollPane" ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( Object style ) {
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
if( key.equals( "focusWidth" ) ) {
|
||||||
|
int focusWidth = (value instanceof Integer) ? (int) value : UIManager.getInt( "Component.focusWidth" );
|
||||||
|
LookAndFeel.installProperty( scrollpane, "opaque", focusWidth == 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( borderShared == null )
|
||||||
|
borderShared = new AtomicBoolean( true );
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, scrollpane, borderShared );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this, scrollpane.getBorder() );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void updateViewport( PropertyChangeEvent e ) {
|
protected void updateViewport( PropertyChangeEvent e ) {
|
||||||
super.updateViewport( e );
|
super.updateViewport( e );
|
||||||
@@ -332,9 +392,7 @@ public class FlatScrollPaneUI
|
|||||||
paint( g, c );
|
paint( g, c );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @since 1.3 */
|
||||||
* @since 1.3
|
|
||||||
*/
|
|
||||||
public static boolean isPermanentFocusOwner( JScrollPane scrollPane ) {
|
public static boolean isPermanentFocusOwner( JScrollPane scrollPane ) {
|
||||||
JViewport viewport = scrollPane.getViewport();
|
JViewport viewport = scrollPane.getViewport();
|
||||||
Component view = (viewport != null) ? viewport.getView() : null;
|
Component view = (viewport != null) ? viewport.getView() : null;
|
||||||
|
|||||||
@@ -21,11 +21,18 @@ import java.awt.Dimension;
|
|||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JSeparator;
|
import javax.swing.JSeparator;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicSeparatorUI;
|
import javax.swing.plaf.basic.BasicSeparatorUI;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JSeparator}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JSeparator}.
|
||||||
@@ -45,15 +52,36 @@ import javax.swing.plaf.basic.BasicSeparatorUI;
|
|||||||
*/
|
*/
|
||||||
public class FlatSeparatorUI
|
public class FlatSeparatorUI
|
||||||
extends BasicSeparatorUI
|
extends BasicSeparatorUI
|
||||||
|
implements StyleableUI, PropertyChangeListener
|
||||||
{
|
{
|
||||||
protected int height;
|
@Styleable protected int height;
|
||||||
protected int stripeWidth;
|
@Styleable protected int stripeWidth;
|
||||||
protected int stripeIndent;
|
@Styleable protected int stripeIndent;
|
||||||
|
|
||||||
|
private final boolean shared;
|
||||||
private boolean defaults_initialized = false;
|
private boolean defaults_initialized = false;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return FlatUIUtils.createSharedUI( FlatSeparatorUI.class, FlatSeparatorUI::new );
|
return FlatUIUtils.canUseSharedUI( c )
|
||||||
|
? FlatUIUtils.createSharedUI( FlatSeparatorUI.class, () -> new FlatSeparatorUI( true ) )
|
||||||
|
: new FlatSeparatorUI( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected FlatSeparatorUI( boolean shared ) {
|
||||||
|
this.shared = shared;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getPropertyPrefix() {
|
||||||
|
return "Separator";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle( (JSeparator) c );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -73,13 +101,75 @@ public class FlatSeparatorUI
|
|||||||
@Override
|
@Override
|
||||||
protected void uninstallDefaults( JSeparator s ) {
|
protected void uninstallDefaults( JSeparator s ) {
|
||||||
super.uninstallDefaults( s );
|
super.uninstallDefaults( s );
|
||||||
|
|
||||||
defaults_initialized = false;
|
defaults_initialized = false;
|
||||||
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getPropertyPrefix() {
|
@Override
|
||||||
|
protected void installListeners( JSeparator s ) {
|
||||||
|
super.installListeners( s );
|
||||||
|
|
||||||
|
s.addPropertyChangeListener( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallListeners( JSeparator s ) {
|
||||||
|
super.uninstallListeners( s );
|
||||||
|
|
||||||
|
s.removePropertyChangeListener( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.0.1 */
|
||||||
|
@Override
|
||||||
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
switch( e.getPropertyName() ) {
|
||||||
|
case FlatClientProperties.STYLE:
|
||||||
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
|
JSeparator s = (JSeparator) e.getSource();
|
||||||
|
if( shared && FlatStylingSupport.hasStyleProperty( s ) ) {
|
||||||
|
// unshare component UI if necessary
|
||||||
|
// updateUI() invokes installStyle() from installUI()
|
||||||
|
s.updateUI();
|
||||||
|
} else
|
||||||
|
installStyle( s );
|
||||||
|
s.revalidate();
|
||||||
|
s.repaint();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle( JSeparator s ) {
|
||||||
|
try {
|
||||||
|
applyStyle( s, FlatStylingSupport.getResolvedStyle( s, getStyleType() ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
String getStyleType() {
|
||||||
return "Separator";
|
return "Separator";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( JSeparator s, Object style ) {
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style,
|
||||||
|
(key, value) -> applyStyleProperty( s, key, value ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( JSeparator s, String key, Object value ) {
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, s, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paint( Graphics g, JComponent c ) {
|
public void paint( Graphics g, JComponent c ) {
|
||||||
Graphics2D g2 = (Graphics2D) g.create();
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ import java.awt.event.MouseEvent;
|
|||||||
import java.awt.geom.Ellipse2D;
|
import java.awt.geom.Ellipse2D;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
import java.awt.geom.RoundRectangle2D;
|
import java.awt.geom.RoundRectangle2D;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JSlider;
|
import javax.swing.JSlider;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
@@ -36,7 +38,11 @@ import javax.swing.SwingUtilities;
|
|||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicSliderUI;
|
import javax.swing.plaf.basic.BasicSliderUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.Graphics2DProxy;
|
||||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,6 +65,8 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault Slider.trackWidth int
|
* @uiDefault Slider.trackWidth int
|
||||||
* @uiDefault Slider.thumbSize Dimension
|
* @uiDefault Slider.thumbSize Dimension
|
||||||
* @uiDefault Slider.focusWidth int
|
* @uiDefault Slider.focusWidth int
|
||||||
|
* @uiDefault Slider.thumbBorderWidth int or float
|
||||||
|
*
|
||||||
* @uiDefault Slider.trackValueColor Color optional; defaults to Slider.thumbColor
|
* @uiDefault Slider.trackValueColor Color optional; defaults to Slider.thumbColor
|
||||||
* @uiDefault Slider.trackColor Color
|
* @uiDefault Slider.trackColor Color
|
||||||
* @uiDefault Slider.thumbColor Color
|
* @uiDefault Slider.thumbColor Color
|
||||||
@@ -75,23 +83,26 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*/
|
*/
|
||||||
public class FlatSliderUI
|
public class FlatSliderUI
|
||||||
extends BasicSliderUI
|
extends BasicSliderUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
protected int trackWidth;
|
@Styleable protected int trackWidth;
|
||||||
protected Dimension thumbSize;
|
@Styleable protected Dimension thumbSize;
|
||||||
protected int focusWidth;
|
@Styleable protected int focusWidth;
|
||||||
|
/** @since 2 */ @Styleable protected float thumbBorderWidth;
|
||||||
|
|
||||||
protected Color trackValueColor;
|
@Styleable protected Color trackValueColor;
|
||||||
protected Color trackColor;
|
@Styleable protected Color trackColor;
|
||||||
protected Color thumbColor;
|
@Styleable protected Color thumbColor;
|
||||||
protected Color thumbBorderColor;
|
@Styleable protected Color thumbBorderColor;
|
||||||
protected Color focusBaseColor;
|
protected Color focusBaseColor;
|
||||||
protected Color focusedColor;
|
@Styleable protected Color focusedColor;
|
||||||
protected Color focusedThumbBorderColor;
|
@Styleable protected Color focusedThumbBorderColor;
|
||||||
protected Color hoverThumbColor;
|
@Styleable protected Color hoverThumbColor;
|
||||||
protected Color pressedThumbColor;
|
@Styleable protected Color pressedThumbColor;
|
||||||
protected Color disabledTrackColor;
|
@Styleable protected Color disabledTrackColor;
|
||||||
protected Color disabledThumbColor;
|
@Styleable protected Color disabledThumbColor;
|
||||||
protected Color disabledThumbBorderColor;
|
@Styleable protected Color disabledThumbBorderColor;
|
||||||
|
@Styleable protected Color tickColor;
|
||||||
|
|
||||||
private Color defaultBackground;
|
private Color defaultBackground;
|
||||||
private Color defaultForeground;
|
private Color defaultForeground;
|
||||||
@@ -100,6 +111,7 @@ public class FlatSliderUI
|
|||||||
protected boolean thumbPressed;
|
protected boolean thumbPressed;
|
||||||
|
|
||||||
private Object[] oldRenderingHints;
|
private Object[] oldRenderingHints;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatSliderUI();
|
return new FlatSliderUI();
|
||||||
@@ -109,6 +121,13 @@ public class FlatSliderUI
|
|||||||
super( null );
|
super( null );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults( JSlider slider ) {
|
protected void installDefaults( JSlider slider ) {
|
||||||
super.installDefaults( slider );
|
super.installDefaults( slider );
|
||||||
@@ -123,6 +142,7 @@ public class FlatSliderUI
|
|||||||
thumbSize = new Dimension( thumbWidth, thumbWidth );
|
thumbSize = new Dimension( thumbWidth, thumbWidth );
|
||||||
}
|
}
|
||||||
focusWidth = FlatUIUtils.getUIInt( "Slider.focusWidth", 4 );
|
focusWidth = FlatUIUtils.getUIInt( "Slider.focusWidth", 4 );
|
||||||
|
thumbBorderWidth = FlatUIUtils.getUIFloat( "Slider.thumbBorderWidth", 1 );
|
||||||
|
|
||||||
trackValueColor = FlatUIUtils.getUIColor( "Slider.trackValueColor", "Slider.thumbColor" );
|
trackValueColor = FlatUIUtils.getUIColor( "Slider.trackValueColor", "Slider.thumbColor" );
|
||||||
trackColor = UIManager.getColor( "Slider.trackColor" );
|
trackColor = UIManager.getColor( "Slider.trackColor" );
|
||||||
@@ -136,6 +156,7 @@ public class FlatSliderUI
|
|||||||
disabledTrackColor = UIManager.getColor( "Slider.disabledTrackColor" );
|
disabledTrackColor = UIManager.getColor( "Slider.disabledTrackColor" );
|
||||||
disabledThumbColor = UIManager.getColor( "Slider.disabledThumbColor" );
|
disabledThumbColor = UIManager.getColor( "Slider.disabledThumbColor" );
|
||||||
disabledThumbBorderColor = FlatUIUtils.getUIColor( "Slider.disabledThumbBorderColor", "Component.disabledBorderColor" );
|
disabledThumbBorderColor = FlatUIUtils.getUIColor( "Slider.disabledThumbBorderColor", "Component.disabledBorderColor" );
|
||||||
|
tickColor = FlatUIUtils.getUIColor( "Slider.tickColor", Color.BLACK ); // see BasicSliderUI.paintTicks()
|
||||||
|
|
||||||
defaultBackground = UIManager.getColor( "Slider.background" );
|
defaultBackground = UIManager.getColor( "Slider.background" );
|
||||||
defaultForeground = UIManager.getColor( "Slider.foreground" );
|
defaultForeground = UIManager.getColor( "Slider.foreground" );
|
||||||
@@ -157,9 +178,12 @@ public class FlatSliderUI
|
|||||||
disabledTrackColor = null;
|
disabledTrackColor = null;
|
||||||
disabledThumbColor = null;
|
disabledThumbColor = null;
|
||||||
disabledThumbBorderColor = null;
|
disabledThumbBorderColor = null;
|
||||||
|
tickColor = null;
|
||||||
|
|
||||||
defaultBackground = null;
|
defaultBackground = null;
|
||||||
defaultForeground = null;
|
defaultForeground = null;
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -167,6 +191,37 @@ public class FlatSliderUI
|
|||||||
return new FlatTrackListener();
|
return new FlatTrackListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PropertyChangeListener createPropertyChangeListener( JSlider slider ) {
|
||||||
|
return FlatStylingSupport.createPropertyChangeListener( slider, this::installStyle,
|
||||||
|
super.createPropertyChangeListener( slider ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle() {
|
||||||
|
try {
|
||||||
|
applyStyle( FlatStylingSupport.getResolvedStyle( slider, "Slider" ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( Object style ) {
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, slider, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getBaseline( JComponent c, int width, int height ) {
|
public int getBaseline( JComponent c, int width, int height ) {
|
||||||
if( c == null )
|
if( c == null )
|
||||||
@@ -179,7 +234,7 @@ public class FlatSliderUI
|
|||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
// use default font (instead of slider font) because the slider font size
|
// use default font (instead of slider font) because the slider font size
|
||||||
// may be different to label font size, but we want align the track/thumb with labels
|
// may be different to label font size, but we want to align the track/thumb with labels
|
||||||
Font font = UIManager.getFont( "defaultFont" );
|
Font font = UIManager.getFont( "defaultFont" );
|
||||||
if( font == null )
|
if( font == null )
|
||||||
font = slider.getFont();
|
font = slider.getFont();
|
||||||
@@ -326,6 +381,19 @@ debug*/
|
|||||||
((Graphics2D)g).fill( track );
|
((Graphics2D)g).fill( track );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paintTicks( Graphics g ) {
|
||||||
|
// because BasicSliderUI.paintTicks() always uses
|
||||||
|
// g.setColor( UIManager.getColor("Slider.tickColor") )
|
||||||
|
// we override this method and use our tickColor field to allow styling
|
||||||
|
super.paintTicks( new Graphics2DProxy( (Graphics2D) g ) {
|
||||||
|
@Override
|
||||||
|
public void setColor( Color c ) {
|
||||||
|
super.setColor( tickColor );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paintThumb( Graphics g ) {
|
public void paintThumb( Graphics g ) {
|
||||||
Color thumbColor = getThumbColor();
|
Color thumbColor = getThumbColor();
|
||||||
@@ -341,11 +409,11 @@ debug*/
|
|||||||
Color focusedColor = FlatUIUtils.deriveColor( this.focusedColor,
|
Color focusedColor = FlatUIUtils.deriveColor( this.focusedColor,
|
||||||
(foreground != defaultForeground) ? foreground : focusBaseColor );
|
(foreground != defaultForeground) ? foreground : focusBaseColor );
|
||||||
|
|
||||||
paintThumb( g, slider, thumbRect, isRoundThumb(), color, borderColor, focusedColor, focusWidth );
|
paintThumb( g, slider, thumbRect, isRoundThumb(), color, borderColor, focusedColor, thumbBorderWidth, focusWidth );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void paintThumb( Graphics g, JSlider slider, Rectangle thumbRect, boolean roundThumb,
|
public static void paintThumb( Graphics g, JSlider slider, Rectangle thumbRect, boolean roundThumb,
|
||||||
Color thumbColor, Color thumbBorderColor, Color focusedColor, int focusWidth )
|
Color thumbColor, Color thumbBorderColor, Color focusedColor, float thumbBorderWidth, int focusWidth )
|
||||||
{
|
{
|
||||||
double systemScaleFactor = UIScale.getSystemScaleFactor( (Graphics2D) g );
|
double systemScaleFactor = UIScale.getSystemScaleFactor( (Graphics2D) g );
|
||||||
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
|
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
|
||||||
@@ -354,18 +422,20 @@ debug*/
|
|||||||
(g2d, x2, y2, width2, height2, scaleFactor) -> {
|
(g2d, x2, y2, width2, height2, scaleFactor) -> {
|
||||||
paintThumbImpl( g, slider, x2, y2, width2, height2,
|
paintThumbImpl( g, slider, x2, y2, width2, height2,
|
||||||
roundThumb, thumbColor, thumbBorderColor, focusedColor,
|
roundThumb, thumbColor, thumbBorderColor, focusedColor,
|
||||||
|
(float) (thumbBorderWidth * scaleFactor),
|
||||||
(float) (focusWidth * scaleFactor) );
|
(float) (focusWidth * scaleFactor) );
|
||||||
} );
|
} );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
paintThumbImpl( g, slider, thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height,
|
paintThumbImpl( g, slider, thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height,
|
||||||
roundThumb, thumbColor, thumbBorderColor, focusedColor, focusWidth );
|
roundThumb, thumbColor, thumbBorderColor, focusedColor, thumbBorderWidth, focusWidth );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void paintThumbImpl( Graphics g, JSlider slider, int x, int y, int width, int height,
|
private static void paintThumbImpl( Graphics g, JSlider slider, int x, int y, int width, int height,
|
||||||
boolean roundThumb, Color thumbColor, Color thumbBorderColor, Color focusedColor, float focusWidth )
|
boolean roundThumb, Color thumbColor, Color thumbBorderColor, Color focusedColor,
|
||||||
|
float thumbBorderWidth, float focusWidth )
|
||||||
{
|
{
|
||||||
int fw = Math.round( UIScale.scale( focusWidth ) );
|
int fw = Math.round( UIScale.scale( focusWidth ) );
|
||||||
int tx = x + fw;
|
int tx = x + fw;
|
||||||
@@ -387,7 +457,7 @@ debug*/
|
|||||||
((Graphics2D)g).fill( createRoundThumbShape( tx, ty, tw, th ) );
|
((Graphics2D)g).fill( createRoundThumbShape( tx, ty, tw, th ) );
|
||||||
|
|
||||||
// paint thumb background
|
// paint thumb background
|
||||||
float lw = UIScale.scale( 1f );
|
float lw = UIScale.scale( thumbBorderWidth );
|
||||||
g.setColor( thumbColor );
|
g.setColor( thumbColor );
|
||||||
((Graphics2D)g).fill( createRoundThumbShape( tx + lw, ty + lw,
|
((Graphics2D)g).fill( createRoundThumbShape( tx + lw, ty + lw,
|
||||||
tw - lw - lw, th - lw - lw ) );
|
tw - lw - lw, th - lw - lw ) );
|
||||||
@@ -428,7 +498,7 @@ debug*/
|
|||||||
g2.fill( createDirectionalThumbShape( fw, fw, tw, th, 0 ) );
|
g2.fill( createDirectionalThumbShape( fw, fw, tw, th, 0 ) );
|
||||||
|
|
||||||
// paint thumb background
|
// paint thumb background
|
||||||
float lw = UIScale.scale( 1f );
|
float lw = UIScale.scale( thumbBorderWidth );
|
||||||
g2.setColor( thumbColor );
|
g2.setColor( thumbColor );
|
||||||
g2.fill( createDirectionalThumbShape( fw + lw, fw + lw,
|
g2.fill( createDirectionalThumbShape( fw + lw, fw + lw,
|
||||||
tw - lw - lw, th - lw - lw - (lw * 0.4142f), 0 ) );
|
tw - lw - lw, th - lw - lw - (lw * 0.4142f), 0 ) );
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ import java.awt.event.FocusListener;
|
|||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JSpinner;
|
import javax.swing.JSpinner;
|
||||||
import javax.swing.JTextField;
|
import javax.swing.JTextField;
|
||||||
@@ -43,6 +45,9 @@ import javax.swing.plaf.ComponentUI;
|
|||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicSpinnerUI;
|
import javax.swing.plaf.basic.BasicSpinnerUI;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JSpinner}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JSpinner}.
|
||||||
@@ -63,12 +68,13 @@ import com.formdev.flatlaf.FlatClientProperties;
|
|||||||
* @uiDefault Spinner.buttonStyle String button (default) or none
|
* @uiDefault Spinner.buttonStyle String button (default) or none
|
||||||
* @uiDefault Component.arrowType String chevron (default) or triangle
|
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
* @uiDefault Component.isIntelliJTheme boolean
|
||||||
* @uiDefault Component.borderColor Color
|
|
||||||
* @uiDefault Component.disabledBorderColor Color
|
|
||||||
* @uiDefault Spinner.disabledBackground Color
|
* @uiDefault Spinner.disabledBackground Color
|
||||||
* @uiDefault Spinner.disabledForeground Color
|
* @uiDefault Spinner.disabledForeground Color
|
||||||
* @uiDefault Spinner.focusedBackground Color optional
|
* @uiDefault Spinner.focusedBackground Color optional
|
||||||
* @uiDefault Spinner.buttonBackground Color
|
* @uiDefault Spinner.buttonBackground Color optional
|
||||||
|
* @uiDefault Spinner.buttonSeparatorWidth int or float optional; defaults to Component.borderWidth
|
||||||
|
* @uiDefault Spinner.buttonSeparatorColor Color optional
|
||||||
|
* @uiDefault Spinner.buttonDisabledSeparatorColor Color optional
|
||||||
* @uiDefault Spinner.buttonArrowColor Color
|
* @uiDefault Spinner.buttonArrowColor Color
|
||||||
* @uiDefault Spinner.buttonDisabledArrowColor Color
|
* @uiDefault Spinner.buttonDisabledArrowColor Color
|
||||||
* @uiDefault Spinner.buttonHoverArrowColor Color
|
* @uiDefault Spinner.buttonHoverArrowColor Color
|
||||||
@@ -79,29 +85,41 @@ import com.formdev.flatlaf.FlatClientProperties;
|
|||||||
*/
|
*/
|
||||||
public class FlatSpinnerUI
|
public class FlatSpinnerUI
|
||||||
extends BasicSpinnerUI
|
extends BasicSpinnerUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
private Handler handler;
|
private Handler handler;
|
||||||
|
|
||||||
protected int minimumWidth;
|
@Styleable protected int minimumWidth;
|
||||||
protected String buttonStyle;
|
@Styleable protected String buttonStyle;
|
||||||
protected String arrowType;
|
@Styleable protected String arrowType;
|
||||||
protected boolean isIntelliJTheme;
|
protected boolean isIntelliJTheme;
|
||||||
protected Color borderColor;
|
@Styleable protected Color disabledBackground;
|
||||||
protected Color disabledBorderColor;
|
@Styleable protected Color disabledForeground;
|
||||||
protected Color disabledBackground;
|
@Styleable protected Color focusedBackground;
|
||||||
protected Color disabledForeground;
|
@Styleable protected Color buttonBackground;
|
||||||
protected Color focusedBackground;
|
/** @since 2 */ @Styleable protected float buttonSeparatorWidth;
|
||||||
protected Color buttonBackground;
|
/** @since 2 */ @Styleable protected Color buttonSeparatorColor;
|
||||||
protected Color buttonArrowColor;
|
/** @since 2 */ @Styleable protected Color buttonDisabledSeparatorColor;
|
||||||
protected Color buttonDisabledArrowColor;
|
@Styleable protected Color buttonArrowColor;
|
||||||
protected Color buttonHoverArrowColor;
|
@Styleable protected Color buttonDisabledArrowColor;
|
||||||
protected Color buttonPressedArrowColor;
|
@Styleable protected Color buttonHoverArrowColor;
|
||||||
protected Insets padding;
|
@Styleable protected Color buttonPressedArrowColor;
|
||||||
|
@Styleable protected Insets padding;
|
||||||
|
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
private AtomicBoolean borderShared;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatSpinnerUI();
|
return new FlatSpinnerUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
@@ -112,12 +130,13 @@ public class FlatSpinnerUI
|
|||||||
buttonStyle = UIManager.getString( "Spinner.buttonStyle" );
|
buttonStyle = UIManager.getString( "Spinner.buttonStyle" );
|
||||||
arrowType = UIManager.getString( "Component.arrowType" );
|
arrowType = UIManager.getString( "Component.arrowType" );
|
||||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
||||||
borderColor = UIManager.getColor( "Component.borderColor" );
|
|
||||||
disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
|
|
||||||
disabledBackground = UIManager.getColor( "Spinner.disabledBackground" );
|
disabledBackground = UIManager.getColor( "Spinner.disabledBackground" );
|
||||||
disabledForeground = UIManager.getColor( "Spinner.disabledForeground" );
|
disabledForeground = UIManager.getColor( "Spinner.disabledForeground" );
|
||||||
focusedBackground = UIManager.getColor( "Spinner.focusedBackground" );
|
focusedBackground = UIManager.getColor( "Spinner.focusedBackground" );
|
||||||
buttonBackground = UIManager.getColor( "Spinner.buttonBackground" );
|
buttonBackground = UIManager.getColor( "Spinner.buttonBackground" );
|
||||||
|
buttonSeparatorWidth = FlatUIUtils.getUIFloat( "Spinner.buttonSeparatorWidth", FlatUIUtils.getUIFloat( "Component.borderWidth", 1 ) );
|
||||||
|
buttonSeparatorColor = UIManager.getColor( "Spinner.buttonSeparatorColor" );
|
||||||
|
buttonDisabledSeparatorColor = UIManager.getColor( "Spinner.buttonDisabledSeparatorColor" );
|
||||||
buttonArrowColor = UIManager.getColor( "Spinner.buttonArrowColor" );
|
buttonArrowColor = UIManager.getColor( "Spinner.buttonArrowColor" );
|
||||||
buttonDisabledArrowColor = UIManager.getColor( "Spinner.buttonDisabledArrowColor" );
|
buttonDisabledArrowColor = UIManager.getColor( "Spinner.buttonDisabledArrowColor" );
|
||||||
buttonHoverArrowColor = UIManager.getColor( "Spinner.buttonHoverArrowColor" );
|
buttonHoverArrowColor = UIManager.getColor( "Spinner.buttonHoverArrowColor" );
|
||||||
@@ -131,18 +150,21 @@ public class FlatSpinnerUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
borderColor = null;
|
|
||||||
disabledBorderColor = null;
|
|
||||||
disabledBackground = null;
|
disabledBackground = null;
|
||||||
disabledForeground = null;
|
disabledForeground = null;
|
||||||
focusedBackground = null;
|
focusedBackground = null;
|
||||||
buttonBackground = null;
|
buttonBackground = null;
|
||||||
|
buttonSeparatorColor = null;
|
||||||
|
buttonDisabledSeparatorColor = null;
|
||||||
buttonArrowColor = null;
|
buttonArrowColor = null;
|
||||||
buttonDisabledArrowColor = null;
|
buttonDisabledArrowColor = null;
|
||||||
buttonHoverArrowColor = null;
|
buttonHoverArrowColor = null;
|
||||||
buttonPressedArrowColor = null;
|
buttonPressedArrowColor = null;
|
||||||
padding = null;
|
padding = null;
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
borderShared = null;
|
||||||
|
|
||||||
MigLayoutVisualPadding.uninstall( spinner );
|
MigLayoutVisualPadding.uninstall( spinner );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,6 +194,35 @@ public class FlatSpinnerUI
|
|||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle() {
|
||||||
|
try {
|
||||||
|
applyStyle( FlatStylingSupport.getResolvedStyle( spinner, "Spinner" ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( Object style ) {
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||||
|
updateEditorPadding();
|
||||||
|
updateArrowButtonsStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
if( borderShared == null )
|
||||||
|
borderShared = new AtomicBoolean( true );
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, spinner, borderShared );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this, spinner.getBorder() );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected JComponent createEditor() {
|
protected JComponent createEditor() {
|
||||||
JComponent editor = super.createEditor();
|
JComponent editor = super.createEditor();
|
||||||
@@ -236,17 +287,13 @@ public class FlatSpinnerUI
|
|||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @since 1.3 */
|
||||||
* @since 1.3
|
|
||||||
*/
|
|
||||||
public static boolean isPermanentFocusOwner( JSpinner spinner ) {
|
public static boolean isPermanentFocusOwner( JSpinner spinner ) {
|
||||||
if( FlatUIUtils.isPermanentFocusOwner( spinner ) )
|
if( FlatUIUtils.isPermanentFocusOwner( spinner ) )
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
JTextField textField = getEditorTextField( spinner.getEditor() );
|
JTextField textField = getEditorTextField( spinner.getEditor() );
|
||||||
return (textField != null)
|
return textField != null && FlatUIUtils.isPermanentFocusOwner( textField );
|
||||||
? FlatUIUtils.isPermanentFocusOwner( textField )
|
|
||||||
: false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Color getBackground( boolean enabled ) {
|
protected Color getBackground( boolean enabled ) {
|
||||||
@@ -297,6 +344,15 @@ public class FlatSpinnerUI
|
|||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateArrowButtonsStyle() {
|
||||||
|
for( Component c : spinner.getComponents() ) {
|
||||||
|
if( c instanceof FlatArrowButton ) {
|
||||||
|
((FlatArrowButton)c).updateStyle( arrowType, buttonArrowColor,
|
||||||
|
buttonDisabledArrowColor, buttonHoverArrowColor, null, buttonPressedArrowColor, null );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update( Graphics g, JComponent c ) {
|
public void update( Graphics g, JComponent c ) {
|
||||||
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
|
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
|
||||||
@@ -327,7 +383,7 @@ public class FlatSpinnerUI
|
|||||||
boolean isLeftToRight = spinner.getComponentOrientation().isLeftToRight();
|
boolean isLeftToRight = spinner.getComponentOrientation().isLeftToRight();
|
||||||
|
|
||||||
// paint arrow buttons background
|
// paint arrow buttons background
|
||||||
if( enabled ) {
|
if( enabled && buttonBackground != null ) {
|
||||||
g2.setColor( buttonBackground );
|
g2.setColor( buttonBackground );
|
||||||
Shape oldClip = g2.getClip();
|
Shape oldClip = g2.getClip();
|
||||||
if( isLeftToRight )
|
if( isLeftToRight )
|
||||||
@@ -339,10 +395,13 @@ public class FlatSpinnerUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// paint vertical line between value and arrow buttons
|
// paint vertical line between value and arrow buttons
|
||||||
g2.setColor( enabled ? borderColor : disabledBorderColor );
|
Color separatorColor = enabled ? buttonSeparatorColor : buttonDisabledSeparatorColor;
|
||||||
float lw = scale( 1f );
|
if( separatorColor != null && buttonSeparatorWidth > 0 ) {
|
||||||
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
|
g2.setColor( separatorColor );
|
||||||
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2) ) );
|
float lw = scale( buttonSeparatorWidth );
|
||||||
|
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
|
||||||
|
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2) ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
paint( g, c );
|
paint( g, c );
|
||||||
@@ -386,7 +445,7 @@ public class FlatSpinnerUI
|
|||||||
Insets padding = scale( FlatSpinnerUI.this.padding );
|
Insets padding = scale( FlatSpinnerUI.this.padding );
|
||||||
Dimension editorSize = (editor != null) ? editor.getPreferredSize() : new Dimension( 0, 0 );
|
Dimension editorSize = (editor != null) ? editor.getPreferredSize() : new Dimension( 0, 0 );
|
||||||
|
|
||||||
// the arrows width is the same as the inner height so that the arrows area is square
|
// the arrow buttons width is the same as the inner height so that the arrow buttons area is square
|
||||||
int minimumWidth = FlatUIUtils.minimumWidth( spinner, FlatSpinnerUI.this.minimumWidth );
|
int minimumWidth = FlatUIUtils.minimumWidth( spinner, FlatSpinnerUI.this.minimumWidth );
|
||||||
int innerHeight = editorSize.height + padding.top + padding.bottom;
|
int innerHeight = editorSize.height + padding.top + padding.bottom;
|
||||||
float focusWidth = FlatUIUtils.getBorderFocusWidth( spinner );
|
float focusWidth = FlatUIUtils.getBorderFocusWidth( spinner );
|
||||||
@@ -477,12 +536,20 @@ public class FlatSpinnerUI
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case FlatClientProperties.COMPONENT_ROUND_RECT:
|
case FlatClientProperties.COMPONENT_ROUND_RECT:
|
||||||
|
case FlatClientProperties.OUTLINE:
|
||||||
spinner.repaint();
|
spinner.repaint();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FlatClientProperties.MINIMUM_WIDTH:
|
case FlatClientProperties.MINIMUM_WIDTH:
|
||||||
spinner.revalidate();
|
spinner.revalidate();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.STYLE:
|
||||||
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
|
installStyle();
|
||||||
|
spinner.revalidate();
|
||||||
|
spinner.repaint();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import java.awt.Graphics;
|
|||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JSplitPane;
|
import javax.swing.JSplitPane;
|
||||||
@@ -32,6 +34,11 @@ import javax.swing.UIManager;
|
|||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicSplitPaneDivider;
|
import javax.swing.plaf.basic.BasicSplitPaneDivider;
|
||||||
import javax.swing.plaf.basic.BasicSplitPaneUI;
|
import javax.swing.plaf.basic.BasicSplitPaneUI;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,6 +53,13 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault SplitPaneDivider.border Border
|
* @uiDefault SplitPaneDivider.border Border
|
||||||
* @uiDefault SplitPaneDivider.draggingColor Color only used if continuousLayout is false
|
* @uiDefault SplitPaneDivider.draggingColor Color only used if continuousLayout is false
|
||||||
*
|
*
|
||||||
|
* <!-- BasicSplitPaneDivider -->
|
||||||
|
*
|
||||||
|
* @uiDefault SplitPane.oneTouchButtonSize int
|
||||||
|
* @uiDefault SplitPane.oneTouchButtonOffset int
|
||||||
|
* @uiDefault SplitPane.centerOneTouchButtons boolean
|
||||||
|
* @uiDefault SplitPane.supportsOneTouchButtons boolean optional; default is true
|
||||||
|
*
|
||||||
* <!-- JSplitPane -->
|
* <!-- JSplitPane -->
|
||||||
*
|
*
|
||||||
* @uiDefault SplitPane.continuousLayout boolean
|
* @uiDefault SplitPane.continuousLayout boolean
|
||||||
@@ -66,16 +80,27 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*/
|
*/
|
||||||
public class FlatSplitPaneUI
|
public class FlatSplitPaneUI
|
||||||
extends BasicSplitPaneUI
|
extends BasicSplitPaneUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
protected String arrowType;
|
@Styleable protected String arrowType;
|
||||||
protected Color oneTouchArrowColor;
|
@Styleable protected Color oneTouchArrowColor;
|
||||||
protected Color oneTouchHoverArrowColor;
|
@Styleable protected Color oneTouchHoverArrowColor;
|
||||||
protected Color oneTouchPressedArrowColor;
|
@Styleable protected Color oneTouchPressedArrowColor;
|
||||||
|
|
||||||
|
private PropertyChangeListener propertyChangeListener;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatSplitPaneUI();
|
return new FlatSplitPaneUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
arrowType = UIManager.getString( "Component.arrowType" );
|
arrowType = UIManager.getString( "Component.arrowType" );
|
||||||
@@ -96,6 +121,24 @@ public class FlatSplitPaneUI
|
|||||||
oneTouchArrowColor = null;
|
oneTouchArrowColor = null;
|
||||||
oneTouchHoverArrowColor = null;
|
oneTouchHoverArrowColor = null;
|
||||||
oneTouchPressedArrowColor = null;
|
oneTouchPressedArrowColor = null;
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installListeners() {
|
||||||
|
super.installListeners();
|
||||||
|
|
||||||
|
propertyChangeListener = FlatStylingSupport.createPropertyChangeListener( splitPane, this::installStyle, null );
|
||||||
|
splitPane.addPropertyChangeListener( propertyChangeListener );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallListeners() {
|
||||||
|
super.uninstallListeners();
|
||||||
|
|
||||||
|
splitPane.removePropertyChangeListener( propertyChangeListener );
|
||||||
|
propertyChangeListener = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -103,16 +146,53 @@ public class FlatSplitPaneUI
|
|||||||
return new FlatSplitPaneDivider( this );
|
return new FlatSplitPaneDivider( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle() {
|
||||||
|
try {
|
||||||
|
applyStyle( FlatStylingSupport.getResolvedStyle( splitPane, "SplitPane" ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( Object style ) {
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||||
|
|
||||||
|
if( divider instanceof FlatSplitPaneDivider )
|
||||||
|
((FlatSplitPaneDivider)divider).updateStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
try {
|
||||||
|
if( divider instanceof FlatSplitPaneDivider )
|
||||||
|
return ((FlatSplitPaneDivider)divider).applyStyleProperty( key, value );
|
||||||
|
} catch( UnknownStyleException ex ) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, splitPane, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
Map<String, Class<?>> infos = FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
|
if( divider instanceof FlatSplitPaneDivider )
|
||||||
|
infos.putAll( ((FlatSplitPaneDivider)divider).getStyleableInfos() );
|
||||||
|
return infos;
|
||||||
|
}
|
||||||
|
|
||||||
//---- class FlatSplitPaneDivider -----------------------------------------
|
//---- class FlatSplitPaneDivider -----------------------------------------
|
||||||
|
|
||||||
protected class FlatSplitPaneDivider
|
protected class FlatSplitPaneDivider
|
||||||
extends BasicSplitPaneDivider
|
extends BasicSplitPaneDivider
|
||||||
{
|
{
|
||||||
protected final String style = UIManager.getString( "SplitPaneDivider.style" );
|
@Styleable protected String style = UIManager.getString( "SplitPaneDivider.style" );
|
||||||
protected final Color gripColor = UIManager.getColor( "SplitPaneDivider.gripColor" );
|
@Styleable protected Color gripColor = UIManager.getColor( "SplitPaneDivider.gripColor" );
|
||||||
protected final int gripDotCount = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotCount", 3 );
|
@Styleable protected int gripDotCount = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotCount", 3 );
|
||||||
protected final int gripDotSize = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotSize", 3 );
|
@Styleable protected int gripDotSize = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotSize", 3 );
|
||||||
protected final int gripGap = FlatUIUtils.getUIInt( "SplitPaneDivider.gripGap", 2 );
|
@Styleable protected int gripGap = FlatUIUtils.getUIInt( "SplitPaneDivider.gripGap", 2 );
|
||||||
|
|
||||||
protected FlatSplitPaneDivider( BasicSplitPaneUI ui ) {
|
protected FlatSplitPaneDivider( BasicSplitPaneUI ui ) {
|
||||||
super( ui );
|
super( ui );
|
||||||
@@ -120,6 +200,27 @@ public class FlatSplitPaneUI
|
|||||||
setLayout( new FlatDividerLayout() );
|
setLayout( new FlatDividerLayout() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
public Map<String, Class<?>> getStyleableInfos() {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateStyle() {
|
||||||
|
if( leftButton instanceof FlatOneTouchButton )
|
||||||
|
((FlatOneTouchButton)leftButton).updateStyle();
|
||||||
|
if( rightButton instanceof FlatOneTouchButton )
|
||||||
|
((FlatOneTouchButton)rightButton).updateStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setDividerSize( int newSize ) {
|
public void setDividerSize( int newSize ) {
|
||||||
super.setDividerSize( UIScale.scale( newSize ) );
|
super.setDividerSize( UIScale.scale( newSize ) );
|
||||||
@@ -142,7 +243,7 @@ public class FlatSplitPaneUI
|
|||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
case JSplitPane.DIVIDER_LOCATION_PROPERTY:
|
case JSplitPane.DIVIDER_LOCATION_PROPERTY:
|
||||||
// necessary to show/hide one-touch buttons on expand/collapse
|
// necessary to show/hide one-touch buttons on expand/collapse
|
||||||
revalidate();
|
doLayout();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,6 +301,11 @@ public class FlatSplitPaneUI
|
|||||||
this.left = left;
|
this.left = left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void updateStyle() {
|
||||||
|
updateStyle( arrowType, oneTouchArrowColor, null,
|
||||||
|
oneTouchHoverArrowColor, null, oneTouchPressedArrowColor, null );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDirection() {
|
public int getDirection() {
|
||||||
return (orientation == JSplitPane.VERTICAL_SPLIT)
|
return (orientation == JSplitPane.VERTICAL_SPLIT)
|
||||||
@@ -247,7 +353,7 @@ public class FlatSplitPaneUI
|
|||||||
if( leftButton == null || rightButton == null || !splitPane.isOneTouchExpandable() )
|
if( leftButton == null || rightButton == null || !splitPane.isOneTouchExpandable() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// increase side of buttons, which makes them easier to hit by the user
|
// increase size of buttons, which makes them easier to hit by the user
|
||||||
// and avoids cut arrows at small divider sizes
|
// and avoids cut arrows at small divider sizes
|
||||||
int extraSize = UIScale.scale( 4 );
|
int extraSize = UIScale.scale( 4 );
|
||||||
if( orientation == JSplitPane.VERTICAL_SPLIT ) {
|
if( orientation == JSplitPane.VERTICAL_SPLIT ) {
|
||||||
@@ -262,10 +368,19 @@ public class FlatSplitPaneUI
|
|||||||
|
|
||||||
// hide buttons if not applicable
|
// hide buttons if not applicable
|
||||||
boolean leftCollapsed = isLeftCollapsed();
|
boolean leftCollapsed = isLeftCollapsed();
|
||||||
if( leftCollapsed )
|
boolean rightCollapsed = isRightCollapsed();
|
||||||
|
if( leftCollapsed || rightCollapsed ) {
|
||||||
|
leftButton.setVisible( !leftCollapsed );
|
||||||
|
rightButton.setVisible( !rightCollapsed );
|
||||||
|
} else {
|
||||||
|
Object expandableSide = splitPane.getClientProperty( FlatClientProperties.SPLIT_PANE_EXPANDABLE_SIDE );
|
||||||
|
leftButton.setVisible( expandableSide == null || !FlatClientProperties.SPLIT_PANE_EXPANDABLE_SIDE_LEFT.equals( expandableSide ) );
|
||||||
|
rightButton.setVisible( expandableSide == null || !FlatClientProperties.SPLIT_PANE_EXPANDABLE_SIDE_RIGHT.equals( expandableSide ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// move right button if left button is hidden
|
||||||
|
if( !leftButton.isVisible() )
|
||||||
rightButton.setLocation( leftButton.getLocation() );
|
rightButton.setLocation( leftButton.getLocation() );
|
||||||
leftButton.setVisible( !leftCollapsed );
|
|
||||||
rightButton.setVisible( !isRightCollapsed() );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,723 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import javax.swing.Icon;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.border.Border;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
|
import com.formdev.flatlaf.util.StringUtils;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Support for styling components in CSS syntax.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
public class FlatStylingSupport
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Indicates that a field is intended to be used by FlatLaf styling support.
|
||||||
|
* <p>
|
||||||
|
* <strong>Do not rename fields annotated with this annotation.</strong>
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface Styleable {
|
||||||
|
boolean dot() default false;
|
||||||
|
Class<?> type() default Void.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public interface StyleableUI {
|
||||||
|
Map<String, Class<?>> getStyleableInfos( JComponent c );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
public interface StyleableBorder {
|
||||||
|
Object applyStyleProperty( String key, Object value );
|
||||||
|
Map<String, Class<?>> getStyleableInfos();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the style specified in client property {@link FlatClientProperties#STYLE}.
|
||||||
|
*/
|
||||||
|
public static Object getStyle( JComponent c ) {
|
||||||
|
return c.getClientProperty( FlatClientProperties.STYLE );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the style class(es) specified in client property {@link FlatClientProperties#STYLE_CLASS}.
|
||||||
|
*/
|
||||||
|
public static Object getStyleClass( JComponent c ) {
|
||||||
|
return c.getClientProperty( FlatClientProperties.STYLE_CLASS );
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean hasStyleProperty( JComponent c ) {
|
||||||
|
return getStyle( c ) != null || getStyleClass( c ) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object getResolvedStyle( JComponent c, String type ) {
|
||||||
|
Object style = getStyle( c );
|
||||||
|
Object styleClass = getStyleClass( c );
|
||||||
|
Object styleForClasses = getStyleForClasses( styleClass, type );
|
||||||
|
return joinStyles( styleForClasses, style );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the styles for the given style class(es) and the given type.
|
||||||
|
* <p>
|
||||||
|
* The style rules must be defined in UI defaults either as strings (in CSS syntax)
|
||||||
|
* or as {@link java.util.Map}<String, Object> (with binary values).
|
||||||
|
* The key must be in syntax: {@code [style]type.styleClass}, where the type is optional.
|
||||||
|
* E.g. in FlatLaf properties file:
|
||||||
|
* <pre>{@code
|
||||||
|
* [style]Button.primary = borderColor: #08f; background: #08f; foreground: #fff
|
||||||
|
* [style].secondary = borderColor: #0f8; background: #0f8
|
||||||
|
* }</pre>
|
||||||
|
* or in Java code:
|
||||||
|
* <pre>{@code
|
||||||
|
* UIManager.put( "[style]Button.primary", "borderColor: #08f; background: #08f; foreground: #fff" );
|
||||||
|
* UIManager.put( "[style].secondary", "borderColor: #0f8; background: #0f8" );
|
||||||
|
* }</pre>
|
||||||
|
* The rule "Button.primary" can be applied to buttons only.
|
||||||
|
* The rule ".secondary" can be applied to any component.
|
||||||
|
* <p>
|
||||||
|
* To have similar behavior as in CSS, this method first gets the rule without type,
|
||||||
|
* then the rule with type and concatenates both rules.
|
||||||
|
* E.g. invoking this method with parameters styleClass="foo" and type="Button" does following:
|
||||||
|
* <pre>{@code
|
||||||
|
* return joinStyles(
|
||||||
|
* UIManager.get( "[style].foo" ),
|
||||||
|
* UIManager.get( "[style]Button.foo" ) );
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @param styleClass the style class(es) either as string (single class or multiple classes separated by space characters)
|
||||||
|
* or as {@code String[]} or {@link java.util.List}<String> (multiple classes)
|
||||||
|
* @param type the type of the component
|
||||||
|
* @return the styles
|
||||||
|
*/
|
||||||
|
public static Object getStyleForClasses( Object styleClass, String type ) {
|
||||||
|
if( styleClass == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if( styleClass instanceof String && ((String)styleClass).indexOf( ' ' ) >= 0 )
|
||||||
|
styleClass = StringUtils.split( (String) styleClass, ' ', true, true );
|
||||||
|
|
||||||
|
if( styleClass instanceof String )
|
||||||
|
return getStyleForClass( ((String)styleClass).trim(), type );
|
||||||
|
else if( styleClass instanceof String[] ) {
|
||||||
|
Object style = null;
|
||||||
|
for( String cls : (String[]) styleClass )
|
||||||
|
style = joinStyles( style, getStyleForClass( cls, type ) );
|
||||||
|
return style;
|
||||||
|
} else if( styleClass instanceof List<?> ) {
|
||||||
|
Object style = null;
|
||||||
|
for( Object cls : (List<?>) styleClass )
|
||||||
|
style = joinStyles( style, getStyleForClass( (String) cls, type ) );
|
||||||
|
return style;
|
||||||
|
} else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object getStyleForClass( String styleClass, String type ) {
|
||||||
|
return joinStyles(
|
||||||
|
UIManager.get( "[style]." + styleClass ),
|
||||||
|
UIManager.get( "[style]" + type + '.' + styleClass ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Joins two styles. They can be either strings (in CSS syntax)
|
||||||
|
* or {@link java.util.Map}<String, Object> (with binary values).
|
||||||
|
* <p>
|
||||||
|
* If both styles are strings, then a joined string is returned.
|
||||||
|
* If both styles are maps, then a joined map is returned.
|
||||||
|
* If one style is a map and the other style a string, then the string
|
||||||
|
* is parsed (using {@link #parse(String)}) to a map and a joined map is returned.
|
||||||
|
*
|
||||||
|
* @param style1 first style as string or map, or {@code null}
|
||||||
|
* @param style2 second style as string or map, or {@code null}
|
||||||
|
* @return new joined style
|
||||||
|
*/
|
||||||
|
@SuppressWarnings( "unchecked" )
|
||||||
|
public static Object joinStyles( Object style1, Object style2 ) {
|
||||||
|
if( style1 == null )
|
||||||
|
return style2;
|
||||||
|
if( style2 == null )
|
||||||
|
return style1;
|
||||||
|
|
||||||
|
// join two strings
|
||||||
|
if( style1 instanceof String && style2 instanceof String )
|
||||||
|
return style1 + "; " + style2;
|
||||||
|
|
||||||
|
// convert first style to map
|
||||||
|
Map<String, Object> map1 = (style1 instanceof String)
|
||||||
|
? parse( (String) style1 )
|
||||||
|
: (Map<String, Object>) style1;
|
||||||
|
if( map1 == null )
|
||||||
|
return style2;
|
||||||
|
|
||||||
|
// convert second style to map
|
||||||
|
Map<String, Object> map2 = (style2 instanceof String)
|
||||||
|
? parse( (String) style2 )
|
||||||
|
: (Map<String, Object>) style2;
|
||||||
|
if( map2 == null )
|
||||||
|
return style1;
|
||||||
|
|
||||||
|
// join two maps
|
||||||
|
Map<String, Object> map = new HashMap<>( map1 );
|
||||||
|
map.putAll( map2 );
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Concatenates two styles in CSS syntax.
|
||||||
|
*
|
||||||
|
* @param style1 first style, or {@code null}
|
||||||
|
* @param style2 second style, or {@code null}
|
||||||
|
* @return concatenation of the two styles separated by a semicolon
|
||||||
|
*/
|
||||||
|
public static String concatStyles( String style1, String style2 ) {
|
||||||
|
if( style1 == null )
|
||||||
|
return style2;
|
||||||
|
if( style2 == null )
|
||||||
|
return style1;
|
||||||
|
return style1 + "; " + style2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses styles in CSS syntax ("key1: value1; key2: value2; ..."),
|
||||||
|
* converts the value strings into binary and invokes the given function
|
||||||
|
* to apply the properties.
|
||||||
|
*
|
||||||
|
* @param oldStyleValues map of old values modified by the previous invocation, or {@code null}
|
||||||
|
* @param style the style in CSS syntax as string, or a Map, or {@code null}
|
||||||
|
* @param applyProperty function that is invoked to apply the properties;
|
||||||
|
* first parameter is the key, second the binary value;
|
||||||
|
* the function must return the old value
|
||||||
|
* @return map of old values modified by the given style, or {@code null}
|
||||||
|
* @throws UnknownStyleException on unknown style keys
|
||||||
|
* @throws IllegalArgumentException on syntax errors
|
||||||
|
* @throws ClassCastException if value type does not fit to expected type
|
||||||
|
*/
|
||||||
|
public static Map<String, Object> parseAndApply( Map<String, Object> oldStyleValues,
|
||||||
|
Object style, BiFunction<String, Object, Object> applyProperty )
|
||||||
|
throws UnknownStyleException, IllegalArgumentException
|
||||||
|
{
|
||||||
|
// restore previous values
|
||||||
|
if( oldStyleValues != null ) {
|
||||||
|
for( Map.Entry<String, Object> e : oldStyleValues.entrySet() )
|
||||||
|
applyProperty.apply( e.getKey(), e.getValue() );
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore empty style
|
||||||
|
if( style == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if( style instanceof String ) {
|
||||||
|
// handle style in CSS syntax
|
||||||
|
String str = (String) style;
|
||||||
|
if( StringUtils.isTrimmedEmpty( str ) )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return applyStyle( parse( str ), applyProperty );
|
||||||
|
} else if( style instanceof Map ) {
|
||||||
|
// handle style of type Map
|
||||||
|
@SuppressWarnings( "unchecked" )
|
||||||
|
Map<String, Object> map = (Map<String, Object>) style;
|
||||||
|
return applyStyle( map, applyProperty );
|
||||||
|
} else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, Object> applyStyle( Map<String, Object> style,
|
||||||
|
BiFunction<String, Object, Object> applyProperty )
|
||||||
|
{
|
||||||
|
if( style.isEmpty() )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Map<String, Object> oldValues = new HashMap<>();
|
||||||
|
for( Map.Entry<String, Object> e : style.entrySet() ) {
|
||||||
|
String key = e.getKey();
|
||||||
|
Object newValue = e.getValue();
|
||||||
|
|
||||||
|
// handle key prefix
|
||||||
|
if( key.startsWith( "[" ) ) {
|
||||||
|
if( (SystemInfo.isWindows && key.startsWith( "[win]" )) ||
|
||||||
|
(SystemInfo.isMacOS && key.startsWith( "[mac]" )) ||
|
||||||
|
(SystemInfo.isLinux && key.startsWith( "[linux]" )) ||
|
||||||
|
(key.startsWith( "[light]" ) && !FlatLaf.isLafDark()) ||
|
||||||
|
(key.startsWith( "[dark]" ) && FlatLaf.isLafDark()) )
|
||||||
|
{
|
||||||
|
// prefix is known and enabled --> remove prefix
|
||||||
|
key = key.substring( key.indexOf( ']' ) + 1 );
|
||||||
|
} else
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object oldValue = applyProperty.apply( key, newValue );
|
||||||
|
oldValues.put( key, oldValue );
|
||||||
|
}
|
||||||
|
return oldValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses styles in CSS syntax ("key1: value1; key2: value2; ..."),
|
||||||
|
* converts the value strings into binary and returns all key/value pairs as map.
|
||||||
|
*
|
||||||
|
* @param style the style in CSS syntax, or {@code null}
|
||||||
|
* @return map of parsed styles, or {@code null}
|
||||||
|
* @throws IllegalArgumentException on syntax errors
|
||||||
|
*/
|
||||||
|
public static Map<String, Object> parse( String style )
|
||||||
|
throws IllegalArgumentException
|
||||||
|
{
|
||||||
|
if( style == null || StringUtils.isTrimmedEmpty( style ) )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Map<String, Object> map = null;
|
||||||
|
|
||||||
|
// split style into parts and process them
|
||||||
|
for( String part : StringUtils.split( style, ';', true, true ) ) {
|
||||||
|
// find separator colon
|
||||||
|
int sepIndex = part.indexOf( ':' );
|
||||||
|
if( sepIndex < 0 )
|
||||||
|
throw new IllegalArgumentException( "missing colon in '" + part + "'" );
|
||||||
|
|
||||||
|
// split into key and value
|
||||||
|
String key = StringUtils.substringTrimmed( part, 0, sepIndex );
|
||||||
|
String value = StringUtils.substringTrimmed( part, sepIndex + 1 );
|
||||||
|
if( key.isEmpty() )
|
||||||
|
throw new IllegalArgumentException( "missing key in '" + part + "'" );
|
||||||
|
if( value.isEmpty() )
|
||||||
|
throw new IllegalArgumentException( "missing value in '" + part + "'" );
|
||||||
|
|
||||||
|
// parse value string and convert it into binary value
|
||||||
|
if( map == null )
|
||||||
|
map = new LinkedHashMap<>();
|
||||||
|
map.put( key, parseValue( key, value ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object parseValue( String key, String value ) {
|
||||||
|
// simple reference
|
||||||
|
if( value.startsWith( "$" ) )
|
||||||
|
return UIManager.get( value.substring( 1 ) );
|
||||||
|
|
||||||
|
// remove key prefix for correct value type detection
|
||||||
|
// (e.g. "[light]padding" would not parse to Insets)
|
||||||
|
if( key.startsWith( "[" ) )
|
||||||
|
key = key.substring( key.indexOf( ']' ) + 1 );
|
||||||
|
|
||||||
|
// parse string
|
||||||
|
return FlatLaf.parseDefaultsValue( key, value, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the given value to an annotated field of the given object.
|
||||||
|
* The field must be annotated with {@link Styleable}.
|
||||||
|
*
|
||||||
|
* @param obj the object
|
||||||
|
* @param key the name of the field
|
||||||
|
* @param value the new value
|
||||||
|
* @return the old value of the field
|
||||||
|
* @throws UnknownStyleException if object does not have an annotated field with given name
|
||||||
|
* @throws IllegalArgumentException if value type does not fit to expected type
|
||||||
|
*/
|
||||||
|
public static Object applyToAnnotatedObject( Object obj, String key, Object value )
|
||||||
|
throws UnknownStyleException, IllegalArgumentException
|
||||||
|
{
|
||||||
|
String fieldName = key;
|
||||||
|
int dotIndex = key.indexOf( '.' );
|
||||||
|
if( dotIndex >= 0 ) {
|
||||||
|
// remove first dot in key and change subsequent character to uppercase
|
||||||
|
fieldName = key.substring( 0, dotIndex )
|
||||||
|
+ Character.toUpperCase( key.charAt( dotIndex + 1 ) )
|
||||||
|
+ key.substring( dotIndex + 2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
return applyToField( obj, fieldName, key, value, field -> {
|
||||||
|
Styleable styleable = field.getAnnotation( Styleable.class );
|
||||||
|
return styleable != null && styleable.dot() == (dotIndex >= 0);
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the given value to a field of the given object.
|
||||||
|
*
|
||||||
|
* @param obj the object
|
||||||
|
* @param fieldName the name of the field
|
||||||
|
* @param key the key (only used for error reporting)
|
||||||
|
* @param value the new value
|
||||||
|
* @return the old value of the field
|
||||||
|
* @throws UnknownStyleException if object does not have a field with given name
|
||||||
|
* @throws IllegalArgumentException if value type does not fit to expected type
|
||||||
|
*/
|
||||||
|
static Object applyToField( Object obj, String fieldName, String key, Object value )
|
||||||
|
throws UnknownStyleException, IllegalArgumentException
|
||||||
|
{
|
||||||
|
return applyToField( obj, fieldName, key, value, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object applyToField( Object obj, String fieldName, String key, Object value, Predicate<Field> predicate )
|
||||||
|
throws UnknownStyleException, IllegalArgumentException
|
||||||
|
{
|
||||||
|
Class<?> cls = obj.getClass();
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
try {
|
||||||
|
Field f = cls.getDeclaredField( fieldName );
|
||||||
|
if( predicate == null || predicate.test( f ) ) {
|
||||||
|
if( !isValidField( f ) )
|
||||||
|
throw new IllegalArgumentException( "field '" + cls.getName() + "." + fieldName + "' is final or static" );
|
||||||
|
|
||||||
|
try {
|
||||||
|
// necessary to access protected fields in other packages
|
||||||
|
f.setAccessible( true );
|
||||||
|
|
||||||
|
// get old value and set new value
|
||||||
|
Object oldValue = f.get( obj );
|
||||||
|
f.set( obj, convertToEnum( value, f.getType() ) );
|
||||||
|
return oldValue;
|
||||||
|
} catch( IllegalAccessException ex ) {
|
||||||
|
throw new IllegalArgumentException( "failed to access field '" + cls.getName() + "." + fieldName + "'", ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch( NoSuchFieldException ex ) {
|
||||||
|
// field not found in class --> try superclass
|
||||||
|
}
|
||||||
|
|
||||||
|
cls = cls.getSuperclass();
|
||||||
|
if( cls == null )
|
||||||
|
throw new UnknownStyleException( key );
|
||||||
|
|
||||||
|
if( predicate != null ) {
|
||||||
|
String superclassName = cls.getName();
|
||||||
|
if( superclassName.startsWith( "java." ) || superclassName.startsWith( "javax." ) )
|
||||||
|
throw new UnknownStyleException( key );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isValidField( Field f ) {
|
||||||
|
int modifiers = f.getModifiers();
|
||||||
|
return (modifiers & (Modifier.FINAL|Modifier.STATIC)) == 0 && !f.isSynthetic();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the given value to a property of the given object.
|
||||||
|
* Works only for properties that have public getter and setter methods.
|
||||||
|
* First the property getter is invoked to get the old value,
|
||||||
|
* then the property setter is invoked to set the new value.
|
||||||
|
*
|
||||||
|
* @param obj the object
|
||||||
|
* @param name the name of the property
|
||||||
|
* @param value the new value
|
||||||
|
* @return the old value of the property
|
||||||
|
* @throws UnknownStyleException if object does not have a property with given name
|
||||||
|
* @throws IllegalArgumentException if value type does not fit to expected type
|
||||||
|
*/
|
||||||
|
private static Object applyToProperty( Object obj, String name, Object value )
|
||||||
|
throws UnknownStyleException, IllegalArgumentException
|
||||||
|
{
|
||||||
|
Class<?> cls = obj.getClass();
|
||||||
|
String getterName = buildMethodName( "get", name );
|
||||||
|
String setterName = buildMethodName( "set", name );
|
||||||
|
|
||||||
|
try {
|
||||||
|
Method getter;
|
||||||
|
try {
|
||||||
|
getter = cls.getMethod( getterName );
|
||||||
|
} catch( NoSuchMethodException ex ) {
|
||||||
|
getter = cls.getMethod( buildMethodName( "is", name ) );
|
||||||
|
}
|
||||||
|
Method setter = cls.getMethod( setterName, getter.getReturnType() );
|
||||||
|
Object oldValue = getter.invoke( obj );
|
||||||
|
setter.invoke( obj, convertToEnum( value, getter.getReturnType() ) );
|
||||||
|
return oldValue;
|
||||||
|
} catch( NoSuchMethodException ex ) {
|
||||||
|
throw new UnknownStyleException( name );
|
||||||
|
} catch( Exception ex ) {
|
||||||
|
throw new IllegalArgumentException( "failed to invoke property methods '" + cls.getName() + "."
|
||||||
|
+ getterName + "()' or '" + setterName + "(...)'", ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildMethodName( String prefix, String name ) {
|
||||||
|
int prefixLength = prefix.length();
|
||||||
|
int nameLength = name.length();
|
||||||
|
char[] chars = new char[prefixLength + nameLength];
|
||||||
|
prefix.getChars( 0, prefixLength, chars, 0 );
|
||||||
|
name.getChars( 0, nameLength, chars, prefixLength );
|
||||||
|
chars[prefixLength] = Character.toUpperCase( chars[prefixLength] );
|
||||||
|
return new String( chars );
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings( { "unchecked", "rawtypes" } )
|
||||||
|
private static Object convertToEnum( Object value, Class<?> type )
|
||||||
|
throws IllegalArgumentException
|
||||||
|
{
|
||||||
|
// if type is an enum, convert string to enum value
|
||||||
|
if( Enum.class.isAssignableFrom( type ) && value instanceof String ) {
|
||||||
|
try {
|
||||||
|
value = Enum.valueOf( (Class<? extends Enum>) type, (String) value );
|
||||||
|
} catch( IllegalArgumentException ex ) {
|
||||||
|
throw new IllegalArgumentException( "unknown enum value '" + value + "' in enum '" + type.getName() + "'", ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the given value to an annotated field of the given object
|
||||||
|
* or to a property of the given component.
|
||||||
|
* The field must be annotated with {@link Styleable}.
|
||||||
|
* The component property must have public getter and setter methods.
|
||||||
|
*
|
||||||
|
* @param obj the object
|
||||||
|
* @param comp the component, or {@code null}
|
||||||
|
* @param key the name of the field
|
||||||
|
* @param value the new value
|
||||||
|
* @return the old value of the field
|
||||||
|
* @throws UnknownStyleException if object does not have an annotated field with given name
|
||||||
|
* @throws IllegalArgumentException if value type does not fit to expected type
|
||||||
|
*/
|
||||||
|
public static Object applyToAnnotatedObjectOrComponent( Object obj, Object comp, String key, Object value )
|
||||||
|
throws UnknownStyleException, IllegalArgumentException
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return applyToAnnotatedObject( obj, key, value );
|
||||||
|
} catch( UnknownStyleException ex ) {
|
||||||
|
try {
|
||||||
|
if( comp != null )
|
||||||
|
return applyToProperty( comp, key, value );
|
||||||
|
} catch( UnknownStyleException ex2 ) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object applyToAnnotatedObjectOrBorder( Object obj, String key, Object value,
|
||||||
|
JComponent c, AtomicBoolean borderShared )
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return applyToAnnotatedObject( obj, key, value );
|
||||||
|
} catch( UnknownStyleException ex ) {
|
||||||
|
// apply to border
|
||||||
|
Border border = c.getBorder();
|
||||||
|
if( border instanceof StyleableBorder ) {
|
||||||
|
if( borderShared.get() ) {
|
||||||
|
border = cloneBorder( border );
|
||||||
|
c.setBorder( border );
|
||||||
|
borderShared.set( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return ((StyleableBorder)border).applyStyleProperty( key, value );
|
||||||
|
} catch( UnknownStyleException ex2 ) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply to component property
|
||||||
|
try {
|
||||||
|
return applyToProperty( c, key, value );
|
||||||
|
} catch( UnknownStyleException ex2 ) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static PropertyChangeListener createPropertyChangeListener( JComponent c,
|
||||||
|
Runnable installStyle, PropertyChangeListener superListener )
|
||||||
|
{
|
||||||
|
return e -> {
|
||||||
|
if( superListener != null )
|
||||||
|
superListener.propertyChange( e );
|
||||||
|
|
||||||
|
switch( e.getPropertyName() ) {
|
||||||
|
case FlatClientProperties.STYLE:
|
||||||
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
|
installStyle.run();
|
||||||
|
c.revalidate();
|
||||||
|
c.repaint();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static Border cloneBorder( Border border ) {
|
||||||
|
Class<? extends Border> borderClass = border.getClass();
|
||||||
|
try {
|
||||||
|
return borderClass.getDeclaredConstructor().newInstance();
|
||||||
|
} catch( Exception ex ) {
|
||||||
|
throw new IllegalArgumentException( "failed to clone border '" + borderClass.getName() + "'", ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Icon cloneIcon( Icon icon ) {
|
||||||
|
Class<? extends Icon> iconClass = icon.getClass();
|
||||||
|
try {
|
||||||
|
return iconClass.getDeclaredConstructor().newInstance();
|
||||||
|
} catch( Exception ex ) {
|
||||||
|
throw new IllegalArgumentException( "failed to clone icon '" + iconClass.getName() + "'", ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a map of all fields annotated with {@link Styleable}.
|
||||||
|
* The key is the name of the field and the value the type of the field.
|
||||||
|
*/
|
||||||
|
public static Map<String, Class<?>> getAnnotatedStyleableInfos( Object obj ) {
|
||||||
|
return getAnnotatedStyleableInfos( obj, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, Class<?>> getAnnotatedStyleableInfos( Object obj, Border border ) {
|
||||||
|
Map<String, Class<?>> infos = new StyleableInfosMap<>();
|
||||||
|
collectAnnotatedStyleableInfos( obj, infos );
|
||||||
|
collectStyleableInfos( border, infos );
|
||||||
|
return infos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for all fields annotated with {@link Styleable} and add them to the given map.
|
||||||
|
* The key is the name of the field and the value the type of the field.
|
||||||
|
*/
|
||||||
|
public static void collectAnnotatedStyleableInfos( Object obj, Map<String, Class<?>> infos ) {
|
||||||
|
HashSet<String> processedFields = new HashSet<>();
|
||||||
|
Class<?> cls = obj.getClass();
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
for( Field f : cls.getDeclaredFields() ) {
|
||||||
|
if( !isValidField( f ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Styleable styleable = f.getAnnotation( Styleable.class );
|
||||||
|
if( styleable == null )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
String name = f.getName();
|
||||||
|
Class<?> type = f.getType();
|
||||||
|
|
||||||
|
// for the case that the same field name is used in a class and in
|
||||||
|
// one of its superclasses (e.g. field 'borderColor' in FlatButtonBorder
|
||||||
|
// and in FlatBorder), do not process field in superclass
|
||||||
|
if( processedFields.contains( name ) )
|
||||||
|
continue;
|
||||||
|
processedFields.add( name );
|
||||||
|
|
||||||
|
// handle "dot" keys (e.g. change field name "iconArrowType" to style key "icon.arrowType")
|
||||||
|
if( styleable.dot() ) {
|
||||||
|
int len = name.length();
|
||||||
|
for( int i = 0; i < len; i++ ) {
|
||||||
|
if( Character.isUpperCase( name.charAt( i ) ) ) {
|
||||||
|
name = name.substring( 0, i ) + '.'
|
||||||
|
+ Character.toLowerCase( name.charAt( i ) )
|
||||||
|
+ name.substring( i + 1 );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// field has a different type
|
||||||
|
if( styleable.type() != Void.class )
|
||||||
|
type = styleable.type();
|
||||||
|
|
||||||
|
infos.put( name, type );
|
||||||
|
}
|
||||||
|
|
||||||
|
cls = cls.getSuperclass();
|
||||||
|
if( cls == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
String superclassName = cls.getName();
|
||||||
|
if( superclassName.startsWith( "java." ) || superclassName.startsWith( "javax." ) )
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void collectStyleableInfos( Border border, Map<String, Class<?>> infos ) {
|
||||||
|
if( border instanceof StyleableBorder )
|
||||||
|
infos.putAll( ((StyleableBorder)border).getStyleableInfos() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void putAllPrefixKey( Map<String, Class<?>> infos, String keyPrefix, Map<String, Class<?>> infos2 ) {
|
||||||
|
for( Map.Entry<String, Class<?>> e : infos2.entrySet() )
|
||||||
|
infos.put( keyPrefix.concat( e.getKey() ), e.getValue() );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class UnknownStyleException ----------------------------------------
|
||||||
|
|
||||||
|
public static class UnknownStyleException
|
||||||
|
extends IllegalArgumentException
|
||||||
|
{
|
||||||
|
public UnknownStyleException( String key ) {
|
||||||
|
super( key );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return "unknown style '" + super.getMessage() + "'";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class StyleableInfosMap --------------------------------------------
|
||||||
|
|
||||||
|
static class StyleableInfosMap<K,V>
|
||||||
|
extends LinkedHashMap<K,V>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public V put( K key, V value ) {
|
||||||
|
V oldValue = super.put( key, value );
|
||||||
|
if( oldValue != null )
|
||||||
|
throw new IllegalArgumentException( "duplicate key '" + key + "'" );
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putAll( Map<? extends K, ? extends V> m ) {
|
||||||
|
for( Map.Entry<? extends K, ? extends V> e : m.entrySet() )
|
||||||
|
put( e.getKey(), e.getValue() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,6 +40,8 @@ import java.awt.event.ComponentEvent;
|
|||||||
import java.awt.event.ComponentListener;
|
import java.awt.event.ComponentListener;
|
||||||
import java.awt.event.ContainerEvent;
|
import java.awt.event.ContainerEvent;
|
||||||
import java.awt.event.ContainerListener;
|
import java.awt.event.ContainerListener;
|
||||||
|
import java.awt.event.FocusEvent;
|
||||||
|
import java.awt.event.FocusListener;
|
||||||
import java.awt.event.InputEvent;
|
import java.awt.event.InputEvent;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
@@ -53,9 +55,11 @@ import java.beans.PropertyChangeEvent;
|
|||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.IntConsumer;
|
import java.util.function.IntConsumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
import javax.accessibility.Accessible;
|
import javax.accessibility.Accessible;
|
||||||
import javax.accessibility.AccessibleContext;
|
import javax.accessibility.AccessibleContext;
|
||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
@@ -79,14 +83,21 @@ import javax.swing.event.ChangeListener;
|
|||||||
import javax.swing.event.PopupMenuEvent;
|
import javax.swing.event.PopupMenuEvent;
|
||||||
import javax.swing.event.PopupMenuListener;
|
import javax.swing.event.PopupMenuListener;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import javax.swing.plaf.TabbedPaneUI;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicTabbedPaneUI;
|
import javax.swing.plaf.basic.BasicTabbedPaneUI;
|
||||||
import javax.swing.text.JTextComponent;
|
import javax.swing.text.JTextComponent;
|
||||||
import javax.swing.text.View;
|
import javax.swing.text.View;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
|
import com.formdev.flatlaf.icons.FlatTabbedPaneCloseIcon;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||||
import com.formdev.flatlaf.util.Animator;
|
import com.formdev.flatlaf.util.Animator;
|
||||||
import com.formdev.flatlaf.util.CubicBezierEasing;
|
import com.formdev.flatlaf.util.CubicBezierEasing;
|
||||||
import com.formdev.flatlaf.util.JavaCompatibility;
|
import com.formdev.flatlaf.util.JavaCompatibility;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.StringUtils;
|
import com.formdev.flatlaf.util.StringUtils;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -101,7 +112,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault TabbedPane.font Font
|
* @uiDefault TabbedPane.font Font
|
||||||
* @uiDefault TabbedPane.background Color
|
* @uiDefault TabbedPane.background Color
|
||||||
* @uiDefault TabbedPane.foreground Color
|
* @uiDefault TabbedPane.foreground Color
|
||||||
* @uiDefault TabbedPane.shadow Color used for scroll arrows and cropped line
|
* @uiDefault TabbedPane.shadow Color used for cropped line
|
||||||
* @uiDefault TabbedPane.textIconGap int
|
* @uiDefault TabbedPane.textIconGap int
|
||||||
* @uiDefault TabbedPane.tabInsets Insets
|
* @uiDefault TabbedPane.tabInsets Insets
|
||||||
* @uiDefault TabbedPane.selectedTabPadInsets Insets unused
|
* @uiDefault TabbedPane.selectedTabPadInsets Insets unused
|
||||||
@@ -119,6 +130,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault TabbedPane.selectedBackground Color optional
|
* @uiDefault TabbedPane.selectedBackground Color optional
|
||||||
* @uiDefault TabbedPane.selectedForeground Color
|
* @uiDefault TabbedPane.selectedForeground Color
|
||||||
* @uiDefault TabbedPane.underlineColor Color
|
* @uiDefault TabbedPane.underlineColor Color
|
||||||
|
* @uiDefault TabbedPane.inactiveUnderlineColor Color
|
||||||
* @uiDefault TabbedPane.disabledUnderlineColor Color
|
* @uiDefault TabbedPane.disabledUnderlineColor Color
|
||||||
* @uiDefault TabbedPane.hoverColor Color
|
* @uiDefault TabbedPane.hoverColor Color
|
||||||
* @uiDefault TabbedPane.focusColor Color
|
* @uiDefault TabbedPane.focusColor Color
|
||||||
@@ -128,12 +140,15 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault TabbedPane.maximumTabWidth int optional
|
* @uiDefault TabbedPane.maximumTabWidth int optional
|
||||||
* @uiDefault TabbedPane.tabHeight int
|
* @uiDefault TabbedPane.tabHeight int
|
||||||
* @uiDefault TabbedPane.tabSelectionHeight int
|
* @uiDefault TabbedPane.tabSelectionHeight int
|
||||||
|
* @uiDefault TabbedPane.cardTabSelectionHeight int
|
||||||
* @uiDefault TabbedPane.contentSeparatorHeight int
|
* @uiDefault TabbedPane.contentSeparatorHeight int
|
||||||
* @uiDefault TabbedPane.showTabSeparators boolean
|
* @uiDefault TabbedPane.showTabSeparators boolean
|
||||||
* @uiDefault TabbedPane.tabSeparatorsFullHeight boolean
|
* @uiDefault TabbedPane.tabSeparatorsFullHeight boolean
|
||||||
* @uiDefault TabbedPane.hasFullBorder boolean
|
* @uiDefault TabbedPane.hasFullBorder boolean
|
||||||
|
* @uiDefault TabbedPane.activeTabBorder boolean
|
||||||
*
|
*
|
||||||
* @uiDefault TabbedPane.tabLayoutPolicy String wrap (default) or scroll
|
* @uiDefault TabbedPane.tabLayoutPolicy String wrap (default) or scroll
|
||||||
|
* @uiDefault TabbedPane.tabType String underlined (default) or card
|
||||||
* @uiDefault TabbedPane.tabsPopupPolicy String never or asNeeded (default)
|
* @uiDefault TabbedPane.tabsPopupPolicy String never or asNeeded (default)
|
||||||
* @uiDefault TabbedPane.scrollButtonsPolicy String never, asNeeded or asNeededSingle (default)
|
* @uiDefault TabbedPane.scrollButtonsPolicy String never, asNeeded or asNeededSingle (default)
|
||||||
* @uiDefault TabbedPane.scrollButtonsPlacement String both (default) or trailing
|
* @uiDefault TabbedPane.scrollButtonsPlacement String both (default) or trailing
|
||||||
@@ -151,12 +166,18 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault TabbedPane.buttonPressedBackground Color
|
* @uiDefault TabbedPane.buttonPressedBackground Color
|
||||||
*
|
*
|
||||||
* @uiDefault TabbedPane.moreTabsButtonToolTipText String
|
* @uiDefault TabbedPane.moreTabsButtonToolTipText String
|
||||||
|
* @uiDefault TabbedPane.tabCloseToolTipText String
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatTabbedPaneUI
|
public class FlatTabbedPaneUI
|
||||||
extends BasicTabbedPaneUI
|
extends BasicTabbedPaneUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
|
// tab type
|
||||||
|
/** @since 2 */ protected static final int TAB_TYPE_UNDERLINED = 0;
|
||||||
|
/** @since 2 */ protected static final int TAB_TYPE_CARD = 1;
|
||||||
|
|
||||||
// tabs popup policy / scroll arrows policy
|
// tabs popup policy / scroll arrows policy
|
||||||
protected static final int NEVER = 0;
|
protected static final int NEVER = 0;
|
||||||
// protected static final int ALWAYS = 1;
|
// protected static final int ALWAYS = 1;
|
||||||
@@ -177,43 +198,53 @@ public class FlatTabbedPaneUI
|
|||||||
private static Set<KeyStroke> focusBackwardTraversalKeys;
|
private static Set<KeyStroke> focusBackwardTraversalKeys;
|
||||||
|
|
||||||
protected Color foreground;
|
protected Color foreground;
|
||||||
protected Color disabledForeground;
|
@Styleable protected Color disabledForeground;
|
||||||
protected Color selectedBackground;
|
@Styleable protected Color selectedBackground;
|
||||||
protected Color selectedForeground;
|
@Styleable protected Color selectedForeground;
|
||||||
protected Color underlineColor;
|
@Styleable protected Color underlineColor;
|
||||||
protected Color disabledUnderlineColor;
|
/** @since 2.2 */ @Styleable protected Color inactiveUnderlineColor;
|
||||||
protected Color hoverColor;
|
@Styleable protected Color disabledUnderlineColor;
|
||||||
protected Color focusColor;
|
@Styleable protected Color hoverColor;
|
||||||
protected Color tabSeparatorColor;
|
@Styleable protected Color focusColor;
|
||||||
protected Color contentAreaColor;
|
@Styleable protected Color tabSeparatorColor;
|
||||||
|
@Styleable protected Color contentAreaColor;
|
||||||
|
|
||||||
private int textIconGapUnscaled;
|
private int textIconGapUnscaled;
|
||||||
protected int minimumTabWidth;
|
@Styleable protected int minimumTabWidth;
|
||||||
protected int maximumTabWidth;
|
@Styleable protected int maximumTabWidth;
|
||||||
protected int tabHeight;
|
@Styleable protected int tabHeight;
|
||||||
protected int tabSelectionHeight;
|
@Styleable protected int tabSelectionHeight;
|
||||||
protected int contentSeparatorHeight;
|
/** @since 2 */ @Styleable protected int cardTabSelectionHeight;
|
||||||
protected boolean showTabSeparators;
|
@Styleable protected int contentSeparatorHeight;
|
||||||
protected boolean tabSeparatorsFullHeight;
|
@Styleable protected boolean showTabSeparators;
|
||||||
protected boolean hasFullBorder;
|
@Styleable protected boolean tabSeparatorsFullHeight;
|
||||||
protected boolean tabsOpaque = true;
|
@Styleable protected boolean hasFullBorder;
|
||||||
|
@Styleable protected boolean tabsOpaque = true;
|
||||||
|
|
||||||
private int tabsPopupPolicy;
|
@Styleable(type=String.class) private int tabType;
|
||||||
private int scrollButtonsPolicy;
|
@Styleable(type=String.class) private int tabsPopupPolicy;
|
||||||
private int scrollButtonsPlacement;
|
@Styleable(type=String.class) private int scrollButtonsPolicy;
|
||||||
|
@Styleable(type=String.class) private int scrollButtonsPlacement;
|
||||||
|
|
||||||
private int tabAreaAlignment;
|
@Styleable(type=String.class) private int tabAreaAlignment;
|
||||||
private int tabAlignment;
|
@Styleable(type=String.class) private int tabAlignment;
|
||||||
private int tabWidthMode;
|
@Styleable(type=String.class) private int tabWidthMode;
|
||||||
protected Icon closeIcon;
|
protected Icon closeIcon;
|
||||||
|
|
||||||
protected String arrowType;
|
@Styleable protected String arrowType;
|
||||||
protected Insets buttonInsets;
|
@Styleable protected Insets buttonInsets;
|
||||||
protected int buttonArc;
|
@Styleable protected int buttonArc;
|
||||||
protected Color buttonHoverBackground;
|
@Styleable protected Color buttonHoverBackground;
|
||||||
protected Color buttonPressedBackground;
|
@Styleable protected Color buttonPressedBackground;
|
||||||
|
|
||||||
protected String moreTabsButtonToolTipText;
|
@Styleable protected String moreTabsButtonToolTipText;
|
||||||
|
/** @since 2 */ @Styleable protected String tabCloseToolTipText;
|
||||||
|
|
||||||
|
// only used via styling (not in UI defaults, but has likewise client properties)
|
||||||
|
/** @since 2 */ @Styleable protected boolean showContentSeparator = true;
|
||||||
|
/** @since 2 */ @Styleable protected boolean hideTabAreaWithOneTab;
|
||||||
|
/** @since 2 */ @Styleable protected boolean tabClosable;
|
||||||
|
/** @since 2 */ @Styleable protected int tabIconPlacement = LEADING;
|
||||||
|
|
||||||
protected JViewport tabViewport;
|
protected JViewport tabViewport;
|
||||||
protected FlatWheelTabScroller wheelTabScroller;
|
protected FlatWheelTabScroller wheelTabScroller;
|
||||||
@@ -231,6 +262,8 @@ public class FlatTabbedPaneUI
|
|||||||
private boolean pressedTabClose;
|
private boolean pressedTabClose;
|
||||||
|
|
||||||
private Object[] oldRenderingHints;
|
private Object[] oldRenderingHints;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
private boolean closeIconShared = true;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatTabbedPaneUI();
|
return new FlatTabbedPaneUI();
|
||||||
@@ -259,6 +292,9 @@ public class FlatTabbedPaneUI
|
|||||||
buttonPressedBackground = UIManager.getColor( "TabbedPane.buttonPressedBackground" );
|
buttonPressedBackground = UIManager.getColor( "TabbedPane.buttonPressedBackground" );
|
||||||
|
|
||||||
super.installUI( c );
|
super.installUI( c );
|
||||||
|
|
||||||
|
FlatSelectedTabRepainter.install();
|
||||||
|
installStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -288,6 +324,7 @@ public class FlatTabbedPaneUI
|
|||||||
selectedBackground = UIManager.getColor( "TabbedPane.selectedBackground" );
|
selectedBackground = UIManager.getColor( "TabbedPane.selectedBackground" );
|
||||||
selectedForeground = UIManager.getColor( "TabbedPane.selectedForeground" );
|
selectedForeground = UIManager.getColor( "TabbedPane.selectedForeground" );
|
||||||
underlineColor = UIManager.getColor( "TabbedPane.underlineColor" );
|
underlineColor = UIManager.getColor( "TabbedPane.underlineColor" );
|
||||||
|
inactiveUnderlineColor = FlatUIUtils.getUIColor( "TabbedPane.inactiveUnderlineColor", underlineColor );
|
||||||
disabledUnderlineColor = UIManager.getColor( "TabbedPane.disabledUnderlineColor" );
|
disabledUnderlineColor = UIManager.getColor( "TabbedPane.disabledUnderlineColor" );
|
||||||
hoverColor = UIManager.getColor( "TabbedPane.hoverColor" );
|
hoverColor = UIManager.getColor( "TabbedPane.hoverColor" );
|
||||||
focusColor = UIManager.getColor( "TabbedPane.focusColor" );
|
focusColor = UIManager.getColor( "TabbedPane.focusColor" );
|
||||||
@@ -299,12 +336,14 @@ public class FlatTabbedPaneUI
|
|||||||
maximumTabWidth = UIManager.getInt( "TabbedPane.maximumTabWidth" );
|
maximumTabWidth = UIManager.getInt( "TabbedPane.maximumTabWidth" );
|
||||||
tabHeight = UIManager.getInt( "TabbedPane.tabHeight" );
|
tabHeight = UIManager.getInt( "TabbedPane.tabHeight" );
|
||||||
tabSelectionHeight = UIManager.getInt( "TabbedPane.tabSelectionHeight" );
|
tabSelectionHeight = UIManager.getInt( "TabbedPane.tabSelectionHeight" );
|
||||||
|
cardTabSelectionHeight = UIManager.getInt( "TabbedPane.cardTabSelectionHeight" );
|
||||||
contentSeparatorHeight = UIManager.getInt( "TabbedPane.contentSeparatorHeight" );
|
contentSeparatorHeight = UIManager.getInt( "TabbedPane.contentSeparatorHeight" );
|
||||||
showTabSeparators = UIManager.getBoolean( "TabbedPane.showTabSeparators" );
|
showTabSeparators = UIManager.getBoolean( "TabbedPane.showTabSeparators" );
|
||||||
tabSeparatorsFullHeight = UIManager.getBoolean( "TabbedPane.tabSeparatorsFullHeight" );
|
tabSeparatorsFullHeight = UIManager.getBoolean( "TabbedPane.tabSeparatorsFullHeight" );
|
||||||
hasFullBorder = UIManager.getBoolean( "TabbedPane.hasFullBorder" );
|
hasFullBorder = UIManager.getBoolean( "TabbedPane.hasFullBorder" );
|
||||||
tabsOpaque = UIManager.getBoolean( "TabbedPane.tabsOpaque" );
|
tabsOpaque = UIManager.getBoolean( "TabbedPane.tabsOpaque" );
|
||||||
|
|
||||||
|
tabType = parseTabType( UIManager.getString( "TabbedPane.tabType" ) );
|
||||||
tabsPopupPolicy = parseTabsPopupPolicy( UIManager.getString( "TabbedPane.tabsPopupPolicy" ) );
|
tabsPopupPolicy = parseTabsPopupPolicy( UIManager.getString( "TabbedPane.tabsPopupPolicy" ) );
|
||||||
scrollButtonsPolicy = parseScrollButtonsPolicy( UIManager.getString( "TabbedPane.scrollButtonsPolicy" ) );
|
scrollButtonsPolicy = parseScrollButtonsPolicy( UIManager.getString( "TabbedPane.scrollButtonsPolicy" ) );
|
||||||
scrollButtonsPlacement = parseScrollButtonsPlacement( UIManager.getString( "TabbedPane.scrollButtonsPlacement" ) );
|
scrollButtonsPlacement = parseScrollButtonsPlacement( UIManager.getString( "TabbedPane.scrollButtonsPlacement" ) );
|
||||||
@@ -313,12 +352,14 @@ public class FlatTabbedPaneUI
|
|||||||
tabAlignment = parseAlignment( UIManager.getString( "TabbedPane.tabAlignment" ), CENTER );
|
tabAlignment = parseAlignment( UIManager.getString( "TabbedPane.tabAlignment" ), CENTER );
|
||||||
tabWidthMode = parseTabWidthMode( UIManager.getString( "TabbedPane.tabWidthMode" ) );
|
tabWidthMode = parseTabWidthMode( UIManager.getString( "TabbedPane.tabWidthMode" ) );
|
||||||
closeIcon = UIManager.getIcon( "TabbedPane.closeIcon" );
|
closeIcon = UIManager.getIcon( "TabbedPane.closeIcon" );
|
||||||
|
closeIconShared = true;
|
||||||
|
|
||||||
buttonInsets = UIManager.getInsets( "TabbedPane.buttonInsets" );
|
buttonInsets = UIManager.getInsets( "TabbedPane.buttonInsets" );
|
||||||
buttonArc = UIManager.getInt( "TabbedPane.buttonArc" );
|
buttonArc = UIManager.getInt( "TabbedPane.buttonArc" );
|
||||||
|
|
||||||
Locale l = tabPane.getLocale();
|
Locale l = tabPane.getLocale();
|
||||||
moreTabsButtonToolTipText = UIManager.getString( "TabbedPane.moreTabsButtonToolTipText", l );
|
moreTabsButtonToolTipText = UIManager.getString( "TabbedPane.moreTabsButtonToolTipText", l );
|
||||||
|
tabCloseToolTipText = UIManager.getString( "TabbedPane.tabCloseToolTipText", l );
|
||||||
|
|
||||||
// scale
|
// scale
|
||||||
textIconGap = scale( textIconGapUnscaled );
|
textIconGap = scale( textIconGapUnscaled );
|
||||||
@@ -351,6 +392,7 @@ public class FlatTabbedPaneUI
|
|||||||
selectedBackground = null;
|
selectedBackground = null;
|
||||||
selectedForeground = null;
|
selectedForeground = null;
|
||||||
underlineColor = null;
|
underlineColor = null;
|
||||||
|
inactiveUnderlineColor = null;
|
||||||
disabledUnderlineColor = null;
|
disabledUnderlineColor = null;
|
||||||
hoverColor = null;
|
hoverColor = null;
|
||||||
focusColor = null;
|
focusColor = null;
|
||||||
@@ -361,6 +403,8 @@ public class FlatTabbedPaneUI
|
|||||||
buttonHoverBackground = null;
|
buttonHoverBackground = null;
|
||||||
buttonPressedBackground = null;
|
buttonPressedBackground = null;
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
|
||||||
MigLayoutVisualPadding.uninstall( tabPane );
|
MigLayoutVisualPadding.uninstall( tabPane );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -537,6 +581,13 @@ public class FlatTabbedPaneUI
|
|||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected FocusListener createFocusListener() {
|
||||||
|
Handler handler = getHandler();
|
||||||
|
handler.focusDelegate = super.createFocusListener();
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected LayoutManager createLayoutManager() {
|
protected LayoutManager createLayoutManager() {
|
||||||
if( tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT )
|
if( tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT )
|
||||||
@@ -558,6 +609,84 @@ public class FlatTabbedPaneUI
|
|||||||
return new FlatScrollableTabButton( direction );
|
return new FlatScrollableTabButton( direction );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle() {
|
||||||
|
try {
|
||||||
|
applyStyle( FlatStylingSupport.getResolvedStyle( tabPane, "TabbedPane" ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( Object style ) {
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||||
|
|
||||||
|
// update buttons
|
||||||
|
for( Component c : tabPane.getComponents() ) {
|
||||||
|
if( c instanceof FlatTabAreaButton )
|
||||||
|
((FlatTabAreaButton)c).updateStyle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
// close icon
|
||||||
|
if( key.startsWith( "close" ) ) {
|
||||||
|
if( !(closeIcon instanceof FlatTabbedPaneCloseIcon) )
|
||||||
|
return new UnknownStyleException( key );
|
||||||
|
|
||||||
|
if( closeIconShared ) {
|
||||||
|
closeIcon = FlatStylingSupport.cloneIcon( closeIcon );
|
||||||
|
closeIconShared = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((FlatTabbedPaneCloseIcon)closeIcon).applyStyleProperty( key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( value instanceof String ) {
|
||||||
|
switch( key ) {
|
||||||
|
case "tabType": value = parseTabType( (String) value ); break;
|
||||||
|
case "tabsPopupPolicy": value = parseTabsPopupPolicy( (String) value ); break;
|
||||||
|
case "scrollButtonsPolicy": value = parseScrollButtonsPolicy( (String) value ); break;
|
||||||
|
case "scrollButtonsPlacement": value = parseScrollButtonsPlacement( (String) value ); break;
|
||||||
|
|
||||||
|
case "tabAreaAlignment": value = parseAlignment( (String) value, LEADING ); break;
|
||||||
|
case "tabAlignment": value = parseAlignment( (String) value, CENTER ); break;
|
||||||
|
case "tabWidthMode": value = parseTabWidthMode( (String) value ); break;
|
||||||
|
|
||||||
|
case "tabIconPlacement": value = parseTabIconPlacement( (String) value ); break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Object oldValue;
|
||||||
|
switch( key ) {
|
||||||
|
// BasicTabbedPaneUI
|
||||||
|
case "tabInsets": oldValue = tabInsets; tabInsets = (Insets) value; return oldValue;
|
||||||
|
case "tabAreaInsets": oldValue = tabAreaInsets; tabAreaInsets = (Insets) value; return oldValue;
|
||||||
|
case "textIconGap":
|
||||||
|
oldValue = textIconGapUnscaled;
|
||||||
|
textIconGapUnscaled = (int) value;
|
||||||
|
textIconGap = scale( textIconGapUnscaled );
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, tabPane, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
Map<String, Class<?>> infos = new FlatStylingSupport.StyleableInfosMap<>();
|
||||||
|
infos.put( "tabInsets", Insets.class );
|
||||||
|
infos.put( "tabAreaInsets", Insets.class );
|
||||||
|
infos.put( "textIconGap", int.class );
|
||||||
|
FlatStylingSupport.collectAnnotatedStyleableInfos( this, infos );
|
||||||
|
if( closeIcon instanceof FlatTabbedPaneCloseIcon )
|
||||||
|
infos.putAll( ((FlatTabbedPaneCloseIcon)closeIcon).getStyleableInfos() );
|
||||||
|
return infos;
|
||||||
|
}
|
||||||
|
|
||||||
protected void setRolloverTab( int x, int y ) {
|
protected void setRolloverTab( int x, int y ) {
|
||||||
setRolloverTab( tabForCoordinate( tabPane, x, y ) );
|
setRolloverTab( tabForCoordinate( tabPane, x, y ) );
|
||||||
}
|
}
|
||||||
@@ -607,8 +736,24 @@ public class FlatTabbedPaneUI
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
Rectangle r = getTabBounds( tabPane, tabIndex );
|
Rectangle r = getTabBounds( tabPane, tabIndex );
|
||||||
if( r != null )
|
if( r == null )
|
||||||
tabPane.repaint( r );
|
return;
|
||||||
|
|
||||||
|
// increase size of repaint region to include part of content border
|
||||||
|
if( contentSeparatorHeight > 0 &&
|
||||||
|
clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_CONTENT_SEPARATOR, true ) )
|
||||||
|
{
|
||||||
|
int sh = scale( contentSeparatorHeight );
|
||||||
|
switch( tabPane.getTabPlacement() ) {
|
||||||
|
default:
|
||||||
|
case TOP: r.height += sh; break;
|
||||||
|
case BOTTOM: r.height += sh; r.y -= sh; break;
|
||||||
|
case LEFT: r.width += sh; break;
|
||||||
|
case RIGHT: r.width += sh; r.x -= sh; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tabPane.repaint( r );
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean inCalculateEqual;
|
private boolean inCalculateEqual;
|
||||||
@@ -639,7 +784,7 @@ public class FlatTabbedPaneUI
|
|||||||
Insets tabInsets = getTabInsets( tabPlacement, tabIndex );
|
Insets tabInsets = getTabInsets( tabPlacement, tabIndex );
|
||||||
tabWidth = icon.getIconWidth() + tabInsets.left + tabInsets.right;
|
tabWidth = icon.getIconWidth() + tabInsets.left + tabInsets.right;
|
||||||
} else {
|
} else {
|
||||||
int iconPlacement = clientPropertyInt( tabPane, TABBED_PANE_TAB_ICON_PLACEMENT, LEADING );
|
int iconPlacement = clientPropertyInt( tabPane, TABBED_PANE_TAB_ICON_PLACEMENT, tabIconPlacement );
|
||||||
if( (iconPlacement == TOP || iconPlacement == BOTTOM) &&
|
if( (iconPlacement == TOP || iconPlacement == BOTTOM) &&
|
||||||
tabPane.getTabComponentAt( tabIndex ) == null &&
|
tabPane.getTabComponentAt( tabIndex ) == null &&
|
||||||
(icon = getIconForTab( tabIndex )) != null )
|
(icon = getIconForTab( tabIndex )) != null )
|
||||||
@@ -682,7 +827,7 @@ public class FlatTabbedPaneUI
|
|||||||
int tabHeight;
|
int tabHeight;
|
||||||
|
|
||||||
Icon icon;
|
Icon icon;
|
||||||
int iconPlacement = clientPropertyInt( tabPane, TABBED_PANE_TAB_ICON_PLACEMENT, LEADING );
|
int iconPlacement = clientPropertyInt( tabPane, TABBED_PANE_TAB_ICON_PLACEMENT, tabIconPlacement );
|
||||||
if( (iconPlacement == TOP || iconPlacement == BOTTOM) &&
|
if( (iconPlacement == TOP || iconPlacement == BOTTOM) &&
|
||||||
tabPane.getTabComponentAt( tabIndex ) == null &&
|
tabPane.getTabComponentAt( tabIndex ) == null &&
|
||||||
(icon = getIconForTab( tabIndex )) != null )
|
(icon = getIconForTab( tabIndex )) != null )
|
||||||
@@ -790,7 +935,7 @@ public class FlatTabbedPaneUI
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected Insets getContentBorderInsets( int tabPlacement ) {
|
protected Insets getContentBorderInsets( int tabPlacement ) {
|
||||||
if( hideTabArea() || contentSeparatorHeight == 0 || !clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_CONTENT_SEPARATOR, true ) )
|
if( hideTabArea() || contentSeparatorHeight == 0 || !clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_CONTENT_SEPARATOR, showContentSeparator ) )
|
||||||
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 );
|
||||||
@@ -945,16 +1090,21 @@ public class FlatTabbedPaneUI
|
|||||||
int x, int y, int w, int h, boolean isSelected )
|
int x, int y, int w, int h, boolean isSelected )
|
||||||
{
|
{
|
||||||
// paint tab background
|
// paint tab background
|
||||||
|
Color background = getTabBackground( tabPlacement, tabIndex, isSelected );
|
||||||
|
g.setColor( FlatUIUtils.deriveColor( background, tabPane.getBackground() ) );
|
||||||
|
g.fillRect( x, y, w, h );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Color getTabBackground( int tabPlacement, int tabIndex, boolean isSelected ) {
|
||||||
boolean enabled = tabPane.isEnabled();
|
boolean enabled = tabPane.isEnabled();
|
||||||
Color background = enabled && tabPane.isEnabledAt( tabIndex ) && getRolloverTab() == tabIndex
|
return enabled && tabPane.isEnabledAt( tabIndex ) && getRolloverTab() == tabIndex
|
||||||
? hoverColor
|
? hoverColor
|
||||||
: (enabled && isSelected && FlatUIUtils.isPermanentFocusOwner( tabPane )
|
: (enabled && isSelected && FlatUIUtils.isPermanentFocusOwner( tabPane )
|
||||||
? focusColor
|
? focusColor
|
||||||
: (selectedBackground != null && enabled && isSelected
|
: (selectedBackground != null && enabled && isSelected
|
||||||
? selectedBackground
|
? selectedBackground
|
||||||
: tabPane.getBackgroundAt( tabIndex )));
|
: tabPane.getBackgroundAt( tabIndex )));
|
||||||
g.setColor( FlatUIUtils.deriveColor( background, tabPane.getBackground() ) );
|
|
||||||
g.fillRect( x, y, w, h );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -964,7 +1114,62 @@ public class FlatTabbedPaneUI
|
|||||||
// paint tab separators
|
// paint tab separators
|
||||||
if( clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_TAB_SEPARATORS, showTabSeparators ) &&
|
if( clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_TAB_SEPARATORS, showTabSeparators ) &&
|
||||||
!isLastInRun( tabIndex ) )
|
!isLastInRun( tabIndex ) )
|
||||||
paintTabSeparator( g, tabPlacement, x, y, w, h );
|
{
|
||||||
|
if( getTabType() == TAB_TYPE_CARD ) {
|
||||||
|
// some separators need to be omitted if selected tab is painted as card
|
||||||
|
int selectedIndex = tabPane.getSelectedIndex();
|
||||||
|
if( tabIndex != selectedIndex - 1 && tabIndex != selectedIndex )
|
||||||
|
paintTabSeparator( g, tabPlacement, x, y, w, h );
|
||||||
|
} else
|
||||||
|
paintTabSeparator( g, tabPlacement, x, y, w, h );
|
||||||
|
}
|
||||||
|
|
||||||
|
// paint active tab border
|
||||||
|
if( isSelected && getTabType() == TAB_TYPE_CARD )
|
||||||
|
paintCardTabBorder( g, tabPlacement, tabIndex, x, y, w, h );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void paintCardTabBorder( Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h ) {
|
||||||
|
Graphics2D g2 = (Graphics2D) g;
|
||||||
|
|
||||||
|
float borderWidth = scale( (float) contentSeparatorHeight );
|
||||||
|
g.setColor( (tabSeparatorColor != null) ? tabSeparatorColor : contentAreaColor );
|
||||||
|
|
||||||
|
switch( tabPlacement ) {
|
||||||
|
default:
|
||||||
|
case TOP:
|
||||||
|
case BOTTOM:
|
||||||
|
// paint left and right tab border
|
||||||
|
g2.fill( new Rectangle2D.Float( x, y, borderWidth, h ) );
|
||||||
|
g2.fill( new Rectangle2D.Float( x + w - borderWidth, y, borderWidth, h ) );
|
||||||
|
break;
|
||||||
|
case LEFT:
|
||||||
|
case RIGHT:
|
||||||
|
// paint top and bottom tab border
|
||||||
|
g2.fill( new Rectangle2D.Float( x, y, w, borderWidth ) );
|
||||||
|
g2.fill( new Rectangle2D.Float( x, y + h - borderWidth, w, borderWidth ) );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( cardTabSelectionHeight <= 0 ) {
|
||||||
|
// if there is no tab selection indicator, paint a top border as well
|
||||||
|
switch( tabPlacement ) {
|
||||||
|
default:
|
||||||
|
case TOP:
|
||||||
|
g2.fill( new Rectangle2D.Float( x, y, w, borderWidth ) );
|
||||||
|
break;
|
||||||
|
case BOTTOM:
|
||||||
|
g2.fill( new Rectangle2D.Float( x, y + h - borderWidth, w, borderWidth ) );
|
||||||
|
break;
|
||||||
|
case LEFT:
|
||||||
|
g2.fill( new Rectangle2D.Float( x, y, borderWidth, h ) );
|
||||||
|
break;
|
||||||
|
case RIGHT:
|
||||||
|
g2.fill( new Rectangle2D.Float( x + w - borderWidth, y, borderWidth, h ) );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 ) {
|
||||||
@@ -1007,33 +1212,56 @@ public class FlatTabbedPaneUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void paintTabSelection( Graphics g, int tabPlacement, int x, int y, int w, int h ) {
|
protected void paintTabSelection( Graphics g, int tabPlacement, int x, int y, int w, int h ) {
|
||||||
g.setColor( tabPane.isEnabled() ? underlineColor : disabledUnderlineColor );
|
g.setColor( tabPane.isEnabled()
|
||||||
|
? (isTabbedPaneOrChildFocused() ? underlineColor : inactiveUnderlineColor)
|
||||||
|
: disabledUnderlineColor );
|
||||||
|
|
||||||
// paint underline selection
|
// paint underline selection
|
||||||
|
boolean atBottom = (getTabType() != TAB_TYPE_CARD);
|
||||||
Insets contentInsets = getContentBorderInsets( tabPlacement );
|
Insets contentInsets = getContentBorderInsets( tabPlacement );
|
||||||
int tabSelectionHeight = scale( this.tabSelectionHeight );
|
int tabSelectionHeight = scale( atBottom ? this.tabSelectionHeight : cardTabSelectionHeight );
|
||||||
|
int sx, sy;
|
||||||
switch( tabPlacement ) {
|
switch( tabPlacement ) {
|
||||||
case TOP:
|
case TOP:
|
||||||
default:
|
default:
|
||||||
int sy = y + h + contentInsets.top - tabSelectionHeight;
|
sy = atBottom ? (y + h + contentInsets.top - tabSelectionHeight) : y;
|
||||||
g.fillRect( x, sy, w, tabSelectionHeight );
|
g.fillRect( x, sy, w, tabSelectionHeight );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BOTTOM:
|
case BOTTOM:
|
||||||
g.fillRect( x, y - contentInsets.bottom, w, tabSelectionHeight );
|
sy = atBottom ? (y - contentInsets.bottom) : (y + h - tabSelectionHeight);
|
||||||
|
g.fillRect( x, sy, w, tabSelectionHeight );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LEFT:
|
case LEFT:
|
||||||
int sx = x + w + contentInsets.left - tabSelectionHeight;
|
sx = atBottom ? (x + w + contentInsets.left - tabSelectionHeight) : x;
|
||||||
g.fillRect( sx, y, tabSelectionHeight, h );
|
g.fillRect( sx, y, tabSelectionHeight, h );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RIGHT:
|
case RIGHT:
|
||||||
g.fillRect( x - contentInsets.right, y, tabSelectionHeight, h );
|
sx = atBottom ? (x - contentInsets.right) : (x + w - tabSelectionHeight);
|
||||||
|
g.fillRect( sx, y, tabSelectionHeight, h );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.2 */
|
||||||
|
@SuppressWarnings( "unchecked" )
|
||||||
|
protected boolean isTabbedPaneOrChildFocused() {
|
||||||
|
KeyboardFocusManager keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||||
|
|
||||||
|
Object value = tabPane.getClientProperty( FlatClientProperties.COMPONENT_FOCUS_OWNER );
|
||||||
|
if( value instanceof Predicate ) {
|
||||||
|
return ((Predicate<JComponent>)value).test( tabPane ) &&
|
||||||
|
FlatUIUtils.isInActiveWindow( tabPane, keyboardFocusManager.getActiveWindow() );
|
||||||
|
}
|
||||||
|
|
||||||
|
Component focusOwner = keyboardFocusManager.getPermanentFocusOwner();
|
||||||
|
return focusOwner != null &&
|
||||||
|
SwingUtilities.isDescendingFrom( focusOwner, tabPane ) &&
|
||||||
|
FlatUIUtils.isInActiveWindow( focusOwner, keyboardFocusManager.getActiveWindow() );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Actually does nearly the same as super.paintContentBorder() but
|
* Actually does nearly the same as super.paintContentBorder() but
|
||||||
* - not using UIManager.getColor("TabbedPane.contentAreaColor") to be GUI builder friendly
|
* - not using UIManager.getColor("TabbedPane.contentAreaColor") to be GUI builder friendly
|
||||||
@@ -1041,12 +1269,13 @@ public class FlatTabbedPaneUI
|
|||||||
* - paint full border (if enabled)
|
* - paint full border (if enabled)
|
||||||
* - not invoking paintContentBorder*Edge() methods
|
* - not invoking paintContentBorder*Edge() methods
|
||||||
* - repaint selection
|
* - repaint selection
|
||||||
|
* - painting active tab border style
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void paintContentBorder( Graphics g, int tabPlacement, int selectedIndex ) {
|
protected void paintContentBorder( Graphics g, int tabPlacement, int selectedIndex ) {
|
||||||
if( tabPane.getTabCount() <= 0 ||
|
if( tabPane.getTabCount() <= 0 ||
|
||||||
contentSeparatorHeight == 0 ||
|
contentSeparatorHeight == 0 ||
|
||||||
!clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_CONTENT_SEPARATOR, true ) )
|
!clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_CONTENT_SEPARATOR, showContentSeparator ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Insets insets = tabPane.getInsets();
|
Insets insets = tabPane.getInsets();
|
||||||
@@ -1089,12 +1318,49 @@ public class FlatTabbedPaneUI
|
|||||||
Insets ci = new Insets( 0, 0, 0, 0 );
|
Insets ci = new Insets( 0, 0, 0, 0 );
|
||||||
rotateInsets( hasFullBorder ? new Insets( sh, sh, sh, sh ) : new Insets( sh, 0, 0, 0 ), ci, tabPlacement );
|
rotateInsets( hasFullBorder ? new Insets( sh, sh, sh, sh ) : new Insets( sh, 0, 0, 0 ), ci, tabPlacement );
|
||||||
|
|
||||||
// paint content separator or full border
|
// create path for content separator or full border
|
||||||
g.setColor( contentAreaColor );
|
|
||||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||||
path.append( new Rectangle2D.Float( x, y, w, h ), false );
|
path.append( new Rectangle2D.Float( x, y, w, h ), false );
|
||||||
path.append( new Rectangle2D.Float( x + (ci.left / 100f), y + (ci.top / 100f),
|
path.append( new Rectangle2D.Float( x + (ci.left / 100f), y + (ci.top / 100f),
|
||||||
w - (ci.left / 100f) - (ci.right / 100f), h - (ci.top / 100f) - (ci.bottom / 100f) ), false );
|
w - (ci.left / 100f) - (ci.right / 100f), h - (ci.top / 100f) - (ci.bottom / 100f) ), false );
|
||||||
|
|
||||||
|
// add gap for selected tab to path
|
||||||
|
if( getTabType() == TAB_TYPE_CARD ) {
|
||||||
|
float csh = scale( (float) contentSeparatorHeight );
|
||||||
|
|
||||||
|
Rectangle tabRect = getTabBounds( tabPane, selectedIndex );
|
||||||
|
Rectangle2D.Float innerTabRect = new Rectangle2D.Float( tabRect.x + csh, tabRect.y + csh,
|
||||||
|
tabRect.width - (csh * 2), tabRect.height - (csh * 2) );
|
||||||
|
|
||||||
|
// Ensure that the separator outside the tabViewport is present (doesn't get cutoff by the active tab)
|
||||||
|
// If left unsolved the active tab is "visible" in the separator (the gap) even when outside the viewport
|
||||||
|
if( tabViewport != null )
|
||||||
|
Rectangle2D.intersect( tabViewport.getBounds(), innerTabRect, innerTabRect );
|
||||||
|
|
||||||
|
Rectangle2D.Float gap = null;
|
||||||
|
if( isHorizontalTabPlacement() ) {
|
||||||
|
if( innerTabRect.width > 0 ) {
|
||||||
|
float y2 = (tabPlacement == TOP) ? y : y + h - csh;
|
||||||
|
gap = new Rectangle2D.Float( innerTabRect.x, y2, innerTabRect.width, csh );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if( innerTabRect.height > 0 ) {
|
||||||
|
float x2 = (tabPlacement == LEFT) ? x : x + w - csh;
|
||||||
|
gap = new Rectangle2D.Float( x2, innerTabRect.y, csh, innerTabRect.height );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( gap != null ) {
|
||||||
|
path.append( gap, false );
|
||||||
|
|
||||||
|
// fill gap in case that the tab is colored (e.g. focused or hover)
|
||||||
|
g.setColor( getTabBackground( tabPlacement, selectedIndex, true ) );
|
||||||
|
((Graphics2D)g).fill( gap );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// paint content separator or full border
|
||||||
|
g.setColor( contentAreaColor );
|
||||||
((Graphics2D)g).fill( path );
|
((Graphics2D)g).fill( path );
|
||||||
|
|
||||||
// repaint selection in scroll-tab-layout because it may be painted before
|
// repaint selection in scroll-tab-layout because it may be painted before
|
||||||
@@ -1137,7 +1403,7 @@ public class FlatTabbedPaneUI
|
|||||||
// icon placement
|
// icon placement
|
||||||
int verticalTextPosition;
|
int verticalTextPosition;
|
||||||
int horizontalTextPosition;
|
int horizontalTextPosition;
|
||||||
switch( clientPropertyInt( tabPane, TABBED_PANE_TAB_ICON_PLACEMENT, LEADING ) ) {
|
switch( clientPropertyInt( tabPane, TABBED_PANE_TAB_ICON_PLACEMENT, tabIconPlacement ) ) {
|
||||||
default:
|
default:
|
||||||
case LEADING: verticalTextPosition = CENTER; horizontalTextPosition = TRAILING; break;
|
case LEADING: verticalTextPosition = CENTER; horizontalTextPosition = TRAILING; break;
|
||||||
case TRAILING: verticalTextPosition = CENTER; horizontalTextPosition = LEADING; break;
|
case TRAILING: verticalTextPosition = CENTER; horizontalTextPosition = LEADING; break;
|
||||||
@@ -1218,8 +1484,11 @@ public class FlatTabbedPaneUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isTabClosable( int tabIndex ) {
|
protected boolean isTabClosable( int tabIndex ) {
|
||||||
|
if( tabIndex < 0 )
|
||||||
|
return false;
|
||||||
|
|
||||||
Object value = getTabClientProperty( tabIndex, TABBED_PANE_TAB_CLOSABLE );
|
Object value = getTabClientProperty( tabIndex, TABBED_PANE_TAB_CLOSABLE );
|
||||||
return (value instanceof Boolean) ? (boolean) value : false;
|
return (value instanceof Boolean) ? (boolean) value : tabClosable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings( { "unchecked" } )
|
@SuppressWarnings( { "unchecked" } )
|
||||||
@@ -1293,7 +1562,16 @@ public class FlatTabbedPaneUI
|
|||||||
return tabPane.getTabCount() == 1 &&
|
return tabPane.getTabCount() == 1 &&
|
||||||
leadingComponent == null &&
|
leadingComponent == null &&
|
||||||
trailingComponent == null &&
|
trailingComponent == null &&
|
||||||
clientPropertyBoolean( tabPane, TABBED_PANE_HIDE_TAB_AREA_WITH_ONE_TAB, false );
|
clientPropertyBoolean( tabPane, TABBED_PANE_HIDE_TAB_AREA_WITH_ONE_TAB, hideTabAreaWithOneTab );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected int getTabType() {
|
||||||
|
Object value = tabPane.getClientProperty( TABBED_PANE_TAB_TYPE );
|
||||||
|
|
||||||
|
return (value instanceof String)
|
||||||
|
? parseTabType( (String) value )
|
||||||
|
: tabType;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int getTabsPopupPolicy() {
|
protected int getTabsPopupPolicy() {
|
||||||
@@ -1348,6 +1626,18 @@ public class FlatTabbedPaneUI
|
|||||||
: tabWidthMode;
|
: tabWidthMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected static int parseTabType( String str ) {
|
||||||
|
if( str == null )
|
||||||
|
return TAB_TYPE_UNDERLINED;
|
||||||
|
|
||||||
|
switch( str ) {
|
||||||
|
default:
|
||||||
|
case TABBED_PANE_TAB_TYPE_UNDERLINED: return TAB_TYPE_UNDERLINED;
|
||||||
|
case TABBED_PANE_TAB_TYPE_CARD: return TAB_TYPE_CARD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected static int parseTabsPopupPolicy( String str ) {
|
protected static int parseTabsPopupPolicy( String str ) {
|
||||||
if( str == null )
|
if( str == null )
|
||||||
return AS_NEEDED;
|
return AS_NEEDED;
|
||||||
@@ -1407,6 +1697,19 @@ public class FlatTabbedPaneUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static int parseTabIconPlacement( String str ) {
|
||||||
|
if( str == null )
|
||||||
|
return LEADING;
|
||||||
|
|
||||||
|
switch( str ) {
|
||||||
|
default:
|
||||||
|
case "leading": return LEADING;
|
||||||
|
case "trailing": return TRAILING;
|
||||||
|
case "top": return TOP;
|
||||||
|
case "bottom": return BOTTOM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void runWithOriginalLayoutManager( Runnable runnable ) {
|
private void runWithOriginalLayoutManager( Runnable runnable ) {
|
||||||
LayoutManager layout = tabPane.getLayout();
|
LayoutManager layout = tabPane.getLayout();
|
||||||
if( layout instanceof FlatTabbedPaneScrollLayout ) {
|
if( layout instanceof FlatTabbedPaneScrollLayout ) {
|
||||||
@@ -1564,7 +1867,13 @@ public class FlatTabbedPaneUI
|
|||||||
super( direction, arrowType,
|
super( direction, arrowType,
|
||||||
FlatTabbedPaneUI.this.foreground, FlatTabbedPaneUI.this.disabledForeground,
|
FlatTabbedPaneUI.this.foreground, FlatTabbedPaneUI.this.disabledForeground,
|
||||||
null, buttonHoverBackground, null, buttonPressedBackground );
|
null, buttonHoverBackground, null, buttonPressedBackground );
|
||||||
setArrowWidth( 10 );
|
setArrowWidth( 11 );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateStyle() {
|
||||||
|
updateStyle( arrowType,
|
||||||
|
FlatTabbedPaneUI.this.foreground, FlatTabbedPaneUI.this.disabledForeground,
|
||||||
|
null, buttonHoverBackground, null, buttonPressedBackground );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1700,7 +2009,7 @@ public class FlatTabbedPaneUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected JMenuItem createTabMenuItem( int tabIndex ) {
|
protected JMenuItem createTabMenuItem( int tabIndex ) {
|
||||||
// search for tab name in this places
|
// search for tab name in these places
|
||||||
// 1. tab title
|
// 1. tab title
|
||||||
// 2. text of label or text component in custom tab component (including children)
|
// 2. text of label or text component in custom tab component (including children)
|
||||||
// 3. accessible name of tab
|
// 3. accessible name of tab
|
||||||
@@ -1733,7 +2042,7 @@ public class FlatTabbedPaneUI
|
|||||||
menuItem.setOpaque( true );
|
menuItem.setOpaque( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !tabPane.isEnabledAt( tabIndex ) )
|
if( !tabPane.isEnabled() || !tabPane.isEnabledAt( tabIndex ) )
|
||||||
menuItem.setEnabled( false );
|
menuItem.setEnabled( false );
|
||||||
|
|
||||||
menuItem.addActionListener( e -> selectTab( tabIndex ) );
|
menuItem.addActionListener( e -> selectTab( tabIndex ) );
|
||||||
@@ -2129,7 +2438,7 @@ public class FlatTabbedPaneUI
|
|||||||
if( tabPane == null || tabViewport == null )
|
if( tabPane == null || tabViewport == null )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if( !scrolled || tabViewport == null )
|
if( !scrolled )
|
||||||
return;
|
return;
|
||||||
scrolled = false;
|
scrolled = false;
|
||||||
|
|
||||||
@@ -2142,11 +2451,12 @@ public class FlatTabbedPaneUI
|
|||||||
|
|
||||||
private class Handler
|
private class Handler
|
||||||
implements MouseListener, MouseMotionListener, PropertyChangeListener,
|
implements MouseListener, MouseMotionListener, PropertyChangeListener,
|
||||||
ChangeListener, ComponentListener, ContainerListener
|
ChangeListener, ComponentListener, ContainerListener, FocusListener
|
||||||
{
|
{
|
||||||
MouseListener mouseDelegate;
|
MouseListener mouseDelegate;
|
||||||
PropertyChangeListener propertyChangeDelegate;
|
PropertyChangeListener propertyChangeDelegate;
|
||||||
ChangeListener changeDelegate;
|
ChangeListener changeDelegate;
|
||||||
|
FocusListener focusDelegate;
|
||||||
|
|
||||||
private final PropertyChangeListener contentListener = this::contentPropertyChange;
|
private final PropertyChangeListener contentListener = this::contentPropertyChange;
|
||||||
|
|
||||||
@@ -2241,9 +2551,7 @@ public class FlatTabbedPaneUI
|
|||||||
setRolloverTab( tabIndex );
|
setRolloverTab( tabIndex );
|
||||||
|
|
||||||
// check whether mouse hit tab close area
|
// check whether mouse hit tab close area
|
||||||
boolean hitClose = isTabClosable( tabIndex )
|
boolean hitClose = isTabClosable( tabIndex ) && getTabCloseHitArea( tabIndex ).contains( x, y );
|
||||||
? getTabCloseHitArea( tabIndex ).contains( x, y )
|
|
||||||
: false;
|
|
||||||
if( e.getID() == MouseEvent.MOUSE_PRESSED )
|
if( e.getID() == MouseEvent.MOUSE_PRESSED )
|
||||||
pressedTabIndex = hitClose ? tabIndex : -1;
|
pressedTabIndex = hitClose ? tabIndex : -1;
|
||||||
setRolloverTabClose( hitClose );
|
setRolloverTabClose( hitClose );
|
||||||
@@ -2252,6 +2560,8 @@ public class FlatTabbedPaneUI
|
|||||||
// update tooltip
|
// update tooltip
|
||||||
if( tabIndex >= 0 && hitClose ) {
|
if( tabIndex >= 0 && hitClose ) {
|
||||||
Object closeTip = getTabClientProperty( tabIndex, TABBED_PANE_TAB_CLOSE_TOOLTIPTEXT );
|
Object closeTip = getTabClientProperty( tabIndex, TABBED_PANE_TAB_CLOSE_TOOLTIPTEXT );
|
||||||
|
if( closeTip == null )
|
||||||
|
closeTip = tabCloseToolTipText;
|
||||||
if( closeTip instanceof String )
|
if( closeTip instanceof String )
|
||||||
setCloseToolTip( tabIndex, (String) closeTip );
|
setCloseToolTip( tabIndex, (String) closeTip );
|
||||||
else
|
else
|
||||||
@@ -2264,8 +2574,7 @@ public class FlatTabbedPaneUI
|
|||||||
if( tabIndex == lastTipTabIndex )
|
if( tabIndex == lastTipTabIndex )
|
||||||
return; // closeTip already set
|
return; // closeTip already set
|
||||||
|
|
||||||
if( tabIndex != lastTipTabIndex )
|
restoreTabToolTip();
|
||||||
restoreTabToolTip();
|
|
||||||
|
|
||||||
lastTipTabIndex = tabIndex;
|
lastTipTabIndex = tabIndex;
|
||||||
lastTip = tabPane.getToolTipTextAt( lastTipTabIndex );
|
lastTip = tabPane.getToolTipTextAt( lastTipTabIndex );
|
||||||
@@ -2314,6 +2623,10 @@ public class FlatTabbedPaneUI
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case TABBED_PANE_SHOW_TAB_SEPARATORS:
|
case TABBED_PANE_SHOW_TAB_SEPARATORS:
|
||||||
|
case TABBED_PANE_TAB_TYPE:
|
||||||
|
tabPane.repaint();
|
||||||
|
break;
|
||||||
|
|
||||||
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_HIDE_TAB_AREA_WITH_ONE_TAB:
|
||||||
@@ -2351,6 +2664,13 @@ public class FlatTabbedPaneUI
|
|||||||
tabPane.repaint();
|
tabPane.repaint();
|
||||||
ensureSelectedTabIsVisibleLater();
|
ensureSelectedTabIsVisibleLater();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case STYLE:
|
||||||
|
case STYLE_CLASS:
|
||||||
|
installStyle();
|
||||||
|
tabPane.revalidate();
|
||||||
|
tabPane.repaint();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2405,6 +2725,20 @@ public class FlatTabbedPaneUI
|
|||||||
if( !(c instanceof UIResource) )
|
if( !(c instanceof UIResource) )
|
||||||
c.removePropertyChangeListener( contentListener );
|
c.removePropertyChangeListener( contentListener );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- interface FocusListener ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void focusGained( FocusEvent e ) {
|
||||||
|
focusDelegate.focusGained( e );
|
||||||
|
repaintTab( tabPane.getSelectedIndex() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void focusLost( FocusEvent e ) {
|
||||||
|
focusDelegate.focusLost( e );
|
||||||
|
repaintTab( tabPane.getSelectedIndex() );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class FlatTabbedPaneLayout -----------------------------------------
|
//---- class FlatTabbedPaneLayout -----------------------------------------
|
||||||
@@ -3033,4 +3367,77 @@ public class FlatTabbedPaneUI
|
|||||||
delegate.actionPerformed( e );
|
delegate.actionPerformed( e );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- class FlatSelectedTabRepainter -------------------------------------
|
||||||
|
|
||||||
|
private static class FlatSelectedTabRepainter
|
||||||
|
implements PropertyChangeListener//, Runnable
|
||||||
|
{
|
||||||
|
private static FlatSelectedTabRepainter instance;
|
||||||
|
|
||||||
|
private KeyboardFocusManager keyboardFocusManager;
|
||||||
|
|
||||||
|
static void install() {
|
||||||
|
synchronized( FlatSelectedTabRepainter.class ) {
|
||||||
|
if( instance != null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
instance = new FlatSelectedTabRepainter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatSelectedTabRepainter() {
|
||||||
|
keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||||
|
keyboardFocusManager.addPropertyChangeListener( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void uninstall() {
|
||||||
|
synchronized( FlatSelectedTabRepainter.class ) {
|
||||||
|
if( instance == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
keyboardFocusManager.removePropertyChangeListener( this );
|
||||||
|
keyboardFocusManager = null;
|
||||||
|
instance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
// uninstall if no longer using FlatLaf
|
||||||
|
if( !(UIManager.getLookAndFeel() instanceof FlatLaf) ) {
|
||||||
|
uninstall();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch( e.getPropertyName() ) {
|
||||||
|
case "permanentFocusOwner":
|
||||||
|
Object oldValue = e.getOldValue();
|
||||||
|
Object newValue = e.getNewValue();
|
||||||
|
if( oldValue instanceof Component )
|
||||||
|
repaintSelectedTabs( (Component) oldValue );
|
||||||
|
if( newValue instanceof Component )
|
||||||
|
repaintSelectedTabs( (Component) newValue );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "activeWindow":
|
||||||
|
repaintSelectedTabs( keyboardFocusManager.getPermanentFocusOwner() );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void repaintSelectedTabs( Component c ) {
|
||||||
|
if( c instanceof JTabbedPane )
|
||||||
|
repaintSelectedTab( (JTabbedPane) c );
|
||||||
|
|
||||||
|
while( (c = SwingUtilities.getAncestorOfClass( JTabbedPane.class, c )) != null )
|
||||||
|
repaintSelectedTab( (JTabbedPane) c );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void repaintSelectedTab( JTabbedPane tabbedPane ) {
|
||||||
|
TabbedPaneUI ui = tabbedPane.getUI();
|
||||||
|
if( ui instanceof FlatTabbedPaneUI )
|
||||||
|
((FlatTabbedPaneUI) ui).repaintTab( tabbedPane.getSelectedIndex() );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,11 +16,15 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Insets;
|
||||||
|
import java.util.function.Function;
|
||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.plaf.TableUI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cell border for {@link javax.swing.table.DefaultTableCellRenderer}
|
* Cell border for {@link javax.swing.table.DefaultTableCellRenderer}
|
||||||
@@ -33,12 +37,54 @@ import javax.swing.UIManager;
|
|||||||
public class FlatTableCellBorder
|
public class FlatTableCellBorder
|
||||||
extends FlatLineBorder
|
extends FlatLineBorder
|
||||||
{
|
{
|
||||||
final boolean showCellFocusIndicator = UIManager.getBoolean( "Table.showCellFocusIndicator" );
|
/** @since 2 */ protected boolean showCellFocusIndicator = UIManager.getBoolean( "Table.showCellFocusIndicator" );
|
||||||
|
|
||||||
|
private Component c;
|
||||||
|
|
||||||
protected FlatTableCellBorder() {
|
protected FlatTableCellBorder() {
|
||||||
super( UIManager.getInsets( "Table.cellMargins" ), UIManager.getColor( "Table.cellFocusColor" ) );
|
super( UIManager.getInsets( "Table.cellMargins" ), UIManager.getColor( "Table.cellFocusColor" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
|
Insets m = getStyleFromTableUI( c, ui -> ui.cellMargins );
|
||||||
|
if( m != null )
|
||||||
|
return scaleInsets( c, insets, m.top, m.left, m.bottom, m.right );
|
||||||
|
|
||||||
|
return super.getBorderInsets( c, insets );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color getLineColor() {
|
||||||
|
if( c != null ) {
|
||||||
|
Color color = getStyleFromTableUI( c, ui -> ui.cellFocusColor );
|
||||||
|
if( color != null )
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
return super.getLineColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
|
this.c = c;
|
||||||
|
super.paintBorder( c, g, x, y, width, height );
|
||||||
|
this.c = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Because this border is always shared for all tables,
|
||||||
|
* get border specific style from FlatTableUI.
|
||||||
|
*/
|
||||||
|
static <T> T getStyleFromTableUI( Component c, Function<FlatTableUI, T> f ) {
|
||||||
|
JTable table = (JTable) SwingUtilities.getAncestorOfClass( JTable.class, c );
|
||||||
|
if( table != null ) {
|
||||||
|
TableUI ui = table.getUI();
|
||||||
|
if( ui instanceof FlatTableUI )
|
||||||
|
return f.apply( (FlatTableUI) ui );
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
//---- class Default ------------------------------------------------------
|
//---- class Default ------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,6 +120,9 @@ public class FlatTableCellBorder
|
|||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
|
Boolean b = getStyleFromTableUI( c, ui -> ui.showCellFocusIndicator );
|
||||||
|
boolean showCellFocusIndicator = (b != null) ? b : this.showCellFocusIndicator;
|
||||||
|
|
||||||
if( !showCellFocusIndicator ) {
|
if( !showCellFocusIndicator ) {
|
||||||
JTable table = (JTable) SwingUtilities.getAncestorOfClass( JTable.class, c );
|
JTable table = (JTable) SwingUtilities.getAncestorOfClass( JTable.class, c );
|
||||||
if( table != null && !isSelectionEditable( table ) )
|
if( table != null && !isSelectionEditable( table ) )
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import java.awt.Component;
|
|||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Insets;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import javax.swing.JScrollBar;
|
import javax.swing.JScrollBar;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
@@ -51,12 +52,30 @@ public class FlatTableHeaderBorder
|
|||||||
super( UIManager.getInsets( "TableHeader.cellMargins" ) );
|
super( UIManager.getInsets( "TableHeader.cellMargins" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
|
JTableHeader header = (JTableHeader) SwingUtilities.getAncestorOfClass( JTableHeader.class, c );
|
||||||
|
if( header != null ) {
|
||||||
|
if( header.getUI() instanceof FlatTableHeaderUI ) {
|
||||||
|
FlatTableHeaderUI ui = (FlatTableHeaderUI) header.getUI();
|
||||||
|
if( ui.cellMargins != null ) {
|
||||||
|
Insets m = ui.cellMargins;
|
||||||
|
return scaleInsets( c, insets, m.top, m.left, m.bottom, m.right );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.getBorderInsets( c, insets );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@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 ) {
|
||||||
JTableHeader header = (JTableHeader) SwingUtilities.getAncestorOfClass( JTableHeader.class, c );
|
JTableHeader header = (JTableHeader) SwingUtilities.getAncestorOfClass( JTableHeader.class, c );
|
||||||
boolean leftToRight = (header != null ? header : c).getComponentOrientation().isLeftToRight();
|
boolean leftToRight = (header != null ? header : c).getComponentOrientation().isLeftToRight();
|
||||||
boolean paintLeft = !leftToRight;
|
boolean paintLeft = !leftToRight;
|
||||||
boolean paintRight = leftToRight;
|
boolean paintRight = leftToRight;
|
||||||
|
Color separatorColor = this.separatorColor;
|
||||||
|
Color bottomSeparatorColor = this.bottomSeparatorColor;
|
||||||
|
|
||||||
if( header != null ) {
|
if( header != null ) {
|
||||||
int hx = SwingUtilities.convertPoint( c, x, y, header ).x;
|
int hx = SwingUtilities.convertPoint( c, x, y, header ).x;
|
||||||
@@ -68,6 +87,16 @@ public class FlatTableHeaderBorder
|
|||||||
if( hx + width >= header.getWidth() && leftToRight && hideTrailingVerticalLine( header ) )
|
if( hx + width >= header.getWidth() && leftToRight && hideTrailingVerticalLine( header ) )
|
||||||
paintRight = false;
|
paintRight = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Because this border is always shared for all table headers,
|
||||||
|
// get border specific style from FlatTableHeaderUI.
|
||||||
|
if( header.getUI() instanceof FlatTableHeaderUI ) {
|
||||||
|
FlatTableHeaderUI ui = (FlatTableHeaderUI) header.getUI();
|
||||||
|
if( ui.separatorColor != null )
|
||||||
|
separatorColor = ui.separatorColor;
|
||||||
|
if( ui.bottomSeparatorColor != null )
|
||||||
|
bottomSeparatorColor = ui.bottomSeparatorColor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float lineWidth = UIScale.scale( 1f );
|
float lineWidth = UIScale.scale( 1f );
|
||||||
@@ -110,6 +139,12 @@ public class FlatTableHeaderBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected boolean hideTrailingVerticalLine( JTableHeader header ) {
|
protected boolean hideTrailingVerticalLine( JTableHeader header ) {
|
||||||
|
if( header.getUI() instanceof FlatTableHeaderUI ) {
|
||||||
|
FlatTableHeaderUI ui = (FlatTableHeaderUI) header.getUI();
|
||||||
|
if( ui.showTrailingVerticalLine != null )
|
||||||
|
return !ui.showTrailingVerticalLine;
|
||||||
|
}
|
||||||
|
|
||||||
if( showTrailingVerticalLine )
|
if( showTrailingVerticalLine )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ 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.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.util.Objects;
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
@@ -40,6 +41,9 @@ 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.TableColumnModel;
|
import javax.swing.table.TableColumnModel;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,32 +68,52 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault TableHeader.bottomSeparatorColor Color
|
* @uiDefault TableHeader.bottomSeparatorColor Color
|
||||||
* @uiDefault TableHeader.showTrailingVerticalLine boolean
|
* @uiDefault TableHeader.showTrailingVerticalLine boolean
|
||||||
*
|
*
|
||||||
|
* <!-- FlatAscendingSortIcon and FlatDescendingSortIcon -->
|
||||||
|
*
|
||||||
|
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||||
|
* @uiDefault Table.sortIconColor Color
|
||||||
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatTableHeaderUI
|
public class FlatTableHeaderUI
|
||||||
extends BasicTableHeaderUI
|
extends BasicTableHeaderUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
protected Color bottomSeparatorColor;
|
@Styleable protected Color bottomSeparatorColor;
|
||||||
protected int height;
|
@Styleable protected int height;
|
||||||
protected int sortIconPosition;
|
@Styleable(type=String.class) protected int sortIconPosition;
|
||||||
|
|
||||||
|
// for FlatTableHeaderBorder
|
||||||
|
/** @since 2 */ @Styleable protected Insets cellMargins;
|
||||||
|
/** @since 2 */ @Styleable protected Color separatorColor;
|
||||||
|
/** @since 2 */ @Styleable protected Boolean showTrailingVerticalLine;
|
||||||
|
|
||||||
|
// for FlatAscendingSortIcon and FlatDescendingSortIcon
|
||||||
|
// (needs to be public because icon classes are in another package)
|
||||||
|
/** @since 2 */ @Styleable public String arrowType;
|
||||||
|
/** @since 2 */ @Styleable public Color sortIconColor;
|
||||||
|
|
||||||
|
private PropertyChangeListener propertyChangeListener;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatTableHeaderUI();
|
return new FlatTableHeaderUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
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" ) ) {
|
sortIconPosition = parseSortIconPosition( UIManager.getString( "TableHeader.sortIconPosition" ) );
|
||||||
default:
|
|
||||||
case "right": sortIconPosition = SwingConstants.RIGHT; break;
|
|
||||||
case "left": sortIconPosition = SwingConstants.LEFT; break;
|
|
||||||
case "top": sortIconPosition = SwingConstants.TOP; break;
|
|
||||||
case "bottom": sortIconPosition = SwingConstants.BOTTOM; break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -97,6 +121,65 @@ public class FlatTableHeaderUI
|
|||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
bottomSeparatorColor = null;
|
bottomSeparatorColor = null;
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installListeners() {
|
||||||
|
super.installListeners();
|
||||||
|
|
||||||
|
propertyChangeListener = FlatStylingSupport.createPropertyChangeListener( header, this::installStyle, null );
|
||||||
|
header.addPropertyChangeListener( propertyChangeListener );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallListeners() {
|
||||||
|
super.uninstallListeners();
|
||||||
|
|
||||||
|
header.removePropertyChangeListener( propertyChangeListener );
|
||||||
|
propertyChangeListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle() {
|
||||||
|
try {
|
||||||
|
applyStyle( FlatStylingSupport.getResolvedStyle( header, "TableHeader" ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( Object style ) {
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
if( key.equals( "sortIconPosition" ) && value instanceof String )
|
||||||
|
value = parseSortIconPosition( (String) value );
|
||||||
|
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, header, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int parseSortIconPosition( String str ) {
|
||||||
|
if( str == null )
|
||||||
|
str = "";
|
||||||
|
|
||||||
|
switch( str ) {
|
||||||
|
default:
|
||||||
|
case "right": return SwingConstants.RIGHT;
|
||||||
|
case "left": return SwingConstants.LEFT;
|
||||||
|
case "top": return SwingConstants.TOP;
|
||||||
|
case "bottom": return SwingConstants.BOTTOM;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -265,9 +348,7 @@ public class FlatTableHeaderUI
|
|||||||
|
|
||||||
//---- class FlatMouseInputHandler ----------------------------------------
|
//---- class FlatMouseInputHandler ----------------------------------------
|
||||||
|
|
||||||
/**
|
/** @since 1.6 */
|
||||||
* @since 1.6
|
|
||||||
*/
|
|
||||||
protected class FlatMouseInputHandler
|
protected class FlatMouseInputHandler
|
||||||
extends MouseInputHandler
|
extends MouseInputHandler
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -22,10 +22,12 @@ import java.awt.Dimension;
|
|||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Insets;
|
||||||
import java.awt.event.FocusEvent;
|
import java.awt.event.FocusEvent;
|
||||||
import java.awt.event.FocusListener;
|
import java.awt.event.FocusListener;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
import javax.swing.JViewport;
|
import javax.swing.JViewport;
|
||||||
@@ -36,7 +38,10 @@ import javax.swing.plaf.ComponentUI;
|
|||||||
import javax.swing.plaf.basic.BasicTableUI;
|
import javax.swing.plaf.basic.BasicTableUI;
|
||||||
import javax.swing.table.JTableHeader;
|
import javax.swing.table.JTableHeader;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.util.Graphics2DProxy;
|
import com.formdev.flatlaf.util.Graphics2DProxy;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -75,6 +80,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault Table.intercellSpacing Dimension
|
* @uiDefault Table.intercellSpacing Dimension
|
||||||
* @uiDefault Table.selectionInactiveBackground Color
|
* @uiDefault Table.selectionInactiveBackground Color
|
||||||
* @uiDefault Table.selectionInactiveForeground Color
|
* @uiDefault Table.selectionInactiveForeground Color
|
||||||
|
* @uiDefault Table.paintOutsideAlternateRows boolean
|
||||||
*
|
*
|
||||||
* <!-- FlatTableCellBorder -->
|
* <!-- FlatTableCellBorder -->
|
||||||
*
|
*
|
||||||
@@ -90,27 +96,41 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*/
|
*/
|
||||||
public class FlatTableUI
|
public class FlatTableUI
|
||||||
extends BasicTableUI
|
extends BasicTableUI
|
||||||
|
implements StyleableUI, FlatViewportUI.ViewportPainter
|
||||||
{
|
{
|
||||||
protected boolean showHorizontalLines;
|
protected boolean showHorizontalLines;
|
||||||
protected boolean showVerticalLines;
|
protected boolean showVerticalLines;
|
||||||
/** @since 1.6 */ protected boolean showTrailingVerticalLine;
|
/** @since 1.6 */ @Styleable protected boolean showTrailingVerticalLine;
|
||||||
protected Dimension intercellSpacing;
|
protected Dimension intercellSpacing;
|
||||||
|
|
||||||
protected Color selectionBackground;
|
@Styleable protected Color selectionBackground;
|
||||||
protected Color selectionForeground;
|
@Styleable protected Color selectionForeground;
|
||||||
protected Color selectionInactiveBackground;
|
@Styleable protected Color selectionInactiveBackground;
|
||||||
protected Color selectionInactiveForeground;
|
@Styleable protected Color selectionInactiveForeground;
|
||||||
|
|
||||||
|
// for FlatTableCellBorder
|
||||||
|
/** @since 2 */ @Styleable protected Insets cellMargins;
|
||||||
|
/** @since 2 */ @Styleable protected Color cellFocusColor;
|
||||||
|
/** @since 2 */ @Styleable protected Boolean showCellFocusIndicator;
|
||||||
|
|
||||||
private boolean oldShowHorizontalLines;
|
private boolean oldShowHorizontalLines;
|
||||||
private boolean oldShowVerticalLines;
|
private boolean oldShowVerticalLines;
|
||||||
private Dimension oldIntercellSpacing;
|
private Dimension oldIntercellSpacing;
|
||||||
|
|
||||||
private PropertyChangeListener propertyChangeListener;
|
private PropertyChangeListener propertyChangeListener;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatTableUI();
|
return new FlatTableUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
@@ -155,6 +175,8 @@ public class FlatTableUI
|
|||||||
selectionInactiveBackground = null;
|
selectionInactiveBackground = null;
|
||||||
selectionInactiveForeground = null;
|
selectionInactiveForeground = null;
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
|
||||||
// restore old show horizontal/vertical lines (if not modified)
|
// restore old show horizontal/vertical lines (if not modified)
|
||||||
if( !showHorizontalLines && oldShowHorizontalLines && !table.getShowHorizontalLines() )
|
if( !showHorizontalLines && oldShowHorizontalLines && !table.getShowHorizontalLines() )
|
||||||
table.setShowHorizontalLines( true );
|
table.setShowHorizontalLines( true );
|
||||||
@@ -171,8 +193,18 @@ public class FlatTableUI
|
|||||||
super.installListeners();
|
super.installListeners();
|
||||||
|
|
||||||
propertyChangeListener = e -> {
|
propertyChangeListener = e -> {
|
||||||
if( FlatClientProperties.COMPONENT_FOCUS_OWNER.equals( e.getPropertyName() ) )
|
switch( e.getPropertyName() ) {
|
||||||
toggleSelectionColors();
|
case FlatClientProperties.COMPONENT_FOCUS_OWNER:
|
||||||
|
toggleSelectionColors();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.STYLE:
|
||||||
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
|
installStyle();
|
||||||
|
table.revalidate();
|
||||||
|
table.repaint();
|
||||||
|
break;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
table.addPropertyChangeListener( propertyChangeListener );
|
table.addPropertyChangeListener( propertyChangeListener );
|
||||||
}
|
}
|
||||||
@@ -206,10 +238,58 @@ public class FlatTableUI
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle() {
|
||||||
|
try {
|
||||||
|
applyStyle( FlatStylingSupport.getResolvedStyle( table, "Table" ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( Object style ) {
|
||||||
|
Color oldSelectionBackground = selectionBackground;
|
||||||
|
Color oldSelectionForeground = selectionForeground;
|
||||||
|
Color oldSelectionInactiveBackground = selectionInactiveBackground;
|
||||||
|
Color oldSelectionInactiveForeground = selectionInactiveForeground;
|
||||||
|
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||||
|
|
||||||
|
// update selection background
|
||||||
|
if( selectionBackground != oldSelectionBackground ) {
|
||||||
|
Color selBg = table.getSelectionBackground();
|
||||||
|
if( selBg == oldSelectionBackground )
|
||||||
|
table.setSelectionBackground( selectionBackground );
|
||||||
|
else if( selBg == oldSelectionInactiveBackground )
|
||||||
|
table.setSelectionBackground( selectionInactiveBackground );
|
||||||
|
}
|
||||||
|
|
||||||
|
// update selection foreground
|
||||||
|
if( selectionForeground != oldSelectionForeground ) {
|
||||||
|
Color selFg = table.getSelectionForeground();
|
||||||
|
if( selFg == oldSelectionForeground )
|
||||||
|
table.setSelectionForeground( selectionForeground );
|
||||||
|
else if( selFg == oldSelectionInactiveForeground )
|
||||||
|
table.setSelectionForeground( selectionInactiveForeground );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, table, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle selection colors from focused to inactive and vice versa.
|
* Toggle selection colors from focused to inactive and vice versa.
|
||||||
*
|
*
|
||||||
* This is not a optimal solution but much easier than rewriting the whole paint methods.
|
* This is not an optimal solution but much easier than rewriting the whole paint methods.
|
||||||
*
|
*
|
||||||
* Using a LaF specific renderer was avoided because often a custom renderer is
|
* Using a LaF specific renderer was avoided because often a custom renderer is
|
||||||
* already used in applications. Then either the inactive colors are not used,
|
* already used in applications. Then either the inactive colors are not used,
|
||||||
@@ -342,4 +422,38 @@ public class FlatTableUI
|
|||||||
? (viewport != rowHeader)
|
? (viewport != rowHeader)
|
||||||
: (viewport == rowHeader || rowHeader == null);
|
: (viewport == rowHeader || rowHeader == null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.3 */
|
||||||
|
@Override
|
||||||
|
public void paintViewport( Graphics g, JComponent c, JViewport viewport ) {
|
||||||
|
int viewportWidth = viewport.getWidth();
|
||||||
|
int viewportHeight = viewport.getHeight();
|
||||||
|
|
||||||
|
// fill viewport background in same color as table background
|
||||||
|
if( viewport.isOpaque() ) {
|
||||||
|
g.setColor( table.getBackground() );
|
||||||
|
g.fillRect( 0, 0, viewportWidth, viewportHeight );
|
||||||
|
}
|
||||||
|
|
||||||
|
// paint alternating empty rows
|
||||||
|
boolean paintOutside = UIManager.getBoolean( "Table.paintOutsideAlternateRows" );
|
||||||
|
Color alternateColor;
|
||||||
|
if( paintOutside && (alternateColor = UIManager.getColor( "Table.alternateRowColor" )) != null ) {
|
||||||
|
g.setColor( alternateColor );
|
||||||
|
|
||||||
|
int rowCount = table.getRowCount();
|
||||||
|
|
||||||
|
// paint alternating empty rows below the table
|
||||||
|
int tableHeight = table.getHeight();
|
||||||
|
if( tableHeight < viewportHeight ) {
|
||||||
|
int tableWidth = table.getWidth();
|
||||||
|
int rowHeight = table.getRowHeight();
|
||||||
|
|
||||||
|
for( int y = tableHeight, row = rowCount; y < viewportHeight; y += rowHeight, row++ ) {
|
||||||
|
if( row % 2 != 0 )
|
||||||
|
g.fillRect( 0, y, tableWidth, rowHeight );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,14 +23,17 @@ import java.awt.Graphics2D;
|
|||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.event.FocusListener;
|
import java.awt.event.FocusListener;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JTextArea;
|
import javax.swing.JTextArea;
|
||||||
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.BasicTextAreaUI;
|
import javax.swing.plaf.basic.BasicTextAreaUI;
|
||||||
import javax.swing.text.JTextComponent;
|
import javax.swing.text.Caret;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JTextArea}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JTextArea}.
|
||||||
@@ -60,17 +63,22 @@ import com.formdev.flatlaf.util.HiDPIUtils;
|
|||||||
*/
|
*/
|
||||||
public class FlatTextAreaUI
|
public class FlatTextAreaUI
|
||||||
extends BasicTextAreaUI
|
extends BasicTextAreaUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
protected int minimumWidth;
|
@Styleable protected int minimumWidth;
|
||||||
protected boolean isIntelliJTheme;
|
protected boolean isIntelliJTheme;
|
||||||
protected Color background;
|
private Color background;
|
||||||
protected Color disabledBackground;
|
@Styleable protected Color disabledBackground;
|
||||||
protected Color inactiveBackground;
|
@Styleable protected Color inactiveBackground;
|
||||||
protected Color focusedBackground;
|
@Styleable protected Color focusedBackground;
|
||||||
|
|
||||||
|
private Color oldDisabledBackground;
|
||||||
|
private Color oldInactiveBackground;
|
||||||
|
|
||||||
private Insets defaultMargin;
|
private Insets defaultMargin;
|
||||||
|
|
||||||
private FocusListener focusListener;
|
private FocusListener focusListener;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatTextAreaUI();
|
return new FlatTextAreaUI();
|
||||||
@@ -80,7 +88,7 @@ public class FlatTextAreaUI
|
|||||||
public void installUI( JComponent c ) {
|
public void installUI( JComponent c ) {
|
||||||
super.installUI( c );
|
super.installUI( c );
|
||||||
|
|
||||||
updateBackground();
|
installStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -105,6 +113,11 @@ public class FlatTextAreaUI
|
|||||||
disabledBackground = null;
|
disabledBackground = null;
|
||||||
inactiveBackground = null;
|
inactiveBackground = null;
|
||||||
focusedBackground = null;
|
focusedBackground = null;
|
||||||
|
|
||||||
|
oldDisabledBackground = null;
|
||||||
|
oldInactiveBackground = null;
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -125,39 +138,55 @@ public class FlatTextAreaUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void propertyChange( PropertyChangeEvent e ) {
|
protected Caret createCaret() {
|
||||||
super.propertyChange( e );
|
return new FlatCaret( null, false );
|
||||||
FlatEditorPaneUI.propertyChange( getComponent(), e );
|
}
|
||||||
|
|
||||||
switch( e.getPropertyName() ) {
|
@Override
|
||||||
case "editable":
|
protected void propertyChange( PropertyChangeEvent e ) {
|
||||||
case "enabled":
|
// invoke updateBackground() before super.propertyChange()
|
||||||
updateBackground();
|
String propertyName = e.getPropertyName();
|
||||||
break;
|
if( "editable".equals( propertyName ) || "enabled".equals( propertyName ) )
|
||||||
|
updateBackground();
|
||||||
|
|
||||||
|
super.propertyChange( e );
|
||||||
|
FlatEditorPaneUI.propertyChange( getComponent(), e, this::installStyle );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle() {
|
||||||
|
try {
|
||||||
|
applyStyle( FlatStylingSupport.getResolvedStyle( getComponent(), "TextArea" ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( Object style ) {
|
||||||
|
oldDisabledBackground = disabledBackground;
|
||||||
|
oldInactiveBackground = inactiveBackground;
|
||||||
|
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||||
|
|
||||||
|
updateBackground();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, getComponent(), key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
|
}
|
||||||
|
|
||||||
private void updateBackground() {
|
private void updateBackground() {
|
||||||
JTextComponent c = getComponent();
|
FlatTextFieldUI.updateBackground( getComponent(), background,
|
||||||
|
disabledBackground, inactiveBackground,
|
||||||
Color background = c.getBackground();
|
oldDisabledBackground, oldInactiveBackground );
|
||||||
if( !(background instanceof UIResource) )
|
|
||||||
return;
|
|
||||||
|
|
||||||
// do not update background if it currently has a unknown color (assigned from outside)
|
|
||||||
if( background != this.background &&
|
|
||||||
background != disabledBackground &&
|
|
||||||
background != inactiveBackground )
|
|
||||||
return;
|
|
||||||
|
|
||||||
Color newBackground = !c.isEnabled()
|
|
||||||
? disabledBackground
|
|
||||||
: (!c.isEditable()
|
|
||||||
? inactiveBackground
|
|
||||||
: this.background);
|
|
||||||
|
|
||||||
if( newBackground != background )
|
|
||||||
c.setBackground( newBackground );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Border for various text components (e.g. {@link javax.swing.JTextField}).
|
* Border for various text components (e.g. {@link javax.swing.JTextField}).
|
||||||
@@ -29,7 +30,10 @@ import javax.swing.UIManager;
|
|||||||
public class FlatTextBorder
|
public class FlatTextBorder
|
||||||
extends FlatBorder
|
extends FlatBorder
|
||||||
{
|
{
|
||||||
protected final int arc = UIManager.getInt( "TextComponent.arc" );
|
@Styleable protected int arc = UIManager.getInt( "TextComponent.arc" );
|
||||||
|
|
||||||
|
// only used via styling (not in UI defaults, but has likewise client properties)
|
||||||
|
/** @since 2 */ @Styleable protected Boolean roundRect;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getArc( Component c ) {
|
protected int getArc( Component c ) {
|
||||||
@@ -37,6 +41,8 @@ public class FlatTextBorder
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Boolean roundRect = FlatUIUtils.isRoundRect( c );
|
Boolean roundRect = FlatUIUtils.isRoundRect( c );
|
||||||
|
if( roundRect == null )
|
||||||
|
roundRect = this.roundRect;
|
||||||
return roundRect != null ? (roundRect ? Short.MAX_VALUE : 0) : arc;
|
return roundRect != null ? (roundRect ? Short.MAX_VALUE : 0) : arc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,33 +16,49 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import static com.formdev.flatlaf.FlatClientProperties.*;
|
||||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
import static com.formdev.flatlaf.util.UIScale.scale;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
|
import java.awt.Cursor;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.FontMetrics;
|
import java.awt.FontMetrics;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
|
import java.awt.LayoutManager;
|
||||||
|
import java.awt.LayoutManager2;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.event.FocusListener;
|
import java.awt.event.FocusListener;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import javax.swing.Icon;
|
||||||
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComboBox;
|
import javax.swing.JComboBox;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JSpinner;
|
import javax.swing.JSpinner;
|
||||||
import javax.swing.JTextField;
|
import javax.swing.JTextField;
|
||||||
|
import javax.swing.JToggleButton;
|
||||||
|
import javax.swing.JToolBar;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.event.DocumentEvent;
|
||||||
|
import javax.swing.event.DocumentListener;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicTextFieldUI;
|
import javax.swing.plaf.basic.BasicTextFieldUI;
|
||||||
import javax.swing.text.Caret;
|
import javax.swing.text.Caret;
|
||||||
|
import javax.swing.text.Document;
|
||||||
import javax.swing.text.JTextComponent;
|
import javax.swing.text.JTextComponent;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.JavaCompatibility;
|
import com.formdev.flatlaf.util.JavaCompatibility;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JTextField}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JTextField}.
|
||||||
@@ -68,6 +84,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault Component.isIntelliJTheme boolean
|
* @uiDefault Component.isIntelliJTheme boolean
|
||||||
* @uiDefault TextField.placeholderForeground Color
|
* @uiDefault TextField.placeholderForeground Color
|
||||||
* @uiDefault TextField.focusedBackground Color optional
|
* @uiDefault TextField.focusedBackground Color optional
|
||||||
|
* @uiDefault TextField.iconTextGap int optional, default is 4
|
||||||
* @uiDefault 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
|
||||||
*
|
*
|
||||||
@@ -75,20 +92,66 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*/
|
*/
|
||||||
public class FlatTextFieldUI
|
public class FlatTextFieldUI
|
||||||
extends BasicTextFieldUI
|
extends BasicTextFieldUI
|
||||||
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
protected int minimumWidth;
|
@Styleable protected int minimumWidth;
|
||||||
protected boolean isIntelliJTheme;
|
protected boolean isIntelliJTheme;
|
||||||
protected Color placeholderForeground;
|
private Color background;
|
||||||
protected Color focusedBackground;
|
@Styleable protected Color disabledBackground;
|
||||||
|
@Styleable protected Color inactiveBackground;
|
||||||
|
@Styleable protected Color placeholderForeground;
|
||||||
|
@Styleable protected Color focusedBackground;
|
||||||
|
/** @since 2 */ @Styleable protected int iconTextGap;
|
||||||
|
|
||||||
|
/** @since 2 */ @Styleable protected Icon leadingIcon;
|
||||||
|
/** @since 2 */ @Styleable protected Icon trailingIcon;
|
||||||
|
/** @since 2 */ protected JComponent leadingComponent;
|
||||||
|
/** @since 2 */ protected JComponent trailingComponent;
|
||||||
|
/** @since 2 */ protected JComponent clearButton;
|
||||||
|
|
||||||
|
// only used via styling (not in UI defaults, but has likewise client properties)
|
||||||
|
/** @since 2 */ @Styleable protected boolean showClearButton;
|
||||||
|
|
||||||
|
private Color oldDisabledBackground;
|
||||||
|
private Color oldInactiveBackground;
|
||||||
|
|
||||||
private Insets defaultMargin;
|
private Insets defaultMargin;
|
||||||
|
|
||||||
private FocusListener focusListener;
|
private FocusListener focusListener;
|
||||||
|
private DocumentListener documentListener;
|
||||||
|
private Map<String, Object> oldStyleValues;
|
||||||
|
private AtomicBoolean borderShared;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatTextFieldUI();
|
return new FlatTextFieldUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
leadingIcon = clientProperty( c, TEXT_FIELD_LEADING_ICON, null, Icon.class );
|
||||||
|
trailingIcon = clientProperty( c, TEXT_FIELD_TRAILING_ICON, null, Icon.class );
|
||||||
|
|
||||||
|
installLeadingComponent();
|
||||||
|
installTrailingComponent();
|
||||||
|
installClearButton();
|
||||||
|
|
||||||
|
installStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uninstallUI( JComponent c ) {
|
||||||
|
uninstallLeadingComponent();
|
||||||
|
uninstallTrailingComponent();
|
||||||
|
uninstallClearButton();
|
||||||
|
|
||||||
|
super.uninstallUI( c );
|
||||||
|
|
||||||
|
leadingIcon = null;
|
||||||
|
trailingIcon = null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
@@ -96,8 +159,12 @@ public class FlatTextFieldUI
|
|||||||
String prefix = getPropertyPrefix();
|
String prefix = getPropertyPrefix();
|
||||||
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
||||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
||||||
|
background = UIManager.getColor( prefix + ".background" );
|
||||||
|
disabledBackground = UIManager.getColor( prefix + ".disabledBackground" );
|
||||||
|
inactiveBackground = UIManager.getColor( prefix + ".inactiveBackground" );
|
||||||
placeholderForeground = UIManager.getColor( prefix + ".placeholderForeground" );
|
placeholderForeground = UIManager.getColor( prefix + ".placeholderForeground" );
|
||||||
focusedBackground = UIManager.getColor( prefix + ".focusedBackground" );
|
focusedBackground = UIManager.getColor( prefix + ".focusedBackground" );
|
||||||
|
iconTextGap = FlatUIUtils.getUIInt( prefix + ".iconTextGap", 4 );
|
||||||
|
|
||||||
defaultMargin = UIManager.getInsets( prefix + ".margin" );
|
defaultMargin = UIManager.getInsets( prefix + ".margin" );
|
||||||
|
|
||||||
@@ -110,9 +177,18 @@ public class FlatTextFieldUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
background = null;
|
||||||
|
disabledBackground = null;
|
||||||
|
inactiveBackground = null;
|
||||||
placeholderForeground = null;
|
placeholderForeground = null;
|
||||||
focusedBackground = null;
|
focusedBackground = null;
|
||||||
|
|
||||||
|
oldDisabledBackground = null;
|
||||||
|
oldInactiveBackground = null;
|
||||||
|
|
||||||
|
oldStyleValues = null;
|
||||||
|
borderShared = null;
|
||||||
|
|
||||||
MigLayoutVisualPadding.uninstall( getComponent() );
|
MigLayoutVisualPadding.uninstall( getComponent() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,6 +207,11 @@ public class FlatTextFieldUI
|
|||||||
|
|
||||||
getComponent().removeFocusListener( focusListener );
|
getComponent().removeFocusListener( focusListener );
|
||||||
focusListener = null;
|
focusListener = null;
|
||||||
|
|
||||||
|
if( documentListener != null ) {
|
||||||
|
getComponent().getDocument().removeDocumentListener( documentListener );
|
||||||
|
documentListener = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -141,29 +222,185 @@ public class FlatTextFieldUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void propertyChange( PropertyChangeEvent e ) {
|
protected void propertyChange( PropertyChangeEvent e ) {
|
||||||
super.propertyChange( e );
|
String propertyName = e.getPropertyName();
|
||||||
propertyChange( getComponent(), e );
|
if( "editable".equals( propertyName ) || "enabled".equals( propertyName ) )
|
||||||
}
|
updateBackground();
|
||||||
|
else
|
||||||
|
super.propertyChange( e );
|
||||||
|
|
||||||
static void propertyChange( JTextComponent c, PropertyChangeEvent e ) {
|
JTextComponent c = getComponent();
|
||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
case FlatClientProperties.PLACEHOLDER_TEXT:
|
case PLACEHOLDER_TEXT:
|
||||||
case FlatClientProperties.COMPONENT_ROUND_RECT:
|
case COMPONENT_ROUND_RECT:
|
||||||
case FlatClientProperties.TEXT_FIELD_PADDING:
|
case OUTLINE:
|
||||||
|
case TEXT_FIELD_PADDING:
|
||||||
c.repaint();
|
c.repaint();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FlatClientProperties.MINIMUM_WIDTH:
|
case MINIMUM_WIDTH:
|
||||||
c.revalidate();
|
c.revalidate();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case STYLE:
|
||||||
|
case STYLE_CLASS:
|
||||||
|
installStyle();
|
||||||
|
c.revalidate();
|
||||||
|
c.repaint();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TEXT_FIELD_LEADING_ICON:
|
||||||
|
leadingIcon = (e.getNewValue() instanceof Icon) ? (Icon) e.getNewValue() : null;
|
||||||
|
c.repaint();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TEXT_FIELD_TRAILING_ICON:
|
||||||
|
trailingIcon = (e.getNewValue() instanceof Icon) ? (Icon) e.getNewValue() : null;
|
||||||
|
c.repaint();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TEXT_FIELD_LEADING_COMPONENT:
|
||||||
|
uninstallLeadingComponent();
|
||||||
|
installLeadingComponent();
|
||||||
|
c.revalidate();
|
||||||
|
c.repaint();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TEXT_FIELD_TRAILING_COMPONENT:
|
||||||
|
uninstallTrailingComponent();
|
||||||
|
installTrailingComponent();
|
||||||
|
c.revalidate();
|
||||||
|
c.repaint();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TEXT_FIELD_SHOW_CLEAR_BUTTON:
|
||||||
|
uninstallClearButton();
|
||||||
|
installClearButton();
|
||||||
|
c.revalidate();
|
||||||
|
c.repaint();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "enabled":
|
||||||
|
case "editable":
|
||||||
|
updateClearButton();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "document":
|
||||||
|
if( documentListener != null ) {
|
||||||
|
if( e.getOldValue() instanceof Document )
|
||||||
|
((Document)e.getOldValue()).removeDocumentListener( documentListener );
|
||||||
|
if( e.getNewValue() instanceof Document )
|
||||||
|
((Document)e.getNewValue()).addDocumentListener( documentListener );
|
||||||
|
|
||||||
|
updateClearButton();
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installDocumentListener() {
|
||||||
|
if( documentListener != null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
documentListener = new FlatDocumentListener();
|
||||||
|
getComponent().getDocument().addDocumentListener( documentListener );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void documentChanged( DocumentEvent e ) {
|
||||||
|
if( clearButton != null )
|
||||||
|
updateClearButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installStyle() {
|
||||||
|
try {
|
||||||
|
applyStyle( FlatStylingSupport.getResolvedStyle( getComponent(), getStyleType() ) );
|
||||||
|
} catch( RuntimeException ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
String getStyleType() {
|
||||||
|
return "TextField";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void applyStyle( Object style ) {
|
||||||
|
oldDisabledBackground = disabledBackground;
|
||||||
|
oldInactiveBackground = inactiveBackground;
|
||||||
|
boolean oldShowClearButton = showClearButton;
|
||||||
|
|
||||||
|
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
|
||||||
|
|
||||||
|
updateBackground();
|
||||||
|
if( showClearButton != oldShowClearButton ) {
|
||||||
|
uninstallClearButton();
|
||||||
|
installClearButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
if( borderShared == null )
|
||||||
|
borderShared = new AtomicBoolean( true );
|
||||||
|
return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, getComponent(), borderShared );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this, getComponent().getBorder() );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateBackground() {
|
||||||
|
updateBackground( getComponent(), background,
|
||||||
|
disabledBackground, inactiveBackground,
|
||||||
|
oldDisabledBackground, oldInactiveBackground );
|
||||||
|
}
|
||||||
|
|
||||||
|
// same functionality as BasicTextUI.updateBackground()
|
||||||
|
static void updateBackground( JTextComponent c, Color background,
|
||||||
|
Color disabledBackground, Color inactiveBackground,
|
||||||
|
Color oldDisabledBackground, Color oldInactiveBackground )
|
||||||
|
{
|
||||||
|
Color oldBackground = c.getBackground();
|
||||||
|
if( !(oldBackground instanceof UIResource) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// do not update background if it currently has an unknown color (assigned from outside)
|
||||||
|
if( oldBackground != background &&
|
||||||
|
oldBackground != disabledBackground &&
|
||||||
|
oldBackground != inactiveBackground &&
|
||||||
|
oldBackground != oldDisabledBackground &&
|
||||||
|
oldBackground != oldInactiveBackground )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Color newBackground = !c.isEnabled()
|
||||||
|
? disabledBackground
|
||||||
|
: (!c.isEditable()
|
||||||
|
? inactiveBackground
|
||||||
|
: background);
|
||||||
|
|
||||||
|
if( newBackground != oldBackground )
|
||||||
|
c.setBackground( newBackground );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintSafely( Graphics g ) {
|
protected void paintSafely( Graphics g ) {
|
||||||
paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground );
|
paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground );
|
||||||
paintPlaceholder( g );
|
paintPlaceholder( g );
|
||||||
|
|
||||||
|
if( hasLeadingIcon() || hasTrailingIcon() )
|
||||||
|
paintIcons( g, new Rectangle( getIconsRect() ) );
|
||||||
|
|
||||||
|
/*debug
|
||||||
|
Rectangle r = getVisibleEditorRect();
|
||||||
|
g.setColor( Color.red );
|
||||||
|
g.drawRect( r.x, r.y, r.width - 1, r.height - 1 );
|
||||||
|
debug*/
|
||||||
|
|
||||||
super.paintSafely( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ) );
|
super.paintSafely( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +414,7 @@ public class FlatTextFieldUI
|
|||||||
// - not opaque and
|
// - not opaque and
|
||||||
// - border is not a flat border and
|
// - border is not a flat border and
|
||||||
// - opaque was explicitly set (to false)
|
// - opaque was explicitly set (to false)
|
||||||
// (same behaviour as in AquaTextFieldUI)
|
// (same behavior as in AquaTextFieldUI)
|
||||||
if( !c.isOpaque() && FlatUIUtils.getOutsideFlatBorder( c ) == null && FlatUIUtils.hasOpaqueBeenExplicitlySet( c ) )
|
if( !c.isOpaque() && FlatUIUtils.getOutsideFlatBorder( c ) == null && FlatUIUtils.hasOpaqueBeenExplicitlySet( c ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -230,15 +467,15 @@ public class FlatTextFieldUI
|
|||||||
JComponent jc = (parent instanceof JComboBox) ? (JComboBox<?>) parent : c;
|
JComponent jc = (parent instanceof JComboBox) ? (JComboBox<?>) parent : c;
|
||||||
|
|
||||||
// get placeholder text
|
// get placeholder text
|
||||||
Object placeholder = jc.getClientProperty( FlatClientProperties.PLACEHOLDER_TEXT );
|
String placeholder = clientProperty( jc, PLACEHOLDER_TEXT, null, String.class );
|
||||||
if( !(placeholder instanceof String) )
|
if( placeholder == null )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// compute placeholder location
|
// compute placeholder location
|
||||||
Rectangle r = getVisibleEditorRect();
|
Rectangle r = getVisibleEditorRect();
|
||||||
FontMetrics fm = c.getFontMetrics( c.getFont() );
|
FontMetrics fm = c.getFontMetrics( c.getFont() );
|
||||||
String clippedPlaceholder = JavaCompatibility.getClippedString( c, fm, (String) placeholder, r.width );
|
String clippedPlaceholder = JavaCompatibility.getClippedString( c, fm, placeholder, r.width );
|
||||||
int x = r.x + (c.getComponentOrientation().isLeftToRight() ? 0 : r.width - fm.stringWidth( clippedPlaceholder ));
|
int x = r.x + (isLeftToRight() ? 0 : r.width - fm.stringWidth( clippedPlaceholder ));
|
||||||
int y = r.y + fm.getAscent() + ((r.height - fm.getHeight()) / 2);
|
int y = r.y + fm.getAscent() + ((r.height - fm.getHeight()) / 2);
|
||||||
|
|
||||||
// paint placeholder
|
// paint placeholder
|
||||||
@@ -246,14 +483,67 @@ public class FlatTextFieldUI
|
|||||||
FlatUIUtils.drawString( c, g, clippedPlaceholder, x, y );
|
FlatUIUtils.drawString( c, g, clippedPlaceholder, x, y );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paints the leading and trailing icons in the given rectangle.
|
||||||
|
* The rectangle is updated by this method so that subclasses can use it
|
||||||
|
* without painting over leading or trailing icons.
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
protected void paintIcons( Graphics g, Rectangle r ) {
|
||||||
|
boolean ltr = isLeftToRight();
|
||||||
|
Icon leftIcon = ltr ? leadingIcon : trailingIcon;
|
||||||
|
Icon rightIcon = ltr ? trailingIcon : leadingIcon;
|
||||||
|
|
||||||
|
// paint left icon
|
||||||
|
if( leftIcon != null ) {
|
||||||
|
int x = r.x;
|
||||||
|
int y = r.y + Math.round( (r.height - leftIcon.getIconHeight()) / 2f );
|
||||||
|
leftIcon.paintIcon( getComponent(), g, x, y );
|
||||||
|
|
||||||
|
// update rectangle so that subclasses can use it
|
||||||
|
int w = leftIcon.getIconWidth() + scale( iconTextGap );
|
||||||
|
r.x += w;
|
||||||
|
r.width -= w;
|
||||||
|
}
|
||||||
|
|
||||||
|
// paint right icon
|
||||||
|
if( rightIcon != null ) {
|
||||||
|
int iconWidth = rightIcon.getIconWidth();
|
||||||
|
int x = r.x + r.width - iconWidth;
|
||||||
|
int y = r.y + Math.round( (r.height - rightIcon.getIconHeight()) / 2f );
|
||||||
|
rightIcon.paintIcon( getComponent(), g, x, y );
|
||||||
|
|
||||||
|
// update rectangle so that subclasses can use it
|
||||||
|
r.width -= iconWidth + scale( iconTextGap );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
return applyMinimumWidth( c, super.getPreferredSize( c ), minimumWidth );
|
return applyMinimumWidth( c, applyExtraSize( super.getPreferredSize( c ) ), minimumWidth );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getMinimumSize( JComponent c ) {
|
public Dimension getMinimumSize( JComponent c ) {
|
||||||
return applyMinimumWidth( c, super.getMinimumSize( c ), minimumWidth );
|
return applyMinimumWidth( c, applyExtraSize( super.getMinimumSize( c ) ), minimumWidth );
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dimension applyExtraSize( Dimension size ) {
|
||||||
|
// add width of leading and trailing icons
|
||||||
|
size.width += getLeadingIconWidth() + getTrailingIconWidth();
|
||||||
|
|
||||||
|
// add width of leading and trailing components
|
||||||
|
for( JComponent comp : getLeadingComponents() ) {
|
||||||
|
if( comp != null && comp.isVisible() )
|
||||||
|
size.width += comp.getPreferredSize().width;
|
||||||
|
}
|
||||||
|
for( JComponent comp : getTrailingComponents() ) {
|
||||||
|
if( comp != null && comp.isVisible() )
|
||||||
|
size.width += comp.getPreferredSize().width;
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Dimension applyMinimumWidth( JComponent c, Dimension size, int minimumWidth ) {
|
private Dimension applyMinimumWidth( JComponent c, Dimension size, int minimumWidth ) {
|
||||||
@@ -283,35 +573,396 @@ public class FlatTextFieldUI
|
|||||||
return margin instanceof UIResource && Objects.equals( margin, defaultMargin );
|
return margin instanceof UIResource && Objects.equals( margin, defaultMargin );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the rectangle used for the root view of the text.
|
||||||
|
* This method is used to place the text.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected Rectangle getVisibleEditorRect() {
|
protected Rectangle getVisibleEditorRect() {
|
||||||
Rectangle r = super.getVisibleEditorRect();
|
Rectangle r = getIconsRect();
|
||||||
if( r != null ) {
|
if( r == null )
|
||||||
// remove padding
|
return null;
|
||||||
Insets padding = getPadding();
|
|
||||||
if( padding != null ) {
|
// remove space needed for leading and trailing icons
|
||||||
r = FlatUIUtils.subtractInsets( r, padding );
|
int leading = getLeadingIconWidth();
|
||||||
r.width = Math.max( r.width, 0 );
|
int trailing = getTrailingIconWidth();
|
||||||
r.height = Math.max( r.height, 0 );
|
if( leading != 0 || trailing != 0 ) {
|
||||||
}
|
boolean ltr = isLeftToRight();
|
||||||
|
int left = ltr ? leading : trailing;
|
||||||
|
int right = ltr ? trailing : leading;
|
||||||
|
r.x += left;
|
||||||
|
r.width -= left + right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove padding
|
||||||
|
Insets padding = getPadding();
|
||||||
|
if( padding != null )
|
||||||
|
r = FlatUIUtils.subtractInsets( r, padding );
|
||||||
|
|
||||||
|
// make sure that width and height are not negative
|
||||||
|
r.width = Math.max( r.width, 0 );
|
||||||
|
r.height = Math.max( r.height, 0 );
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 1.4
|
* Returns the rectangle used to paint leading and trailing icons.
|
||||||
|
* It invokes {@code super.getVisibleEditorRect()} and reduces left and/or
|
||||||
|
* right margin if the text field has leading or trailing icons or components.
|
||||||
|
* Also, the preferred widths of leading and trailing components are removed.
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
*/
|
*/
|
||||||
protected Insets getPadding() {
|
protected Rectangle getIconsRect() {
|
||||||
Object padding = getComponent().getClientProperty( FlatClientProperties.TEXT_FIELD_PADDING );
|
Rectangle r = super.getVisibleEditorRect();
|
||||||
return (padding instanceof Insets) ? UIScale.scale( (Insets) padding ) : null;
|
if( r == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
boolean ltr = isLeftToRight();
|
||||||
|
|
||||||
|
// remove width of leading/trailing components
|
||||||
|
JComponent[] leftComponents = ltr ? getLeadingComponents() : getTrailingComponents();
|
||||||
|
JComponent[] rightComponents = ltr ? getTrailingComponents() : getLeadingComponents();
|
||||||
|
boolean leftVisible = false;
|
||||||
|
boolean rightVisible = false;
|
||||||
|
for( JComponent leftComponent : leftComponents ) {
|
||||||
|
if( leftComponent != null && leftComponent.isVisible() ) {
|
||||||
|
int w = leftComponent.getPreferredSize().width;
|
||||||
|
r.x += w;
|
||||||
|
r.width -= w;
|
||||||
|
leftVisible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for( JComponent rightComponent : rightComponents ) {
|
||||||
|
if( rightComponent != null && rightComponent.isVisible() ) {
|
||||||
|
r.width -= rightComponent.getPreferredSize().width;
|
||||||
|
rightVisible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if a leading/trailing icons (or components) are shown, then the left/right margins are reduced
|
||||||
|
// to the top margin, which places the icon nicely centered on left/right side
|
||||||
|
if( leftVisible || (ltr ? hasLeadingIcon() : hasTrailingIcon()) ) {
|
||||||
|
// reduce left margin
|
||||||
|
Insets margin = getComponent().getMargin();
|
||||||
|
int newLeftMargin = Math.min( margin.left, margin.top );
|
||||||
|
if( newLeftMargin < margin.left ) {
|
||||||
|
int diff = scale( margin.left - newLeftMargin );
|
||||||
|
r.x -= diff;
|
||||||
|
r.width += diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( rightVisible || (ltr ? hasTrailingIcon() : hasLeadingIcon()) ) {
|
||||||
|
// reduce right margin
|
||||||
|
Insets margin = getComponent().getMargin();
|
||||||
|
int newRightMargin = Math.min( margin.right, margin.top );
|
||||||
|
if( newRightMargin < margin.left )
|
||||||
|
r.width += scale( margin.right - newRightMargin );
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure that width and height are not negative
|
||||||
|
r.width = Math.max( r.width, 0 );
|
||||||
|
r.height = Math.max( r.height, 0 );
|
||||||
|
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @since 2 */
|
||||||
* @since 1.4
|
protected boolean hasLeadingIcon() {
|
||||||
*/
|
return leadingIcon != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected boolean hasTrailingIcon() {
|
||||||
|
return trailingIcon != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected int getLeadingIconWidth() {
|
||||||
|
return (leadingIcon != null) ? leadingIcon.getIconWidth() + scale( iconTextGap ) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected int getTrailingIconWidth() {
|
||||||
|
return (trailingIcon != null) ? trailingIcon.getIconWidth() + scale( iconTextGap ) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isLeftToRight() {
|
||||||
|
return getComponent().getComponentOrientation().isLeftToRight();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 1.4 */
|
||||||
|
protected Insets getPadding() {
|
||||||
|
return scale( clientProperty( getComponent(), TEXT_FIELD_PADDING, null, Insets.class ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 1.4 */
|
||||||
protected void scrollCaretToVisible() {
|
protected void scrollCaretToVisible() {
|
||||||
Caret caret = getComponent().getCaret();
|
Caret caret = getComponent().getCaret();
|
||||||
if( caret instanceof FlatCaret )
|
if( caret instanceof FlatCaret )
|
||||||
((FlatCaret)caret).scrollCaretToVisible();
|
((FlatCaret)caret).scrollCaretToVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installLeadingComponent() {
|
||||||
|
JTextComponent c = getComponent();
|
||||||
|
leadingComponent = clientProperty( c, TEXT_FIELD_LEADING_COMPONENT, null, JComponent.class );
|
||||||
|
if( leadingComponent != null ) {
|
||||||
|
prepareLeadingOrTrailingComponent( leadingComponent );
|
||||||
|
installLayout();
|
||||||
|
c.add( leadingComponent );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installTrailingComponent() {
|
||||||
|
JTextComponent c = getComponent();
|
||||||
|
trailingComponent = clientProperty( c, TEXT_FIELD_TRAILING_COMPONENT, null, JComponent.class );
|
||||||
|
if( trailingComponent != null ) {
|
||||||
|
prepareLeadingOrTrailingComponent( trailingComponent );
|
||||||
|
installLayout();
|
||||||
|
c.add( trailingComponent );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void uninstallLeadingComponent() {
|
||||||
|
if( leadingComponent != null ) {
|
||||||
|
getComponent().remove( leadingComponent );
|
||||||
|
leadingComponent = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void uninstallTrailingComponent() {
|
||||||
|
if( trailingComponent != null ) {
|
||||||
|
getComponent().remove( trailingComponent );
|
||||||
|
trailingComponent = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installClearButton() {
|
||||||
|
JTextComponent c = getComponent();
|
||||||
|
if( clientPropertyBoolean( c, TEXT_FIELD_SHOW_CLEAR_BUTTON, showClearButton ) ) {
|
||||||
|
clearButton = createClearButton();
|
||||||
|
updateClearButton();
|
||||||
|
installDocumentListener();
|
||||||
|
installLayout();
|
||||||
|
c.add( clearButton );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void uninstallClearButton() {
|
||||||
|
if( clearButton != null ) {
|
||||||
|
getComponent().remove( clearButton );
|
||||||
|
clearButton = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected JComponent createClearButton() {
|
||||||
|
JButton button = new JButton();
|
||||||
|
button.setName( "TextField.clearButton" );
|
||||||
|
button.putClientProperty( STYLE_CLASS, "clearButton" );
|
||||||
|
button.putClientProperty( BUTTON_TYPE, BUTTON_TYPE_TOOLBAR_BUTTON );
|
||||||
|
button.setCursor( Cursor.getDefaultCursor() );
|
||||||
|
button.addActionListener( e -> clearButtonClicked() );
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
@SuppressWarnings( "unchecked" )
|
||||||
|
protected void clearButtonClicked() {
|
||||||
|
JTextComponent c = getComponent();
|
||||||
|
Object callback = c.getClientProperty( TEXT_FIELD_CLEAR_CALLBACK );
|
||||||
|
if( callback instanceof Runnable )
|
||||||
|
((Runnable)callback).run();
|
||||||
|
else if( callback instanceof Consumer )
|
||||||
|
((Consumer<JTextComponent>)callback).accept( c );
|
||||||
|
else
|
||||||
|
c.setText( "" );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void updateClearButton() {
|
||||||
|
if( clearButton == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
JTextComponent c = getComponent();
|
||||||
|
boolean visible = c.isEnabled() && c.isEditable() && c.getDocument().getLength() > 0;
|
||||||
|
if( visible != clearButton.isVisible() ) {
|
||||||
|
clearButton.setVisible( visible );
|
||||||
|
c.revalidate();
|
||||||
|
c.repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns components placed at the leading side of the text field.
|
||||||
|
* The returned array may contain {@code null}.
|
||||||
|
* The default implementation returns {@link #leadingComponent}.
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
protected JComponent[] getLeadingComponents() {
|
||||||
|
return new JComponent[] { leadingComponent };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns components placed at the trailing side of the text field.
|
||||||
|
* The returned array may contain {@code null}.
|
||||||
|
* The default implementation returns {@link #trailingComponent} and {@link #clearButton}.
|
||||||
|
* <p>
|
||||||
|
* <strong>Note</strong>: The components in the array must be in reverse (visual) order.
|
||||||
|
*
|
||||||
|
* @since 2
|
||||||
|
*/
|
||||||
|
protected JComponent[] getTrailingComponents() {
|
||||||
|
return new JComponent[] { trailingComponent, clearButton };
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void prepareLeadingOrTrailingComponent( JComponent c ) {
|
||||||
|
c.putClientProperty( STYLE_CLASS, "inTextField" );
|
||||||
|
|
||||||
|
if( c instanceof JButton || c instanceof JToggleButton ) {
|
||||||
|
c.putClientProperty( BUTTON_TYPE, BUTTON_TYPE_TOOLBAR_BUTTON );
|
||||||
|
|
||||||
|
if( !c.isCursorSet() )
|
||||||
|
c.setCursor( Cursor.getDefaultCursor() );
|
||||||
|
} else if( c instanceof JToolBar ) {
|
||||||
|
for( Component child : c.getComponents() ) {
|
||||||
|
if( child instanceof JComponent )
|
||||||
|
((JComponent)child).putClientProperty( STYLE_CLASS, "inTextField" );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !c.isCursorSet() )
|
||||||
|
c.setCursor( Cursor.getDefaultCursor() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2 */
|
||||||
|
protected void installLayout() {
|
||||||
|
JTextComponent c = getComponent();
|
||||||
|
LayoutManager oldLayout = c.getLayout();
|
||||||
|
if( !(oldLayout instanceof FlatTextFieldLayout) )
|
||||||
|
c.setLayout( new FlatTextFieldLayout( oldLayout ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class FlatTextFieldLayout ------------------------------------------
|
||||||
|
|
||||||
|
private class FlatTextFieldLayout
|
||||||
|
implements LayoutManager2, UIResource
|
||||||
|
{
|
||||||
|
private final LayoutManager delegate;
|
||||||
|
|
||||||
|
FlatTextFieldLayout( LayoutManager delegate ) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addLayoutComponent( String name, Component comp ) {
|
||||||
|
if( delegate != null )
|
||||||
|
delegate.addLayoutComponent( name, comp );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeLayoutComponent( Component comp ) {
|
||||||
|
if( delegate != null )
|
||||||
|
delegate.removeLayoutComponent( comp );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension preferredLayoutSize( Container parent ) {
|
||||||
|
return (delegate != null) ? delegate.preferredLayoutSize( parent ) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension minimumLayoutSize( Container parent ) {
|
||||||
|
return (delegate != null) ? delegate.minimumLayoutSize( parent ) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void layoutContainer( Container parent ) {
|
||||||
|
if( delegate != null )
|
||||||
|
delegate.layoutContainer( parent );
|
||||||
|
|
||||||
|
int ow = FlatUIUtils.getBorderFocusAndLineWidth( getComponent() );
|
||||||
|
int h = parent.getHeight() - ow - ow;
|
||||||
|
boolean ltr = isLeftToRight();
|
||||||
|
JComponent[] leftComponents = ltr ? getLeadingComponents() : getTrailingComponents();
|
||||||
|
JComponent[] rightComponents = ltr ? getTrailingComponents() : getLeadingComponents();
|
||||||
|
|
||||||
|
// layout left components
|
||||||
|
int x = ow;
|
||||||
|
for( JComponent leftComponent : leftComponents ) {
|
||||||
|
if( leftComponent != null && leftComponent.isVisible() ) {
|
||||||
|
int cw = leftComponent.getPreferredSize().width;
|
||||||
|
leftComponent.setBounds( x, ow, cw, h );
|
||||||
|
x += cw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// layout right components
|
||||||
|
x = parent.getWidth() - ow;
|
||||||
|
for( JComponent rightComponent : rightComponents ) {
|
||||||
|
if( rightComponent != null && rightComponent.isVisible() ) {
|
||||||
|
int cw = rightComponent.getPreferredSize().width;
|
||||||
|
x -= cw;
|
||||||
|
rightComponent.setBounds( x, ow, cw, h );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addLayoutComponent( Component comp, Object constraints ) {
|
||||||
|
if( delegate instanceof LayoutManager2 )
|
||||||
|
((LayoutManager2)delegate).addLayoutComponent( comp, constraints );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension maximumLayoutSize( Container target ) {
|
||||||
|
return (delegate instanceof LayoutManager2) ? ((LayoutManager2)delegate).maximumLayoutSize( target ) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getLayoutAlignmentX( Container target ) {
|
||||||
|
return (delegate instanceof LayoutManager2) ? ((LayoutManager2)delegate).getLayoutAlignmentX( target ) : 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getLayoutAlignmentY( Container target ) {
|
||||||
|
return (delegate instanceof LayoutManager2) ? ((LayoutManager2)delegate).getLayoutAlignmentY( target ) : 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidateLayout( Container target ) {
|
||||||
|
if( delegate instanceof LayoutManager2 )
|
||||||
|
((LayoutManager2)delegate).invalidateLayout( target );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class FlatDocumentListener -----------------------------------------
|
||||||
|
|
||||||
|
private class FlatDocumentListener
|
||||||
|
implements DocumentListener
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void insertUpdate( DocumentEvent e ) {
|
||||||
|
documentChanged( e );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeUpdate( DocumentEvent e ) {
|
||||||
|
documentChanged( e );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changedUpdate( DocumentEvent e ) {
|
||||||
|
documentChanged( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user