mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2025-12-06 14:00:55 +03:00
Compare commits
411 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13a418f74e | ||
|
|
5c56dbfed6 | ||
|
|
0d2f37e1da | ||
|
|
0494c2161c | ||
|
|
635a620439 | ||
|
|
0a7c76ec72 | ||
|
|
9ad8fb38e8 | ||
|
|
1dbe968952 | ||
|
|
460b6492cb | ||
|
|
67b0faa9ae | ||
|
|
5553425a1a | ||
|
|
8ff516e43a | ||
|
|
b6207bafde | ||
|
|
b9f43fd560 | ||
|
|
c617d9f569 | ||
|
|
9efb9761c6 | ||
|
|
03f9115fbf | ||
|
|
a2859cedb5 | ||
|
|
cdee0594f8 | ||
|
|
808833d749 | ||
|
|
581c64b601 | ||
|
|
c953ff84d0 | ||
|
|
96cd207df3 | ||
|
|
7a75f62a6a | ||
|
|
61e5fe58c2 | ||
|
|
1a3baba702 | ||
|
|
58dc14bb46 | ||
|
|
a5b7e04943 | ||
|
|
22f2aa5475 | ||
|
|
d4e9cb12be | ||
|
|
75da361480 | ||
|
|
7488bcb7b0 | ||
|
|
1b1a9be107 | ||
|
|
db2f94aa53 | ||
|
|
810146b993 | ||
|
|
93091662ab | ||
|
|
d349227fbf | ||
|
|
c9423e3aa8 | ||
|
|
b9737ca4f1 | ||
|
|
4b4990635d | ||
|
|
afaa2c8c78 | ||
|
|
f506ef0d4f | ||
|
|
d30fe66cac | ||
|
|
270e998e86 | ||
|
|
c395386c05 | ||
|
|
4f1207b0db | ||
|
|
dc3878e290 | ||
|
|
be2876149d | ||
|
|
52bae9dfb0 | ||
|
|
bb636bac3f | ||
|
|
502b18fa86 | ||
|
|
e0a5450264 | ||
|
|
5ffb23c37f | ||
|
|
b75f22b7bd | ||
|
|
35fa3197c8 | ||
|
|
f03725ae36 | ||
|
|
2b640e2129 | ||
|
|
2a983f5c03 | ||
|
|
cacc5daa14 | ||
|
|
593502287d | ||
|
|
7a9bdf9be0 | ||
|
|
170c22c5ed | ||
|
|
7e8fa58bd7 | ||
|
|
046200625c | ||
|
|
710ed55152 | ||
|
|
ce527329a6 | ||
|
|
b455dd41ab | ||
|
|
b47ed94f40 | ||
|
|
f1351a2093 | ||
|
|
c1c5e81df0 | ||
|
|
8e3c8ba6c5 | ||
|
|
dfe4404a17 | ||
|
|
b3fb63c9f5 | ||
|
|
9db3dfff26 | ||
|
|
3c9051e7de | ||
|
|
798a6d061c | ||
|
|
19afbe99d9 | ||
|
|
4715d8d16c | ||
|
|
193da2bc4d | ||
|
|
799f8efe22 | ||
|
|
f6062e1ec4 | ||
|
|
c790778a46 | ||
|
|
4344f1b3a0 | ||
|
|
d520b30500 | ||
|
|
11c02e5f50 | ||
|
|
aa4c6ee9da | ||
|
|
98f8557392 | ||
|
|
6f6a860887 | ||
|
|
38695e9e16 | ||
|
|
242c478cb3 | ||
|
|
f003e835bd | ||
|
|
267defb321 | ||
|
|
4392c7627b | ||
|
|
fde65b2730 | ||
|
|
e908362f0a | ||
|
|
a40b837634 | ||
|
|
b391465fbf | ||
|
|
bad0428f5b | ||
|
|
97018df2f9 | ||
|
|
9d84501bc8 | ||
|
|
e9fb2b3fdc | ||
|
|
f60250fd8a | ||
|
|
5fc3cae28a | ||
|
|
e7935be85b | ||
|
|
89363b2ea1 | ||
|
|
e84390ee46 | ||
|
|
65a0f467ae | ||
|
|
4afb150106 | ||
|
|
0f6702217e | ||
|
|
13a0097858 | ||
|
|
01c830ad92 | ||
|
|
dce4f4623c | ||
|
|
d530624362 | ||
|
|
2e878b62d1 | ||
|
|
d27a246dfe | ||
|
|
778def118a | ||
|
|
bc5587477b | ||
|
|
03a775cd31 | ||
|
|
875083a924 | ||
|
|
f6fc925c9e | ||
|
|
74e1972781 | ||
|
|
2f5c54bb49 | ||
|
|
465798ee3d | ||
|
|
425f3acced | ||
|
|
546382e471 | ||
|
|
7e91d78633 | ||
|
|
136e1e4e30 | ||
|
|
f5f6850172 | ||
|
|
28cdde3f17 | ||
|
|
29b801e13d | ||
|
|
1435469ee5 | ||
|
|
4a0bd2c09f | ||
|
|
f8d67f863f | ||
|
|
0291dd5416 | ||
|
|
9014435d4d | ||
|
|
07ad467c73 | ||
|
|
35e23574cf | ||
|
|
9b62b8395f | ||
|
|
45e7022deb | ||
|
|
32dce16363 | ||
|
|
e34b5eafe1 | ||
|
|
4e1e749094 | ||
|
|
ede9293377 | ||
|
|
9101324a1f | ||
|
|
4b844353ee | ||
|
|
2134c19c58 | ||
|
|
c974784ebb | ||
|
|
5eb6961023 | ||
|
|
07cbd8b97b | ||
|
|
09c7f15364 | ||
|
|
b879b393ad | ||
|
|
e4503c2a54 | ||
|
|
7e2d02b997 | ||
|
|
d286550572 | ||
|
|
4e44e25d30 | ||
|
|
9fef2f9d05 | ||
|
|
04f1f5921d | ||
|
|
f9ecffb850 | ||
|
|
c9b5274ccf | ||
|
|
d209d47b9e | ||
|
|
21baaf810c | ||
|
|
95b4366270 | ||
|
|
c3adadfe2f | ||
|
|
adf7753617 | ||
|
|
d491847754 | ||
|
|
6afc747790 | ||
|
|
ff46935448 | ||
|
|
78c2f98f1f | ||
|
|
91be9aa2fe | ||
|
|
13e5da584f | ||
|
|
1762e0b7a6 | ||
|
|
05240abfe0 | ||
|
|
b515e8be04 | ||
|
|
24bc7fb0b5 | ||
|
|
0d2e1e6d18 | ||
|
|
f23c523baf | ||
|
|
76fee29f5b | ||
|
|
ec77746a43 | ||
|
|
92cd6f8f34 | ||
|
|
e7d2b5cbb6 | ||
|
|
4d175da3a0 | ||
|
|
5f047ddda9 | ||
|
|
ccca6fe88e | ||
|
|
a1f18e1ec9 | ||
|
|
afdaf7a0a5 | ||
|
|
62f0ef19f4 | ||
|
|
b736502c27 | ||
|
|
2be2dae3d6 | ||
|
|
aefe104ca4 | ||
|
|
3e6bce9cec | ||
|
|
a6394cac38 | ||
|
|
1e09ddfc93 | ||
|
|
664f5c98e9 | ||
|
|
c7bfd2ea82 | ||
|
|
9ce7ddd088 | ||
|
|
cca8d427d2 | ||
|
|
aa9263a2e7 | ||
|
|
5eaebde437 | ||
|
|
7f15f557a5 | ||
|
|
b459715cb5 | ||
|
|
bfede219d0 | ||
|
|
ef21efecf5 | ||
|
|
2bfbc9dc12 | ||
|
|
c3a1b45546 | ||
|
|
b72508f920 | ||
|
|
22bb80218d | ||
|
|
873a7e8572 | ||
|
|
0c5016fe89 | ||
|
|
607b084697 | ||
|
|
9d8ffec276 | ||
|
|
15f08e9b7c | ||
|
|
08aa6b1894 | ||
|
|
06b02c4f7c | ||
|
|
b56acd271f | ||
|
|
b24e2db59e | ||
|
|
f215356629 | ||
|
|
069a7c809c | ||
|
|
883b4d735a | ||
|
|
9f39b269bb | ||
|
|
36c405c708 | ||
|
|
bc7c68ebe4 | ||
|
|
6c502ad4c5 | ||
|
|
100aa0b621 | ||
|
|
8e42b19934 | ||
|
|
1a456d5d68 | ||
|
|
e83c26a76a | ||
|
|
6e7c2a616b | ||
|
|
0699454df8 | ||
|
|
92c110548a | ||
|
|
ca88023560 | ||
|
|
12fc2299ec | ||
|
|
2089c77b84 | ||
|
|
4f5a3e8d8b | ||
|
|
95522846ac | ||
|
|
614ac956de | ||
|
|
c228362c01 | ||
|
|
f6c5db07f2 | ||
|
|
78e7839213 | ||
|
|
f7be12df67 | ||
|
|
a1d1e221ae | ||
|
|
0a4dc54fb9 | ||
|
|
b8c7801365 | ||
|
|
a7099c039f | ||
|
|
a4d2d347e3 | ||
|
|
829c537fd3 | ||
|
|
28437f99cf | ||
|
|
c1402d85e1 | ||
|
|
32e071ab89 | ||
|
|
01125e030e | ||
|
|
b43278439a | ||
|
|
7a445aabd7 | ||
|
|
86a4f306c6 | ||
|
|
0e523f1193 | ||
|
|
9041a16b22 | ||
|
|
596ff3382d | ||
|
|
cbd80252ed | ||
|
|
bcd7a7e3dd | ||
|
|
9c98f1a553 | ||
|
|
c3d214aa23 | ||
|
|
d301f6e104 | ||
|
|
eb9fa585f7 | ||
|
|
16c6ffb032 | ||
|
|
7858e42e37 | ||
|
|
30132aa6b0 | ||
|
|
bf4d4cc2c5 | ||
|
|
9f0554c883 | ||
|
|
218ea6ce47 | ||
|
|
0baae7da8b | ||
|
|
fb4576fc1b | ||
|
|
16f3f9e6ff | ||
|
|
fee7cf6265 | ||
|
|
2dd75c4a64 | ||
|
|
d2f46cd0b5 | ||
|
|
10914083e6 | ||
|
|
5d167da55e | ||
|
|
b381e20e57 | ||
|
|
380dae1017 | ||
|
|
fb15cdc546 | ||
|
|
a525fe29db | ||
|
|
475cc9a9a5 | ||
|
|
264d6fbd6d | ||
|
|
2826cf379b | ||
|
|
d28745df29 | ||
|
|
94f9e4a1be | ||
|
|
ec547e1d65 | ||
|
|
61d4eb649b | ||
|
|
52ad15e375 | ||
|
|
ff00a6c0f0 | ||
|
|
9b1ebd658d | ||
|
|
f842530537 | ||
|
|
63077bbb19 | ||
|
|
4dad337377 | ||
|
|
10a965d765 | ||
|
|
3e9c9c9066 | ||
|
|
8b5a738e65 | ||
|
|
2c041dce3a | ||
|
|
ef151c68f4 | ||
|
|
52feaac92a | ||
|
|
cddbb3d7d4 | ||
|
|
42764550e6 | ||
|
|
6ee737b314 | ||
|
|
f460ef7685 | ||
|
|
9977bcb468 | ||
|
|
7437d984c7 | ||
|
|
5cd0b2403c | ||
|
|
a372da22f3 | ||
|
|
8b10d3ba5a | ||
|
|
a8b15c6a12 | ||
|
|
23bac7e5fd | ||
|
|
b82ee2ef61 | ||
|
|
b7761f4b71 | ||
|
|
f9a4f9771c | ||
|
|
d2acb2c98a | ||
|
|
bf0685cee2 | ||
|
|
d60bd5df14 | ||
|
|
73b6ca3762 | ||
|
|
8262793751 | ||
|
|
5ec7848fb0 | ||
|
|
450fc123ff | ||
|
|
3802c64be3 | ||
|
|
7bf1b26812 | ||
|
|
6c18431a30 | ||
|
|
a49d20249f | ||
|
|
ad384acd57 | ||
|
|
69851b7f3a | ||
|
|
92b53bf0df | ||
|
|
93e0496fd2 | ||
|
|
5151951f46 | ||
|
|
58dbccec2d | ||
|
|
90de14d013 | ||
|
|
f1792e46c6 | ||
|
|
84e9c36280 | ||
|
|
2ef6a2c3c9 | ||
|
|
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 |
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -20,7 +20,9 @@
|
||||
*.gif binary
|
||||
*.jar binary
|
||||
*.lib binary
|
||||
*.otf binary
|
||||
*.png binary
|
||||
*.sketch binary
|
||||
*.so binary
|
||||
*.ttf binary
|
||||
*.zip binary
|
||||
|
||||
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
custom: https://www.formdev.com/flatlaf/sponsor/
|
||||
41
.github/workflows/ci.yml
vendored
41
.github/workflows/ci.yml
vendored
@@ -1,4 +1,5 @@
|
||||
# https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
|
||||
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
|
||||
# https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle
|
||||
|
||||
name: CI
|
||||
|
||||
@@ -8,9 +9,6 @@ on:
|
||||
- '*'
|
||||
tags:
|
||||
- '[0-9]*'
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -26,30 +24,39 @@ jobs:
|
||||
- 8
|
||||
- 11 # LTS
|
||||
- 17 # LTS
|
||||
toolchain: [""]
|
||||
include:
|
||||
- java: 17
|
||||
toolchain: 21 # latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
if: matrix.java == '8'
|
||||
|
||||
- name: Setup Java ${{ matrix.java }}
|
||||
uses: actions/setup-java@v2
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: ${{ matrix.java }}
|
||||
distribution: adopt # Java 8 and 11 are pre-installed on ubuntu-latest
|
||||
distribution: temurin # Java 8, 11 and 17 are pre-installed on ubuntu-latest
|
||||
cache: gradle
|
||||
|
||||
- name: Check with Error Prone
|
||||
if: matrix.java == '11'
|
||||
run: ./gradlew errorprone clean -Dtoolchain=${{ matrix.toolchain }}
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
run: ./gradlew build -Dtoolchain=${{ matrix.toolchain }}
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
if: matrix.java == '11'
|
||||
with:
|
||||
name: FlatLaf-build-artifacts
|
||||
path: |
|
||||
flatlaf-*/build/libs
|
||||
flatlaf-*/flatlaf-*/build/libs
|
||||
!**/*-javadoc.jar
|
||||
!**/*-sources.jar
|
||||
|
||||
@@ -63,17 +70,17 @@ jobs:
|
||||
github.repository == 'JFormDesigner/FlatLaf'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Java 11
|
||||
uses: actions/setup-java@v2
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: 11
|
||||
distribution: adopt # pre-installed on ubuntu-latest
|
||||
distribution: temurin # pre-installed on ubuntu-latest
|
||||
cache: gradle
|
||||
|
||||
- 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 -PskipFonts -Dorg.gradle.internal.publish.checksums.insecure=true -Dorg.gradle.parallel=false
|
||||
env:
|
||||
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
|
||||
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
|
||||
@@ -99,17 +106,17 @@ jobs:
|
||||
github.repository == 'JFormDesigner/FlatLaf'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Java 11
|
||||
uses: actions/setup-java@v2
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: 11
|
||||
distribution: adopt # pre-installed on ubuntu-latest
|
||||
distribution: temurin # pre-installed on ubuntu-latest
|
||||
cache: gradle
|
||||
|
||||
- name: Release a new stable version to Maven Central
|
||||
run: ./gradlew publish :flatlaf-demo:build :flatlaf-theme-editor:build -Drelease=true
|
||||
run: ./gradlew publish :flatlaf-demo:build :flatlaf-theme-editor:build -PskipFonts -Prelease -Dorg.gradle.parallel=false
|
||||
env:
|
||||
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
|
||||
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
|
||||
|
||||
60
.github/workflows/fonts.yml
vendored
Normal file
60
.github/workflows/fonts.yml
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
|
||||
# https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle
|
||||
|
||||
name: Fonts
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
tags:
|
||||
- 'fonts/*-[0-9]*'
|
||||
paths:
|
||||
- 'flatlaf-fonts/**'
|
||||
- '.github/workflows/fonts.yml'
|
||||
- 'gradle/wrapper/gradle-wrapper.properties'
|
||||
|
||||
jobs:
|
||||
Fonts:
|
||||
strategy:
|
||||
matrix:
|
||||
font:
|
||||
- inter
|
||||
- jetbrains-mono
|
||||
- roboto
|
||||
- roboto-mono
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.event_name == 'push' &&
|
||||
github.repository == 'JFormDesigner/FlatLaf'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Java 11
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: 11
|
||||
distribution: temurin # pre-installed on ubuntu-latest
|
||||
cache: gradle
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew :flatlaf-fonts-${{ matrix.font }}:build
|
||||
if: startsWith( github.ref, format( 'refs/tags/fonts/{0}-', matrix.font ) ) != true
|
||||
|
||||
- name: Publish snapshot to oss.sonatype.org
|
||||
run: ./gradlew :flatlaf-fonts-${{ matrix.font }}:publish -Dorg.gradle.internal.publish.checksums.insecure=true
|
||||
env:
|
||||
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
|
||||
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
|
||||
if: github.ref == 'refs/heads/main' || startsWith( github.ref, 'refs/heads/develop-' )
|
||||
|
||||
- name: Release a new stable version to Maven Central
|
||||
run: ./gradlew :flatlaf-fonts-${{ matrix.font }}:build :flatlaf-fonts-${{ matrix.font }}:publish -Prelease
|
||||
env:
|
||||
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
|
||||
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
|
||||
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
|
||||
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
|
||||
if: startsWith( github.ref, format( 'refs/tags/fonts/{0}-', matrix.font ) )
|
||||
39
.github/workflows/natives.yml
vendored
39
.github/workflows/natives.yml
vendored
@@ -1,4 +1,5 @@
|
||||
# https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
|
||||
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
|
||||
# https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle
|
||||
|
||||
name: Native Libraries
|
||||
|
||||
@@ -9,41 +10,41 @@ on:
|
||||
tags:
|
||||
- '[0-9]*'
|
||||
paths:
|
||||
- 'flatlaf-natives/flatlaf-natives-windows/**'
|
||||
- '.github/workflows/natives.yml'
|
||||
- 'gradle/wrapper/gradle-wrapper.properties'
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
paths:
|
||||
- 'flatlaf-natives/flatlaf-natives-windows/**'
|
||||
- 'flatlaf-natives/**'
|
||||
- '.github/workflows/natives.yml'
|
||||
- 'gradle/wrapper/gradle-wrapper.properties'
|
||||
|
||||
jobs:
|
||||
Windows:
|
||||
runs-on: windows-latest
|
||||
Natives:
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- windows
|
||||
- ubuntu
|
||||
|
||||
runs-on: ${{ matrix.os }}-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
|
||||
- name: Setup Java 11
|
||||
uses: actions/setup-java@v2
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: 11
|
||||
distribution: adopt
|
||||
distribution: temurin
|
||||
cache: gradle
|
||||
|
||||
- name: Build with Gradle
|
||||
# --no-daemon is necessary on Windows otherwise caching Gradle would fail with:
|
||||
# --no-daemon is necessary on Windows otherwise caching Gradle would fail with:
|
||||
# tar.exe: Couldn't open ~/.gradle/caches/modules-2/modules-2.lock: Permission denied
|
||||
run: ./gradlew :flatlaf-natives-windows:build --no-daemon
|
||||
run: ./gradlew build-natives --no-daemon
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: FlatLaf-natives-windows-build-artifacts
|
||||
name: FlatLaf-natives-build-artifacts-${{ matrix.os }}
|
||||
path: |
|
||||
flatlaf-natives/flatlaf-natives-windows/build
|
||||
flatlaf-core/src/main/resources/com/formdev/flatlaf/natives
|
||||
flatlaf-natives/flatlaf-natives-*/build
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -9,5 +9,7 @@ out/
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
*.xcuserstate
|
||||
*.xcworkspacedata
|
||||
.vs/
|
||||
.vscode/
|
||||
|
||||
441
CHANGELOG.md
441
CHANGELOG.md
@@ -1,6 +1,445 @@
|
||||
FlatLaf Change Log
|
||||
==================
|
||||
|
||||
## 3.2.3
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- Popup: Popups that request focus were not shown on Linux with Wayland and Java 21.
|
||||
(issue #752)
|
||||
|
||||
|
||||
## 3.2.2
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- Button: Fixed painting icon and text at wrong location when using HTML text,
|
||||
left/right vertical alignment and running in Java 19+. (issue #746)
|
||||
- CheckBox and RadioButton: Fixed cut off right side when border is removed and
|
||||
horizontal alignment is set to `right`. (issue #734)
|
||||
- TabbedPane: Fixed NPE when using focusable component as tab component and
|
||||
switching theme. (issue #745)
|
||||
|
||||
|
||||
## 3.2.1
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- Fixed memory leak in
|
||||
`MultiResolutionImageSupport.create(int,Dimension[],Function<Dimension,Image>)`,
|
||||
which caches images created by the producer function. Used by
|
||||
`FlatSVGIcon.getImage()` and `FlatSVGUtils.createWindowIconImages()`. If you
|
||||
use one of these methods, it is **strongly recommended** to upgrade to this
|
||||
version, because if the returned image is larger and painted very often it may
|
||||
result in an out-of-memory situation. (issue #726)
|
||||
- FileChooser: Fixed occasional NPE in `FlatShortcutsPanel` on Windows. (issue
|
||||
#718)
|
||||
- TextField: Fixed placeholder text painting, which did not respect horizontal
|
||||
alignment property of `JTextField`. (issue #721)
|
||||
- Popup: Fixed drop shadow if popup overlaps a heavyweight component. (Windows
|
||||
10 only; issue #626)
|
||||
|
||||
|
||||
## 3.2
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- TabbedPane: Support rounded underline selection and rounded card tabs. (PR
|
||||
#703)
|
||||
- FlatLaf window decorations:
|
||||
- Support for Windows on ARM 64-bit. (issue #443, PR #707)
|
||||
- Support toolbox-style "small" window title bar. (issue #659, PR #702)
|
||||
- Extras: Class `FlatSVGIcon` now uses [JSVG](https://github.com/weisJ/jsvg)
|
||||
library (instead of svgSalamander) for rendering. JSVG provides improved SVG
|
||||
rendering and uses less memory compared to svgSalamander. (PR #684)
|
||||
- ComboBox: Improved location of selected item in popup if list is large and
|
||||
scrollable.
|
||||
- FileChooser: Show localized text for all locales supported by Java's Metal
|
||||
look and feel. (issue #680)
|
||||
- Added system property `flatlaf.useNativeLibrary` to allow disabling loading of
|
||||
FlatLaf native library. (issue #674)
|
||||
- IntelliJ Themes:
|
||||
- Reduced memory footprint by releasing Json data and ignoring IntelliJ UI
|
||||
properties that are not used in FlatLaf.
|
||||
- Updated "Hiberbee Dark" and "Gradianto" themes.
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- Styling: Fixed scaling of some styling properties (`rowHeight` for Table and
|
||||
Tree; `iconTextGap` for Button, CheckBox and RadioButton). (issue #682)
|
||||
- Fixed `IllegalComponentStateException` when invoker is not showing in
|
||||
`SubMenuUsabilityHelper`. (issue #692)
|
||||
- macOS themes: Changing `@accentColor` variable in FlatLaf properties files did
|
||||
not change all accent related colors for all components.
|
||||
- IntelliJ Themes:
|
||||
- "Light Owl" theme: Fixed wrong (unreadable) text color in selected menu
|
||||
items, selected text in text components, and selection in ComboBox popup
|
||||
list. (issue #687)
|
||||
- "Gradianto Midnight Blue" theme: Fixed color of ScrollBar track, which was
|
||||
not visible. (issue #686)
|
||||
- "Monocai" theme: Fixed unreadable text color of default buttons. (issue
|
||||
#693)
|
||||
- "Vuesion" theme: Fixed foreground colors of disabled text.
|
||||
- "Material UI Lite" themes: Fixed non-editable ComboBox button background.
|
||||
- CheckBox and RadioButton: Fixed unselected icon colors for themes "Atom One
|
||||
Light", "Cyan Light", "GitHub", "Light Owl", "Material Lighter" and
|
||||
"Solarized Light".
|
||||
- TabbedPane: Fixed focused tab background color for themes "Arc *", "Material
|
||||
Design Dark", "Monocai", "One Dark", "Spacegray" and "Xcode-Dark". (issue
|
||||
#697)
|
||||
- TextComponents, ComboBox and Spinner: Fixed background colors of enabled
|
||||
text components, to distinguish from disabled, for themes "Carbon", "Cobalt
|
||||
2", "Gradianto *", "Gruvbox *", "Monocai", "Spacegray", "Vuesion",
|
||||
"Xcode-Dark", "GitHub", and "Light Owl". (issue #528)
|
||||
- Fixed wrong disabled text colors in "Dark Flat", "Hiberbee Dark", "Light
|
||||
Flat", "Nord", "Solarized Dark" and "Solarized Light" themes.
|
||||
- Fixed colors for selection background/foreground, Separator, Slider track
|
||||
and ProgressBar background in various themes.
|
||||
- Native Windows libraries: Fixed crash when running in Java 8 and newer Java
|
||||
version is installed in `PATH` environment variable and using class
|
||||
`SystemInfo` before AWT initialization. (issue #673)
|
||||
- ComboBox: Fixed search in item list for text with spaces. (issue #691)
|
||||
- FormattedTextField: On Linux, fixed `IllegalArgumentException: Invalid
|
||||
location` if `JFormattedTextField.setDocument()` is invoked in a focus gained
|
||||
listener on that formatted text field. (issue #698)
|
||||
- PopupMenu: Make sure that popup menu does not overlap any operating system
|
||||
task bar. (issue #701)
|
||||
- FileChooser: Use system icons on Windows with Java 17.0.3 (and later) 32-bit.
|
||||
Only Java 17 - 17.0.2 32-bit do not use system icons because of a bug in Java
|
||||
32-bit that crashes the application. (PR #709)
|
||||
- FileChooser: Fixed crash on Windows with Java 17 to 17.0.2 32-bit. Java 17
|
||||
64-bit is not affected. (regression since FlatLaf 2.3; PR #522, see also issue
|
||||
#403)
|
||||
|
||||
#### Incompatibilities
|
||||
|
||||
- Extras: Class `FlatSVGIcon` now uses [JSVG](https://github.com/weisJ/jsvg)
|
||||
library for SVG rendering. You need to replace svgSalamander with JSVG in your
|
||||
build scripts and distribute `jsvg.jar` with your application. Also replace
|
||||
`com.kitfox.svg` with `com.github.weisj.jsvg` in `module-info.java` files.
|
||||
- IntelliJ Themes: Removed all "Contrast" themes from "Material UI Lite".
|
||||
|
||||
|
||||
## 3.1.1
|
||||
|
||||
- IntelliJ Themes:
|
||||
- Fixed too large menu item paddings and too large table/tree row heights (all
|
||||
"Material Theme UI Lite" themes; issue #667; regression in FlatLaf 3.1).
|
||||
- Fixed too large tree row height in "Carbon", "Dark Purple", "Gray",
|
||||
"Material Design Dark", "Monokai Pro", "One Dark" and "Spacegray" themes.
|
||||
- Native libraries: Fixed `IllegalArgumentException: URI scheme is not "file"`
|
||||
when using FlatLaf in WebStart. (issue #668; regression in FlatLaf 3.1)
|
||||
|
||||
|
||||
## 3.1
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- Windows 11: Popups (`JPopupMenu`, `JComboBox`, `JToolTip`, etc.) now use
|
||||
native Windows 11 rounded borders and drop shadows. (PR #643)
|
||||
- Fonts:
|
||||
- Added **Roboto Mono** (https://fonts.google.com/specimen/Roboto+Mono). (PR
|
||||
#639, issue #638)
|
||||
- Updated **JetBrains Mono** to
|
||||
[v2.304](https://github.com/JetBrains/JetBrainsMono/releases/tag/v2.304).
|
||||
- Theme Editor: Support macOS light and dark themes.
|
||||
- TabbedPane: Support hover and focused tab foreground colors. (issue #627)
|
||||
- TabbedPane: `tabbedPane.getBackgroundAt(tabIndex)` now has higher priority
|
||||
than `TabbedPane.focusColor` and `TabbedPane.selectedBackground`. If
|
||||
`tabbedPane.setBackgroundAt(tabIndex)` is used to set a color for a single
|
||||
tab, then this color is now used even if the tab is focused or selected.
|
||||
- TableHeader: Support column hover and pressed background and foreground
|
||||
colors. (issue #636)
|
||||
- Native libraries: Made it easier to distribute FlatLaf native libraries
|
||||
(Windows `.dll` and Linux `.so`) to avoid problems on operating systems with
|
||||
enabled execution restrictions.
|
||||
See https://www.formdev.com/flatlaf/native-libraries/ for more details. (issue #624)
|
||||
- Published native libraries to Maven Central for easy using them as
|
||||
dependencies in Gradle and Maven.
|
||||
- If available, native libraries are now loaded from same location as
|
||||
`flatlaf.jar`, otherwise they are extract from `flatlaf.jar` to temporary
|
||||
folder and loaded from there.
|
||||
- Windows DLLs are now digitally signed with FormDev Software GmbH
|
||||
certificate.
|
||||
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- FlatLaf window decorations:
|
||||
- Fixed inconsistent size of glass pane depending on whether FlatLaf window
|
||||
decorations are used (e.g. Windows 10/11) or not (e.g. macOS). Now the glass
|
||||
pane no longer overlaps the FlatLaf window title bar. (issue #630)
|
||||
- Linux: Fixed broken window resizing on multi-screen setups. (issue #632)
|
||||
- Linux: Fixed behavior of maximize/restore button when tiling window to left
|
||||
or right half of screen. (issue #647)
|
||||
- IntelliJ Themes:
|
||||
- Fixed default button hover background in "Solarized Light" theme. (issue
|
||||
#628)
|
||||
- Avoid that accent color affect some colors in some IntelliJ themes. (issue
|
||||
#625)
|
||||
- Updated "Hiberbee Dark" and "Material Theme UI Lite" themes.
|
||||
- Styling: Fixed resolving of UI variables in styles that use other variables.
|
||||
- MenuItem: Fixed horizontal alignment of icons. (issue #631)
|
||||
- Table: Fixed potential performance issue with paint cell focus indicator
|
||||
border. (issue #654)
|
||||
- Tree: Fixed missing custom closed/opened/leaf icons of a custom
|
||||
`DefaultTreeCellRenderer`. (issue #653; regression since implementing PR #609
|
||||
in FlatLaf 3.0)
|
||||
- Tree: Fixed truncated node text and too small painted non-wide node background
|
||||
if custom cell renderer sets icon, but not disabled icon, and tree is
|
||||
disabled. (issue #640)
|
||||
- Fixed `HiDPIUtils.paintAtScale1x()`, which painted at wrong location if
|
||||
graphics is rotated, is scaled and `x` or `y` parameters are not zero. (issue
|
||||
#646)
|
||||
|
||||
|
||||
## 3.0
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- **macOS light and dark themes**: The two new themes `FlatMacLightLaf` and
|
||||
`FlatMacDarkLaf` use macOS colors and look similar to native macOS controls.
|
||||
(PRs #533, #612 and #607)
|
||||
- **Fonts**: Packaged some fonts into JARs and provide an easy way to use them
|
||||
with FlatLaf. (PRs #545, #614 and #615) At the moment there are three fonts:
|
||||
- **Inter** (https://rsms.me/inter/) - a typeface carefully crafted & designed
|
||||
for computer screens
|
||||
- **Roboto** (https://fonts.google.com/specimen/Roboto) - default font on
|
||||
Android and recommended for Material Design
|
||||
- **JetBrains Mono** (https://www.jetbrains.com/mono) - a monospaced typeface
|
||||
- **Rounded selection**: Optionally use rounded selection in:
|
||||
- Menus (PR #536)
|
||||
- ComboBox (PR #548)
|
||||
- List (PR #547)
|
||||
- Tree (PR #546)
|
||||
- Tree: Hide default closed/opened/leaf icons by default. Set UI value
|
||||
`Tree.showDefaultIcons` to `true` to show them.
|
||||
- ToolBar: Hover effect for button groups. (PR #534)
|
||||
- Icons: New modern **rounded outlined icons** for `JFileChooser`,
|
||||
`JOptionPane`, `JPasswordField` and `JTree`. (PR #577)
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- FileChooser: Fixed layout of (optional) accessory component and fixed too
|
||||
large right margin. (issue #604; regression since implementing PR #522 in
|
||||
FlatLaf 2.3)
|
||||
- Tree:
|
||||
- Fixed missing tree lines (if enabled) for wide-selected rows. (issue #598)
|
||||
- Fixed scaling of tree lines and fixed alignment to expand/collapse arrows.
|
||||
- Removed support for dashed tree lines. `Tree.lineTypeDashed` is now ignored.
|
||||
- SwingX: Fonts in `JXHeader`, `JXMonthView`, `JXTaskPane` and `JXTitledPanel`
|
||||
were not updated when changing default font.
|
||||
|
||||
|
||||
## 2.6
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- If value of system property `flatlaf.nativeLibraryPath` is `system`, then
|
||||
`System.loadLibrary(String)` is used to load the native library.
|
||||
- TabbedPane: Switch and close tabs on left mouse click only. (PR #595)
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- ComboBox and Spinner: Fixed missing arrow buttons if preferred height is zero.
|
||||
Minimum width of arrow buttons is 3/4 of default width.
|
||||
- MenuBar: Fixed NPE in `FlatMenuItemRenderer.getTopLevelFont()` if menu item
|
||||
does not have a parent. (issue #600; regression since implementing #589 in
|
||||
FlatLaf 2.5)
|
||||
- ScrollBar: Show "pressed" feedback on track/thumb only for left mouse button.
|
||||
If absolute positioning is enabled (the default), then also for middle mouse
|
||||
button.
|
||||
- Arrow buttons in ComboBox, Spinner, ScrollBar and TabbedPane: Show "pressed"
|
||||
feedback only for left mouse button.
|
||||
- ScaledImageIcon: Do not throw exceptions if image was has invalid size (e.g.
|
||||
not found). Instead, paint a red rectangle (similar to `FlatSVGIcon`).
|
||||
- Fixed NPE in `FlatUIUtils.isCellEditor()`. (issue #601)
|
||||
|
||||
|
||||
## 2.5
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- Linux: Use X11 window manager events to move window and to show window menu
|
||||
(right-click on window title bar), if custom window decorations are enabled.
|
||||
This gives FlatLaf windows a more "native" feeling. (issue #482)
|
||||
- MenuBar: Support different menu selection style UI defaults for `MenuBar` and
|
||||
`MenuItem`. (issue #587)
|
||||
- MenuBar: Top level menus now use `MenuBar.font` instead of `Menu.font`. (issue
|
||||
#589)
|
||||
- PasswordField: Reveal button is now hidden (and turned off) if password field
|
||||
is disabled. (issue #501)
|
||||
- TabbedPane: New option to disable tab run rotation in wrap layout. Set UI
|
||||
value `TabbedPane.rotateTabRuns` to `false`. (issue #574)
|
||||
- Window decorations:
|
||||
- Added client property to mark components in embedded menu bar as "caption"
|
||||
(allow moving window). (issue #569)
|
||||
- Option to show window icon only in frames, but not in dialogs. Set UI value
|
||||
`TitlePane.showIconInDialogs` to `false`. (issue #589)
|
||||
- Added UI value `TitlePane.font` to customize window title font. (issue #589)
|
||||
- Added system property `flatlaf.updateUIOnSystemFontChange` to allow disabling
|
||||
automatic UI update when system font changes. (issue #580)
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- Fixed missing UI value `MenuItem.acceleratorDelimiter` on macOS. (was `null`,
|
||||
is now an empty string)
|
||||
- Fixed possible exception in `FlatUIUtils.resetRenderingHints()`. (issue #575)
|
||||
- Fixed AWT components on macOS, which use Swing components internally. (issue
|
||||
#583)
|
||||
- SwingX: Fixed missing highlighting of "today" in `JXMonthView` and
|
||||
`JXDatePicker`.
|
||||
|
||||
|
||||
## 2.4
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- Native window decorations (Windows 10/11 only):
|
||||
- There is now a small area at top of the embedded menu bar to resize the
|
||||
window.
|
||||
- Improved window title bar layout for small window widths:
|
||||
- Width of iconify/maximize/close buttons is reduced (if necessary) to give
|
||||
more space to embedded menu bar and title.
|
||||
- Window title now has a minimum width to always allow moving window
|
||||
(click-and-drag on window title). Instead, embedded menu bar is made
|
||||
smaller.
|
||||
- Option to show window icon beside window title, if menu bar is embedded or
|
||||
title is centered. Set UI value `TitlePane.showIconBesideTitle` to `true`.
|
||||
- No longer reduce height of window title bar if it has an embedded menu bar
|
||||
and is maximized.
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- ComboBox: Fixed vertical alignment of text in popup list with text in combo
|
||||
box in IntelliJ/Darcula themes.
|
||||
- Menus: Fixed application freeze under very special conditions (invoking
|
||||
`FlatLaf.initialize()` twice in NetBeans GUI builder) and using menu that has
|
||||
submenus. See
|
||||
[NetBeans issue #4231](https://github.com/apache/netbeans/issues/4231#issuecomment-1179611682)
|
||||
for details.
|
||||
- MenuItem: Fixed sometimes wrapped HTML text on HiDPI screens on Windows.
|
||||
- TableHeader: Fixed exception when changing table structure (e.g. removing
|
||||
column) from a table header popup menu action. (issue #532)
|
||||
- `HiDPIUtils.paintAtScale1x()` now supports rotated graphics. (issue #557)
|
||||
- Typography: No longer use `Consolas` or `Courier New` as monospaced font on
|
||||
Windows because they have bad vertically placement.
|
||||
- Native window decorations (Windows 10/11 only):
|
||||
- Do not center window title if embedded menu bar is empty or has no menus at
|
||||
left side, but some components at right side. (issue #558)
|
||||
- Do not use window decorations if system property `sun.java2d.opengl` is
|
||||
`true` on Windows 10. (issue #540)
|
||||
- Fixed missing top window border in dark themes if window drop shadows are
|
||||
disabled in system settings. (issue #554; Windows 10 only)
|
||||
- Right-to-left component orientation of title bar was lost when switching
|
||||
theme.
|
||||
|
||||
|
||||
## 2.3
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- FileChooser: Added (optional) shortcuts panel. On Windows it contains "Recent
|
||||
Items", "Desktop", "Documents", "This PC" and "Network". On macOS and Linux it
|
||||
is empty/hidden. (issue #100)
|
||||
- Button and ToggleButton: Added missing foreground colors for hover, pressed,
|
||||
focused and selected states. (issue #535)
|
||||
- Table: Optionally paint alternating rows below table if table is smaller than
|
||||
scroll pane. Set UI value `Table.paintOutsideAlternateRows` to `true`.
|
||||
Requires that `Table.alternateRowColor` is set to a color. (issue #504)
|
||||
- ToggleButton: Made the underline placement of tab-style toggle buttons
|
||||
configurable. (PR #530; issue #529)
|
||||
- Added spanish translation. (PR #525)
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- IntelliJ Themes: Fixed `TitledBorder` text color in "Monokai Pro" theme.
|
||||
(issue #524)
|
||||
|
||||
|
||||
## 2.2
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- SplitPane: Allow limiting one-touch expanding to a single side (set client
|
||||
property `JSplitPane.expandableSide` to `"left"` or `"right"`). (issue #355)
|
||||
- TabbedPane: Selected tab underline color now changes depending on whether the
|
||||
focus is within the tab content. (issue #398)
|
||||
- IntelliJ Themes:
|
||||
- Added "Monokai Pro" and "Xcode-Dark" themes.
|
||||
- TabbedPane now use different background color for selected tabs in all "Arc"
|
||||
themes, in "Hiberbee Dark" and in all "Material UI Lite" themes.
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- Native window decorations (Windows 10/11 only): Fixed wrong window title
|
||||
character encoding used in Windows taskbar. (issue #502)
|
||||
- Button: Fixed icon layout and preferred width of default buttons that use bold
|
||||
font. (issue #506)
|
||||
- FileChooser: Enabled full row selection for details view to fix alternate row
|
||||
coloring. (issue #512)
|
||||
- SplitPane: Fixed `StackOverflowError` caused by layout loop that may occur
|
||||
under special circumstances. (issue #513)
|
||||
- Table: Slightly changed grid colors to make grid better recognizable. (issue
|
||||
#514)
|
||||
- ToolBar: Fixed endless loop in focus navigation that may occur under special
|
||||
circumstances. (issue #505)
|
||||
- IntelliJ Themes: `Component.accentColor` UI property now has useful theme
|
||||
specific values. (issue #507)
|
||||
|
||||
|
||||
## 2.1
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- Menus: Improved usability of submenus. (PR #490; issue #247)
|
||||
- Menus: Scroll large menus using mouse wheel or up/down arrows. (issue #225)
|
||||
- Linux: Support using custom window decorations. Enable with
|
||||
`JFrame.setDefaultLookAndFeelDecorated(true)` and
|
||||
`JDialog.setDefaultLookAndFeelDecorated(true)` before creating a window.
|
||||
(issue #482)
|
||||
- ScrollBar: Added UI value `ScrollBar.minimumButtonSize` to specify minimum
|
||||
scroll arrow button size (if shown). (issue #493)
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- PasswordField: Fixed reveal button appearance in IntelliJ themes. (issue #494)
|
||||
- ScrollBar: Center and scale arrows in scroll up/down buttons (if shown).
|
||||
(issue #493)
|
||||
- TextArea, TextPane and EditorPane: No longer select all text when component is
|
||||
focused for the first time. (issue #498; regression in FlatLaf 2.0)
|
||||
- TabbedPane: Disable all items in "Show Hidden Tabs" popup menu if tabbed pane
|
||||
is disabled.
|
||||
|
||||
#### Incompatibilities
|
||||
|
||||
- Method `FlatUIUtils.paintArrow()` (and class `FlatArrowButton`) now paints
|
||||
arrows one pixel smaller than before. To fix this, increase parameter
|
||||
`arrowSize` by one.
|
||||
|
||||
|
||||
## 2.0.2
|
||||
|
||||
- Native window decorations (Windows 10/11 only): Fixed rendering artifacts on
|
||||
HiDPI screens when dragging window partly offscreen and back into screen
|
||||
bounds. (issue #477)
|
||||
- Repaint component when setting client property `JComponent.outline` (issue
|
||||
#480).
|
||||
- macOS: Fixed NPE when using some icons in main menu items. (issue #483)
|
||||
|
||||
|
||||
## 2.0.1
|
||||
|
||||
- Fixed memory leak in Panel, Separator and ToolBarSeparator. (issue #471;
|
||||
regression in FlatLaf 2.0)
|
||||
- ToolTip: Fixed wrong tooltip location if component overrides
|
||||
`JComponent.getToolTipLocation()` and wants place tooltip under mouse
|
||||
location. (issue #468)
|
||||
- Extras: Added copy constructor to `FlatSVGIcon`. (issue #465)
|
||||
- Moved `module-info.class` from `META-INF\versions\9\` to root folder of JARs.
|
||||
(issue #466)
|
||||
|
||||
|
||||
## 2.0
|
||||
|
||||
- Added system property `flatlaf.nativeLibraryPath` to load native libraries
|
||||
@@ -58,7 +497,7 @@ FlatLaf Change Log
|
||||
- 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 be made visibly by
|
||||
- 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)
|
||||
|
||||
293
README.md
293
README.md
@@ -6,14 +6,19 @@ Swing desktop applications.
|
||||
|
||||
It looks almost flat (no shadows or gradients), clean, simple and elegant.
|
||||
FlatLaf comes with **Light**, **Dark**, **IntelliJ** and **Darcula** themes,
|
||||
scales on **HiDPI** displays and runs on Java 8 or newer.
|
||||
scales on **HiDPI** displays and runs on Java 8 or newer (LTS and latest).
|
||||
|
||||
The look is heavily inspired by **Darcula** and **IntelliJ** themes from
|
||||
IntelliJ IDEA 2019.2+ and uses almost the same colors and icons.
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

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

|
||||
|
||||
|
||||
IntelliJ Platform Themes
|
||||
@@ -25,6 +30,17 @@ FlatLaf can use 3rd party themes created for IntelliJ Platform (see
|
||||

|
||||
|
||||
|
||||
Sponsors
|
||||
--------
|
||||
|
||||
<a href="https://www.ej-technologies.com/"><img src="https://www.formdev.com/flatlaf/sponsor/ej-technologies.png" width="200" alt="ej-technologies" title="ej-technologies - Java APM, Java Profiler, Java Installer Builder"></a>
|
||||
|
||||
<a href="https://www.dbvis.com/"><img src="https://www.formdev.com/flatlaf/sponsor/dbvisualizer.svg" width="200" alt="DbVisualizer" title="DbVisualizer - SQL Client and Editor"></a>
|
||||
|
||||
<a href="https://www.dscsag.com/"><img src="https://www.formdev.com/flatlaf/sponsor/DSC.png" height="48" alt="DSC Software AG" title="DSC Software AG - Your Companion for Integrative PLM"></a>
|
||||
|
||||
[Become a Sponsor](https://www.formdev.com/flatlaf/sponsor/)
|
||||
|
||||
Demo
|
||||
----
|
||||
|
||||
@@ -50,6 +66,11 @@ Otherwise download `flatlaf-<version>.jar` here:
|
||||
|
||||
[](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf)
|
||||
|
||||
See also
|
||||
[Native Libraries distribution](https://www.formdev.com/flatlaf/native-libraries/)
|
||||
for instructions on how to redistribute FlatLaf native libraries with your
|
||||
application.
|
||||
|
||||
|
||||
### Snapshots
|
||||
|
||||
@@ -74,6 +95,8 @@ Addons
|
||||
- [SwingX](flatlaf-swingx) - support for SwingX components
|
||||
- [JIDE Common Layer](flatlaf-jide-oss) - support for JIDE Common Layer
|
||||
components
|
||||
- [Fonts](flatlaf-fonts) - some font families bundled in easy-to-use and
|
||||
redistributable JARs
|
||||
|
||||
|
||||
Getting started
|
||||
@@ -105,10 +128,21 @@ For more information and documentation visit
|
||||
- [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
|
||||
----
|
||||
|
||||
- [What others say about FlatLaf on Twitter](https://twitter.com/search?f=live&q=flatlaf)
|
||||
- [FlatLaf 3.1 (and 3.0) announcement on Reddit](https://www.reddit.com/r/java/comments/12xgrsu/flatlaf_31_and_30_swing_look_and_feel/)
|
||||
- [FlatLaf 1.0 announcement on Reddit](https://www.reddit.com/r/java/comments/lsbcwe/flatlaf_10_swing_look_and_feel/)
|
||||
- [FlatLaf announcement on Reddit](https://www.reddit.com/r/java/comments/dl0hu3/flatlaf_flat_look_and_feel/)
|
||||
|
||||
@@ -116,96 +150,193 @@ Buzz
|
||||
Applications using FlatLaf
|
||||
--------------------------
|
||||
|
||||
-  [MooInfo](https://github.com/rememberber/MooInfo) -
|
||||
visual implementation of OSHI, to view information about the system and
|
||||
hardware
|
||||
-  [Jailer](https://github.com/Wisser/Jailer) 11.2 -
|
||||
database subsetting and relational data browsing tool
|
||||
- [Apache NetBeans](https://netbeans.apache.org/) 11.3 - IDE for Java, PHP, HTML
|
||||
and much more
|
||||
- [jclasslib bytecode viewer](https://github.com/ingokegel/jclasslib) 5.5
|
||||
- [KeyStore Explorer](https://keystore-explorer.org/) 5.4.3
|
||||
- 
|
||||
### Featured
|
||||
|
||||
-  [JFormDesigner](https://www.formdev.com/)
|
||||
(**commercial**) - Java/Swing GUI Designer (from the FlatLaf creators)
|
||||
- 
|
||||
[JProfiler](https://www.ej-technologies.com/products/jprofiler/overview.html)
|
||||
(**commercial**) - the award-winning all-in-one Java profiler
|
||||
- 
|
||||
[install4j](https://www.ej-technologies.com/products/install4j/overview.html)
|
||||
9.0 (**commercial**) - the powerful multi-platform Java installer builder
|
||||
-  [DbVisualizer](https://www.dbvis.com/) 12.0
|
||||
(**commercial**) - the powerful multi-platform Java installer builder
|
||||
-  [DbVisualizer](https://www.dbvis.com/)
|
||||
(**commercial**) - the universal database tool for developers, analysts and
|
||||
DBAs
|
||||
-  [MagicPlot](https://magicplot.com/) 3.0
|
||||
(**commercial**) - Software for nonlinear fitting, plotting and data analysis
|
||||
- 
|
||||
[Thermo-Calc](https://thermocalc.com/products/thermo-calc/) 2021a
|
||||
(**commercial**) - Thermodynamics and Properties Software
|
||||
- [OWASP ZAP](https://www.zaproxy.org/) 2.10 - the worlds most widely used web
|
||||
app scanner
|
||||
-  [Apache NetBeans](https://netbeans.apache.org/) - IDE
|
||||
for Java, PHP, HTML and much more
|
||||
- 
|
||||
[Thermo-Calc](https://thermocalc.com/products/thermo-calc/) (**commercial**) -
|
||||
Thermodynamics and Properties Software
|
||||
|
||||
### Data
|
||||
|
||||
-  [Ultorg](https://www.ultorg.com/) (**commercial**) - a
|
||||
visual query system for relational databases
|
||||
- [Jailer](https://github.com/Wisser/Jailer) - database subsetting and
|
||||
relational data browsing tool
|
||||
-  [MagicPlot](https://magicplot.com/) (**commercial**) -
|
||||
Software for nonlinear fitting, plotting and data analysis
|
||||
-  [Constellation](https://www.constellation-app.com/) -
|
||||
Data Visualization and Analytics (based on NetBeans platform)
|
||||
- 
|
||||
[Kafka Visualizer](https://github.com/kumait/kafkavisualizer) - Kafka GUI
|
||||
client
|
||||
|
||||
### Security
|
||||
|
||||
-  [ZAP](https://www.zaproxy.org/) - the world's most
|
||||
widely used web app scanner
|
||||
- 
|
||||
[Burp Suite Professional and Community Edition](https://portswigger.net/burp/pro)
|
||||
2020.11.2 (**commercial**) - the leading software for web security testing
|
||||
(**commercial**) - the leading software for web security testing
|
||||
- 
|
||||
[BurpCustomizer](https://github.com/CoreyD97/BurpCustomizer) - adds more
|
||||
[Ghidra](https://github.com/NationalSecurityAgency/ghidra) - a software
|
||||
reverse engineering (SRE) framework
|
||||
-  [jadx](https://github.com/skylot/jadx) - Dex to Java
|
||||
decompiler
|
||||
- [BurpCustomizer](https://github.com/CoreyD97/BurpCustomizer) - adds more
|
||||
FlatLaf themes to Burp Suite
|
||||
- [JOSM](https://josm.openstreetmap.de/) - an extensible editor for
|
||||
[OpenStreetMap](https://www.openstreetmap.org/) (requires FlatLaf JOSM plugin)
|
||||
- [jAlbum](https://jalbum.net/) 21 (**commercial**) - creates photo album
|
||||
websites
|
||||
-  [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**)
|
||||
- [Total Validator](https://www.totalvalidator.com/) 15 (**commercial**) -
|
||||
checks your website
|
||||
- [j-lawyer](https://github.com/jlawyerorg/j-lawyer-org) - Kanzleisoftware
|
||||
- [MegaMek](https://github.com/MegaMek/megamek) v0.47.4 and
|
||||
[MekHQ](https://github.com/MegaMek/mekhq) v0.47.5 - a turn-based sci-fi board
|
||||
game
|
||||
- [GUIslice Builder](https://github.com/ImpulseAdventure/GUIslice-Builder)
|
||||
0.13.b024 - GUI builder for
|
||||
[GUIslice](https://github.com/ImpulseAdventure/GUIslice), a lightweight GUI
|
||||
framework for embedded displays
|
||||
- [Rest Suite](https://github.com/supanadit/restsuite) - Rest API testing
|
||||
- [ControllerBuddy](https://github.com/bwRavencl/ControllerBuddy) - advanced
|
||||
gamepad mapping software
|
||||
- [SpringRemote](https://github.com/HaleyWang/SpringRemote) - remote Linux SSH
|
||||
connections manager
|
||||
- [jEnTunnel](https://github.com/ggrandes/jentunnel) - manage SSH Tunnels made
|
||||
easy
|
||||
- [Total Validator](https://www.totalvalidator.com/) (**commercial**) - checks
|
||||
your website
|
||||
- [JPass](https://github.com/gaborbata/jpass) - password manager with strong
|
||||
encryption
|
||||
|
||||
### Software Development
|
||||
|
||||
- [jclasslib bytecode viewer](https://github.com/ingokegel/jclasslib)
|
||||
- [KeyStore Explorer](https://keystore-explorer.org/)
|
||||
- 
|
||||
[muCommander](https://github.com/mucommander/mucommander) - lightweight
|
||||
cross-platform file manager
|
||||
-  [Guiffy](https://www.guiffy.com/) (**commercial**) -
|
||||
advanced cross-platform Diff/Merge
|
||||
-  [HashGarten](https://github.com/jonelo/HashGarten) -
|
||||
cross-platform Swing GUI for Jacksum
|
||||
- [Pseudo Assembler IDE](https://github.com/tomasz-herman/PseudoAssemblerIDE) -
|
||||
IDE for Pseudo-Assembler
|
||||
- [Linotte](https://github.com/cpc6128/LangageLinotte) - French programming
|
||||
language created to learn programming
|
||||
- [lsfusion platform](https://github.com/lsfusion/platform) - information
|
||||
systems development platform
|
||||
|
||||
### Electrical
|
||||
|
||||
- [Antares](https://www.antarescircuit.io/) - a free, powerful platform for
|
||||
designing, simulating and explaining digital circuits
|
||||
- [Logisim-evolution](https://github.com/logisim-evolution/logisim-evolution) -
|
||||
Digital logic design tool and simulator
|
||||
- [Makelangelo Software](https://github.com/MarginallyClever/Makelangelo-software) -
|
||||
for plotters, especially the wall-hanging polargraph
|
||||
- [GUIslice Builder](https://github.com/ImpulseAdventure/GUIslice-Builder) - GUI
|
||||
builder for [GUIslice](https://github.com/ImpulseAdventure/GUIslice), a
|
||||
lightweight GUI framework for embedded displays
|
||||
- [ThunderFocus](https://github.com/marcocipriani01/ThunderFocus) -
|
||||
Arduino-based telescope focuser
|
||||
- [RemoteLight](https://github.com/Drumber/RemoteLight) - multifunctional LED
|
||||
control software
|
||||
|
||||
### Media
|
||||
|
||||
-  [jAlbum](https://jalbum.net/) (**commercial**) -
|
||||
creates photo album websites
|
||||
-  [MediathekView](https://mediathekview.de/) - search in
|
||||
media libraries of various German broadcasters
|
||||
- [Cinecred](https://loadingbyte.com/cinecred/) - create beautiful film credit
|
||||
sequences
|
||||
- [tinyMediaManager](https://www.tinymediamanager.org/) (**commercial**) - a
|
||||
media management tool
|
||||
- [Weasis](https://nroduit.github.io/) - medical DICOM viewer used in healthcare
|
||||
by hospitals, health networks, etc
|
||||
- [Shutter Encoder](https://www.shutterencoder.com/)
|
||||
([source code](https://github.com/paulpacifico/shutter-encoder)) -
|
||||
professional video converter and compression tool
|
||||
- [Sound Analysis](https://github.com/tomasz-herman/SoundAnalysis) - analyze
|
||||
sound files in time or frequency domain
|
||||
- [Novel-Grabber](https://github.com/Flameish/Novel-Grabber) - download novels
|
||||
from any webnovel and lightnovel site
|
||||
- [lectureStudio](https://www.lecturestudio.org/) - digitize your lectures with
|
||||
ease
|
||||
|
||||
### Modelling
|
||||
|
||||
-  [Astah](https://astah.net/) (**commercial**) - create
|
||||
UML, ER Diagram, Flowchart, Data Flow Diagram, Requirement Diagram, SysML
|
||||
diagrams and more
|
||||
- [IGMAS+](https://www.gfz-potsdam.de/igmas) - Interactive Gravity and Magnetic
|
||||
Application System
|
||||
|
||||
### Documents
|
||||
|
||||
-  [Big Faceless (BFO) PDF Viewer](https://bfo.com/)
|
||||
(**commercial**) - Swing PDF Viewer
|
||||
- [PDF Studio](https://www.qoppa.com/pdfstudio/) (**commercial**) - create,
|
||||
review and edit PDF documents
|
||||
- [XMLmind XML Editor](https://www.xmlmind.com/xmleditor/) (**commercial**)
|
||||
|
||||
### Geo
|
||||
|
||||
-  [JOSM](https://josm.openstreetmap.de/) - an extensible
|
||||
editor for [OpenStreetMap](https://www.openstreetmap.org/) (requires FlatLaf
|
||||
JOSM plugin)
|
||||
- [Mapton](https://mapton.org/)
|
||||
([source code](https://github.com/trixon/mapton)) - some kind of map
|
||||
application (based on NetBeans platform)
|
||||
- [MeteoInfo](https://github.com/meteoinfo/MeteoInfo) - GIS and scientific
|
||||
computation environment for meteorological community
|
||||
|
||||
### Business / Legal
|
||||
|
||||
- 
|
||||
[j-lawyer](https://github.com/jlawyerorg/j-lawyer-org) - Kanzleisoftware
|
||||
-  [Jeyla Studio](https://www.jeylastudio.com/) -
|
||||
Salon Software
|
||||
- [Fanurio](https://www.fanuriotimetracking.com/) (**commercial**) - time
|
||||
tracking and billing for freelancers and teams
|
||||
- [Jes](https://www.jes-eur.de) - Die Java-EÜR
|
||||
- [mendelson AS2](https://sourceforge.net/projects/mec-as2/),
|
||||
[AS4](https://sourceforge.net/projects/mendelson-as4/) and
|
||||
[OFTP2](https://sourceforge.net/projects/mendelson-oftp2/) (open-source) and
|
||||
[mendelson AS2](https://mendelson-e-c.com/as2/),
|
||||
[AS4](https://mendelson-e-c.com/as4/) and
|
||||
[OFTP2](https://mendelson-e-c.com/oftp2) (**commercial**)
|
||||
-  [IGMAS+](https://www.gfz-potsdam.de/igmas) -
|
||||
Interactive Gravity and Magnetic Application System
|
||||
- [MeteoInfo](https://github.com/meteoinfo/MeteoInfo) 2.2 - GIS and scientific
|
||||
computation environment for meteorological community
|
||||
- [lsfusion platform](https://github.com/lsfusion/platform) 4 - information
|
||||
systems development platform
|
||||
- [JPass](https://github.com/gaborbata/jpass) - password manager with strong
|
||||
encryption
|
||||
- [Jes - Die Java-EÜR](https://www.jes-eur.de)
|
||||
- [Mapton](https://mapton.org/) 2.0
|
||||
([source code](https://github.com/trixon/mapton)) - some kind of map
|
||||
application (based on NetBeans platform)
|
||||
- [Pseudo Assembler IDE](https://github.com/tomasz-herman/PseudoAssemblerIDE) -
|
||||
IDE for Pseudo-Assembler
|
||||
- [Linotte](https://github.com/cpc6128/LangageLinotte) 3.1 - French programming
|
||||
language created to learn programming
|
||||
- [MEKA](https://github.com/Waikato/meka) 1.9.3 - multi-label classifiers and
|
||||
evaluation procedures using the Weka machine learning framework
|
||||
- [Shutter Encoder](https://www.shutterencoder.com/) 14.2
|
||||
([source code](https://github.com/paulpacifico/shutter-encoder)) -
|
||||
professional video converter and compression tool (screenshots show **old**
|
||||
look)
|
||||
- [Sound Analysis](https://github.com/tomasz-herman/SoundAnalysis) - analyze
|
||||
sound files in time or frequency domain
|
||||
- [RemoteLight](https://github.com/Drumber/RemoteLight) - multifunctional LED
|
||||
control software
|
||||
- [ThunderFocus](https://github.com/marcocipriani01/ThunderFocus) -
|
||||
Arduino-based telescope focuser
|
||||
- [Novel-Grabber](https://github.com/Flameish/Novel-Grabber) - download novels
|
||||
from any webnovel and lightnovel site
|
||||
- [lectureStudio](https://www.lecturestudio.org/) 4.3.1060 - digitize your
|
||||
lectures with ease
|
||||
|
||||
### Messaging
|
||||
|
||||
-  [Spark](https://github.com/igniterealtime/Spark) -
|
||||
cross-platform IM client optimized for businesses and organizations
|
||||
-  [Chatty](https://github.com/chatty/chatty) - Twitch
|
||||
Chat Client
|
||||
|
||||
### Gaming
|
||||
|
||||
-  
|
||||
[BGBlitz](https://www.bgblitz.com/) (**commercial**) - professional Backgammon
|
||||
-  [MapTool](https://github.com/RPTools/maptool) - virtual
|
||||
Tabletop for playing role-playing games
|
||||
- [MegaMek](https://github.com/MegaMek/megamek),
|
||||
[MegaMekLab](https://github.com/MegaMek/megameklab) and
|
||||
[MekHQ](https://github.com/MegaMek/mekhq) - a sci-fi tabletop BattleTech
|
||||
simulator suite handling battles, unit building, and campaigns
|
||||
- [ControllerBuddy](https://github.com/bwRavencl/ControllerBuddy) - advanced
|
||||
gamepad mapping software
|
||||
|
||||
### Utilities
|
||||
|
||||
- [MooInfo](https://github.com/rememberber/MooInfo) - visual implementation of
|
||||
OSHI, to view information about the system and hardware
|
||||
- 
|
||||
[Linux Task Manager (LTM)](https://github.com/ajee10x/LTM-LinuxTaskManager) -
|
||||
GUI for monitoring and managing various aspects of a Linux system
|
||||
- [Rest Suite](https://github.com/supanadit/restsuite) - Rest API testing
|
||||
- [SpringRemote](https://github.com/HaleyWang/SpringRemote) - remote Linux SSH
|
||||
connections manager
|
||||
- [jEnTunnel](https://github.com/ggrandes/jentunnel) - manage SSH Tunnels made
|
||||
easy
|
||||
- [Android Tool](https://github.com/fast-geek/Android-Tool) - makes popular adb
|
||||
and fastboot commands easier to use
|
||||
- and more...
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
- [MEKA](https://github.com/Waikato/meka) - multi-label classifiers and
|
||||
evaluation procedures using the Weka machine learning framework
|
||||
|
||||
@@ -14,10 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
val releaseVersion = "2.0"
|
||||
val developmentVersion = "2.1-SNAPSHOT"
|
||||
import net.ltgt.gradle.errorprone.errorprone
|
||||
|
||||
version = if( java.lang.Boolean.getBoolean( "release" ) ) releaseVersion else developmentVersion
|
||||
version = property( if( hasProperty( "release" ) ) "flatlaf.releaseVersion" else "flatlaf.developmentVersion" ) as String
|
||||
|
||||
allprojects {
|
||||
version = rootProject.version
|
||||
@@ -37,9 +36,16 @@ println( "----------------------------------------------------------------------
|
||||
println( "FlatLaf Version: ${version}" )
|
||||
println( "Gradle ${gradle.gradleVersion} at ${gradle.gradleHomeDir}" )
|
||||
println( "Java ${System.getProperty( "java.version" )}" )
|
||||
val toolchainJavaVersion = System.getProperty( "toolchain" )
|
||||
if( !toolchainJavaVersion.isNullOrEmpty() )
|
||||
println( "Java toolchain ${toolchainJavaVersion}" )
|
||||
println()
|
||||
|
||||
|
||||
plugins {
|
||||
alias( libs.plugins.errorprone ) apply false
|
||||
}
|
||||
|
||||
allprojects {
|
||||
tasks {
|
||||
withType<JavaCompile>().configureEach {
|
||||
@@ -78,4 +84,56 @@ allprojects {
|
||||
isFailOnError = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---- Error Prone ----
|
||||
|
||||
tasks.register( "errorprone" ) {
|
||||
group = "verification"
|
||||
tasks.withType<JavaCompile>().forEach {
|
||||
dependsOn( it )
|
||||
}
|
||||
}
|
||||
|
||||
val useErrorProne = gradle.startParameter.taskNames.contains( "errorprone" )
|
||||
if( useErrorProne ) {
|
||||
plugins.withType<JavaPlugin> {
|
||||
apply( plugin = libs.plugins.errorprone.get().pluginId )
|
||||
|
||||
dependencies {
|
||||
"errorprone"( libs.errorprone )
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
options.compilerArgs.add( "-Werror" )
|
||||
options.errorprone {
|
||||
disable(
|
||||
"ReferenceEquality", // reports usage of '==' for objects
|
||||
"StringSplitter", // reports String.split()
|
||||
"JavaTimeDefaultTimeZone", // reports Year.now()
|
||||
"MissingSummary", // reports `/** @since 2 */`
|
||||
"InvalidBlockTag", // reports @uiDefault in javadoc
|
||||
"AlreadyChecked", // reports false positives
|
||||
"InlineMeSuggester", // suggests using Error Prone annotations for deprecated methods
|
||||
"TypeParameterUnusedInFormals",
|
||||
"UnsynchronizedOverridesSynchronized",
|
||||
"NonApiType", // reports ArrayList/HashSet in parameter or return type
|
||||
)
|
||||
when( project.name ) {
|
||||
"flatlaf-intellij-themes" -> disable(
|
||||
"MutablePublicArray", // reports FlatAllIJThemes.INFOS
|
||||
)
|
||||
"flatlaf-theme-editor" -> disable(
|
||||
"CatchAndPrintStackTrace",
|
||||
)
|
||||
"flatlaf-testing" -> disable(
|
||||
"CatchAndPrintStackTrace",
|
||||
"JdkObsolete", // reports Hashtable used for JSlider.setLabelTable()
|
||||
"JavaUtilDate", // reports usage of class Date
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
46
buildSrc/src/main/kotlin/flatlaf-cpp-library.gradle.kts
Normal file
46
buildSrc/src/main/kotlin/flatlaf-cpp-library.gradle.kts
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2022 FormDev Software GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
`cpp-library`
|
||||
}
|
||||
|
||||
library {
|
||||
// disable debuggable for release builds to make shared libraries smaller
|
||||
binaries.configureEach( CppSharedLibrary::class ) {
|
||||
with( compileTask.get() ) {
|
||||
if( name.contains( "Release" ) )
|
||||
isDebuggable = false
|
||||
}
|
||||
with( linkTask.get() ) {
|
||||
if( name.contains( "Release" ) )
|
||||
debuggable.set( false )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks {
|
||||
withType<CppCompile>().configureEach {
|
||||
doFirst {
|
||||
println( "Used Tool Chain:" )
|
||||
println( " - ${toolChain.get()}" )
|
||||
println( "Available Tool Chains:" )
|
||||
toolChains.forEach {
|
||||
println( " - $it" )
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
36
buildSrc/src/main/kotlin/flatlaf-jni-headers.gradle.kts
Normal file
36
buildSrc/src/main/kotlin/flatlaf-jni-headers.gradle.kts
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2022 FormDev Software GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
open class JniHeadersExtension {
|
||||
var headers: List<String> = emptyList()
|
||||
}
|
||||
|
||||
val extension = project.extensions.create<JniHeadersExtension>( "flatlafJniHeaders" )
|
||||
|
||||
|
||||
tasks {
|
||||
register<Copy>( "jni-headers" ) {
|
||||
// depend on :flatlaf-core:compileJava because it generates the JNI headers
|
||||
dependsOn( ":flatlaf-core:compileJava" )
|
||||
|
||||
from( project( ":flatlaf-core" ).layout.buildDirectory.dir( "generated/jni-headers" ) )
|
||||
into( "src/main/headers" )
|
||||
include( extension.headers )
|
||||
filter<org.apache.tools.ant.filters.FixCrLfFilter>(
|
||||
"eol" to org.apache.tools.ant.filters.FixCrLfFilter.CrLf.newInstance( "lf" )
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -61,12 +61,8 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest.attributes( "Multi-Release" to "true" )
|
||||
|
||||
into( "META-INF/versions/9" ) {
|
||||
from( sourceSets["module-info"].output ) {
|
||||
include( "module-info.class" )
|
||||
}
|
||||
from( sourceSets["module-info"].output ) {
|
||||
include( "module-info.class" )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,13 @@
|
||||
*/
|
||||
|
||||
|
||||
open class NativeArtifact( val fileName: String, val classifier: String, val type: String ) {}
|
||||
|
||||
open class PublishExtension {
|
||||
var artifactId: String? = null
|
||||
var name: String? = null
|
||||
var description: String? = null
|
||||
var nativeArtifacts: List<NativeArtifact>? = null
|
||||
}
|
||||
|
||||
val extension = project.extensions.create<PublishExtension>( "flatlafPublish" )
|
||||
@@ -71,6 +74,15 @@ publishing {
|
||||
url.set( "https://github.com/JFormDesigner/FlatLaf/issues" )
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
extension.nativeArtifacts?.forEach {
|
||||
artifact( artifacts.add( "archives", file( it.fileName ) ) {
|
||||
classifier = it.classifier
|
||||
type = it.type
|
||||
} )
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +92,7 @@ publishing {
|
||||
|
||||
val releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
|
||||
val snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots/"
|
||||
url = uri( if( java.lang.Boolean.getBoolean( "release" ) ) releasesRepoUrl else snapshotsRepoUrl )
|
||||
url = uri( if( rootProject.hasProperty( "release" ) ) releasesRepoUrl else snapshotsRepoUrl )
|
||||
|
||||
credentials {
|
||||
// get from gradle.properties
|
||||
@@ -108,5 +120,13 @@ signing {
|
||||
|
||||
// disable signing of snapshots
|
||||
tasks.withType<Sign>().configureEach {
|
||||
onlyIf { java.lang.Boolean.getBoolean( "release" ) }
|
||||
onlyIf { rootProject.hasProperty( "release" ) }
|
||||
}
|
||||
|
||||
// check whether parallel build is enabled
|
||||
tasks.withType<PublishToMavenRepository>().configureEach {
|
||||
doFirst {
|
||||
if( System.getProperty( "org.gradle.parallel" ) == "true" )
|
||||
throw RuntimeException( "Publishing does not work correctly with enabled parallel build. Disable parallel build with VM option '-Dorg.gradle.parallel=false'." )
|
||||
}
|
||||
}
|
||||
|
||||
26
buildSrc/src/main/kotlin/flatlaf-toolchain.gradle.kts
Normal file
26
buildSrc/src/main/kotlin/flatlaf-toolchain.gradle.kts
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2022 FormDev Software GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
java
|
||||
}
|
||||
|
||||
val toolchainJavaVersion = System.getProperty( "toolchain" )
|
||||
if( !toolchainJavaVersion.isNullOrEmpty() ) {
|
||||
java.toolchain {
|
||||
languageVersion.set( JavaLanguageVersion.of( toolchainJavaVersion ) )
|
||||
}
|
||||
}
|
||||
2
flatlaf-core/.settings/org.eclipse.core.resources.prefs
Normal file
2
flatlaf-core/.settings/org.eclipse.core.resources.prefs
Normal file
@@ -0,0 +1,2 @@
|
||||
eclipse.preferences.version=1
|
||||
encoding/<project>=ISO-8859-1
|
||||
@@ -14,8 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Flatlaf_publish_gradle.NativeArtifact
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
`flatlaf-toolchain`
|
||||
`flatlaf-module-info`
|
||||
`flatlaf-java9`
|
||||
`flatlaf-publish`
|
||||
@@ -24,12 +27,11 @@ plugins {
|
||||
val sigtest = configurations.create( "sigtest" )
|
||||
|
||||
dependencies {
|
||||
testImplementation( "org.junit.jupiter:junit-jupiter-api:5.7.2" )
|
||||
testImplementation( "org.junit.jupiter:junit-jupiter-params" )
|
||||
testRuntimeOnly( "org.junit.jupiter:junit-jupiter-engine" )
|
||||
testImplementation( libs.bundles.junit )
|
||||
testRuntimeOnly( libs.junit.engine )
|
||||
|
||||
// https://github.com/jtulach/netbeans-apitest
|
||||
sigtest( "org.netbeans.tools:sigtest-maven-plugin:1.4" )
|
||||
sigtest( libs.sigtest )
|
||||
}
|
||||
|
||||
java {
|
||||
@@ -40,12 +42,7 @@ java {
|
||||
tasks {
|
||||
compileJava {
|
||||
// generate JNI headers
|
||||
options.headerOutputDirectory.set( buildDir.resolve( "generated/jni-headers" ) )
|
||||
}
|
||||
|
||||
processResources {
|
||||
// build native libraries
|
||||
dependsOn( ":flatlaf-natives-windows:assemble" )
|
||||
options.headerOutputDirectory.set( layout.buildDirectory.dir( "generated/jni-headers" ) )
|
||||
}
|
||||
|
||||
jar {
|
||||
@@ -71,6 +68,9 @@ tasks {
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
||||
|
||||
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 )
|
||||
jvmArgs( listOf( "--add-opens", "java.desktop/javax.swing.plaf.basic=ALL-UNNAMED" ) )
|
||||
}
|
||||
|
||||
register( "sigtestGenerate" ) {
|
||||
@@ -88,7 +88,7 @@ tasks {
|
||||
"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",
|
||||
"packages" to "com.formdev.flatlaf,com.formdev.flatlaf.themes,com.formdev.flatlaf.util",
|
||||
"version" to version,
|
||||
"release" to "1.8", // Java version
|
||||
"failonerror" to "true" )
|
||||
@@ -124,4 +124,12 @@ flatlafPublish {
|
||||
artifactId = "flatlaf"
|
||||
name = "FlatLaf"
|
||||
description = "Flat Look and Feel"
|
||||
|
||||
val natives = "src/main/resources/com/formdev/flatlaf/natives"
|
||||
nativeArtifacts = listOf(
|
||||
NativeArtifact( "${natives}/flatlaf-windows-x86.dll", "windows-x86", "dll" ),
|
||||
NativeArtifact( "${natives}/flatlaf-windows-x86_64.dll", "windows-x86_64", "dll" ),
|
||||
NativeArtifact( "${natives}/flatlaf-windows-arm64.dll", "windows-arm64", "dll" ),
|
||||
NativeArtifact( "${natives}/libflatlaf-linux-x86_64.so", "linux-x86_64", "so" ),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#Signature file v4.1
|
||||
#Version 1.6
|
||||
#Version 3.2.3
|
||||
|
||||
CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties
|
||||
fld public final static java.lang.String BUTTON_TYPE = "JButton.buttonType"
|
||||
@@ -11,6 +11,8 @@ fld public final static java.lang.String BUTTON_TYPE_TAB = "tab"
|
||||
fld public final static java.lang.String BUTTON_TYPE_TOOLBAR_BUTTON = "toolBarButton"
|
||||
fld public final static java.lang.String COMPONENT_FOCUS_OWNER = "JComponent.focusOwner"
|
||||
fld public final static java.lang.String COMPONENT_ROUND_RECT = "JComponent.roundRect"
|
||||
fld public final static java.lang.String COMPONENT_TITLE_BAR_CAPTION = "JComponent.titleBarCaption"
|
||||
fld public final static java.lang.String GLASS_PANE_FULL_HEIGHT = "JRootPane.glassPaneFullHeight"
|
||||
fld public final static java.lang.String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded"
|
||||
fld public final static java.lang.String MINIMUM_HEIGHT = "JComponent.minimumHeight"
|
||||
fld public final static java.lang.String MINIMUM_WIDTH = "JComponent.minimumWidth"
|
||||
@@ -18,6 +20,7 @@ fld public final static java.lang.String OUTLINE = "JComponent.outline"
|
||||
fld public final static java.lang.String OUTLINE_ERROR = "error"
|
||||
fld public final static java.lang.String OUTLINE_WARNING = "warning"
|
||||
fld public final static java.lang.String PLACEHOLDER_TEXT = "JTextField.placeholderText"
|
||||
fld public final static java.lang.String POPUP_BORDER_CORNER_RADIUS = "Popup.borderCornerRadius"
|
||||
fld public final static java.lang.String POPUP_DROP_SHADOW_PAINTED = "Popup.dropShadowPainted"
|
||||
fld public final static java.lang.String POPUP_FORCE_HEAVY_WEIGHT = "Popup.forceHeavyWeight"
|
||||
fld public final static java.lang.String PROGRESS_BAR_LARGE_HEIGHT = "JProgressBar.largeHeight"
|
||||
@@ -30,7 +33,12 @@ fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY = "JTextFiel
|
||||
fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_ALWAYS = "always"
|
||||
fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_NEVER = "never"
|
||||
fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_ONCE = "once"
|
||||
fld public final static java.lang.String SPLIT_PANE_EXPANDABLE_SIDE = "JSplitPane.expandableSide"
|
||||
fld public final static java.lang.String SPLIT_PANE_EXPANDABLE_SIDE_LEFT = "left"
|
||||
fld public final static java.lang.String SPLIT_PANE_EXPANDABLE_SIDE_RIGHT = "right"
|
||||
fld public final static java.lang.String SQUARE_SIZE = "JButton.squareSize"
|
||||
fld public final static java.lang.String STYLE = "FlatLaf.style"
|
||||
fld public final static java.lang.String STYLE_CLASS = "FlatLaf.styleClass"
|
||||
fld public final static java.lang.String TABBED_PANE_ALIGN_CENTER = "center"
|
||||
fld public final static java.lang.String TABBED_PANE_ALIGN_FILL = "fill"
|
||||
fld public final static java.lang.String TABBED_PANE_ALIGN_LEADING = "leading"
|
||||
@@ -59,6 +67,9 @@ fld public final static java.lang.String TABBED_PANE_TAB_CLOSE_TOOLTIPTEXT = "JT
|
||||
fld public final static java.lang.String TABBED_PANE_TAB_HEIGHT = "JTabbedPane.tabHeight"
|
||||
fld public final static java.lang.String TABBED_PANE_TAB_ICON_PLACEMENT = "JTabbedPane.tabIconPlacement"
|
||||
fld public final static java.lang.String TABBED_PANE_TAB_INSETS = "JTabbedPane.tabInsets"
|
||||
fld public final static java.lang.String TABBED_PANE_TAB_TYPE = "JTabbedPane.tabType"
|
||||
fld public final static java.lang.String TABBED_PANE_TAB_TYPE_CARD = "card"
|
||||
fld public final static java.lang.String TABBED_PANE_TAB_TYPE_UNDERLINED = "underlined"
|
||||
fld public final static java.lang.String TABBED_PANE_TAB_WIDTH_MODE = "JTabbedPane.tabWidthMode"
|
||||
fld public final static java.lang.String TABBED_PANE_TAB_WIDTH_MODE_COMPACT = "compact"
|
||||
fld public final static java.lang.String TABBED_PANE_TAB_WIDTH_MODE_EQUAL = "equal"
|
||||
@@ -67,12 +78,27 @@ fld public final static java.lang.String TABBED_PANE_TRAILING_COMPONENT = "JTabb
|
||||
fld public final static java.lang.String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground"
|
||||
fld public final static java.lang.String TAB_BUTTON_UNDERLINE_COLOR = "JToggleButton.tab.underlineColor"
|
||||
fld public final static java.lang.String TAB_BUTTON_UNDERLINE_HEIGHT = "JToggleButton.tab.underlineHeight"
|
||||
fld public final static java.lang.String TAB_BUTTON_UNDERLINE_PLACEMENT = "JToggleButton.tab.underlinePlacement"
|
||||
fld public final static java.lang.String TEXT_FIELD_CLEAR_CALLBACK = "JTextField.clearCallback"
|
||||
fld public final static java.lang.String TEXT_FIELD_LEADING_COMPONENT = "JTextField.leadingComponent"
|
||||
fld public final static java.lang.String TEXT_FIELD_LEADING_ICON = "JTextField.leadingIcon"
|
||||
fld public final static java.lang.String TEXT_FIELD_PADDING = "JTextField.padding"
|
||||
fld public final static java.lang.String TEXT_FIELD_SHOW_CLEAR_BUTTON = "JTextField.showClearButton"
|
||||
fld public final static java.lang.String TEXT_FIELD_TRAILING_COMPONENT = "JTextField.trailingComponent"
|
||||
fld public final static java.lang.String TEXT_FIELD_TRAILING_ICON = "JTextField.trailingIcon"
|
||||
fld public final static java.lang.String TITLE_BAR_BACKGROUND = "JRootPane.titleBarBackground"
|
||||
fld public final static java.lang.String TITLE_BAR_FOREGROUND = "JRootPane.titleBarForeground"
|
||||
fld public final static java.lang.String TITLE_BAR_SHOW_CLOSE = "JRootPane.titleBarShowClose"
|
||||
fld public final static java.lang.String TITLE_BAR_SHOW_ICON = "JRootPane.titleBarShowIcon"
|
||||
fld public final static java.lang.String TITLE_BAR_SHOW_ICONIFFY = "JRootPane.titleBarShowIconify"
|
||||
fld public final static java.lang.String TITLE_BAR_SHOW_MAXIMIZE = "JRootPane.titleBarShowMaximize"
|
||||
fld public final static java.lang.String TITLE_BAR_SHOW_TITLE = "JRootPane.titleBarShowTitle"
|
||||
fld public final static java.lang.String TREE_PAINT_SELECTION = "JTree.paintSelection"
|
||||
fld public final static java.lang.String TREE_WIDE_SELECTION = "JTree.wideSelection"
|
||||
fld public final static java.lang.String USE_WINDOW_DECORATIONS = "JRootPane.useWindowDecorations"
|
||||
fld public final static java.lang.String WINDOW_STYLE = "Window.style"
|
||||
fld public final static java.lang.String WINDOW_STYLE_SMALL = "small"
|
||||
meth public static <%0 extends java.lang.Object> {%%0} clientProperty(javax.swing.JComponent,java.lang.String,{%%0},java.lang.Class<{%%0}>)
|
||||
meth public static boolean clientPropertyBoolean(javax.swing.JComponent,java.lang.String,boolean)
|
||||
meth public static boolean clientPropertyEquals(javax.swing.JComponent,java.lang.String,java.lang.Object)
|
||||
meth public static int clientPropertyInt(javax.swing.JComponent,java.lang.String,int)
|
||||
@@ -165,8 +191,10 @@ meth public boolean isSupportedLookAndFeel()
|
||||
meth public final boolean equals(java.lang.Object)
|
||||
meth public final int hashCode()
|
||||
meth public java.lang.String getID()
|
||||
meth public java.util.Map<java.lang.String,java.lang.String> getExtraDefaults()
|
||||
meth public javax.swing.Icon getDisabledIcon(javax.swing.JComponent,javax.swing.Icon)
|
||||
meth public javax.swing.UIDefaults getDefaults()
|
||||
meth public static <%0 extends java.lang.Object> {%%0} getStyleableValue(javax.swing.JComponent,java.lang.String)
|
||||
meth public static boolean install(javax.swing.LookAndFeel)
|
||||
anno 0 java.lang.Deprecated()
|
||||
meth public static boolean isLafDark()
|
||||
@@ -174,6 +202,14 @@ meth public static boolean isShowMnemonics()
|
||||
meth public static boolean isUseNativeWindowDecorations()
|
||||
meth public static boolean setup(javax.swing.LookAndFeel)
|
||||
meth public static boolean supportsNativeWindowDecorations()
|
||||
meth public static java.lang.Object parseDefaultsValue(java.lang.String,java.lang.String,java.lang.Class<?>)
|
||||
meth public static java.lang.String getPreferredFontFamily()
|
||||
meth public static java.lang.String getPreferredLightFontFamily()
|
||||
meth public static java.lang.String getPreferredMonospacedFontFamily()
|
||||
meth public static java.lang.String getPreferredSemiboldFontFamily()
|
||||
meth public static java.util.Map<java.lang.String,java.lang.Class<?>> getStyleableInfos(javax.swing.JComponent)
|
||||
meth public static java.util.Map<java.lang.String,java.lang.String> getGlobalExtraDefaults()
|
||||
meth public static java.util.function.Function<java.lang.String,java.awt.Color> getSystemColorGetter()
|
||||
meth public static javax.swing.UIDefaults$ActiveValue createActiveFontValue(float)
|
||||
meth public static void hideMnemonics()
|
||||
meth public static void initIconColors(javax.swing.UIDefaults,boolean)
|
||||
@@ -181,22 +217,31 @@ meth public static void installLafInfo(java.lang.String,java.lang.Class<? extend
|
||||
meth public static void registerCustomDefaultsSource(java.io.File)
|
||||
meth public static void registerCustomDefaultsSource(java.lang.String)
|
||||
meth public static void registerCustomDefaultsSource(java.lang.String,java.lang.ClassLoader)
|
||||
meth public static void registerCustomDefaultsSource(java.net.URL)
|
||||
meth public static void repaintAllFramesAndDialogs()
|
||||
meth public static void revalidateAndRepaintAllFramesAndDialogs()
|
||||
meth public static void runWithUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>,java.lang.Runnable)
|
||||
meth public static void setGlobalExtraDefaults(java.util.Map<java.lang.String,java.lang.String>)
|
||||
meth public static void setPreferredFontFamily(java.lang.String)
|
||||
meth public static void setPreferredLightFontFamily(java.lang.String)
|
||||
meth public static void setPreferredMonospacedFontFamily(java.lang.String)
|
||||
meth public static void setPreferredSemiboldFontFamily(java.lang.String)
|
||||
meth public static void setSystemColorGetter(java.util.function.Function<java.lang.String,java.awt.Color>)
|
||||
meth public static void setUseNativeWindowDecorations(boolean)
|
||||
meth public static void showMnemonics(java.awt.Component)
|
||||
meth public static void unregisterCustomDefaultsSource(java.io.File)
|
||||
meth public static void unregisterCustomDefaultsSource(java.lang.String)
|
||||
meth public static void unregisterCustomDefaultsSource(java.lang.String,java.lang.ClassLoader)
|
||||
meth public static void unregisterCustomDefaultsSource(java.net.URL)
|
||||
meth public static void updateUI()
|
||||
meth public static void updateUILater()
|
||||
meth public void initialize()
|
||||
meth public void registerUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>)
|
||||
meth public void setExtraDefaults(java.util.Map<java.lang.String,java.lang.String>)
|
||||
meth public void uninitialize()
|
||||
meth public void unregisterUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>)
|
||||
supr javax.swing.plaf.basic.BasicLookAndFeel
|
||||
hfds DESKTOPFONTHINTS,aquaLoaded,customDefaultsSources,desktopPropertyListener,desktopPropertyName,desktopPropertyName2,mnemonicHandler,oldPopupFactory,postInitialization,uiDefaultsGetters,updateUIPending
|
||||
hfds DESKTOPFONTHINTS,aquaLoaded,customDefaultsSources,desktopPropertyListener,desktopPropertyName,desktopPropertyName2,extraDefaults,getUIMethod,getUIMethodInitialized,globalExtraDefaults,mnemonicHandler,oldPopupFactory,postInitialization,preferredFontFamily,preferredLightFontFamily,preferredMonospacedFontFamily,preferredSemiboldFontFamily,subMenuUsabilityHelperInstalled,systemColorGetter,uiDefaultsGetters,updateUIPending
|
||||
hcls ActiveFont,FlatUIDefaults,ImageIconUIResource
|
||||
|
||||
CLSS public abstract interface static com.formdev.flatlaf.FlatLaf$DisabledIconProvider
|
||||
@@ -231,10 +276,13 @@ hfds baseTheme,dark,name,properties
|
||||
CLSS public abstract interface com.formdev.flatlaf.FlatSystemProperties
|
||||
fld public final static java.lang.String ANIMATION = "flatlaf.animation"
|
||||
fld public final static java.lang.String MENUBAR_EMBEDDED = "flatlaf.menuBarEmbedded"
|
||||
fld public final static java.lang.String NATIVE_LIBRARY_PATH = "flatlaf.nativeLibraryPath"
|
||||
fld public final static java.lang.String UI_SCALE = "flatlaf.uiScale"
|
||||
fld public final static java.lang.String UI_SCALE_ALLOW_SCALE_DOWN = "flatlaf.uiScale.allowScaleDown"
|
||||
fld public final static java.lang.String UI_SCALE_ENABLED = "flatlaf.uiScale.enabled"
|
||||
fld public final static java.lang.String UPDATE_UI_ON_SYSTEM_FONT_CHANGE = "flatlaf.updateUIOnSystemFontChange"
|
||||
fld public final static java.lang.String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations"
|
||||
fld public final static java.lang.String USE_NATIVE_LIBRARY = "flatlaf.useNativeLibrary"
|
||||
fld public final static java.lang.String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection"
|
||||
fld public final static java.lang.String USE_UBUNTU_FONT = "flatlaf.useUbuntuFont"
|
||||
fld public final static java.lang.String USE_WINDOW_DECORATIONS = "flatlaf.useWindowDecorations"
|
||||
@@ -253,7 +301,7 @@ meth public static boolean setup(java.io.InputStream)
|
||||
meth public static com.formdev.flatlaf.FlatLaf createLaf(com.formdev.flatlaf.IntelliJTheme)
|
||||
meth public static com.formdev.flatlaf.FlatLaf createLaf(java.io.InputStream) throws java.io.IOException
|
||||
supr java.lang.Object
|
||||
hfds checkboxDuplicateColors,checkboxKeyMapping,colors,icons,isMaterialUILite,namedColors,ui,uiKeyCopying,uiKeyInverseMapping,uiKeyMapping
|
||||
hfds checkboxDuplicateColors,checkboxKeyMapping,colors,icons,isMaterialUILite,namedColors,ui,uiKeyCopying,uiKeyDoNotOverride,uiKeyExcludes,uiKeyInverseMapping,uiKeyMapping
|
||||
|
||||
CLSS public static com.formdev.flatlaf.IntelliJTheme$ThemeLaf
|
||||
outer com.formdev.flatlaf.IntelliJTheme
|
||||
@@ -266,6 +314,26 @@ meth public java.lang.String getName()
|
||||
supr com.formdev.flatlaf.FlatLaf
|
||||
hfds theme
|
||||
|
||||
CLSS public com.formdev.flatlaf.themes.FlatMacDarkLaf
|
||||
cons public init()
|
||||
fld public final static java.lang.String NAME = "FlatLaf macOS Dark"
|
||||
meth public boolean isDark()
|
||||
meth public java.lang.String getDescription()
|
||||
meth public java.lang.String getName()
|
||||
meth public static boolean setup()
|
||||
meth public static void installLafInfo()
|
||||
supr com.formdev.flatlaf.FlatDarkLaf
|
||||
|
||||
CLSS public com.formdev.flatlaf.themes.FlatMacLightLaf
|
||||
cons public init()
|
||||
fld public final static java.lang.String NAME = "FlatLaf macOS Light"
|
||||
meth public boolean isDark()
|
||||
meth public java.lang.String getDescription()
|
||||
meth public java.lang.String getName()
|
||||
meth public static boolean setup()
|
||||
meth public static void installLafInfo()
|
||||
supr com.formdev.flatlaf.FlatLightLaf
|
||||
|
||||
CLSS public abstract interface com.formdev.flatlaf.util.AnimatedIcon
|
||||
innr public static AnimationSupport
|
||||
intf javax.swing.Icon
|
||||
@@ -330,7 +398,16 @@ innr public static HSLIncreaseDecrease
|
||||
innr public static Mix
|
||||
meth public !varargs static java.awt.Color applyFunctions(java.awt.Color,com.formdev.flatlaf.util.ColorFunctions$ColorFunction[])
|
||||
meth public static float clamp(float)
|
||||
meth public static float luma(java.awt.Color)
|
||||
meth public static java.awt.Color darken(java.awt.Color,float)
|
||||
meth public static java.awt.Color desaturate(java.awt.Color,float)
|
||||
meth public static java.awt.Color fade(java.awt.Color,float)
|
||||
meth public static java.awt.Color lighten(java.awt.Color,float)
|
||||
meth public static java.awt.Color mix(java.awt.Color,java.awt.Color,float)
|
||||
meth public static java.awt.Color saturate(java.awt.Color,float)
|
||||
meth public static java.awt.Color shade(java.awt.Color,float)
|
||||
meth public static java.awt.Color spin(java.awt.Color,float)
|
||||
meth public static java.awt.Color tint(java.awt.Color,float)
|
||||
supr java.lang.Object
|
||||
|
||||
CLSS public abstract interface static com.formdev.flatlaf.util.ColorFunctions$ColorFunction
|
||||
@@ -400,6 +477,17 @@ meth public java.lang.String toString()
|
||||
supr javax.swing.plaf.ColorUIResource
|
||||
hfds baseOfDefaultColorRGB,functions,hasBaseOfDefaultColor
|
||||
|
||||
CLSS public com.formdev.flatlaf.util.FontUtils
|
||||
cons public init()
|
||||
meth public static boolean installFont(java.net.URL)
|
||||
meth public static java.awt.Font getCompositeFont(java.lang.String,int,int)
|
||||
meth public static java.awt.Font[] getAllFonts()
|
||||
meth public static java.lang.String[] getAvailableFontFamilyNames()
|
||||
meth public static void loadFontFamily(java.lang.String)
|
||||
meth public static void registerFontFamilyLoader(java.lang.String,java.lang.Runnable)
|
||||
supr java.lang.Object
|
||||
hfds loadersMap
|
||||
|
||||
CLSS public com.formdev.flatlaf.util.Graphics2DProxy
|
||||
cons public init(java.awt.Graphics2D)
|
||||
meth public boolean drawImage(java.awt.Image,int,int,int,int,int,int,int,int,java.awt.Color,java.awt.image.ImageObserver)
|
||||
@@ -536,7 +624,7 @@ meth public static void drawStringWithYCorrection(javax.swing.JComponent,java.aw
|
||||
meth public static void paintAtScale1x(java.awt.Graphics2D,int,int,int,int,com.formdev.flatlaf.util.HiDPIUtils$Painter)
|
||||
meth public static void paintAtScale1x(java.awt.Graphics2D,javax.swing.JComponent,com.formdev.flatlaf.util.HiDPIUtils$Painter)
|
||||
supr java.lang.Object
|
||||
hfds useTextYCorrection
|
||||
hfds CORRECTION_INTER,CORRECTION_OPEN_SANS,CORRECTION_SEGOE_UI,CORRECTION_TAHOMA,SCALE_FACTORS,useDebugScaleFactor,useTextYCorrection
|
||||
|
||||
CLSS public abstract interface static com.formdev.flatlaf.util.HiDPIUtils$Painter
|
||||
outer com.formdev.flatlaf.util.HiDPIUtils
|
||||
@@ -566,6 +654,8 @@ meth public static java.util.List<java.awt.Image> getResolutionVariants(java.awt
|
||||
supr java.lang.Object
|
||||
|
||||
CLSS public com.formdev.flatlaf.util.NativeLibrary
|
||||
cons public init(java.io.File,boolean)
|
||||
cons public init(java.lang.String,boolean)
|
||||
cons public init(java.lang.String,java.lang.ClassLoader,boolean)
|
||||
meth public boolean isLoaded()
|
||||
supr java.lang.Object
|
||||
@@ -589,23 +679,59 @@ meth public void paintIcon(java.awt.Component,java.awt.Graphics,int,int)
|
||||
supr java.lang.Object
|
||||
hfds iconHeight,iconWidth,imageIcon,lastImage,lastSystemScaleFactor,lastUserScaleFactor
|
||||
|
||||
CLSS public com.formdev.flatlaf.util.SoftCache<%0 extends java.lang.Object, %1 extends java.lang.Object>
|
||||
cons public init()
|
||||
cons public init(int)
|
||||
intf java.util.Map<{com.formdev.flatlaf.util.SoftCache%0},{com.formdev.flatlaf.util.SoftCache%1}>
|
||||
meth public boolean containsKey(java.lang.Object)
|
||||
meth public boolean containsValue(java.lang.Object)
|
||||
meth public boolean isEmpty()
|
||||
meth public int size()
|
||||
meth public java.util.Collection<{com.formdev.flatlaf.util.SoftCache%1}> values()
|
||||
meth public java.util.Set<java.util.Map$Entry<{com.formdev.flatlaf.util.SoftCache%0},{com.formdev.flatlaf.util.SoftCache%1}>> entrySet()
|
||||
meth public java.util.Set<{com.formdev.flatlaf.util.SoftCache%0}> keySet()
|
||||
meth public void clear()
|
||||
meth public void forEach(java.util.function.BiConsumer<? super {com.formdev.flatlaf.util.SoftCache%0},? super {com.formdev.flatlaf.util.SoftCache%1}>)
|
||||
meth public void putAll(java.util.Map<? extends {com.formdev.flatlaf.util.SoftCache%0},? extends {com.formdev.flatlaf.util.SoftCache%1}>)
|
||||
meth public void replaceAll(java.util.function.BiFunction<? super {com.formdev.flatlaf.util.SoftCache%0},? super {com.formdev.flatlaf.util.SoftCache%1},? extends {com.formdev.flatlaf.util.SoftCache%1}>)
|
||||
meth public {com.formdev.flatlaf.util.SoftCache%1} get(java.lang.Object)
|
||||
meth public {com.formdev.flatlaf.util.SoftCache%1} put({com.formdev.flatlaf.util.SoftCache%0},{com.formdev.flatlaf.util.SoftCache%1})
|
||||
meth public {com.formdev.flatlaf.util.SoftCache%1} remove(java.lang.Object)
|
||||
supr java.lang.Object
|
||||
hfds map,queue
|
||||
hcls CacheReference
|
||||
|
||||
CLSS public com.formdev.flatlaf.util.StringUtils
|
||||
cons public init()
|
||||
meth public static boolean isEmpty(java.lang.String)
|
||||
meth public static boolean isTrimmedEmpty(java.lang.String)
|
||||
meth public static java.lang.String removeLeading(java.lang.String,java.lang.String)
|
||||
meth public static java.lang.String removeTrailing(java.lang.String,java.lang.String)
|
||||
meth public static java.lang.String substringTrimmed(java.lang.String,int)
|
||||
meth public static java.lang.String substringTrimmed(java.lang.String,int,int)
|
||||
meth public static java.util.List<java.lang.String> split(java.lang.String,char)
|
||||
meth public static java.util.List<java.lang.String> split(java.lang.String,char,boolean,boolean)
|
||||
supr java.lang.Object
|
||||
|
||||
CLSS public com.formdev.flatlaf.util.SwingUtils
|
||||
cons public init()
|
||||
meth public static <%0 extends java.awt.Component> {%%0} getComponentByName(java.awt.Container,java.lang.String)
|
||||
supr java.lang.Object
|
||||
|
||||
CLSS public com.formdev.flatlaf.util.SystemInfo
|
||||
cons public init()
|
||||
fld public final static boolean isAARCH64
|
||||
fld public final static boolean isJava_11_orLater
|
||||
fld public final static boolean isJava_12_orLater
|
||||
fld public final static boolean isJava_15_orLater
|
||||
fld public final static boolean isJava_17_orLater
|
||||
fld public final static boolean isJava_18_orLater
|
||||
fld public final static boolean isJava_9_orLater
|
||||
fld public final static boolean isJetBrainsJVM
|
||||
fld public final static boolean isJetBrainsJVM_11_orLater
|
||||
fld public final static boolean isKDE
|
||||
fld public final static boolean isLinux
|
||||
fld public final static boolean isMacFullWindowContentSupported
|
||||
fld public final static boolean isMacOS
|
||||
fld public final static boolean isMacOS_10_11_ElCapitan_orLater
|
||||
fld public final static boolean isMacOS_10_14_Mojave_orLater
|
||||
@@ -615,6 +741,8 @@ fld public final static boolean isWebswing
|
||||
fld public final static boolean isWinPE
|
||||
fld public final static boolean isWindows
|
||||
fld public final static boolean isWindows_10_orLater
|
||||
fld public final static boolean isWindows_11_orLater
|
||||
fld public final static boolean isX86
|
||||
fld public final static boolean isX86_64
|
||||
fld public final static long javaVersion
|
||||
fld public final static long osVersion
|
||||
@@ -627,6 +755,7 @@ cons public init()
|
||||
meth public static boolean isSystemScalingEnabled()
|
||||
meth public static double getSystemScaleFactor(java.awt.Graphics2D)
|
||||
meth public static double getSystemScaleFactor(java.awt.GraphicsConfiguration)
|
||||
meth public static float computeFontScaleFactor(java.awt.Font)
|
||||
meth public static float getUserScaleFactor()
|
||||
meth public static float scale(float)
|
||||
meth public static float unscale(float)
|
||||
@@ -933,6 +1062,34 @@ CLSS public abstract interface !annotation java.lang.annotation.Target
|
||||
intf java.lang.annotation.Annotation
|
||||
meth public abstract java.lang.annotation.ElementType[] value()
|
||||
|
||||
CLSS public abstract interface java.util.Map<%0 extends java.lang.Object, %1 extends java.lang.Object>
|
||||
innr public abstract interface static Entry
|
||||
meth public abstract boolean containsKey(java.lang.Object)
|
||||
meth public abstract boolean containsValue(java.lang.Object)
|
||||
meth public abstract boolean equals(java.lang.Object)
|
||||
meth public abstract boolean isEmpty()
|
||||
meth public abstract int hashCode()
|
||||
meth public abstract int size()
|
||||
meth public abstract java.util.Collection<{java.util.Map%1}> values()
|
||||
meth public abstract java.util.Set<java.util.Map$Entry<{java.util.Map%0},{java.util.Map%1}>> entrySet()
|
||||
meth public abstract java.util.Set<{java.util.Map%0}> keySet()
|
||||
meth public abstract void clear()
|
||||
meth public abstract void putAll(java.util.Map<? extends {java.util.Map%0},? extends {java.util.Map%1}>)
|
||||
meth public abstract {java.util.Map%1} get(java.lang.Object)
|
||||
meth public abstract {java.util.Map%1} put({java.util.Map%0},{java.util.Map%1})
|
||||
meth public abstract {java.util.Map%1} remove(java.lang.Object)
|
||||
meth public boolean remove(java.lang.Object,java.lang.Object)
|
||||
meth public boolean replace({java.util.Map%0},{java.util.Map%1},{java.util.Map%1})
|
||||
meth public void forEach(java.util.function.BiConsumer<? super {java.util.Map%0},? super {java.util.Map%1}>)
|
||||
meth public void replaceAll(java.util.function.BiFunction<? super {java.util.Map%0},? super {java.util.Map%1},? extends {java.util.Map%1}>)
|
||||
meth public {java.util.Map%1} compute({java.util.Map%0},java.util.function.BiFunction<? super {java.util.Map%0},? super {java.util.Map%1},? extends {java.util.Map%1}>)
|
||||
meth public {java.util.Map%1} computeIfAbsent({java.util.Map%0},java.util.function.Function<? super {java.util.Map%0},? extends {java.util.Map%1}>)
|
||||
meth public {java.util.Map%1} computeIfPresent({java.util.Map%0},java.util.function.BiFunction<? super {java.util.Map%0},? super {java.util.Map%1},? extends {java.util.Map%1}>)
|
||||
meth public {java.util.Map%1} getOrDefault(java.lang.Object,{java.util.Map%1})
|
||||
meth public {java.util.Map%1} merge({java.util.Map%0},{java.util.Map%1},java.util.function.BiFunction<? super {java.util.Map%1},? super {java.util.Map%1},? extends {java.util.Map%1}>)
|
||||
meth public {java.util.Map%1} putIfAbsent({java.util.Map%0},{java.util.Map%1})
|
||||
meth public {java.util.Map%1} replace({java.util.Map%0},{java.util.Map%1})
|
||||
|
||||
CLSS public abstract interface javax.swing.Icon
|
||||
meth public abstract int getIconHeight()
|
||||
meth public abstract int getIconWidth()
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
package com.formdev.flatlaf;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.IllegalComponentStateException;
|
||||
import java.awt.Window;
|
||||
import java.util.Objects;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.SwingConstants;
|
||||
@@ -124,6 +126,7 @@ public interface FlatClientProperties
|
||||
*/
|
||||
String SQUARE_SIZE = "JButton.squareSize";
|
||||
|
||||
|
||||
//---- JComponent ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -253,8 +256,41 @@ public interface FlatClientProperties
|
||||
*/
|
||||
String COMPONENT_FOCUS_OWNER = "JComponent.focusOwner";
|
||||
|
||||
/**
|
||||
* Specifies whether a component in an embedded menu bar should behave as caption
|
||||
* (left-click allows moving window, right-click shows window system menu).
|
||||
* The component does not receive mouse pressed/released/clicked/dragged events,
|
||||
* but it gets mouse entered/exited/moved events.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||
*
|
||||
* @since 2.5
|
||||
*/
|
||||
String COMPONENT_TITLE_BAR_CAPTION = "JComponent.titleBarCaption";
|
||||
|
||||
|
||||
//---- Popup --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Specifies the popup border corner radius if the component is shown in a popup
|
||||
* or if the component is the owner of another component that is shown in a popup.
|
||||
* <p>
|
||||
* Note that this is not available on all platforms since it requires special support.
|
||||
* Supported platforms:
|
||||
* <p>
|
||||
* <strong>Windows 11</strong> (x86 or x86_64): Only two corner radiuses are supported
|
||||
* by the OS: {@code DWMWCP_ROUND} is 8px and {@code DWMWCP_ROUNDSMALL} is 4px.
|
||||
* If this value is {@code 1 - 4}, then {@code DWMWCP_ROUNDSMALL} is used.
|
||||
* If it is {@code >= 5}, then {@code DWMWCP_ROUND} is used.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Integer}<br>
|
||||
*
|
||||
* @since 3.1
|
||||
*/
|
||||
String POPUP_BORDER_CORNER_RADIUS = "Popup.borderCornerRadius";
|
||||
|
||||
/**
|
||||
* Specifies whether a drop shadow is painted if the component is shown in a popup
|
||||
* or if the component is the owner of another component that is shown in a popup.
|
||||
@@ -273,6 +309,7 @@ public interface FlatClientProperties
|
||||
*/
|
||||
String POPUP_FORCE_HEAVY_WEIGHT = "Popup.forceHeavyWeight";
|
||||
|
||||
|
||||
//---- JProgressBar -------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -291,6 +328,7 @@ public interface FlatClientProperties
|
||||
*/
|
||||
String PROGRESS_BAR_SQUARE = "JProgressBar.square";
|
||||
|
||||
|
||||
//---- JRootPane ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -333,7 +371,7 @@ public interface FlatClientProperties
|
||||
|
||||
/**
|
||||
* Specifies whether the window icon should be shown in the window title bar
|
||||
* (requires enabled window decorations).
|
||||
* (requires enabled window decorations). Default is UI property {@code TitlePane.showIcon}.
|
||||
* <p>
|
||||
* Setting this shows/hides the windows icon
|
||||
* for the {@code JFrame} or {@code JDialog} that contains the root pane.
|
||||
@@ -349,6 +387,62 @@ public interface FlatClientProperties
|
||||
*/
|
||||
String TITLE_BAR_SHOW_ICON = "JRootPane.titleBarShowIcon";
|
||||
|
||||
/**
|
||||
* Specifies whether the window title should be shown in the window title bar
|
||||
* (requires enabled window decorations). Default is {@code true}.
|
||||
* <p>
|
||||
* Setting this shows/hides the windows title
|
||||
* for the {@code JFrame} or {@code JDialog} that contains the root pane.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||
*
|
||||
* @since 3
|
||||
*/
|
||||
String TITLE_BAR_SHOW_TITLE = "JRootPane.titleBarShowTitle";
|
||||
|
||||
/**
|
||||
* Specifies whether the "iconfify" button should be shown in the window title bar
|
||||
* (requires enabled window decorations). Default is {@code true}.
|
||||
* <p>
|
||||
* Setting this shows/hides the "iconfify" button
|
||||
* for the {@code JFrame} that contains the root pane.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||
*
|
||||
* @since 3
|
||||
*/
|
||||
String TITLE_BAR_SHOW_ICONIFFY = "JRootPane.titleBarShowIconify";
|
||||
|
||||
/**
|
||||
* Specifies whether the "maximize/restore" button should be shown in the window title bar
|
||||
* (requires enabled window decorations). Default is {@code true}.
|
||||
* <p>
|
||||
* Setting this shows/hides the "maximize/restore" button
|
||||
* for the {@code JFrame} that contains the root pane.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||
*
|
||||
* @since 3
|
||||
*/
|
||||
String TITLE_BAR_SHOW_MAXIMIZE = "JRootPane.titleBarShowMaximize";
|
||||
|
||||
/**
|
||||
* Specifies whether the "close" button should be shown in the window title bar
|
||||
* (requires enabled window decorations). Default is {@code true}.
|
||||
* <p>
|
||||
* Setting this shows/hides the "close" button
|
||||
* for the {@code JFrame} or {@code JDialog} that contains the root pane.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||
*
|
||||
* @since 3
|
||||
*/
|
||||
String TITLE_BAR_SHOW_CLOSE = "JRootPane.titleBarShowClose";
|
||||
|
||||
/**
|
||||
* Background color of window title bar (requires enabled window decorations).
|
||||
* <p>
|
||||
@@ -373,6 +467,48 @@ public interface FlatClientProperties
|
||||
*/
|
||||
String TITLE_BAR_FOREGROUND = "JRootPane.titleBarForeground";
|
||||
|
||||
/**
|
||||
* Specifies whether the glass pane should have full height and overlap the title bar,
|
||||
* if FlatLaf window decorations are enabled. Default is {@code false}.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||
*
|
||||
* @since 3.1
|
||||
*/
|
||||
String GLASS_PANE_FULL_HEIGHT = "JRootPane.glassPaneFullHeight";
|
||||
|
||||
/**
|
||||
* Specifies the style of the window title bar.
|
||||
* Besides the default title bar style, you can use a Utility-style title bar,
|
||||
* which is smaller than the default title bar.
|
||||
* <p>
|
||||
* On Windows 10/11, this requires FlatLaf window decorations.
|
||||
* On macOS, Java supports this out of the box.
|
||||
* <p>
|
||||
* Note that this client property must be set before the window becomes displayable.
|
||||
* Otherwise an {@link IllegalComponentStateException} is thrown.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||
* <strong>Allowed Values</strong>
|
||||
* {@link #WINDOW_STYLE_SMALL}
|
||||
*
|
||||
* @since 3.2
|
||||
*/
|
||||
String WINDOW_STYLE = "Window.style";
|
||||
|
||||
/**
|
||||
* The window has Utility-style title bar, which is smaller than default title bar.
|
||||
* <p>
|
||||
* This is the same as using {@link Window#setType}( {@link Window.Type#UTILITY} ).
|
||||
*
|
||||
* @see #WINDOW_STYLE
|
||||
* @since 3.2
|
||||
*/
|
||||
String WINDOW_STYLE_SMALL = "small";
|
||||
|
||||
|
||||
//---- JScrollBar / JScrollPane -------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -391,6 +527,42 @@ public interface FlatClientProperties
|
||||
*/
|
||||
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 --------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -786,6 +958,7 @@ public interface FlatClientProperties
|
||||
*/
|
||||
String TABBED_PANE_TRAILING_COMPONENT = "JTabbedPane.trailingComponent";
|
||||
|
||||
|
||||
//---- JTextField ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -955,10 +1128,26 @@ public interface FlatClientProperties
|
||||
*/
|
||||
String TEXT_FIELD_CLEAR_CALLBACK = "JTextField.clearCallback";
|
||||
|
||||
|
||||
//---- 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>
|
||||
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Integer}
|
||||
@@ -981,6 +1170,7 @@ public interface FlatClientProperties
|
||||
*/
|
||||
String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground";
|
||||
|
||||
|
||||
//---- JTree --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -1000,6 +1190,7 @@ public interface FlatClientProperties
|
||||
*/
|
||||
String TREE_PAINT_SELECTION = "JTree.paintSelection";
|
||||
|
||||
|
||||
//---- helper methods -----------------------------------------------------
|
||||
|
||||
/**
|
||||
|
||||
@@ -34,6 +34,8 @@ public class FlatDarculaLaf
|
||||
/**
|
||||
* Sets the application look and feel to this LaF
|
||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public static boolean setup() {
|
||||
return setup( new FlatDarculaLaf() );
|
||||
|
||||
@@ -33,6 +33,8 @@ public class FlatDarkLaf
|
||||
/**
|
||||
* Sets the application look and feel to this LaF
|
||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public static boolean setup() {
|
||||
return setup( new FlatDarkLaf() );
|
||||
|
||||
@@ -26,7 +26,7 @@ import javax.swing.UIDefaults;
|
||||
* Allows loading of additional .properties files from addon JARs.
|
||||
* {@link java.util.ServiceLoader} is used to load extensions of this class from addon JARs.
|
||||
* <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}
|
||||
* to the addon JAR. The file must contain a single line with the class name.
|
||||
* <p>
|
||||
@@ -61,7 +61,7 @@ public abstract class FlatDefaultsAddon
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Returns 10000 by default.
|
||||
*/
|
||||
|
||||
@@ -19,7 +19,7 @@ package com.formdev.flatlaf;
|
||||
/**
|
||||
* Default color palette for action icons and object icons.
|
||||
* <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 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.
|
||||
@@ -35,7 +35,7 @@ package com.formdev.flatlaf;
|
||||
* <p>
|
||||
* 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>
|
||||
* Instead use {@code UIManager.getColor( FlatIconColors.ACTIONS_GREY.key )}.
|
||||
* Instead, use {@code UIManager.getColor( FlatIconColors.ACTIONS_GREY.key )}.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
|
||||
@@ -50,7 +50,8 @@ class FlatInputMaps
|
||||
}
|
||||
|
||||
modifyInputMap( defaults, "ComboBox.ancestorInputMap",
|
||||
"SPACE", "spacePopup",
|
||||
// Space key still shows popup, but from FlatComboBoxUI.FlatKeySelectionManager
|
||||
// "SPACE", "spacePopup",
|
||||
|
||||
"UP", mac( "selectPrevious2", "selectPrevious" ),
|
||||
"DOWN", mac( "selectNext2", "selectNext" ),
|
||||
@@ -596,7 +597,7 @@ class FlatInputMaps
|
||||
//---- class LazyInputMapEx -----------------------------------------------
|
||||
|
||||
/**
|
||||
* Lazily creates a input map.
|
||||
* Lazily creates an input map.
|
||||
* Similar to {@link UIDefaults.LazyInputMap}, but can use multiple bindings arrays.
|
||||
*/
|
||||
private static class LazyInputMapEx
|
||||
|
||||
@@ -34,6 +34,8 @@ public class FlatIntelliJLaf
|
||||
/**
|
||||
* Sets the application look and feel to this LaF
|
||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public static boolean setup() {
|
||||
return setup( new FlatIntelliJLaf() );
|
||||
|
||||
@@ -30,6 +30,9 @@ import java.awt.image.ImageProducer;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.io.File;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
@@ -63,16 +66,20 @@ import javax.swing.UIDefaults.LazyValue;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.UnsupportedLookAndFeelException;
|
||||
import javax.swing.plaf.ColorUIResource;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.FontUIResource;
|
||||
import javax.swing.plaf.IconUIResource;
|
||||
import javax.swing.plaf.UIResource;
|
||||
import javax.swing.plaf.basic.BasicLookAndFeel;
|
||||
import javax.swing.plaf.metal.MetalLookAndFeel;
|
||||
import javax.swing.text.StyleContext;
|
||||
import javax.swing.text.html.HTMLEditorKit;
|
||||
import com.formdev.flatlaf.ui.FlatNativeWindowBorder;
|
||||
import com.formdev.flatlaf.ui.FlatPopupFactory;
|
||||
import com.formdev.flatlaf.ui.FlatRootPaneUI;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.util.FontUtils;
|
||||
import com.formdev.flatlaf.util.GrayFilter;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
|
||||
@@ -93,6 +100,7 @@ public abstract class FlatLaf
|
||||
private static List<Object> customDefaultsSources;
|
||||
private static Map<String, String> globalExtraDefaults;
|
||||
private Map<String, String> extraDefaults;
|
||||
private static Function<String, Color> systemColorGetter;
|
||||
|
||||
private String desktopPropertyName;
|
||||
private String desktopPropertyName2;
|
||||
@@ -103,13 +111,21 @@ public abstract class FlatLaf
|
||||
|
||||
private PopupFactory oldPopupFactory;
|
||||
private MnemonicHandler mnemonicHandler;
|
||||
private boolean subMenuUsabilityHelperInstalled;
|
||||
|
||||
private Consumer<UIDefaults> postInitialization;
|
||||
private List<Function<Object, Object>> uiDefaultsGetters;
|
||||
|
||||
private static String preferredFontFamily;
|
||||
private static String preferredLightFontFamily;
|
||||
private static String preferredSemiboldFontFamily;
|
||||
private static String preferredMonospacedFontFamily;
|
||||
|
||||
/**
|
||||
* Sets the application look and feel to the given LaF
|
||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public static boolean setup( LookAndFeel newLookAndFeel ) {
|
||||
try {
|
||||
@@ -166,18 +182,19 @@ public abstract class FlatLaf
|
||||
* Returns whether FlatLaf supports custom window decorations.
|
||||
* This depends on the operating system and on the used Java runtime.
|
||||
* <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>
|
||||
* Returns also {@code false} on Windows 10 if:
|
||||
* Returns also {@code false} on Windows 10/11 if:
|
||||
* <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
|
||||
* <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime 11 (or later)</a>
|
||||
* (<a href="https://github.com/JetBrains/JetBrainsRuntime">source code on github</a>)
|
||||
* and JBR supports custom window decorations
|
||||
* </li>
|
||||
* </ul>
|
||||
* In 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
|
||||
* {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} is not necessary.
|
||||
*/
|
||||
@@ -190,7 +207,7 @@ public abstract class FlatLaf
|
||||
FlatNativeWindowBorder.isSupported() )
|
||||
return false;
|
||||
|
||||
return SystemInfo.isWindows_10_orLater;
|
||||
return SystemInfo.isWindows_10_orLater || SystemInfo.isLinux;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -230,6 +247,15 @@ public abstract class FlatLaf
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
// do not initialize if this is not the current look and feel
|
||||
// This is only necessary for special Laf usage. E.g. in GUI builders,
|
||||
// which may use multiple Lafs and may invoke this method directly.
|
||||
// This avoids that listeners and factories are installed multiple times.
|
||||
// In case of the NetBeans GUI builder, which does not invoke uninitialize(),
|
||||
// this also avoids that listeners stay registered in the system.
|
||||
if( UIManager.getLookAndFeel() != this )
|
||||
return;
|
||||
|
||||
if( SystemInfo.isMacOS )
|
||||
initializeAqua();
|
||||
|
||||
@@ -243,6 +269,9 @@ public abstract class FlatLaf
|
||||
mnemonicHandler = new MnemonicHandler();
|
||||
mnemonicHandler.install();
|
||||
|
||||
// install submenu usability helper
|
||||
subMenuUsabilityHelperInstalled = SubMenuUsabilityHelper.install();
|
||||
|
||||
// listen to desktop property changes to update UI if system font or scaling changes
|
||||
if( SystemInfo.isWindows ) {
|
||||
// Windows 10 allows increasing font size independent of scaling:
|
||||
@@ -260,6 +289,9 @@ public abstract class FlatLaf
|
||||
}
|
||||
if( desktopPropertyName != null ) {
|
||||
desktopPropertyListener = e -> {
|
||||
if( !FlatSystemProperties.getBoolean( FlatSystemProperties.UPDATE_UI_ON_SYSTEM_FONT_CHANGE, true ) )
|
||||
return;
|
||||
|
||||
String propertyName = e.getPropertyName();
|
||||
if( desktopPropertyName.equals( propertyName ) || propertyName.equals( desktopPropertyName2 ) )
|
||||
reSetLookAndFeel();
|
||||
@@ -298,6 +330,10 @@ public abstract class FlatLaf
|
||||
|
||||
@Override
|
||||
public void uninitialize() {
|
||||
// do not uninitialize if this is not the current look and feel
|
||||
if( UIManager.getLookAndFeel() != this )
|
||||
return;
|
||||
|
||||
// remove desktop property listener
|
||||
if( desktopPropertyListener != null ) {
|
||||
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
||||
@@ -322,6 +358,12 @@ public abstract class FlatLaf
|
||||
mnemonicHandler = null;
|
||||
}
|
||||
|
||||
// uninstall submenu usability helper
|
||||
if( subMenuUsabilityHelperInstalled ) {
|
||||
SubMenuUsabilityHelper.uninstall();
|
||||
subMenuUsabilityHelperInstalled = false;
|
||||
}
|
||||
|
||||
// restore default link color
|
||||
new HTMLEditorKit().getStyleSheet().addRule( "a, address { color: blue; }" );
|
||||
postInitialization = null;
|
||||
@@ -350,7 +392,7 @@ public abstract class FlatLaf
|
||||
Method m = UIManager.class.getMethod( "createLookAndFeel", String.class );
|
||||
aquaLaf = (BasicLookAndFeel) m.invoke( null, "Mac OS X" );
|
||||
} else
|
||||
aquaLaf = (BasicLookAndFeel) Class.forName( aquaLafClassName ).getDeclaredConstructor().newInstance();
|
||||
aquaLaf = Class.forName( aquaLafClassName ).asSubclass( BasicLookAndFeel.class ).getDeclaredConstructor().newInstance();
|
||||
} catch( Exception ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to initialize Aqua look and feel '" + aquaLafClassName + "'.", ex );
|
||||
throw new IllegalStateException();
|
||||
@@ -502,7 +544,7 @@ public abstract class FlatLaf
|
||||
// which can happen in applications that use some plugin system
|
||||
// and load FlatLaf in a plugin that uses its own classloader.
|
||||
// (e.g. Apache NetBeans)
|
||||
if( defaults.get( "FileChooser.fileNameHeaderText" ) != null )
|
||||
if( defaults.get( "TabbedPane.moreTabsButtonToolTipText" ) != null )
|
||||
return;
|
||||
|
||||
// load FlatLaf resource bundle and add content to defaults
|
||||
@@ -540,13 +582,17 @@ public abstract class FlatLaf
|
||||
Object activeFont = new ActiveFont( null, null, -1, 0, 0, 0, 0 );
|
||||
|
||||
// override fonts
|
||||
List<String> fontKeys = new ArrayList<>( 50 );
|
||||
for( Object key : defaults.keySet() ) {
|
||||
if( key instanceof String && (((String)key).endsWith( ".font" ) || ((String)key).endsWith( "Font" )) )
|
||||
defaults.put( key, activeFont );
|
||||
fontKeys.add( (String) key );
|
||||
}
|
||||
for( String key : fontKeys )
|
||||
defaults.put( key, activeFont );
|
||||
|
||||
// add fonts that are not set in BasicLookAndFeel
|
||||
defaults.put( "RootPane.font", activeFont );
|
||||
defaults.put( "TitlePane.font", activeFont );
|
||||
}
|
||||
|
||||
private void initDefaultFont( UIDefaults defaults ) {
|
||||
@@ -595,6 +641,13 @@ public abstract class FlatLaf
|
||||
if( uiFont == null )
|
||||
uiFont = createCompositeFont( Font.SANS_SERIF, Font.PLAIN, 12 );
|
||||
|
||||
// use preferred font family (if specified)
|
||||
if( preferredFontFamily != null ) {
|
||||
FontUIResource preferredFont = createCompositeFont( preferredFontFamily, uiFont.getStyle(), uiFont.getSize() );
|
||||
if( !ActiveFont.isFallbackFont( preferredFont ) || ActiveFont.isDialogFamily( preferredFontFamily ) )
|
||||
uiFont = preferredFont;
|
||||
}
|
||||
|
||||
// get/remove "defaultFont" from defaults if set in properties files
|
||||
// (use remove() to avoid that ActiveFont.createValue() gets invoked)
|
||||
Object defaultFont = defaults.remove( "defaultFont" );
|
||||
@@ -605,7 +658,7 @@ public abstract class FlatLaf
|
||||
uiFont = ((ActiveFont)defaultFont).derive( baseFont, fontSize -> {
|
||||
return Math.round( fontSize * UIScale.computeFontScaleFactor( baseFont ) );
|
||||
} );
|
||||
};
|
||||
}
|
||||
|
||||
// increase font size if system property "flatlaf.uiScale" is set
|
||||
uiFont = UIScale.applyCustomScaleFactor( uiFont );
|
||||
@@ -615,6 +668,9 @@ public abstract class FlatLaf
|
||||
}
|
||||
|
||||
static FontUIResource createCompositeFont( String family, int style, int size ) {
|
||||
// load lazy font family
|
||||
FontUtils.loadFontFamily( family );
|
||||
|
||||
// using StyleContext.getFont() here because it uses
|
||||
// sun.font.FontUtilities.getCompositeFontUIResource()
|
||||
// and creates a composite font that is able to display all Unicode characters
|
||||
@@ -760,7 +816,7 @@ public abstract class FlatLaf
|
||||
* 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)}.
|
||||
* Otherwise, use {@link #registerCustomDefaultsSource(URL)}.
|
||||
*
|
||||
* @param packageName a package name (e.g. "com.myapp.resources")
|
||||
*/
|
||||
@@ -862,14 +918,14 @@ public abstract class FlatLaf
|
||||
* 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.
|
||||
* 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:
|
||||
* Sample that setups "FlatLaf Light" theme with white background color:
|
||||
* <pre>{@code
|
||||
* FlatLaf.setGlobalExtraDefaults( Collections.singletonMap( "@accentColor", "#f00" ) );
|
||||
* FlatLaf.setGlobalExtraDefaults( Collections.singletonMap( "@background", "#fff" ) );
|
||||
* FlatLightLaf.setup();
|
||||
* }</pre>
|
||||
*
|
||||
@@ -894,15 +950,15 @@ public abstract class FlatLaf
|
||||
* 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.
|
||||
* 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:
|
||||
* Sample that setups "FlatLaf Light" theme with white background color:
|
||||
* <pre>{@code
|
||||
* FlatLaf laf = new FlatLightLaf();
|
||||
* laf.setExtraDefaults( Collections.singletonMap( "@accentColor", "#f00" ) );
|
||||
* laf.setExtraDefaults( Collections.singletonMap( "@background", "#fff" ) );
|
||||
* FlatLaf.setup( laf );
|
||||
* }</pre>
|
||||
*
|
||||
@@ -944,6 +1000,36 @@ public abstract class FlatLaf
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the system color getter function, or {@code null}.
|
||||
*
|
||||
* @since 3
|
||||
*/
|
||||
public static Function<String, Color> getSystemColorGetter() {
|
||||
return systemColorGetter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a system color getter function that is invoked when function
|
||||
* {@code systemColor()} is used in FlatLaf properties files.
|
||||
* <p>
|
||||
* The name of the system color is passed as parameter to the function.
|
||||
* The function should return {@code null} for unknown system colors.
|
||||
* <p>
|
||||
* Can be used to change the accent color:
|
||||
* <pre>{@code
|
||||
* FlatLaf.setSystemColorGetter( name -> {
|
||||
* return name.equals( "accent" ) ? Color.red : null;
|
||||
* } );
|
||||
* FlatLightLaf.setup();
|
||||
* }</pre>
|
||||
*
|
||||
* @since 3
|
||||
*/
|
||||
public static void setSystemColorGetter( Function<String, Color> systemColorGetter ) {
|
||||
FlatLaf.systemColorGetter = systemColorGetter;
|
||||
}
|
||||
|
||||
private static void reSetLookAndFeel() {
|
||||
EventQueue.invokeLater( () -> {
|
||||
LookAndFeel lookAndFeel = UIManager.getLookAndFeel();
|
||||
@@ -951,7 +1037,7 @@ public abstract class FlatLaf
|
||||
// re-set current LaF
|
||||
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 );
|
||||
for( PropertyChangeListener l : UIManager.getPropertyChangeListeners() )
|
||||
l.propertyChange( e );
|
||||
@@ -995,7 +1081,7 @@ public abstract class FlatLaf
|
||||
/**
|
||||
* Returns whether native window decorations are supported on current platform.
|
||||
* <p>
|
||||
* This requires Windows 10, but may be disabled if running in special environments
|
||||
* 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.
|
||||
* If system property {@link FlatSystemProperties#USE_WINDOW_DECORATIONS} is set to
|
||||
* {@code false}, then this method also returns {@code false}.
|
||||
@@ -1201,31 +1287,181 @@ public abstract class FlatLaf
|
||||
*/
|
||||
public static final Object NULL_VALUE = new Object();
|
||||
|
||||
/**
|
||||
* Returns information about styleable values of a component.
|
||||
* <p>
|
||||
* This is equivalent to: {@code ((StyleableUI)c.getUI()).getStyleableInfos(c)}
|
||||
*
|
||||
* @since 2.5
|
||||
*/
|
||||
public static Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
StyleableUI ui = getStyleableUI( c );
|
||||
return (ui != null) ? ui.getStyleableInfos( c ) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the (styled) value for the given key from the given component.
|
||||
* <p>
|
||||
* This is equivalent to: {@code ((StyleableUI)c.getUI()).getStyleableValue(c, key)}
|
||||
*
|
||||
* @since 2.5
|
||||
*/
|
||||
@SuppressWarnings( "unchecked" )
|
||||
public static <T> T getStyleableValue( JComponent c, String key ) {
|
||||
StyleableUI ui = getStyleableUI( c );
|
||||
return (ui != null) ? (T) ui.getStyleableValue( c, key ) : null;
|
||||
}
|
||||
|
||||
private static StyleableUI getStyleableUI( JComponent c ) {
|
||||
if( !getUIMethodInitialized ) {
|
||||
getUIMethodInitialized = true;
|
||||
|
||||
if( SystemInfo.isJava_9_orLater ) {
|
||||
try {
|
||||
// JComponent.getUI() is available since Java 9
|
||||
getUIMethod = MethodHandles.lookup().findVirtual( JComponent.class, "getUI",
|
||||
MethodType.methodType( ComponentUI.class ) );
|
||||
} catch( Exception ex ) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Object ui;
|
||||
if( getUIMethod != null )
|
||||
ui = getUIMethod.invoke( c );
|
||||
else
|
||||
ui = c.getClass().getMethod( "getUI" ).invoke( c );
|
||||
return (ui instanceof StyleableUI) ? (StyleableUI) ui : null;
|
||||
} catch( Throwable ex ) {
|
||||
// ignore
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean getUIMethodInitialized;
|
||||
private static MethodHandle getUIMethod;
|
||||
|
||||
/**
|
||||
* Returns the preferred font family to be used for (nearly) all fonts; or {@code null}.
|
||||
*
|
||||
* @since 3
|
||||
*/
|
||||
public static String getPreferredFontFamily() {
|
||||
return preferredFontFamily;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the preferred font family to be used for (nearly) all fonts.
|
||||
* <p>
|
||||
* <strong>Note</strong>: This must be invoked <strong>before</strong> setting
|
||||
* the application look and feel.
|
||||
*
|
||||
* @since 3
|
||||
*/
|
||||
public static void setPreferredFontFamily( String preferredFontFamily ) {
|
||||
FlatLaf.preferredFontFamily = preferredFontFamily;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the preferred font family to be used for "light" fonts; or {@code null}.
|
||||
*
|
||||
* @since 3
|
||||
*/
|
||||
public static String getPreferredLightFontFamily() {
|
||||
return preferredLightFontFamily;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the preferred font family to be used for "light" fonts.
|
||||
* <p>
|
||||
* <strong>Note</strong>: This must be invoked <strong>before</strong> setting
|
||||
* the application look and feel.
|
||||
*
|
||||
* @since 3
|
||||
*/
|
||||
public static void setPreferredLightFontFamily( String preferredLightFontFamily ) {
|
||||
FlatLaf.preferredLightFontFamily = preferredLightFontFamily;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the preferred font family to be used for "semibold" fonts; or {@code null}.
|
||||
*
|
||||
* @since 3
|
||||
*/
|
||||
public static String getPreferredSemiboldFontFamily() {
|
||||
return preferredSemiboldFontFamily;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the preferred font family to be used for "semibold" fonts.
|
||||
* <p>
|
||||
* <strong>Note</strong>: This must be invoked <strong>before</strong> setting
|
||||
* the application look and feel.
|
||||
*
|
||||
* @since 3
|
||||
*/
|
||||
public static void setPreferredSemiboldFontFamily( String preferredSemiboldFontFamily ) {
|
||||
FlatLaf.preferredSemiboldFontFamily = preferredSemiboldFontFamily;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the preferred font family to be used for monospaced fonts; or {@code null}.
|
||||
*
|
||||
* @since 3
|
||||
*/
|
||||
public static String getPreferredMonospacedFontFamily() {
|
||||
return preferredMonospacedFontFamily;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the preferred font family to be used for monospaced fonts.
|
||||
* <p>
|
||||
* <strong>Note</strong>: This must be invoked <strong>before</strong> setting
|
||||
* the application look and feel.
|
||||
*
|
||||
* @since 3
|
||||
*/
|
||||
public static void setPreferredMonospacedFontFamily( String preferredMonospacedFontFamily ) {
|
||||
FlatLaf.preferredMonospacedFontFamily = preferredMonospacedFontFamily;
|
||||
}
|
||||
|
||||
//---- class FlatUIDefaults -----------------------------------------------
|
||||
|
||||
private class FlatUIDefaults
|
||||
extends UIDefaults
|
||||
{
|
||||
private UIDefaults metalDefaults;
|
||||
|
||||
FlatUIDefaults( int initialCapacity, float loadFactor ) {
|
||||
super( initialCapacity, loadFactor );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get( Object key ) {
|
||||
Object value = getValue( key );
|
||||
return (value != null) ? (value != NULL_VALUE ? value : null) : super.get( key );
|
||||
return get( key, null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get( Object key, Locale l ) {
|
||||
Object value = getValue( key );
|
||||
return (value != null) ? (value != NULL_VALUE ? value : null) : super.get( key, l );
|
||||
Object value = getFromUIDefaultsGetters( key );
|
||||
if( value != null )
|
||||
return (value != NULL_VALUE) ? value : null;
|
||||
|
||||
value = super.get( key, l );
|
||||
if( value != null )
|
||||
return value;
|
||||
|
||||
// get file chooser texts from Metal
|
||||
return (key instanceof String && ((String)key).startsWith( "FileChooser." ))
|
||||
? getFromMetal( (String) key, l )
|
||||
: null;
|
||||
}
|
||||
|
||||
private Object getValue( Object key ) {
|
||||
private Object getFromUIDefaultsGetters( Object key ) {
|
||||
// use local variable for getters to avoid potential multi-threading issues
|
||||
List<Function<Object, Object>> uiDefaultsGetters = FlatLaf.this.uiDefaultsGetters;
|
||||
|
||||
if( uiDefaultsGetters == null )
|
||||
return null;
|
||||
|
||||
@@ -1237,6 +1473,22 @@ public abstract class FlatLaf
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private synchronized Object getFromMetal( String key, Locale l ) {
|
||||
if( metalDefaults == null ) {
|
||||
metalDefaults = new MetalLookAndFeel() {
|
||||
// avoid unnecessary initialization
|
||||
@Override protected void initClassDefaults( UIDefaults table ) {}
|
||||
@Override protected void initSystemColorDefaults( UIDefaults table ) {}
|
||||
}.getDefaults();
|
||||
|
||||
// empty not needed defaults (to save memory) because we're only interested
|
||||
// in resource bundle strings, which are stored in another internal map
|
||||
metalDefaults.clear();
|
||||
}
|
||||
|
||||
return metalDefaults.get( key, l );
|
||||
}
|
||||
}
|
||||
|
||||
//---- class ActiveFont ---------------------------------------------------
|
||||
@@ -1319,7 +1571,7 @@ public abstract class FlatLaf
|
||||
int newStyle = (style != -1)
|
||||
? style
|
||||
: (styleChange != 0)
|
||||
? baseStyle & ~((styleChange >> 16) & 0xffff) | (styleChange & 0xffff)
|
||||
? (baseStyle & ~((styleChange >> 16) & 0xffff)) | (styleChange & 0xffff)
|
||||
: baseStyle;
|
||||
|
||||
// new size
|
||||
@@ -1335,9 +1587,16 @@ public abstract class FlatLaf
|
||||
|
||||
// create font for family
|
||||
if( families != null && !families.isEmpty() ) {
|
||||
String preferredFamily = preferredFamily( families );
|
||||
if( preferredFamily != null ) {
|
||||
Font font = createCompositeFont( preferredFamily, newStyle, newSize );
|
||||
if( !isFallbackFont( font ) || isDialogFamily( preferredFamily ) )
|
||||
return toUIResource( font );
|
||||
}
|
||||
|
||||
for( String family : families ) {
|
||||
Font font = createCompositeFont( family, newStyle, newSize );
|
||||
if( !isFallbackFont( font ) || family.equalsIgnoreCase( Font.DIALOG ) )
|
||||
if( !isFallbackFont( font ) || isDialogFamily( family ) )
|
||||
return toUIResource( font );
|
||||
}
|
||||
}
|
||||
@@ -1366,9 +1625,26 @@ public abstract class FlatLaf
|
||||
: new FontUIResource( font );
|
||||
}
|
||||
|
||||
private boolean isFallbackFont( Font font ) {
|
||||
private static boolean isFallbackFont( Font font ) {
|
||||
return Font.DIALOG.equalsIgnoreCase( font.getFamily() );
|
||||
}
|
||||
|
||||
private static boolean isDialogFamily( String family ) {
|
||||
return family.equalsIgnoreCase( Font.DIALOG );
|
||||
}
|
||||
|
||||
private static String preferredFamily( List<String> families ) {
|
||||
for( String family : families ) {
|
||||
family = family.toLowerCase( Locale.ENGLISH );
|
||||
if( family.endsWith( " light" ) || family.endsWith( "-thin" ) )
|
||||
return preferredLightFontFamily;
|
||||
if( family.endsWith( " semibold" ) || family.endsWith( "-medium" ) )
|
||||
return preferredSemiboldFontFamily;
|
||||
if( family.equals( "monospaced" ) )
|
||||
return preferredMonospacedFontFamily;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//---- class ImageIconUIResource ------------------------------------------
|
||||
|
||||
@@ -33,6 +33,8 @@ public class FlatLightLaf
|
||||
/**
|
||||
* Sets the application look and feel to this LaF
|
||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public static boolean setup() {
|
||||
return setup( new FlatLightLaf() );
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
@@ -96,7 +97,7 @@ public class FlatPropertiesLaf
|
||||
protected ArrayList<Class<?>> getLafClassesForDefaultsLoading() {
|
||||
ArrayList<Class<?>> lafClasses = new ArrayList<>();
|
||||
lafClasses.add( FlatLaf.class );
|
||||
switch( baseTheme.toLowerCase() ) {
|
||||
switch( baseTheme.toLowerCase( Locale.ENGLISH ) ) {
|
||||
default:
|
||||
case "light":
|
||||
lafClasses.add( FlatLightLaf.class );
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.formdev.flatlaf;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
/**
|
||||
@@ -34,7 +35,7 @@ public interface FlatSystemProperties
|
||||
* To replace the Java 9+ system scale factor, use system property "sun.java2d.uiScale",
|
||||
* which has the same syntax as this one.
|
||||
* <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>
|
||||
* <strong>Allowed Values</strong> e.g. {@code 1.5}, {@code 1.5x}, {@code 150%} or {@code 144dpi} (96dpi is 100%)<br>
|
||||
*/
|
||||
@@ -140,9 +141,47 @@ public interface FlatSystemProperties
|
||||
String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection";
|
||||
|
||||
/**
|
||||
* Specifies a directory in which the native FlatLaf library have been extracted.
|
||||
* Specifies whether FlatLaf updates the UI when the system font changes.
|
||||
* If {@code true}, {@link SwingUtilities#updateComponentTreeUI(java.awt.Component)}
|
||||
* gets invoked for all windows if the system font has changed.
|
||||
* This is the similar to when switching to another look and feel (theme).
|
||||
* Applications that do not work correctly when switching look and feel,
|
||||
* should disable this option to avoid corrupted UI.
|
||||
* <p>
|
||||
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||
* <strong>Default</strong> {@code true}
|
||||
*
|
||||
* @since 2.5
|
||||
*/
|
||||
String UPDATE_UI_ON_SYSTEM_FONT_CHANGE = "flatlaf.updateUIOnSystemFontChange";
|
||||
|
||||
/**
|
||||
* Specifies whether FlatLaf native library should be used.
|
||||
* <p>
|
||||
* Setting this to {@code false} disables loading native library,
|
||||
* which also disables some features that depend on the native library.
|
||||
* <p>
|
||||
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||
* <strong>Default</strong> {@code true}
|
||||
*
|
||||
* @since 3.2
|
||||
*/
|
||||
String USE_NATIVE_LIBRARY = "flatlaf.useNativeLibrary";
|
||||
|
||||
/**
|
||||
* Specifies a directory in which the native FlatLaf libraries 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.
|
||||
* <p>
|
||||
* If the value is {@code "system"}, then {@link System#loadLibrary(String)} is
|
||||
* used to load the native library.
|
||||
* Searches for the native library in classloader of caller
|
||||
* (using {@link ClassLoader#findLibrary(String)}) and in paths specified
|
||||
* in system properties {@code sun.boot.library.path} and {@code java.library.path}.
|
||||
* (supported since FlatLaf 2.6)
|
||||
* <p>
|
||||
* If the native library can not loaded from the given path (or via {@link System#loadLibrary(String)}),
|
||||
* then the embedded native library is extracted to the temporary directory and loaded from there.
|
||||
*
|
||||
* @since 2
|
||||
*/
|
||||
|
||||
@@ -23,13 +23,17 @@ import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import javax.swing.UIDefaults;
|
||||
import javax.swing.plaf.ColorUIResource;
|
||||
import com.formdev.flatlaf.json.Json;
|
||||
@@ -37,6 +41,7 @@ import com.formdev.flatlaf.json.ParseException;
|
||||
import com.formdev.flatlaf.util.ColorFunctions;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
import com.formdev.flatlaf.util.StringUtils;
|
||||
import com.formdev.flatlaf.util.SystemInfo;
|
||||
|
||||
/**
|
||||
* This class supports loading IntelliJ .theme.json files and using them as a Laf.
|
||||
@@ -60,9 +65,9 @@ public class IntelliJTheme
|
||||
|
||||
private final boolean isMaterialUILite;
|
||||
|
||||
private final Map<String, String> colors;
|
||||
private final Map<String, Object> ui;
|
||||
private final Map<String, Object> icons;
|
||||
private Map<String, String> colors;
|
||||
private Map<String, Object> ui;
|
||||
private Map<String, Object> icons;
|
||||
|
||||
private Map<String, ColorUIResource> namedColors = Collections.emptyMap();
|
||||
|
||||
@@ -72,6 +77,8 @@ public class IntelliJTheme
|
||||
*
|
||||
* The input stream is automatically closed.
|
||||
* Using a buffered input stream is not necessary.
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public static boolean setup( InputStream in ) {
|
||||
try {
|
||||
@@ -162,8 +169,11 @@ public class IntelliJTheme
|
||||
applyCheckBoxColors( defaults );
|
||||
|
||||
// copy values
|
||||
for( Map.Entry<String, String> e : uiKeyCopying.entrySet() )
|
||||
defaults.put( e.getKey(), defaults.get( e.getValue() ) );
|
||||
for( Map.Entry<String, String> e : uiKeyCopying.entrySet() ) {
|
||||
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
|
||||
Object panelBackground = defaults.get( "Panel.background" );
|
||||
@@ -175,7 +185,7 @@ public class IntelliJTheme
|
||||
defaults.put( "Button.hoverBorderColor", defaults.get( "Button.focusedBorderColor" ) );
|
||||
defaults.put( "HelpButton.hoverBorderColor", defaults.get( "Button.focusedBorderColor" ) );
|
||||
|
||||
// IDEA uses a SVG icon for the help button, but paints the background with Button.startBackground and Button.endBackground
|
||||
// IDEA uses an SVG icon for the help button, but paints the background with Button.startBackground and Button.endBackground
|
||||
Object helpButtonBackground = defaults.get( "Button.startBackground" );
|
||||
Object helpButtonBorderColor = defaults.get( "Button.startBorderColor" );
|
||||
if( helpButtonBackground == null )
|
||||
@@ -190,8 +200,9 @@ public class IntelliJTheme
|
||||
defaults.put( "HelpButton.focusedBackground", defaults.get( "Button.focusedBackground" ) );
|
||||
|
||||
// IDEA uses TextField.background for editable ComboBox and Spinner
|
||||
defaults.put( "ComboBox.editableBackground", defaults.get( "TextField.background" ) );
|
||||
defaults.put( "Spinner.background", defaults.get( "TextField.background" ) );
|
||||
Object textFieldBackground = get( defaults, themeSpecificDefaults, "TextField.background" );
|
||||
defaults.put( "ComboBox.editableBackground", textFieldBackground );
|
||||
defaults.put( "Spinner.background", textFieldBackground );
|
||||
|
||||
// Spinner arrow button always has same colors as ComboBox arrow button
|
||||
defaults.put( "Spinner.buttonBackground", defaults.get( "ComboBox.buttonEditableBackground" ) );
|
||||
@@ -199,22 +210,41 @@ public class IntelliJTheme
|
||||
defaults.put( "Spinner.buttonDisabledArrowColor", defaults.get( "ComboBox.buttonDisabledArrowColor" ) );
|
||||
|
||||
// some themes specify colors for TextField.background, but forget to specify it for other components
|
||||
// (probably because those components are not used in IntelliJ)
|
||||
if( uiKeys.contains( "TextField.background" ) ) {
|
||||
Object textFieldBackground = defaults.get( "TextField.background" );
|
||||
if( !uiKeys.contains( "FormattedTextField.background" ) )
|
||||
defaults.put( "FormattedTextField.background", textFieldBackground );
|
||||
if( !uiKeys.contains( "PasswordField.background" ) )
|
||||
defaults.put( "PasswordField.background", textFieldBackground );
|
||||
if( !uiKeys.contains( "EditorPane.background" ) )
|
||||
defaults.put( "EditorPane.background", textFieldBackground );
|
||||
if( !uiKeys.contains( "TextArea.background" ) )
|
||||
defaults.put( "TextArea.background", textFieldBackground );
|
||||
if( !uiKeys.contains( "TextPane.background" ) )
|
||||
defaults.put( "TextPane.background", textFieldBackground );
|
||||
if( !uiKeys.contains( "Spinner.background" ) )
|
||||
defaults.put( "Spinner.background", textFieldBackground );
|
||||
}
|
||||
// (probably because those components are not used in IntelliJ IDEA)
|
||||
putAll( defaults, textFieldBackground,
|
||||
"EditorPane.background",
|
||||
"FormattedTextField.background",
|
||||
"PasswordField.background",
|
||||
"TextArea.background",
|
||||
"TextPane.background"
|
||||
);
|
||||
putAll( defaults, get( defaults, themeSpecificDefaults, "TextField.selectionBackground" ),
|
||||
"EditorPane.selectionBackground",
|
||||
"FormattedTextField.selectionBackground",
|
||||
"PasswordField.selectionBackground",
|
||||
"TextArea.selectionBackground",
|
||||
"TextPane.selectionBackground"
|
||||
);
|
||||
putAll( defaults, get( defaults, themeSpecificDefaults, "TextField.selectionForeground" ),
|
||||
"EditorPane.selectionForeground",
|
||||
"FormattedTextField.selectionForeground",
|
||||
"PasswordField.selectionForeground",
|
||||
"TextArea.selectionForeground",
|
||||
"TextPane.selectionForeground"
|
||||
);
|
||||
|
||||
// fix disabled and not-editable backgrounds for text components, combobox and spinner
|
||||
// (IntelliJ IDEA does not use those colors; instead it used background color of parent)
|
||||
putAll( defaults, panelBackground,
|
||||
"ComboBox.disabledBackground",
|
||||
"EditorPane.disabledBackground", "EditorPane.inactiveBackground",
|
||||
"FormattedTextField.disabledBackground", "FormattedTextField.inactiveBackground",
|
||||
"PasswordField.disabledBackground", "PasswordField.inactiveBackground",
|
||||
"Spinner.disabledBackground",
|
||||
"TextArea.disabledBackground", "TextArea.inactiveBackground",
|
||||
"TextField.disabledBackground", "TextField.inactiveBackground",
|
||||
"TextPane.disabledBackground", "TextPane.inactiveBackground"
|
||||
);
|
||||
|
||||
// fix ToggleButton
|
||||
if( !uiKeys.contains( "ToggleButton.startBackground" ) && !uiKeys.contains( "*.startBackground" ) )
|
||||
@@ -241,8 +271,61 @@ public class IntelliJTheme
|
||||
if( rowHeight > 22 )
|
||||
defaults.put( "Tree.rowHeight", 22 );
|
||||
|
||||
// get (and remove) theme specific wildcard replacements, which override all other defaults that end with same suffix
|
||||
HashMap<String, Object> wildcards = new HashMap<>();
|
||||
Iterator<Entry<Object, Object>> it = themeSpecificDefaults.entrySet().iterator();
|
||||
while( it.hasNext() ) {
|
||||
Entry<Object, Object> e = it.next();
|
||||
String key = (String) e.getKey();
|
||||
if( key.startsWith( "*." ) ) {
|
||||
wildcards.put( key.substring( "*.".length() ), e.getValue() );
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// override UI defaults with theme specific wildcard replacements
|
||||
if( !wildcards.isEmpty() ) {
|
||||
for( Object key : defaults.keySet().toArray() ) {
|
||||
int dot;
|
||||
if( !(key instanceof String) ||
|
||||
(dot = ((String)key).lastIndexOf( '.' )) < 0 )
|
||||
continue;
|
||||
|
||||
String wildcardKey = ((String)key).substring( dot + 1 );
|
||||
Object wildcardValue = wildcards.get( wildcardKey );
|
||||
if( wildcardValue != null )
|
||||
defaults.put( key, wildcardValue );
|
||||
}
|
||||
}
|
||||
|
||||
// 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 );
|
||||
}
|
||||
|
||||
// let Java release memory
|
||||
colors = null;
|
||||
ui = null;
|
||||
icons = null;
|
||||
}
|
||||
|
||||
private Object get( UIDefaults defaults, Map<Object, Object> themeSpecificDefaults, String key ) {
|
||||
return themeSpecificDefaults.getOrDefault( key, defaults.get( key ) );
|
||||
}
|
||||
|
||||
private void putAll( UIDefaults defaults, Object value, String... keys ) {
|
||||
for( String key : keys )
|
||||
defaults.put( key, value );
|
||||
}
|
||||
|
||||
private Map<Object, Object> removeThemeSpecificDefaults( UIDefaults defaults ) {
|
||||
@@ -284,7 +367,7 @@ public class IntelliJTheme
|
||||
|
||||
for( Map.Entry<String, String> e : colors.entrySet() ) {
|
||||
String value = e.getValue();
|
||||
ColorUIResource color = UIDefaultsLoader.parseColor( value );
|
||||
ColorUIResource color = parseColor( value );
|
||||
if( color != null ) {
|
||||
String key = e.getKey();
|
||||
namedColors.put( key, color );
|
||||
@@ -299,23 +382,47 @@ public class IntelliJTheme
|
||||
@SuppressWarnings( "unchecked" )
|
||||
private void apply( String key, Object value, UIDefaults defaults, ArrayList<Object> defaultsKeysCache, Set<String> uiKeys ) {
|
||||
if( value instanceof Map ) {
|
||||
for( Map.Entry<String, Object> e : ((Map<String, Object>)value).entrySet() )
|
||||
apply( key + '.' + e.getKey(), e.getValue(), defaults, defaultsKeysCache, uiKeys );
|
||||
Map<String, Object> map = (Map<String, Object>)value;
|
||||
if( map.containsKey( "os.default" ) || map.containsKey( "os.windows" ) || map.containsKey( "os.mac" ) || map.containsKey( "os.linux" ) ) {
|
||||
String osKey = SystemInfo.isWindows ? "os.windows"
|
||||
: SystemInfo.isMacOS ? "os.mac"
|
||||
: SystemInfo.isLinux ? "os.linux" : null;
|
||||
if( osKey != null && map.containsKey( osKey ) )
|
||||
apply( key, map.get( osKey ), defaults, defaultsKeysCache, uiKeys );
|
||||
else if( map.containsKey( "os.default" ) )
|
||||
apply( key, map.get( "os.default" ), defaults, defaultsKeysCache, uiKeys );
|
||||
} else {
|
||||
for( Map.Entry<String, Object> e : map.entrySet() )
|
||||
apply( key + '.' + e.getKey(), e.getValue(), defaults, defaultsKeysCache, uiKeys );
|
||||
}
|
||||
} else {
|
||||
if( "".equals( value ) )
|
||||
return; // ignore empty value
|
||||
|
||||
uiKeys.add( key );
|
||||
|
||||
// fix ComboBox size and Spinner border in all Material UI Lite themes
|
||||
if( isMaterialUILite && (key.equals( "ComboBox.padding" ) || key.equals( "Spinner.border" )) )
|
||||
return; // ignore
|
||||
// ignore some properties that affect sizes
|
||||
if( key.endsWith( ".border" ) ||
|
||||
key.endsWith( ".rowHeight" ) ||
|
||||
key.equals( "ComboBox.padding" ) ||
|
||||
key.equals( "Spinner.padding" ) ||
|
||||
key.equals( "Tree.leftChildIndent" ) ||
|
||||
key.equals( "Tree.rightChildIndent" ) )
|
||||
return; // ignore
|
||||
|
||||
// map keys
|
||||
key = uiKeyMapping.getOrDefault( key, key );
|
||||
if( key.isEmpty() )
|
||||
return; // ignore key
|
||||
|
||||
// exclude properties
|
||||
int dot = key.indexOf( '.' );
|
||||
if( dot > 0 && uiKeyExcludes.contains( key.substring( 0, dot + 1 ) ) )
|
||||
return;
|
||||
|
||||
if( uiKeyDoNotOverride.contains( key ) && uiKeys.contains( key ) )
|
||||
return;
|
||||
|
||||
uiKeys.add( key );
|
||||
|
||||
String valueStr = value.toString();
|
||||
|
||||
// map named colors
|
||||
@@ -361,7 +468,8 @@ public class IntelliJTheme
|
||||
// replace all values in UI defaults that match the wildcard key
|
||||
for( Object k : defaultsKeysCache ) {
|
||||
if( k.equals( "Desktop.background" ) ||
|
||||
k.equals( "DesktopIcon.background" ) )
|
||||
k.equals( "DesktopIcon.background" ) ||
|
||||
k.equals( "TabbedPane.focusColor" ) )
|
||||
continue;
|
||||
|
||||
if( k instanceof String ) {
|
||||
@@ -419,12 +527,20 @@ public class IntelliJTheme
|
||||
ColorUIResource color = namedColors.get( value );
|
||||
|
||||
// parse color
|
||||
return (color != null) ? color : UIDefaultsLoader.parseColor( value );
|
||||
return (color != null) ? color : parseColor( value );
|
||||
}
|
||||
|
||||
private ColorUIResource parseColor( String value ) {
|
||||
try {
|
||||
return UIDefaultsLoader.parseColor( value );
|
||||
} catch( IllegalArgumentException ex ) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Because IDEA uses SVGs for check boxes and radio buttons, the colors for
|
||||
* this two components are specified in "icons > ColorPalette".
|
||||
* these two components are specified in "icons > ColorPalette".
|
||||
* FlatLaf uses vector icons and expects colors for the two components in UI defaults.
|
||||
*/
|
||||
private void applyCheckBoxColors( UIDefaults defaults ) {
|
||||
@@ -444,18 +560,6 @@ public class IntelliJTheme
|
||||
if( !key.startsWith( "Checkbox." ) || !(value instanceof String) )
|
||||
continue;
|
||||
|
||||
if( key.equals( "Checkbox.Background.Default" ) ||
|
||||
key.equals( "Checkbox.Foreground.Selected" ) )
|
||||
{
|
||||
// This two keys do not work correctly in IDEA because they
|
||||
// map SVG color "#ffffff" to another color, but checkBox.svg and
|
||||
// radio.svg (in package com.intellij.ide.ui.laf.icons.intellij)
|
||||
// use "#fff". So use white to get same appearance as in IDEA.
|
||||
value = "#ffffff";
|
||||
}
|
||||
|
||||
String key2 = checkboxDuplicateColors.get( key );
|
||||
|
||||
if( dark )
|
||||
key = StringUtils.removeTrailing( key, ".Dark" );
|
||||
|
||||
@@ -469,6 +573,7 @@ public class IntelliJTheme
|
||||
if( color != null ) {
|
||||
defaults.put( newKey, color );
|
||||
|
||||
String key2 = checkboxDuplicateColors.get( key + ".Dark");
|
||||
if( key2 != null ) {
|
||||
// When IDEA replaces colors in SVGs it uses color values and not the keys
|
||||
// from com.intellij.ide.ui.UITheme.colorPalette, but there are some keys that
|
||||
@@ -511,7 +616,7 @@ public class IntelliJTheme
|
||||
// radioFocused.svg and radioSelectedFocused.svg
|
||||
// use opacity=".65" for the border
|
||||
// --> add alpha to focused border colors
|
||||
String[] focusedBorderColorKeys = new String[] {
|
||||
String[] focusedBorderColorKeys = {
|
||||
"CheckBox.icon.focusedBorderColor",
|
||||
"CheckBox.icon.focusedSelectedBorderColor",
|
||||
"CheckBox.icon[filled].focusedBorderColor",
|
||||
@@ -533,17 +638,59 @@ public class IntelliJTheme
|
||||
defaults.put( destKey, defaults.get( srcKey ) );
|
||||
}
|
||||
|
||||
private static final Set<String> uiKeyExcludes;
|
||||
private static final Set<String> uiKeyDoNotOverride;
|
||||
/** 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). */
|
||||
private static Map<String, String> uiKeyCopying = new HashMap<>();
|
||||
private static Map<String, String> uiKeyInverseMapping = new HashMap<>();
|
||||
private static Map<String, String> checkboxKeyMapping = new HashMap<>();
|
||||
private static Map<String, String> checkboxDuplicateColors = new HashMap<>();
|
||||
private static final Map<String, String> uiKeyCopying = new LinkedHashMap<>();
|
||||
private static final Map<String, String> uiKeyInverseMapping = new HashMap<>();
|
||||
private static final Map<String, String> checkboxKeyMapping = new HashMap<>();
|
||||
private static final Map<String, String> checkboxDuplicateColors = new HashMap<>();
|
||||
|
||||
static {
|
||||
// IntelliJ UI properties that are not used in FlatLaf
|
||||
uiKeyExcludes = new HashSet<>( Arrays.asList(
|
||||
"ActionButton.", "ActionToolbar.", "ActionsList.", "AppInspector.", "AssignedMnemonic.", "Autocomplete.",
|
||||
"AvailableMnemonic.",
|
||||
"BigSpinner.", "Bookmark.", "BookmarkIcon.", "BookmarkMnemonicAssigned.", "BookmarkMnemonicAvailable.",
|
||||
"BookmarkMnemonicCurrent.", "BookmarkMnemonicIcon.", "Borders.", "Breakpoint.",
|
||||
"Canvas.", "CodeWithMe.", "ComboBoxButton.", "CompletionPopup.", "ComplexPopup.", "Content.",
|
||||
"CurrentMnemonic.", "Counter.",
|
||||
"Debugger.", "DebuggerPopup.", "DebuggerTabs.", "DefaultTabs.", "Dialog.", "DialogWrapper.", "DragAndDrop.",
|
||||
"Editor.", "EditorGroupsTabs.", "EditorTabs.",
|
||||
"FileColor.", "FlameGraph.", "Focus.",
|
||||
"Git.", "Github.", "GotItTooltip.", "Group.", "Gutter.", "GutterTooltip.",
|
||||
"HeaderColor.", "HelpTooltip.", "Hg.",
|
||||
"IconBadge.", "InformationHint.", "InplaceRefactoringPopup.",
|
||||
"Lesson.", "Link.", "LiveIndicator.",
|
||||
"MainMenu.", "MainToolbar.", "MemoryIndicator.", "MlModelBinding.", "MnemonicIcon.",
|
||||
"NavBar.", "NewClass.", "NewPSD.", "Notification.", "Notifications.", "NotificationsToolwindow.",
|
||||
"OnePixelDivider.", "OptionButton.", "Outline.",
|
||||
"ParameterInfo.", "Plugins.", "ProgressIcon.", "PsiViewer.",
|
||||
"ReviewList.", "RunWidget.",
|
||||
"ScreenView.", "SearchEverywhere.", "SearchFieldWithExtension.", "SearchMatch.", "SearchOption.",
|
||||
"SearchResults.", "SegmentedButton.", "Settings.", "SidePanel.", "Space.", "SpeedSearch.", "StateWidget.",
|
||||
"StatusBar.",
|
||||
"Tag.", "TipOfTheDay.", "ToolbarComboWidget.", "ToolWindow.",
|
||||
"UIDesigner.", "UnattendedHostStatus.",
|
||||
"ValidationTooltip.", "VersionControl.",
|
||||
"WelcomeScreen.",
|
||||
|
||||
// lower case
|
||||
"darcula.", "dropArea.", "icons.", "intellijlaf.", "macOSWindow.", "material.", "tooltips.",
|
||||
|
||||
// possible typos in .theme.json files
|
||||
"Checkbox.", "Toolbar.", "Tooltip.", "UiDesigner.", "link."
|
||||
) );
|
||||
|
||||
uiKeyDoNotOverride = new HashSet<>( Arrays.asList(
|
||||
"TabbedPane.selectedForeground"
|
||||
) );
|
||||
|
||||
// ComboBox
|
||||
uiKeyMapping.put( "ComboBox.background", "" ); // ignore
|
||||
uiKeyMapping.put( "ComboBox.buttonBackground", "" ); // ignore
|
||||
uiKeyMapping.put( "ComboBox.nonEditableBackground", "ComboBox.background" );
|
||||
uiKeyMapping.put( "ComboBox.ArrowButton.background", "ComboBox.buttonEditableBackground" );
|
||||
uiKeyMapping.put( "ComboBox.ArrowButton.disabledIconColor", "ComboBox.buttonDisabledArrowColor" );
|
||||
@@ -600,6 +747,11 @@ public class IntelliJTheme
|
||||
uiKeyCopying.put( "Spinner.buttonSeparatorColor", "Component.borderColor" );
|
||||
uiKeyCopying.put( "Spinner.buttonDisabledSeparatorColor", "Component.disabledBorderColor" );
|
||||
|
||||
// TabbedPane
|
||||
uiKeyMapping.put( "DefaultTabs.underlinedTabBackground", "TabbedPane.selectedBackground" );
|
||||
uiKeyMapping.put( "DefaultTabs.underlinedTabForeground", "TabbedPane.selectedForeground" );
|
||||
uiKeyMapping.put( "DefaultTabs.inactiveUnderlineColor", "TabbedPane.inactiveUnderlineColor" );
|
||||
|
||||
// TitlePane
|
||||
uiKeyCopying.put( "TitlePane.inactiveBackground", "TitlePane.background" );
|
||||
uiKeyMapping.put( "TitlePane.infoForeground", "TitlePane.foreground" );
|
||||
|
||||
@@ -23,11 +23,14 @@ import java.awt.GraphicsEnvironment;
|
||||
import java.awt.Toolkit;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.StringTokenizer;
|
||||
import javax.swing.text.StyleContext;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
@@ -68,7 +71,7 @@ class LinuxFontPolicy
|
||||
if( word.endsWith( "," ) )
|
||||
word = word.substring( 0, word.length() - 1 ).trim();
|
||||
|
||||
String lword = word.toLowerCase();
|
||||
String lword = word.toLowerCase( Locale.ENGLISH );
|
||||
if( lword.equals( "italic" ) || lword.equals( "oblique" ) )
|
||||
style |= Font.ITALIC;
|
||||
else if( lword.equals( "bold" ) )
|
||||
@@ -104,7 +107,7 @@ class LinuxFontPolicy
|
||||
size = 1;
|
||||
|
||||
// handle logical font names
|
||||
String logicalFamily = mapFcName( family.toLowerCase() );
|
||||
String logicalFamily = mapFcName( family.toLowerCase( Locale.ENGLISH ) );
|
||||
if( logicalFamily != null )
|
||||
family = logicalFamily;
|
||||
|
||||
@@ -143,7 +146,7 @@ class LinuxFontPolicy
|
||||
return createFont( Font.DIALOG, style, size, dsize );
|
||||
|
||||
// check whether last work contains some font weight (e.g. Ultra-Bold or Heavy)
|
||||
String lastWord = family.substring( index + 1 ).toLowerCase();
|
||||
String lastWord = family.substring( index + 1 ).toLowerCase( Locale.ENGLISH );
|
||||
if( lastWord.contains( "bold" ) || lastWord.contains( "heavy" ) || lastWord.contains( "black" ) )
|
||||
style |= Font.BOLD;
|
||||
|
||||
@@ -170,7 +173,7 @@ class LinuxFontPolicy
|
||||
|
||||
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "gnome.Xft/DPI" );
|
||||
if( value instanceof Integer ) {
|
||||
int dpi = ((Integer)value).intValue() / 1024;
|
||||
int dpi = (Integer) value / 1024;
|
||||
if( dpi == -1 )
|
||||
dpi = 96;
|
||||
if( dpi < 50 )
|
||||
@@ -197,7 +200,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
|
||||
* (System Settings > Fonts > Force Font DPI). A application restart is necessary.
|
||||
@@ -257,6 +260,7 @@ class LinuxFontPolicy
|
||||
return createFont( family, style, size, dsize );
|
||||
}
|
||||
|
||||
@SuppressWarnings( "MixedMutabilityReturnType" ) // Error Prone
|
||||
private static List<String> readConfig( String filename ) {
|
||||
File userHome = new File( System.getProperty( "user.home" ) );
|
||||
|
||||
@@ -277,8 +281,10 @@ class LinuxFontPolicy
|
||||
|
||||
// read config file
|
||||
ArrayList<String> lines = new ArrayList<>( 200 );
|
||||
try( BufferedReader reader = new BufferedReader( new FileReader( file ) ) ) {
|
||||
String line = null;
|
||||
try( BufferedReader reader = new BufferedReader( new InputStreamReader(
|
||||
new FileInputStream( file ), StandardCharsets.US_ASCII ) ) )
|
||||
{
|
||||
String line;
|
||||
while( (line = reader.readLine()) != null )
|
||||
lines.add( line );
|
||||
} catch( IOException ex ) {
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
/*
|
||||
* Copyright 2022 FormDev Software GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.formdev.flatlaf;
|
||||
|
||||
import java.awt.AWTEvent;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.MouseInfo;
|
||||
import java.awt.Point;
|
||||
import java.awt.PointerInfo;
|
||||
import java.awt.Polygon;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.MouseEvent;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLayeredPane;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.MenuElement;
|
||||
import javax.swing.MenuSelectionManager;
|
||||
import javax.swing.RootPaneContainer;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.Timer;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
|
||||
/**
|
||||
* Improves usability of submenus by using a
|
||||
* <a href="https://height.app/blog/guide-to-build-context-menus#safe-triangle">safe triangle</a>
|
||||
* to avoid that the submenu closes while the user moves the mouse to it.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
class SubMenuUsabilityHelper
|
||||
implements ChangeListener
|
||||
{
|
||||
private static final String KEY_USE_SAFE_TRIANGLE = "Menu.useSafeTriangle";
|
||||
private static final String KEY_SHOW_SAFE_TRIANGLE = "FlatLaf.debug.menu.showSafeTriangle";
|
||||
|
||||
// Using a static field to ensure that there is only one instance in the system.
|
||||
// Multiple instances would freeze the application.
|
||||
// https://github.com/apache/netbeans/issues/4231#issuecomment-1179616607
|
||||
private static SubMenuUsabilityHelper instance;
|
||||
|
||||
private SubMenuEventQueue subMenuEventQueue;
|
||||
private SafeTrianglePainter safeTrianglePainter;
|
||||
private boolean changePending;
|
||||
|
||||
// mouse location in screen coordinates
|
||||
private int mouseX;
|
||||
private int mouseY;
|
||||
|
||||
// target popup bounds in screen coordinates
|
||||
private int targetX;
|
||||
private int targetTopY;
|
||||
private int targetBottomY;
|
||||
|
||||
private Rectangle invokerBounds;
|
||||
|
||||
static synchronized boolean install() {
|
||||
if( instance != null )
|
||||
return false;
|
||||
|
||||
instance = new SubMenuUsabilityHelper();
|
||||
MenuSelectionManager.defaultManager().addChangeListener( instance );
|
||||
return true;
|
||||
}
|
||||
|
||||
static synchronized void uninstall() {
|
||||
if( instance == null )
|
||||
return;
|
||||
|
||||
MenuSelectionManager.defaultManager().removeChangeListener( instance );
|
||||
instance.uninstallEventQueue();
|
||||
instance = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateChanged( ChangeEvent e ) {
|
||||
if( !FlatUIUtils.getUIBoolean( KEY_USE_SAFE_TRIANGLE, true ))
|
||||
return;
|
||||
|
||||
// handle menu selection change later, but only once in case of temporary changes
|
||||
// e.g. moving mouse from one menu item to another one, fires two events:
|
||||
// 1. old menu item is removed from menu selection
|
||||
// 2. new menu item is added to menu selection
|
||||
synchronized( this ) {
|
||||
if( changePending )
|
||||
return;
|
||||
changePending = true;
|
||||
}
|
||||
|
||||
EventQueue.invokeLater( () -> {
|
||||
synchronized( this ) {
|
||||
changePending = false;
|
||||
}
|
||||
menuSelectionChanged();
|
||||
} );
|
||||
}
|
||||
|
||||
private void menuSelectionChanged() {
|
||||
MenuElement[] path = MenuSelectionManager.defaultManager().getSelectedPath();
|
||||
|
||||
/*debug
|
||||
System.out.println( "--- " + path.length );
|
||||
for( int i = 0; i < path.length; i++ )
|
||||
System.out.println( " " + i + ": " + path[i].getClass().getName() );
|
||||
debug*/
|
||||
|
||||
// find submenu in menu selection
|
||||
int subMenuIndex = findSubMenu( path );
|
||||
|
||||
// uninstall if there is no submenu in selection
|
||||
if( subMenuIndex < 0 || subMenuIndex != path.length - 1 ) {
|
||||
uninstallEventQueue();
|
||||
return;
|
||||
}
|
||||
|
||||
// get current mouse location
|
||||
PointerInfo pointerInfo = MouseInfo.getPointerInfo();
|
||||
Point mouseLocation = (pointerInfo != null) ? pointerInfo.getLocation() : new Point();
|
||||
mouseX = mouseLocation.x;
|
||||
mouseY = mouseLocation.y;
|
||||
|
||||
// check whether popup is showing, which is e.g. not the case if it is empty
|
||||
JPopupMenu popup = (JPopupMenu) path[subMenuIndex];
|
||||
if( !popup.isShowing() ) {
|
||||
uninstallEventQueue();
|
||||
return;
|
||||
}
|
||||
|
||||
// get invoker screen bounds
|
||||
Component invoker = popup.getInvoker();
|
||||
invokerBounds = (invoker != null && invoker.isShowing())
|
||||
? new Rectangle( invoker.getLocationOnScreen(), invoker.getSize() )
|
||||
: null;
|
||||
|
||||
// check whether mouse location is within invoker
|
||||
if( invokerBounds != null && !invokerBounds.contains( mouseX, mouseY ) ) {
|
||||
uninstallEventQueue();
|
||||
return;
|
||||
}
|
||||
|
||||
// compute top/bottom target locations
|
||||
Point popupLocation = popup.getLocationOnScreen();
|
||||
Dimension popupSize = popup.getSize();
|
||||
targetX = (mouseX < popupLocation.x + (popupSize.width / 2))
|
||||
? popupLocation.x
|
||||
: popupLocation.x + popupSize.width;
|
||||
targetTopY = popupLocation.y;
|
||||
targetBottomY = popupLocation.y + popupSize.height;
|
||||
|
||||
// install own event queue to supress mouse events when mouse is moved within safe triangle
|
||||
if( subMenuEventQueue == null )
|
||||
subMenuEventQueue = new SubMenuEventQueue();
|
||||
|
||||
// create safe triangle painter
|
||||
if( safeTrianglePainter == null && UIManager.getBoolean( KEY_SHOW_SAFE_TRIANGLE ) )
|
||||
safeTrianglePainter = new SafeTrianglePainter( popup );
|
||||
}
|
||||
|
||||
private void uninstallEventQueue() {
|
||||
if( subMenuEventQueue != null ) {
|
||||
subMenuEventQueue.uninstall();
|
||||
subMenuEventQueue = null;
|
||||
}
|
||||
|
||||
if( safeTrianglePainter != null ) {
|
||||
safeTrianglePainter.uninstall();
|
||||
safeTrianglePainter = null;
|
||||
}
|
||||
}
|
||||
|
||||
private int findSubMenu( MenuElement[] path ) {
|
||||
for( int i = path.length - 1; i >= 1; i-- ) {
|
||||
if( path[i] instanceof JPopupMenu &&
|
||||
path[i - 1] instanceof JMenu &&
|
||||
!((JMenu)path[i - 1]).isTopLevelMenu() )
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private Polygon createSafeTriangle() {
|
||||
return new Polygon(
|
||||
new int[] { mouseX, targetX, targetX },
|
||||
new int[] { mouseY, targetTopY, targetBottomY },
|
||||
3 );
|
||||
}
|
||||
|
||||
//---- class SubMenuEventQueue --------------------------------------------
|
||||
|
||||
private class SubMenuEventQueue
|
||||
extends EventQueue
|
||||
{
|
||||
private Timer mouseUpdateTimer;
|
||||
private Timer timeoutTimer;
|
||||
|
||||
private int newMouseX;
|
||||
private int newMouseY;
|
||||
private AWTEvent lastMouseEvent;
|
||||
|
||||
SubMenuEventQueue() {
|
||||
// timer used to slightly delay update of mouse location used for safe triangle
|
||||
mouseUpdateTimer = new Timer( 50, e -> {
|
||||
mouseX = newMouseX;
|
||||
mouseY = newMouseY;
|
||||
|
||||
if( safeTrianglePainter != null )
|
||||
safeTrianglePainter.repaint();
|
||||
} );
|
||||
mouseUpdateTimer.setRepeats( false );
|
||||
|
||||
// timer used to timeout safe triangle when mouse stops moving
|
||||
timeoutTimer = new Timer( 200, e -> {
|
||||
if( invokerBounds != null && !invokerBounds.contains( newMouseX, newMouseY ) ) {
|
||||
// post last mouse event, which selects menu item at mouse location
|
||||
if( lastMouseEvent != null ) {
|
||||
postEvent( lastMouseEvent );
|
||||
lastMouseEvent = null;
|
||||
}
|
||||
|
||||
uninstallEventQueue();
|
||||
return;
|
||||
}
|
||||
} );
|
||||
timeoutTimer.setRepeats( false );
|
||||
|
||||
Toolkit.getDefaultToolkit().getSystemEventQueue().push( this );
|
||||
}
|
||||
|
||||
void uninstall() {
|
||||
mouseUpdateTimer.stop();
|
||||
mouseUpdateTimer = null;
|
||||
|
||||
timeoutTimer.stop();
|
||||
timeoutTimer = null;
|
||||
|
||||
lastMouseEvent = null;
|
||||
|
||||
super.pop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispatchEvent( AWTEvent e ) {
|
||||
int id = e.getID();
|
||||
|
||||
if( e instanceof MouseEvent &&
|
||||
(id == MouseEvent.MOUSE_MOVED || id == MouseEvent.MOUSE_DRAGGED) )
|
||||
{
|
||||
newMouseX = ((MouseEvent)e).getXOnScreen();
|
||||
newMouseY = ((MouseEvent)e).getYOnScreen();
|
||||
|
||||
if( safeTrianglePainter != null )
|
||||
safeTrianglePainter.repaint();
|
||||
|
||||
mouseUpdateTimer.stop();
|
||||
timeoutTimer.stop();
|
||||
|
||||
// check whether mouse moved within safe triangle
|
||||
if( createSafeTriangle().contains( newMouseX, newMouseY ) ) {
|
||||
// update mouse location delayed (this changes the safe triangle)
|
||||
mouseUpdateTimer.start();
|
||||
|
||||
timeoutTimer.start();
|
||||
|
||||
// remember last mouse event, which will be posted if the mouse stops moving
|
||||
lastMouseEvent = e;
|
||||
|
||||
// ignore mouse event
|
||||
return;
|
||||
}
|
||||
|
||||
// update mouse location immediately (this changes the safe triangle)
|
||||
mouseX = newMouseX;
|
||||
mouseY = newMouseY;
|
||||
}
|
||||
|
||||
super.dispatchEvent( e );
|
||||
}
|
||||
}
|
||||
|
||||
//---- class SafeTrianglePainter ------------------------------------------
|
||||
|
||||
private class SafeTrianglePainter
|
||||
extends JComponent
|
||||
{
|
||||
SafeTrianglePainter( JPopupMenu popup ) {
|
||||
Window window = SwingUtilities.windowForComponent( popup.getInvoker() );
|
||||
if( window instanceof RootPaneContainer ) {
|
||||
JLayeredPane layeredPane = ((RootPaneContainer)window).getLayeredPane();
|
||||
setSize( layeredPane.getSize() );
|
||||
layeredPane.add( this, Integer.valueOf( JLayeredPane.POPUP_LAYER + 1 ) );
|
||||
}
|
||||
}
|
||||
|
||||
void uninstall() {
|
||||
Container parent = getParent();
|
||||
if( parent != null ) {
|
||||
parent.remove( this );
|
||||
parent.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent( Graphics g ) {
|
||||
Point locationOnScreen = getLocationOnScreen();
|
||||
g.translate( -locationOnScreen.x, -locationOnScreen.y );
|
||||
|
||||
g.setColor( Color.red );
|
||||
((Graphics2D)g).draw( createSafeTriangle() );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,8 +27,12 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StreamTokenizer;
|
||||
import java.io.StringReader;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
@@ -84,6 +88,7 @@ class UIDefaultsLoader
|
||||
|
||||
private static int parseColorDepth;
|
||||
|
||||
private static Map<String, ColorUIResource> systemColorCache;
|
||||
private static final SoftCache<String, Object> fontCache = new SoftCache<>();
|
||||
|
||||
static void loadDefaultsFromProperties( Class<?> lookAndFeelClass, List<FlatDefaultsAddon> addons,
|
||||
@@ -105,6 +110,10 @@ class UIDefaultsLoader
|
||||
Properties additionalDefaults, boolean dark, UIDefaults defaults )
|
||||
{
|
||||
try {
|
||||
// temporary cache system colors while loading defaults,
|
||||
// which avoids that system color getter is invoked multiple times
|
||||
systemColorCache = (FlatLaf.getSystemColorGetter() != null) ? new HashMap<>() : null;
|
||||
|
||||
// load core properties files
|
||||
Properties properties = new Properties();
|
||||
for( Class<?> lafClass : lafClasses ) {
|
||||
@@ -266,8 +275,9 @@ class UIDefaultsLoader
|
||||
continue;
|
||||
}
|
||||
|
||||
String value = resolveValue( (String) e.getValue(), propertiesGetter );
|
||||
String value = (String) e.getValue();
|
||||
try {
|
||||
value = resolveValue( value, propertiesGetter );
|
||||
defaults.put( key, parseValue( key, value, null, null, resolver, addonClassLoaders ) );
|
||||
} catch( RuntimeException ex ) {
|
||||
logParseError( key, value, ex, true );
|
||||
@@ -276,6 +286,9 @@ class UIDefaultsLoader
|
||||
|
||||
// remember variables in defaults to allow using them in styles
|
||||
defaults.put( KEY_VARIABLES, variables );
|
||||
|
||||
// clear/disable system color cache
|
||||
systemColorCache = null;
|
||||
} catch( IOException ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to load properties files.", ex );
|
||||
}
|
||||
@@ -289,7 +302,9 @@ class UIDefaultsLoader
|
||||
LoggingFacade.INSTANCE.logConfig( message, ex );
|
||||
}
|
||||
|
||||
static String resolveValue( String value, Function<String, String> propertiesGetter ) {
|
||||
static String resolveValue( String value, Function<String, String> propertiesGetter )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
value = value.trim();
|
||||
String value0 = value;
|
||||
|
||||
@@ -318,7 +333,9 @@ class UIDefaultsLoader
|
||||
return resolveValue( newValue, propertiesGetter );
|
||||
}
|
||||
|
||||
static String resolveValueFromUIManager( String value ) {
|
||||
static String resolveValueFromUIManager( String value )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
if( value.startsWith( VARIABLE_PREFIX ) ) {
|
||||
@SuppressWarnings( "unchecked" )
|
||||
Map<String, String> variables = (Map<String, String>) UIManager.get( KEY_VARIABLES );
|
||||
@@ -326,7 +343,7 @@ class UIDefaultsLoader
|
||||
if( newValue == null )
|
||||
throw new IllegalArgumentException( "variable '" + value + "' not found" );
|
||||
|
||||
return newValue;
|
||||
return resolveValueFromUIManager( newValue );
|
||||
}
|
||||
|
||||
if( !value.startsWith( PROPERTY_PREFIX ) )
|
||||
@@ -340,8 +357,11 @@ class UIDefaultsLoader
|
||||
// convert binary color to string
|
||||
if( newValue instanceof Color ) {
|
||||
Color color = (Color) newValue;
|
||||
int rgb = color.getRGB() & 0xffffff;
|
||||
int alpha = color.getAlpha();
|
||||
return String.format( (alpha != 255) ? "#%06x%02x" : "#%06x", color.getRGB() & 0xffffff, alpha );
|
||||
return (alpha != 255)
|
||||
? String.format( "#%06x%02x", rgb, alpha )
|
||||
: String.format( "#%06x", rgb );
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException( "property value type '" + newValue.getClass().getName() + "' not supported in references" );
|
||||
@@ -350,16 +370,19 @@ class UIDefaultsLoader
|
||||
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 ValueType[] tempResultValueType = new ValueType[1];
|
||||
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 ) {
|
||||
static Object parseValue( String key, String value, Class<?> valueType )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
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 )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
if( resultValueType == null )
|
||||
resultValueType = tempResultValueType;
|
||||
@@ -389,7 +412,7 @@ class UIDefaultsLoader
|
||||
if( value.startsWith( "if(" ) && value.endsWith( ")" ) ) {
|
||||
List<String> params = splitFunctionParams( value.substring( 3, value.length() - 1 ), ',' );
|
||||
if( params.size() != 3 )
|
||||
throwMissingParametersException( value );
|
||||
throw newMissingParametersException( value );
|
||||
|
||||
boolean ifCondition = parseCondition( params.get( 0 ), resolver, addonClassLoaders );
|
||||
String ifValue = params.get( ifCondition ? 1 : 2 );
|
||||
@@ -466,6 +489,10 @@ class UIDefaultsLoader
|
||||
if( knownValueTypes == null ) {
|
||||
// create lazy
|
||||
knownValueTypes = new HashMap<>();
|
||||
// system colors
|
||||
knownValueTypes.put( "activeCaptionBorder", ValueType.COLOR );
|
||||
knownValueTypes.put( "inactiveCaptionBorder", ValueType.COLOR );
|
||||
knownValueTypes.put( "windowBorder", ValueType.COLOR );
|
||||
// SplitPane
|
||||
knownValueTypes.put( "SplitPane.dividerSize", ValueType.INTEGER );
|
||||
knownValueTypes.put( "SplitPaneDivider.gripDotSize", ValueType.INTEGER );
|
||||
@@ -521,20 +548,20 @@ class UIDefaultsLoader
|
||||
case STRING: return value;
|
||||
case BOOLEAN: return parseBoolean( value );
|
||||
case CHARACTER: return parseCharacter( value );
|
||||
case INTEGER: return parseInteger( value, true );
|
||||
case INTEGERORFLOAT:return parseIntegerOrFloat( value, true );
|
||||
case FLOAT: return parseFloat( value, true );
|
||||
case INTEGER: return parseInteger( value );
|
||||
case INTEGERORFLOAT:return parseIntegerOrFloat( value );
|
||||
case FLOAT: return parseFloat( value );
|
||||
case BORDER: return parseBorder( value, resolver, addonClassLoaders );
|
||||
case ICON: return parseInstance( value, addonClassLoaders );
|
||||
case ICON: return parseInstance( value, resolver, addonClassLoaders );
|
||||
case INSETS: return parseInsets( value );
|
||||
case DIMENSION: return parseDimension( value );
|
||||
case COLOR: return parseColorOrFunction( value, resolver, true );
|
||||
case COLOR: return parseColorOrFunction( value, resolver );
|
||||
case FONT: return parseFont( value );
|
||||
case SCALEDINTEGER: return parseScaledInteger( value );
|
||||
case SCALEDFLOAT: return parseScaledFloat( value );
|
||||
case SCALEDINSETS: return parseScaledInsets( value );
|
||||
case SCALEDDIMENSION:return parseScaledDimension( value );
|
||||
case INSTANCE: return parseInstance( value, addonClassLoaders );
|
||||
case INSTANCE: return parseInstance( value, resolver, addonClassLoaders );
|
||||
case CLASS: return parseClass( value, addonClassLoaders );
|
||||
case GRAYFILTER: return parseGrayFilter( value );
|
||||
case UNKNOWN:
|
||||
@@ -546,24 +573,34 @@ class UIDefaultsLoader
|
||||
}
|
||||
|
||||
// colors
|
||||
Object color = parseColorOrFunction( value, resolver, false );
|
||||
if( color != null ) {
|
||||
resultValueType[0] = ValueType.COLOR;
|
||||
if( value.startsWith( "#" ) || value.endsWith( ")" ) ) {
|
||||
Object color = parseColorOrFunction( value, resolver );
|
||||
resultValueType[0] = (color != null) ? ValueType.COLOR : ValueType.NULL;
|
||||
return color;
|
||||
}
|
||||
|
||||
// integer
|
||||
Integer integer = parseInteger( value, false );
|
||||
if( integer != null ) {
|
||||
resultValueType[0] = ValueType.INTEGER;
|
||||
return integer;
|
||||
}
|
||||
// integer or float
|
||||
char firstChar = value.charAt( 0 );
|
||||
if( (firstChar >= '0' && firstChar <= '9') ||
|
||||
firstChar == '-' || firstChar == '+' || firstChar == '.' )
|
||||
{
|
||||
// integer
|
||||
try {
|
||||
Integer integer = parseInteger( value );
|
||||
resultValueType[0] = ValueType.INTEGER;
|
||||
return integer;
|
||||
} catch( NumberFormatException ex ) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// float
|
||||
Float f = parseFloat( value, false );
|
||||
if( f != null ) {
|
||||
resultValueType[0] = ValueType.FLOAT;
|
||||
return f;
|
||||
// float
|
||||
try {
|
||||
Float f = parseFloat( value );
|
||||
resultValueType[0] = ValueType.FLOAT;
|
||||
return f;
|
||||
} catch( NumberFormatException ex ) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
// string
|
||||
@@ -586,16 +623,18 @@ class UIDefaultsLoader
|
||||
}
|
||||
}
|
||||
|
||||
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 )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
if( value.indexOf( ',' ) >= 0 ) {
|
||||
// top,left,bottom,right[,lineColor[,lineThickness[,arc]]]
|
||||
// Syntax: top,left,bottom,right[,lineColor[,lineThickness[,arc]]]
|
||||
List<String> parts = splitFunctionParams( value, ',' );
|
||||
Insets insets = parseInsets( value );
|
||||
ColorUIResource lineColor = (parts.size() >= 5)
|
||||
? (ColorUIResource) parseColorOrFunction( resolver.apply( parts.get( 4 ) ), resolver, true )
|
||||
? (ColorUIResource) parseColorOrFunction( resolver.apply( parts.get( 4 ) ), resolver )
|
||||
: null;
|
||||
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;
|
||||
float lineThickness = (parts.size() >= 6 && !parts.get( 5 ).isEmpty()) ? parseFloat( parts.get( 5 ) ) : 1f;
|
||||
int arc = (parts.size() >= 7) ? parseInteger( parts.get( 6 ) ) : 0;
|
||||
|
||||
return (LazyValue) t -> {
|
||||
return (lineColor != null)
|
||||
@@ -603,13 +642,29 @@ class UIDefaultsLoader
|
||||
: new FlatEmptyBorder( insets );
|
||||
};
|
||||
} else
|
||||
return parseInstance( value, addonClassLoaders );
|
||||
return parseInstance( value, resolver, addonClassLoaders );
|
||||
}
|
||||
|
||||
private static Object parseInstance( String value, List<ClassLoader> addonClassLoaders ) {
|
||||
private static Object parseInstance( String value, Function<String, String> resolver, List<ClassLoader> addonClassLoaders ) {
|
||||
return (LazyValue) t -> {
|
||||
try {
|
||||
return findClass( value, addonClassLoaders ).getDeclaredConstructor().newInstance();
|
||||
if( value.indexOf( ',' ) >= 0 ) {
|
||||
// Syntax: className,param1,param2,...
|
||||
List<String> parts = splitFunctionParams( value, ',' );
|
||||
String className = parts.get( 0 );
|
||||
Class<?> cls = findClass( className, addonClassLoaders );
|
||||
|
||||
Constructor<?>[] constructors = cls.getDeclaredConstructors();
|
||||
Object result = invokeConstructorOrStaticMethod( constructors, parts, resolver );
|
||||
if( result != null )
|
||||
return result;
|
||||
|
||||
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to instantiate '" + className
|
||||
+ "': no constructor found for parameters '"
|
||||
+ value.substring( value.indexOf( ',' + 1 ) ) + "'.", null );
|
||||
return null;
|
||||
} else
|
||||
return findClass( value, addonClassLoaders ).getDeclaredConstructor().newInstance();
|
||||
} catch( Exception ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to instantiate '" + value + "'.", ex );
|
||||
return null;
|
||||
@@ -646,7 +701,9 @@ class UIDefaultsLoader
|
||||
}
|
||||
}
|
||||
|
||||
private static Insets parseInsets( String value ) {
|
||||
private static Insets parseInsets( String value )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
List<String> numbers = StringUtils.split( value, ',', true, false );
|
||||
try {
|
||||
return new InsetsUIResource(
|
||||
@@ -659,7 +716,9 @@ class UIDefaultsLoader
|
||||
}
|
||||
}
|
||||
|
||||
private static Dimension parseDimension( String value ) {
|
||||
private static Dimension parseDimension( String value )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
List<String> numbers = StringUtils.split( value, ',', true, false );
|
||||
try {
|
||||
return new DimensionUIResource(
|
||||
@@ -670,43 +729,39 @@ class UIDefaultsLoader
|
||||
}
|
||||
}
|
||||
|
||||
private static Object parseColorOrFunction( String value, Function<String, String> resolver, boolean reportError ) {
|
||||
private static Object parseColorOrFunction( String value, Function<String, String> resolver )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
if( value.endsWith( ")" ) )
|
||||
return parseColorFunctions( value, resolver, reportError );
|
||||
return parseColorFunctions( value, resolver );
|
||||
|
||||
return parseColor( value, reportError );
|
||||
return parseColor( value );
|
||||
}
|
||||
|
||||
static ColorUIResource parseColor( String value ) {
|
||||
return parseColor( value, false );
|
||||
}
|
||||
|
||||
private static ColorUIResource parseColor( String value, boolean reportError ) {
|
||||
try {
|
||||
int rgba = parseColorRGBA( value );
|
||||
return ((rgba & 0xff000000) == 0xff000000)
|
||||
? new ColorUIResource( rgba )
|
||||
: new ColorUIResource( new Color( rgba, true ) );
|
||||
} catch( IllegalArgumentException ex ) {
|
||||
if( reportError )
|
||||
throw new IllegalArgumentException( "invalid color '" + value + "'" );
|
||||
|
||||
// not a color --> ignore
|
||||
}
|
||||
return null;
|
||||
/**
|
||||
* Parses a hex color in {@code #RGB}, {@code #RGBA}, {@code #RRGGBB} or {@code #RRGGBBAA}
|
||||
* format and returns it as color object.
|
||||
*/
|
||||
static ColorUIResource parseColor( String value )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
int rgba = parseColorRGBA( value );
|
||||
return ((rgba & 0xff000000) == 0xff000000)
|
||||
? new ColorUIResource( rgba )
|
||||
: new ColorUIResource( new Color( rgba, true ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a hex color in {@code #RGB}, {@code #RGBA}, {@code #RRGGBB} or {@code #RRGGBBAA}
|
||||
* format and returns it as {@code rgba} integer suitable for {@link java.awt.Color},
|
||||
* which includes alpha component in bits 24-31.
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
static int parseColorRGBA( String value ) {
|
||||
static int parseColorRGBA( String value )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
int len = value.length();
|
||||
if( (len != 4 && len != 5 && len != 7 && len != 9) || value.charAt( 0 ) != '#' )
|
||||
throw new IllegalArgumentException();
|
||||
throw newInvalidColorException( value );
|
||||
|
||||
// parse hex
|
||||
int n = 0;
|
||||
@@ -721,7 +776,7 @@ class UIDefaultsLoader
|
||||
else if( ch >= 'A' && ch <= 'F' )
|
||||
digit = ch - 'A' + 10;
|
||||
else
|
||||
throw new IllegalArgumentException();
|
||||
throw newInvalidColorException( value );
|
||||
|
||||
n = (n << 4) | digit;
|
||||
}
|
||||
@@ -740,18 +795,21 @@ class UIDefaultsLoader
|
||||
: (((n >> 8) & 0xffffff) | ((n & 0xff) << 24)); // move alpha from lowest to highest byte
|
||||
}
|
||||
|
||||
private static Object parseColorFunctions( String value, Function<String, String> resolver, boolean reportError ) {
|
||||
private static IllegalArgumentException newInvalidColorException( String value ) {
|
||||
return new IllegalArgumentException( "invalid color '" + value + "'" );
|
||||
}
|
||||
|
||||
private static Object parseColorFunctions( String value, Function<String, String> resolver )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
int paramsStart = value.indexOf( '(' );
|
||||
if( paramsStart < 0 ) {
|
||||
if( reportError )
|
||||
throw new IllegalArgumentException( "missing opening parenthesis in function '" + value + "'" );
|
||||
return null;
|
||||
}
|
||||
if( paramsStart < 0 )
|
||||
throw new IllegalArgumentException( "missing opening parenthesis in function '" + value + "'" );
|
||||
|
||||
String function = StringUtils.substringTrimmed( value, 0, paramsStart );
|
||||
List<String> params = splitFunctionParams( value.substring( paramsStart + 1, value.length() - 1 ), ',' );
|
||||
if( params.isEmpty() )
|
||||
throwMissingParametersException( value );
|
||||
throw newMissingParametersException( value );
|
||||
|
||||
if( parseColorDepth > 100 )
|
||||
throw new IllegalArgumentException( "endless recursion in color function '" + value + "'" );
|
||||
@@ -759,27 +817,29 @@ class UIDefaultsLoader
|
||||
parseColorDepth++;
|
||||
try {
|
||||
switch( function ) {
|
||||
case "if": return parseColorIf( value, params, resolver, reportError );
|
||||
case "rgb": return parseColorRgbOrRgba( false, params, resolver, reportError );
|
||||
case "rgba": return parseColorRgbOrRgba( true, params, resolver, reportError );
|
||||
case "if": return parseColorIf( value, params, resolver );
|
||||
case "systemColor": return parseColorSystemColor( value, params, resolver );
|
||||
case "rgb": return parseColorRgbOrRgba( false, params, resolver );
|
||||
case "rgba": return parseColorRgbOrRgba( true, params, resolver );
|
||||
case "hsl": return parseColorHslOrHsla( false, params );
|
||||
case "hsla": return parseColorHslOrHsla( true, params );
|
||||
case "lighten": return parseColorHSLIncreaseDecrease( 2, true, params, resolver, reportError );
|
||||
case "darken": return parseColorHSLIncreaseDecrease( 2, false, params, resolver, reportError );
|
||||
case "saturate": return parseColorHSLIncreaseDecrease( 1, true, params, resolver, reportError );
|
||||
case "desaturate": return parseColorHSLIncreaseDecrease( 1, false, params, resolver, reportError );
|
||||
case "fadein": return parseColorHSLIncreaseDecrease( 3, true, params, resolver, reportError );
|
||||
case "fadeout": return parseColorHSLIncreaseDecrease( 3, false, params, resolver, reportError );
|
||||
case "fade": return parseColorFade( params, resolver, reportError );
|
||||
case "spin": return parseColorSpin( params, resolver, reportError );
|
||||
case "changeHue": return parseColorChange( 0, params, resolver, reportError );
|
||||
case "changeSaturation":return parseColorChange( 1, params, resolver, reportError );
|
||||
case "changeLightness": return parseColorChange( 2, params, resolver, reportError );
|
||||
case "changeAlpha": return parseColorChange( 3, params, resolver, reportError );
|
||||
case "mix": return parseColorMix( null, params, resolver, reportError );
|
||||
case "tint": return parseColorMix( "#fff", params, resolver, reportError );
|
||||
case "shade": return parseColorMix( "#000", params, resolver, reportError );
|
||||
case "contrast": return parseColorContrast( params, resolver, reportError );
|
||||
case "lighten": return parseColorHSLIncreaseDecrease( 2, true, params, resolver );
|
||||
case "darken": return parseColorHSLIncreaseDecrease( 2, false, params, resolver );
|
||||
case "saturate": return parseColorHSLIncreaseDecrease( 1, true, params, resolver );
|
||||
case "desaturate": return parseColorHSLIncreaseDecrease( 1, false, params, resolver );
|
||||
case "fadein": return parseColorHSLIncreaseDecrease( 3, true, params, resolver );
|
||||
case "fadeout": return parseColorHSLIncreaseDecrease( 3, false, params, resolver );
|
||||
case "fade": return parseColorFade( params, resolver );
|
||||
case "spin": return parseColorSpin( params, resolver );
|
||||
case "changeHue": return parseColorChange( 0, params, resolver );
|
||||
case "changeSaturation":return parseColorChange( 1, params, resolver );
|
||||
case "changeLightness": return parseColorChange( 2, params, resolver );
|
||||
case "changeAlpha": return parseColorChange( 3, params, resolver );
|
||||
case "mix": return parseColorMix( null, params, resolver );
|
||||
case "tint": return parseColorMix( "#fff", params, resolver );
|
||||
case "shade": return parseColorMix( "#000", params, resolver );
|
||||
case "contrast": return parseColorContrast( params, resolver );
|
||||
case "over": return parseColorOver( params, resolver );
|
||||
}
|
||||
} finally {
|
||||
parseColorDepth--;
|
||||
@@ -792,15 +852,57 @@ class UIDefaultsLoader
|
||||
* 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.
|
||||
* color function. Otherwise, the general "if" function is used.
|
||||
*/
|
||||
private static Object parseColorIf( String value, List<String> params, Function<String, String> resolver, boolean reportError ) {
|
||||
private static Object parseColorIf( String value, List<String> params, Function<String, String> resolver )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
if( params.size() != 3 )
|
||||
throwMissingParametersException( value );
|
||||
throw newMissingParametersException( value );
|
||||
|
||||
boolean ifCondition = parseCondition( params.get( 0 ), resolver, Collections.emptyList() );
|
||||
String ifValue = params.get( ifCondition ? 1 : 2 );
|
||||
return parseColorOrFunction( resolver.apply( ifValue ), resolver, reportError );
|
||||
return parseColorOrFunction( resolver.apply( ifValue ), resolver );
|
||||
}
|
||||
|
||||
/**
|
||||
* Syntax: systemColor(name[,defaultValue])
|
||||
* - name: system color name
|
||||
* - defaultValue: default color value used if system color is not available
|
||||
*/
|
||||
private static Object parseColorSystemColor( String value, List<String> params, Function<String, String> resolver )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
if( params.size() < 1 )
|
||||
throw newMissingParametersException( value );
|
||||
|
||||
ColorUIResource systemColor = getSystemColor( params.get( 0 ) );
|
||||
if( systemColor != null )
|
||||
return systemColor;
|
||||
|
||||
String defaultValue = (params.size() > 1) ? params.get( 1 ) : "";
|
||||
if( defaultValue.equals( "null" ) || defaultValue.isEmpty() )
|
||||
return null;
|
||||
|
||||
return parseColorOrFunction( resolver.apply( defaultValue ), resolver );
|
||||
}
|
||||
|
||||
private static ColorUIResource getSystemColor( String name ) {
|
||||
Function<String, Color> systemColorGetter = FlatLaf.getSystemColorGetter();
|
||||
if( systemColorGetter == null )
|
||||
return null;
|
||||
|
||||
// use containsKey() because value may be null
|
||||
if( systemColorCache != null && systemColorCache.containsKey( name ) )
|
||||
return systemColorCache.get( name );
|
||||
|
||||
Color color = systemColorGetter.apply( name );
|
||||
ColorUIResource uiColor = (color != null) ? new ColorUIResource( color ) : null;
|
||||
|
||||
if( systemColorCache != null )
|
||||
systemColorCache.put( name, uiColor );
|
||||
|
||||
return uiColor;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -811,7 +913,8 @@ class UIDefaultsLoader
|
||||
* - alpha: an integer 0-255 or a percentage 0-100%
|
||||
*/
|
||||
private static ColorUIResource parseColorRgbOrRgba( boolean hasAlpha, List<String> params,
|
||||
Function<String, String> resolver, boolean reportError )
|
||||
Function<String, String> resolver )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
if( hasAlpha && params.size() == 2 ) {
|
||||
// syntax rgba(color,alpha), which allows adding alpha to any color
|
||||
@@ -820,7 +923,7 @@ class UIDefaultsLoader
|
||||
String colorStr = params.get( 0 );
|
||||
int alpha = parseInteger( params.get( 1 ), 0, 255, true );
|
||||
|
||||
ColorUIResource color = (ColorUIResource) parseColorOrFunction( resolver.apply( colorStr ), resolver, reportError );
|
||||
ColorUIResource color = (ColorUIResource) parseColorOrFunction( resolver.apply( colorStr ), resolver );
|
||||
return new ColorUIResource( new Color( ((alpha & 0xff) << 24) | (color.getRGB() & 0xffffff), true ) );
|
||||
}
|
||||
|
||||
@@ -841,13 +944,15 @@ class UIDefaultsLoader
|
||||
* - lightness: a percentage 0-100%
|
||||
* - alpha: a percentage 0-100%
|
||||
*/
|
||||
private static ColorUIResource parseColorHslOrHsla( boolean hasAlpha, List<String> params ) {
|
||||
private static ColorUIResource parseColorHslOrHsla( boolean hasAlpha, List<String> params )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
int hue = parseInteger( params.get( 0 ), 0, 360, false );
|
||||
int saturation = parsePercentage( params.get( 1 ) );
|
||||
int lightness = parsePercentage( params.get( 2 ) );
|
||||
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 ) );
|
||||
}
|
||||
|
||||
@@ -860,7 +965,8 @@ class UIDefaultsLoader
|
||||
* - options: [relative] [autoInverse] [noAutoInverse] [lazy] [derived]
|
||||
*/
|
||||
private static Object parseColorHSLIncreaseDecrease( int hslIndex, boolean increase,
|
||||
List<String> params, Function<String, String> resolver, boolean reportError )
|
||||
List<String> params, Function<String, String> resolver )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
String colorStr = params.get( 0 );
|
||||
int amount = parsePercentage( params.get( 1 ) );
|
||||
@@ -895,7 +1001,7 @@ class UIDefaultsLoader
|
||||
}
|
||||
|
||||
// parse base color, apply function and create derived color
|
||||
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
|
||||
return parseFunctionBaseColor( colorStr, function, derived, resolver );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -904,7 +1010,9 @@ class UIDefaultsLoader
|
||||
* - amount: percentage 0-100%
|
||||
* - 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 )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
String colorStr = params.get( 0 );
|
||||
int amount = parsePercentage( params.get( 1 ) );
|
||||
boolean derived = false;
|
||||
@@ -929,7 +1037,7 @@ class UIDefaultsLoader
|
||||
}
|
||||
|
||||
// parse base color, apply function and create derived color
|
||||
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
|
||||
return parseFunctionBaseColor( colorStr, function, derived, resolver );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -938,9 +1046,11 @@ class UIDefaultsLoader
|
||||
* - angle: number of degrees to rotate
|
||||
* - options: [derived]
|
||||
*/
|
||||
private static Object parseColorSpin( List<String> params, Function<String, String> resolver, boolean reportError ) {
|
||||
private static Object parseColorSpin( List<String> params, Function<String, String> resolver )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
String colorStr = params.get( 0 );
|
||||
int amount = parseInteger( params.get( 1 ), true );
|
||||
int amount = parseInteger( params.get( 1 ) );
|
||||
boolean derived = false;
|
||||
|
||||
if( params.size() > 2 ) {
|
||||
@@ -952,7 +1062,7 @@ class UIDefaultsLoader
|
||||
ColorFunction function = new ColorFunctions.HSLIncreaseDecrease( 0, true, amount, false, false );
|
||||
|
||||
// parse base color, apply function and create derived color
|
||||
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
|
||||
return parseFunctionBaseColor( colorStr, function, derived, resolver );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -965,11 +1075,12 @@ class UIDefaultsLoader
|
||||
* - options: [derived]
|
||||
*/
|
||||
private static Object parseColorChange( int hslIndex,
|
||||
List<String> params, Function<String, String> resolver, boolean reportError )
|
||||
List<String> params, Function<String, String> resolver )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
String colorStr = params.get( 0 );
|
||||
int value = (hslIndex == 0)
|
||||
? parseInteger( params.get( 1 ), true )
|
||||
? parseInteger( params.get( 1 ) )
|
||||
: parsePercentage( params.get( 1 ) );
|
||||
boolean derived = false;
|
||||
|
||||
@@ -982,7 +1093,7 @@ class UIDefaultsLoader
|
||||
ColorFunction function = new ColorFunctions.HSLChange( hslIndex, value );
|
||||
|
||||
// parse base color, apply function and create derived color
|
||||
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
|
||||
return parseFunctionBaseColor( colorStr, function, derived, resolver );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -994,7 +1105,9 @@ class UIDefaultsLoader
|
||||
* - weight: the weight (in range 0-100%) to mix the two colors
|
||||
* larger weight uses more of first color, smaller weight more of second color
|
||||
*/
|
||||
private static Object parseColorMix( String color1Str, List<String> params, Function<String, String> resolver, boolean reportError ) {
|
||||
private static Object parseColorMix( String color1Str, List<String> params, Function<String, String> resolver )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
int i = 0;
|
||||
if( color1Str == null )
|
||||
color1Str = params.get( i++ );
|
||||
@@ -1002,7 +1115,7 @@ class UIDefaultsLoader
|
||||
int weight = (params.size() > i) ? parsePercentage( params.get( i ) ) : 50;
|
||||
|
||||
// parse second color
|
||||
ColorUIResource color2 = (ColorUIResource) parseColorOrFunction( resolver.apply( color2Str ), resolver, reportError );
|
||||
ColorUIResource color2 = (ColorUIResource) parseColorOrFunction( resolver.apply( color2Str ), resolver );
|
||||
if( color2 == null )
|
||||
return null;
|
||||
|
||||
@@ -1010,7 +1123,7 @@ class UIDefaultsLoader
|
||||
ColorFunction function = new ColorFunctions.Mix( color2, weight );
|
||||
|
||||
// parse first color, apply function and create mixed color
|
||||
return parseFunctionBaseColor( color1Str, function, false, resolver, reportError );
|
||||
return parseFunctionBaseColor( color1Str, function, false, resolver );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1021,14 +1134,16 @@ class UIDefaultsLoader
|
||||
* - 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 ) {
|
||||
private static Object parseColorContrast( List<String> params, Function<String, String> resolver )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
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 );
|
||||
ColorUIResource color = (ColorUIResource) parseColorOrFunction( resolver.apply( colorStr ), resolver );
|
||||
if( color == null )
|
||||
return null;
|
||||
|
||||
@@ -1038,15 +1153,46 @@ class UIDefaultsLoader
|
||||
: darkStr;
|
||||
|
||||
// parse dark or light color
|
||||
return parseColorOrFunction( resolver.apply( darkOrLightColor ), resolver, reportError );
|
||||
return parseColorOrFunction( resolver.apply( darkOrLightColor ), resolver );
|
||||
}
|
||||
|
||||
/**
|
||||
* Syntax: over(foreground,background)
|
||||
* - foreground: a foreground color (e.g. #f00) or a color function;
|
||||
* the alpha of this color is used as weight to mix the two colors
|
||||
* - background: a background color (e.g. #f00) or a color function
|
||||
*/
|
||||
private static ColorUIResource parseColorOver( List<String> params, Function<String, String> resolver )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
String foregroundStr = params.get( 0 );
|
||||
String backgroundStr = params.get( 1 );
|
||||
|
||||
// parse foreground color
|
||||
ColorUIResource foreground = (ColorUIResource) parseColorOrFunction( resolver.apply( foregroundStr ), resolver );
|
||||
if( foreground == null || foreground.getAlpha() == 255 )
|
||||
return foreground;
|
||||
|
||||
// foreground color without alpha
|
||||
ColorUIResource foreground2 = new ColorUIResource( foreground.getRGB() );
|
||||
|
||||
// parse background color
|
||||
ColorUIResource background = (ColorUIResource) parseColorOrFunction( resolver.apply( backgroundStr ), resolver );
|
||||
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,
|
||||
boolean derived, Function<String, String> resolver, boolean reportError )
|
||||
boolean derived, Function<String, String> resolver )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
// parse base color
|
||||
String resolvedColorStr = resolver.apply( colorStr );
|
||||
ColorUIResource baseColor = (ColorUIResource) parseColorOrFunction( resolvedColorStr, resolver, reportError );
|
||||
ColorUIResource baseColor = (ColorUIResource) parseColorOrFunction( resolvedColorStr, resolver );
|
||||
if( baseColor == null )
|
||||
return null;
|
||||
|
||||
@@ -1074,7 +1220,9 @@ class UIDefaultsLoader
|
||||
/**
|
||||
* Syntax: [normal] [bold|+bold|-bold] [italic|+italic|-italic] [<size>|+<incr>|-<decr>|<percent>%] [family[, family]] [$baseFontKey]
|
||||
*/
|
||||
private static Object parseFont( String value ) {
|
||||
private static Object parseFont( String value )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
Object font = fontCache.get( value );
|
||||
if( font != null )
|
||||
return font;
|
||||
@@ -1130,11 +1278,11 @@ class UIDefaultsLoader
|
||||
throw new IllegalArgumentException( "size specified more than once in '" + value + "'" );
|
||||
|
||||
if( firstChar == '+' || firstChar == '-' )
|
||||
relativeSize = parseInteger( param, true );
|
||||
relativeSize = parseInteger( param );
|
||||
else if( param.endsWith( "%" ) )
|
||||
scaleSize = parseInteger( param.substring( 0, param.length() - 1 ), true ) / 100f;
|
||||
scaleSize = parseInteger( param.substring( 0, param.length() - 1 ) ) / 100f;
|
||||
else
|
||||
absoluteSize = parseInteger( param, true );
|
||||
absoluteSize = parseInteger( param );
|
||||
} else if( firstChar == '$' ) {
|
||||
// reference to base font
|
||||
if( baseFontKey != null )
|
||||
@@ -1172,7 +1320,9 @@ class UIDefaultsLoader
|
||||
return font;
|
||||
}
|
||||
|
||||
private static int parsePercentage( String value ) {
|
||||
private static int parsePercentage( String value )
|
||||
throws IllegalArgumentException, NumberFormatException
|
||||
{
|
||||
if( !value.endsWith( "%" ) )
|
||||
throw new NumberFormatException( "invalid percentage '" + value + "'" );
|
||||
|
||||
@@ -1188,7 +1338,9 @@ class UIDefaultsLoader
|
||||
return val;
|
||||
}
|
||||
|
||||
private static Boolean parseBoolean( String value ) {
|
||||
private static Boolean parseBoolean( String value )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
switch( value ) {
|
||||
case "false": return false;
|
||||
case "true": return true;
|
||||
@@ -1196,87 +1348,101 @@ class UIDefaultsLoader
|
||||
throw new IllegalArgumentException( "invalid boolean '" + value + "'" );
|
||||
}
|
||||
|
||||
private static Character parseCharacter( String value ) {
|
||||
private static Character parseCharacter( String value )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
if( value.length() != 1 )
|
||||
throw new IllegalArgumentException( "invalid character '" + value + "'" );
|
||||
return value.charAt( 0 );
|
||||
}
|
||||
|
||||
private static Integer parseInteger( String value, int min, int max, boolean allowPercentage ) {
|
||||
private static Integer parseInteger( String value, int min, int max, boolean allowPercentage )
|
||||
throws IllegalArgumentException, NumberFormatException
|
||||
{
|
||||
if( allowPercentage && value.endsWith( "%" ) ) {
|
||||
int percent = parsePercentage( value );
|
||||
return (max * percent) / 100;
|
||||
}
|
||||
|
||||
Integer integer = parseInteger( value, true );
|
||||
if( integer.intValue() < min || integer.intValue() > max )
|
||||
Integer integer = parseInteger( value );
|
||||
if( integer < min || integer > max )
|
||||
throw new NumberFormatException( "integer '" + value + "' out of range (" + min + '-' + max + ')' );
|
||||
return integer;
|
||||
}
|
||||
|
||||
private static Integer parseInteger( String value, boolean reportError ) {
|
||||
private static Integer parseInteger( String value )
|
||||
throws NumberFormatException
|
||||
{
|
||||
try {
|
||||
return Integer.parseInt( value );
|
||||
} catch( NumberFormatException ex ) {
|
||||
if( reportError )
|
||||
throw new NumberFormatException( "invalid integer '" + value + "'" );
|
||||
throw new NumberFormatException( "invalid integer '" + value + "'" );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Number parseIntegerOrFloat( String value, boolean reportError ) {
|
||||
private static Number parseIntegerOrFloat( String value )
|
||||
throws NumberFormatException
|
||||
{
|
||||
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 + "'" );
|
||||
throw new NumberFormatException( "invalid integer or float '" + value + "'" );
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Float parseFloat( String value, boolean reportError ) {
|
||||
private static Float parseFloat( String value )
|
||||
throws NumberFormatException
|
||||
{
|
||||
try {
|
||||
return Float.parseFloat( value );
|
||||
} catch( NumberFormatException ex ) {
|
||||
if( reportError )
|
||||
throw new NumberFormatException( "invalid float '" + value + "'" );
|
||||
throw new NumberFormatException( "invalid float '" + value + "'" );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static ActiveValue parseScaledInteger( String value ) {
|
||||
int val = parseInteger( value, true );
|
||||
return (ActiveValue) t -> {
|
||||
private static ActiveValue parseScaledInteger( String value )
|
||||
throws NumberFormatException
|
||||
{
|
||||
int val = parseInteger( value );
|
||||
return t -> {
|
||||
return UIScale.scale( val );
|
||||
};
|
||||
}
|
||||
|
||||
private static ActiveValue parseScaledFloat( String value ) {
|
||||
float val = parseFloat( value, true );
|
||||
return (ActiveValue) t -> {
|
||||
private static ActiveValue parseScaledFloat( String value )
|
||||
throws NumberFormatException
|
||||
{
|
||||
float val = parseFloat( value );
|
||||
return t -> {
|
||||
return UIScale.scale( val );
|
||||
};
|
||||
}
|
||||
|
||||
private static ActiveValue parseScaledInsets( String value ) {
|
||||
private static ActiveValue parseScaledInsets( String value )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
Insets insets = parseInsets( value );
|
||||
return (ActiveValue) t -> {
|
||||
return t -> {
|
||||
return UIScale.scale( insets );
|
||||
};
|
||||
}
|
||||
|
||||
private static ActiveValue parseScaledDimension( String value ) {
|
||||
private static ActiveValue parseScaledDimension( String value )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
Dimension dimension = parseDimension( value );
|
||||
return (ActiveValue) t -> {
|
||||
return t -> {
|
||||
return UIScale.scale( dimension );
|
||||
};
|
||||
}
|
||||
|
||||
private static Object parseGrayFilter( String value ) {
|
||||
private static Object parseGrayFilter( String value )
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
List<String> numbers = StringUtils.split( value, ',', true, false );
|
||||
try {
|
||||
int brightness = Integer.parseInt( numbers.get( 0 ) );
|
||||
@@ -1311,11 +1477,95 @@ class UIDefaultsLoader
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
strs.add( StringUtils.substringTrimmed( str, start ) );
|
||||
|
||||
// last param
|
||||
String s = StringUtils.substringTrimmed( str, start );
|
||||
if( !s.isEmpty() || !strs.isEmpty() )
|
||||
strs.add( s );
|
||||
|
||||
return strs;
|
||||
}
|
||||
|
||||
private static Object invokeConstructorOrStaticMethod( Executable[] constructorsOrMethods,
|
||||
List<String> parts, Function<String, String> resolver )
|
||||
throws Exception
|
||||
{
|
||||
// order constructors/methods by parameter types:
|
||||
// - String parameters to the end
|
||||
// - int before float parameters
|
||||
constructorsOrMethods = constructorsOrMethods.clone();
|
||||
Arrays.sort( constructorsOrMethods, (c1, c2) -> {
|
||||
Class<?>[] ptypes1 = c1.getParameterTypes();
|
||||
Class<?>[] ptypes2 = c2.getParameterTypes();
|
||||
if( ptypes1.length != ptypes2.length )
|
||||
return ptypes1.length - ptypes2.length;
|
||||
|
||||
for( int i = 0; i < ptypes1.length; i++ ) {
|
||||
Class<?> pt1 = ptypes1[i];
|
||||
Class<?> pt2 = ptypes2[i];
|
||||
|
||||
if( pt1 == pt2 )
|
||||
continue;
|
||||
|
||||
// order methods with String parameters to the end
|
||||
if( pt1 == String.class )
|
||||
return 2;
|
||||
if( pt2 == String.class )
|
||||
return -2;
|
||||
|
||||
// order int before float
|
||||
if( pt1 == int.class )
|
||||
return -1;
|
||||
if( pt2 == int.class )
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
} );
|
||||
|
||||
// search for best constructor/method for given parameter values
|
||||
for( Executable cm : constructorsOrMethods ) {
|
||||
if( cm.getParameterCount() != parts.size() - 1 )
|
||||
continue;
|
||||
|
||||
Object[] params = parseMethodParams( cm.getParameterTypes(), parts, resolver );
|
||||
if( params == null )
|
||||
continue;
|
||||
|
||||
// invoke constructor or static method
|
||||
if( cm instanceof Constructor )
|
||||
return ((Constructor<?>)cm).newInstance( params );
|
||||
else
|
||||
return ((Method)cm).invoke( null, params );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Object[] parseMethodParams( Class<?>[] paramTypes, List<String> parts, Function<String, String> resolver ) {
|
||||
Object[] params = new Object[paramTypes.length];
|
||||
try {
|
||||
for( int i = 0; i < params.length; i++ ) {
|
||||
Class<?> paramType = paramTypes[i];
|
||||
String paramValue = parts.get( i + 1 );
|
||||
if( paramType == String.class )
|
||||
params[i] = paramValue;
|
||||
else if( paramType == boolean.class )
|
||||
params[i] = parseBoolean( paramValue );
|
||||
else if( paramType == int.class )
|
||||
params[i] = parseInteger( paramValue );
|
||||
else if( paramType == float.class )
|
||||
params[i] = parseFloat( paramValue );
|
||||
else if( paramType == Color.class )
|
||||
params[i] = parseColorOrFunction( resolver.apply( paramValue ), resolver );
|
||||
else
|
||||
return null; // unsupported parameter type
|
||||
}
|
||||
} catch( IllegalArgumentException ex ) {
|
||||
return null; // failed to parse parameter for expected parameter type
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -1333,7 +1583,7 @@ class UIDefaultsLoader
|
||||
return value;
|
||||
}
|
||||
|
||||
private static void throwMissingParametersException( String value ) {
|
||||
throw new IllegalArgumentException( "missing parameters in function '" + value + "'" );
|
||||
private static IllegalArgumentException newMissingParametersException( String value ) {
|
||||
return new IllegalArgumentException( "missing parameters in function '" + value + "'" );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,13 +23,13 @@ import java.awt.Graphics2D;
|
||||
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.
|
||||
* <p>
|
||||
* Subclasses do not need to scale icon painting.
|
||||
* <p>
|
||||
* This class does not store any state information (needed for animation) in its instance.
|
||||
* Instead a client property is set on the painted component.
|
||||
* Instead, a client property is set on the painted component.
|
||||
* This makes it possible to use a share icon instance for multiple components.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
|
||||
@@ -51,7 +51,7 @@ public class FlatAscendingSortIcon
|
||||
boolean chevron = this.chevron;
|
||||
Color sortIconColor = this.sortIconColor;
|
||||
|
||||
// Because this icons are always shared for all table headers,
|
||||
// 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 ) {
|
||||
|
||||
@@ -16,9 +16,12 @@
|
||||
|
||||
package com.formdev.flatlaf.icons;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.geom.Area;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
@@ -36,6 +39,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
public class FlatCapsLockIcon
|
||||
extends FlatAbstractIcon
|
||||
{
|
||||
private Path2D path;
|
||||
|
||||
public FlatCapsLockIcon() {
|
||||
super( 16, 16, UIManager.getColor( "PasswordField.capsLockIconColor" ) );
|
||||
}
|
||||
@@ -49,22 +54,36 @@ public class FlatCapsLockIcon
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
public Object getStyleableValue( String key ) {
|
||||
switch( key ) {
|
||||
case "capsLockIconColor": return color;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
/*
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<rect width="16" height="16" fill="#6E6E6E" rx="3"/>
|
||||
<rect width="6" height="2" x="5" y="11.5" fill="#FFF"/>
|
||||
<path fill="#FFF" d="M2,8 L8,2 L14,8 L11,8 L11,10 L5,10 L5,8 L2,8 Z"/>
|
||||
<rect width="5" height="2" x="5.5" y="11.5" stroke="#FFF" stroke-linejoin="round"/>
|
||||
<path stroke="#FFF" stroke-linejoin="round" d="M2.5,7.5 L8,2 L13.5,7.5 L10.5,7.5 L10.5,9.5 L5.5,9.5 L5.5,7.5 L2.5,7.5 Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
*/
|
||||
|
||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||
path.append( new RoundRectangle2D.Float( 0, 0, 16, 16, 6, 6 ), false );
|
||||
path.append( new Rectangle2D.Float( 5, 11.5f, 6, 2 ), false );
|
||||
path.append( FlatUIUtils.createPath( 2,8, 8,2, 14,8, 11,8, 11,10, 5,10, 5,8 ), false );
|
||||
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
|
||||
BasicStroke stroke = new BasicStroke( 1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND );
|
||||
|
||||
if( path == null ) {
|
||||
path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||
path.append( new RoundRectangle2D.Float( 0, 0, 16, 16, 6, 6 ), false );
|
||||
path.append( new Area( stroke.createStrokedShape( new Rectangle2D.Float( 5.5f, 11.5f, 5, 2 ) ) ), false );
|
||||
path.append( new Area( stroke.createStrokedShape( FlatUIUtils.createPath(
|
||||
2.5,7.5, 8,2, 13.5,7.5, 10.5,7.5, 10.5,9.5, 5.5,9.5, 5.5,7.5, 2.5,7.5 ) ) ), false );
|
||||
}
|
||||
g.fill( path );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,6 +172,11 @@ public class FlatCheckBoxIcon
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
public Object getStyleableValue( String key ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
boolean indeterminate = isIndeterminate( c );
|
||||
@@ -237,7 +242,7 @@ public class FlatCheckBoxIcon
|
||||
}
|
||||
|
||||
protected void paintCheckmark( Component c, Graphics2D g ) {
|
||||
Path2D.Float path = new Path2D.Float();
|
||||
Path2D.Float path = new Path2D.Float( Path2D.WIND_NON_ZERO, 3 );
|
||||
path.moveTo( 4.5f, 7.5f );
|
||||
path.lineTo( 6.6f, 10f );
|
||||
path.lineTo( 11.25f, 3.5f );
|
||||
|
||||
@@ -59,6 +59,11 @@ public class FlatCheckBoxMenuItemIcon
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
public Object getStyleableValue( String key ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g2 ) {
|
||||
boolean selected = (c instanceof AbstractButton) && ((AbstractButton)c).isSelected();
|
||||
@@ -71,7 +76,7 @@ public class FlatCheckBoxMenuItemIcon
|
||||
}
|
||||
|
||||
protected void paintCheckmark( Graphics2D g2 ) {
|
||||
Path2D.Float path = new Path2D.Float();
|
||||
Path2D.Float path = new Path2D.Float( Path2D.WIND_NON_ZERO, 3 );
|
||||
path.moveTo( 4.5f, 7.5f );
|
||||
path.lineTo( 6.6f, 10f );
|
||||
path.lineTo( 11.25f, 3.5f );
|
||||
|
||||
@@ -20,7 +20,6 @@ import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Line2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.util.Map;
|
||||
import javax.swing.AbstractButton;
|
||||
@@ -69,6 +68,11 @@ public class FlatClearIcon
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
public Object getStyleableValue( String key ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
if( !ignoreButtonState && c instanceof AbstractButton ) {
|
||||
@@ -98,9 +102,11 @@ public class FlatClearIcon
|
||||
|
||||
// paint cross
|
||||
g.setColor( clearIconColor );
|
||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||
path.append( new Line2D.Float( 5,5, 11,11 ), false );
|
||||
path.append( new Line2D.Float( 5,11, 11,5 ), false );
|
||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD, 4 );
|
||||
path.moveTo( 5, 5 );
|
||||
path.lineTo( 11, 11 );
|
||||
path.moveTo( 5, 11 );
|
||||
path.lineTo( 11, 5 );
|
||||
g.draw( path );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,21 +39,25 @@ public class FlatFileChooserDetailsViewIcon
|
||||
/*
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<rect width="2" height="2" x="2" y="3" fill="#6E6E6E"/>
|
||||
<rect width="2" height="2" x="2" y="7" fill="#6E6E6E"/>
|
||||
<rect width="2" height="2" x="2" y="11" fill="#6E6E6E"/>
|
||||
<rect width="8" height="2" x="6" y="3" fill="#6E6E6E"/>
|
||||
<rect width="8" height="2" x="6" y="7" fill="#6E6E6E"/>
|
||||
<rect width="8" height="2" x="6" y="11" fill="#6E6E6E"/>
|
||||
<rect width="2" height="1" x="2" y="3" fill="#6E6E6E" rx=".5"/>
|
||||
<rect width="2" height="1" x="2" y="6" fill="#6E6E6E" rx=".5"/>
|
||||
<rect width="2" height="1" x="2" y="9" fill="#6E6E6E" rx=".5"/>
|
||||
<rect width="2" height="1" x="2" y="12" fill="#6E6E6E" rx=".5"/>
|
||||
<rect width="8" height="1" x="6" y="3" fill="#6E6E6E" rx=".5"/>
|
||||
<rect width="8" height="1" x="6" y="6" fill="#6E6E6E" rx=".5"/>
|
||||
<rect width="8" height="1" x="6" y="9" fill="#6E6E6E" rx=".5"/>
|
||||
<rect width="8" height="1" x="6" y="12" fill="#6E6E6E" rx=".5"/>
|
||||
</g>
|
||||
</svg>
|
||||
*/
|
||||
|
||||
g.fillRect( 2, 3, 2, 2 );
|
||||
g.fillRect( 2, 7, 2, 2 );
|
||||
g.fillRect( 2, 11, 2, 2 );
|
||||
g.fillRect( 6, 3, 8, 2 );
|
||||
g.fillRect( 6, 7, 8, 2 );
|
||||
g.fillRect( 6, 11, 8, 2 );
|
||||
g.fillRoundRect( 2, 3, 2, 1, 1, 1 );
|
||||
g.fillRoundRect( 2, 6, 2, 1, 1, 1 );
|
||||
g.fillRoundRect( 2, 9, 2, 1, 1, 1 );
|
||||
g.fillRoundRect( 2, 12, 2, 1, 1, 1 );
|
||||
g.fillRoundRect( 6, 3, 8, 1, 1, 1 );
|
||||
g.fillRoundRect( 6, 6, 8, 1, 1, 1 );
|
||||
g.fillRoundRect( 6, 9, 8, 1, 1, 1 );
|
||||
g.fillRoundRect( 6, 12, 8, 1, 1, 1 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,10 @@
|
||||
|
||||
package com.formdev.flatlaf.icons;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
|
||||
@@ -39,10 +41,22 @@ public class FlatFileChooserHomeFolderIcon
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
/*
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<polygon fill="#6E6E6E" fill-rule="evenodd" points="2 8 8 2 14 8 12 8 12 13 9 13 9 10 7 10 7 13 4 13 4 8"/>
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<polyline stroke="#6E6E6E" stroke-linejoin="round" points="6.5 13 6.5 9.5 9.5 9.5 9.5 13"/>
|
||||
<path stroke="#6E6E6E" d="M3.5,6.5 L3.5,12.5 C3.5,13.0522847 3.94771525,13.5 4.5,13.5 L11.5,13.5 C12.0522847,13.5 12.5,13.0522847 12.5,12.5 L12.5,6.5 L12.5,6.5"/>
|
||||
<polyline stroke="#6E6E6E" stroke-linecap="round" stroke-linejoin="round" points="1.5 8.5 8 2 14.5 8.5"/>
|
||||
</g>
|
||||
</svg>
|
||||
*/
|
||||
|
||||
g.fill( FlatUIUtils.createPath( 2,8, 8,2, 14,8, 12,8, 12,13, 9,13, 9,10, 7,10, 7,13, 4,13, 4,8 ) );
|
||||
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
|
||||
g.setStroke( new BasicStroke( 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
|
||||
|
||||
g.draw( FlatUIUtils.createPath( false, 6.5,13, 6.5,9.5, 9.5,9.5, 9.5,13 ) );
|
||||
g.draw( FlatUIUtils.createPath( false, 3.5,6.5,
|
||||
3.5,12.5, FlatUIUtils.QUAD_TO, 3.5,13.5, 4.5,13.5,
|
||||
11.5,13.5, FlatUIUtils.QUAD_TO, 12.5,13.5, 12.5,12.5,
|
||||
12.5,6.5 ) );
|
||||
g.draw( FlatUIUtils.createPath( false, 1.5,8.5, 8,2, 14.5,8.5 ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,11 @@
|
||||
|
||||
package com.formdev.flatlaf.icons;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
/**
|
||||
@@ -39,17 +42,20 @@ public class FlatFileChooserListViewIcon
|
||||
/*
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<rect width="4" height="4" x="3" y="3" fill="#6E6E6E"/>
|
||||
<rect width="4" height="4" x="3" y="9" fill="#6E6E6E"/>
|
||||
<rect width="4" height="4" x="9" y="9" fill="#6E6E6E"/>
|
||||
<rect width="4" height="4" x="9" y="3" fill="#6E6E6E"/>
|
||||
<rect width="4" height="4" x="2.5" y="2.5" stroke="#6E6E6E" rx="1.5"/>
|
||||
<rect width="4" height="4" x="2.5" y="9.5" stroke="#6E6E6E" rx="1.5"/>
|
||||
<rect width="4" height="4" x="9.5" y="9.5" stroke="#6E6E6E" rx="1.5"/>
|
||||
<rect width="4" height="4" x="9.5" y="2.5" stroke="#6E6E6E" rx="1.5"/>
|
||||
</g>
|
||||
</svg>
|
||||
*/
|
||||
|
||||
g.fillRect( 3, 3, 4, 4 );
|
||||
g.fillRect( 3, 9, 4, 4 );
|
||||
g.fillRect( 9, 9, 4, 4 );
|
||||
g.fillRect( 9, 3, 4, 4 );
|
||||
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
|
||||
g.setStroke( new BasicStroke( 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
|
||||
|
||||
g.draw( new RoundRectangle2D.Float( 2.5f, 2.5f, 4, 4, 2, 2 ) );
|
||||
g.draw( new RoundRectangle2D.Float( 2.5f, 9.5f, 4, 4, 2, 2 ) );
|
||||
g.draw( new RoundRectangle2D.Float( 9.5f, 9.5f, 4, 4, 2, 2 ) );
|
||||
g.draw( new RoundRectangle2D.Float( 9.5f, 2.5f, 4, 4, 2, 2 ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,13 @@
|
||||
|
||||
package com.formdev.flatlaf.icons;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.geom.Line2D;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
|
||||
/**
|
||||
* "new folder" icon for {@link javax.swing.JFileChooser}.
|
||||
@@ -31,6 +34,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
public class FlatFileChooserNewFolderIcon
|
||||
extends FlatAbstractIcon
|
||||
{
|
||||
private final Color greenColor = UIManager.getColor( "Actions.Green" );
|
||||
|
||||
public FlatFileChooserNewFolderIcon() {
|
||||
super( 16, 16, UIManager.getColor( "Actions.Grey" ) );
|
||||
}
|
||||
@@ -40,13 +45,20 @@ public class FlatFileChooserNewFolderIcon
|
||||
/*
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<polygon fill="#6E6E6E" points="2 3 5.5 3 7 5 14 5 14 8 11 8 11 10 9 10 9 13 2 13"/>
|
||||
<path fill="#59A869" d="M14,11 L16,11 L16,13 L14,13 L14,15 L12,15 L12,13 L10,13 L10,11 L12,11 L12,9 L14,9 L14,11 Z"/>
|
||||
<path stroke="#6E6E6E" d="M13,13.5 L3,13.5 C2.17157288,13.5 1.5,12.8284271 1.5,12 L1.5,4 C1.5,3.17157288 2.17157288,2.5 3,2.5 L6.29289322,2.5 C6.42550146,2.5 6.55267842,2.55267842 6.64644661,2.64644661 L8.5,4.5 L8.5,4.5 L13,4.5 C13.8284271,4.5 14.5,5.17157288 14.5,6 L14.5,12 C14.5,12.8284271 13.8284271,13.5 13,13.5 Z"/>
|
||||
<line x1="5.5" x2="10.5" y1="9" y2="9" stroke="#59A869" stroke-linecap="round"/>
|
||||
<line x1="8" x2="8" y1="6.5" y2="11.5" stroke="#59A869" stroke-linecap="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
*/
|
||||
|
||||
g.fill( FlatUIUtils.createPath( 2,3, 5.5,3, 7,5, 14,5, 14,8, 11,8, 11,10, 9,10, 9,13, 2,13 ) );
|
||||
g.fill( FlatUIUtils.createPath( 14,11, 16,11, 16,13, 14,13, 14,15, 12,15, 12,13, 10,13, 10,11, 12,11, 12,9, 14,9, 14,11 ) );
|
||||
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
|
||||
g.setStroke( new BasicStroke( 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
|
||||
|
||||
g.draw( FlatFileViewDirectoryIcon.createFolderPath() );
|
||||
|
||||
g.setColor( greenColor );
|
||||
g.draw( new Line2D.Float( 5.5f, 9, 10.5f, 9 ) );
|
||||
g.draw( new Line2D.Float( 8, 6.5f, 8, 11.5f ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,12 @@
|
||||
|
||||
package com.formdev.flatlaf.icons;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.geom.Line2D;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
|
||||
@@ -44,15 +47,20 @@ public class FlatFileChooserUpFolderIcon
|
||||
/*
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<polygon fill="#6E6E6E" points="2 3 5.5 3 7 5 9 5 9 9 13 9 13 5 14 5 14 13 2 13"/>
|
||||
<path fill="#389FD6" d="M12,4 L12,8 L10,8 L10,4 L8,4 L11,1 L14,4 L12,4 Z"/>
|
||||
<path stroke="#6E6E6E" d="M13,13.5 L3,13.5 C2.17157288,13.5 1.5,12.8284271 1.5,12 L1.5,4 C1.5,3.17157288 2.17157288,2.5 3,2.5 L6.29289322,2.5 C6.42550146,2.5 6.55267842,2.55267842 6.64644661,2.64644661 L8.5,4.5 L8.5,4.5 L13,4.5 C13.8284271,4.5 14.5,5.17157288 14.5,6 L14.5,12 C14.5,12.8284271 13.8284271,13.5 13,13.5 Z"/>
|
||||
<line x1="8" x2="8" y1="6.5" y2="11.5" stroke="#389FD6" stroke-linecap="round"/>
|
||||
<polyline stroke="#389FD6" stroke-linecap="round" stroke-linejoin="round" points="5.5 9 8 6.5 10.5 9"/>
|
||||
</g>
|
||||
</svg>
|
||||
*/
|
||||
|
||||
g.fill( FlatUIUtils.createPath( 2,3, 5.5,3, 7,5, 9,5, 9,9, 13,9, 13,5, 14,5, 14,13, 2,13 ) );
|
||||
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
|
||||
g.setStroke( new BasicStroke( 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
|
||||
|
||||
g.draw( FlatFileViewDirectoryIcon.createFolderPath() );
|
||||
|
||||
g.setColor( blueColor );
|
||||
g.fill( FlatUIUtils.createPath( 12,4, 12,8, 10,8, 10,4, 8,4, 11,1, 14,4, 12,4 ) );
|
||||
g.draw( new Line2D.Float( 8, 6.5f, 8, 11.5f ) );
|
||||
g.draw( FlatUIUtils.createPath( false, 5.5,9, 8,6.5, 10.5,9 ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,12 @@
|
||||
|
||||
package com.formdev.flatlaf.icons;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.geom.Line2D;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
/**
|
||||
@@ -41,17 +43,18 @@ public class FlatFileViewComputerIcon
|
||||
/*
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path fill="#6E6E6E" d="M2,3 L14,3 L14,11 L2,11 L2,3 Z M4,5 L4,9 L12,9 L12,5 L4,5 Z"/>
|
||||
<rect width="12" height="2" x="2" y="12" fill="#6E6E6E"/>
|
||||
<rect width="11" height="7" x="2.5" y="3.5" stroke="#6E6E6E" rx="1"/>
|
||||
<line x1="8" x2="8" y1="11" y2="12" stroke="#6E6E6E"/>
|
||||
<line x1="4.5" x2="11.5" y1="12.5" y2="12.5" stroke="#6E6E6E" stroke-linecap="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
*/
|
||||
|
||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||
path.append( new Rectangle2D.Float( 2, 3, 12, 8 ), false );
|
||||
path.append( new Rectangle2D.Float( 4, 5, 8, 4 ), false );
|
||||
g.fill( path );
|
||||
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
|
||||
g.setStroke( new BasicStroke( 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
|
||||
|
||||
g.fillRect( 2, 12, 12, 2 );
|
||||
g.draw( new RoundRectangle2D.Float( 2.5f, 3.5f, 11, 7, 2, 2 ) );
|
||||
g.drawLine( 8, 11, 8, 12 );
|
||||
g.draw( new Line2D.Float( 4.5f, 12.5f, 11.5f, 12.5f ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package com.formdev.flatlaf.icons;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.geom.Path2D;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
|
||||
@@ -31,6 +33,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
public class FlatFileViewDirectoryIcon
|
||||
extends FlatAbstractIcon
|
||||
{
|
||||
private Path2D path;
|
||||
|
||||
public FlatFileViewDirectoryIcon() {
|
||||
super( 16, 16, UIManager.getColor( "Objects.Grey" ) );
|
||||
}
|
||||
@@ -39,10 +43,32 @@ public class FlatFileViewDirectoryIcon
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
/*
|
||||
<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"/>
|
||||
<path fill="none" stroke="#6E6E6E" d="M13,13.5 L3,13.5 C2.17157288,13.5 1.5,12.8284271 1.5,12 L1.5,4 C1.5,3.17157288 2.17157288,2.5 3,2.5 L6.29289322,2.5 C6.42550146,2.5 6.55267842,2.55267842 6.64644661,2.64644661 L8.5,4.5 L8.5,4.5 L13,4.5 C13.8284271,4.5 14.5,5.17157288 14.5,6 L14.5,12 C14.5,12.8284271 13.8284271,13.5 13,13.5 Z"/>
|
||||
</svg>
|
||||
*/
|
||||
|
||||
g.fill( FlatUIUtils.createPath( 1,2, 6,2, 8,4, 15,4, 15,13, 1,13 ) );
|
||||
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
|
||||
|
||||
if( path == null )
|
||||
path = createFolderPath();
|
||||
g.draw( path );
|
||||
}
|
||||
|
||||
static Path2D createFolderPath() {
|
||||
double arc = 1.5;
|
||||
double arc2 = 0.5;
|
||||
return FlatUIUtils.createPath(
|
||||
// bottom-right
|
||||
14.5,13.5-arc, FlatUIUtils.QUAD_TO, 14.5,13.5, 14.5-arc,13.5,
|
||||
// bottom-left
|
||||
1.5+arc,13.5, FlatUIUtils.QUAD_TO, 1.5,13.5, 1.5,13.5-arc,
|
||||
// top-left
|
||||
1.5,2.5+arc, FlatUIUtils.QUAD_TO, 1.5,2.5, 1.5+arc,2.5,
|
||||
// top-mid-left
|
||||
6.5-arc2,2.5, FlatUIUtils.QUAD_TO, 6.5,2.5, 6.5+arc2,2.5+arc2,
|
||||
// top-mid-right
|
||||
8.5,4.5,
|
||||
// top-right
|
||||
14.5-arc,4.5, FlatUIUtils.QUAD_TO, 14.5,4.5, 14.5,4.5+arc );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,11 @@
|
||||
|
||||
package com.formdev.flatlaf.icons;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.geom.Path2D;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
|
||||
@@ -31,6 +34,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
public class FlatFileViewFileIcon
|
||||
extends FlatAbstractIcon
|
||||
{
|
||||
private Path2D path;
|
||||
|
||||
public FlatFileViewFileIcon() {
|
||||
super( 16, 16, UIManager.getColor( "Objects.Grey" ) );
|
||||
}
|
||||
@@ -39,14 +44,33 @@ public class FlatFileViewFileIcon
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
/*
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<polygon fill="#6E6E6E" points="8 6 8 1 13 1 13 15 3 15 3 6"/>
|
||||
<polygon fill="#6E6E6E" points="3 5 7 5 7 1"/>
|
||||
<g fill="none" fill-rule="evenodd" stroke-linejoin="round">
|
||||
<path stroke="#6E6E6E" d="M4,1.5 L8.8,1.5 L8.8,1.5 L13.5,6.2 L13.5,13 C13.5,13.8284271 12.8284271,14.5 12,14.5 L4,14.5 C3.17157288,14.5 2.5,13.8284271 2.5,13 L2.5,3 C2.5,2.17157288 3.17157288,1.5 4,1.5 Z"/>
|
||||
<path stroke="#6E6E6E" d="M8.5,2 L8.5,5 C8.5,5.82842712 9.17157288,6.5 10,6.5 L13,6.5 L13,6.5"/>
|
||||
</g>
|
||||
</svg>
|
||||
*/
|
||||
|
||||
g.fill( FlatUIUtils.createPath( 8,6, 8,1, 13,1, 13,15, 3,15, 3,6 ) );
|
||||
g.fill( FlatUIUtils.createPath( 3,5, 7,5, 7,1 ) );
|
||||
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
|
||||
g.setStroke( new BasicStroke( 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
|
||||
|
||||
if( path == null ) {
|
||||
double arc = 1.5;
|
||||
path = FlatUIUtils.createPath( false,
|
||||
// top-left
|
||||
2.5,1.5+arc, FlatUIUtils.QUAD_TO, 2.5,1.5, 2.5+arc,1.5,
|
||||
// top-right
|
||||
8.8,1.5, 13.5,6.2,
|
||||
// bottom-right
|
||||
13.5,14.5-arc, FlatUIUtils.QUAD_TO, 13.5,14.5, 13.5-arc,14.5,
|
||||
// bottom-left
|
||||
2.5+arc,14.5, FlatUIUtils.QUAD_TO, 2.5,14.5, 2.5,14.5-arc,
|
||||
FlatUIUtils.CLOSE_PATH,
|
||||
|
||||
FlatUIUtils.MOVE_TO, 8.5,2,
|
||||
8.5,6.5-arc, FlatUIUtils.QUAD_TO, 8.5,6.5, 8.5+arc,6.5,
|
||||
13,6.5 );
|
||||
}
|
||||
g.draw( path );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,10 @@
|
||||
|
||||
package com.formdev.flatlaf.icons;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.awt.RenderingHints;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
|
||||
@@ -40,18 +41,22 @@ public class FlatFileViewFloppyDriveIcon
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
/*
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path fill="#6E6E6E" d="M11,14 L11,11 L5,11 L5,14 L2,14 L2,2 L14,2 L14,14 L11,14 Z M4,4 L4,8 L12,8 L12,4 L4,4 Z"/>
|
||||
<rect width="4" height="2" x="6" y="12" fill="#6E6E6E"/>
|
||||
<g fill="none" fill-rule="evenodd" stroke-linejoin="round">
|
||||
<path stroke="#6E6E6E" d="M3.5,2.5 L11.5,2.5 L11.5,2.5 L13.5,4.5 L13.5,12.5 C13.5,13.0522847 13.0522847,13.5 12.5,13.5 L3.5,13.5 C2.94771525,13.5 2.5,13.0522847 2.5,12.5 L2.5,3.5 C2.5,2.94771525 2.94771525,2.5 3.5,2.5 Z"/>
|
||||
<polyline stroke="#6E6E6E" points="4.5 13 4.5 9.5 11.5 9.5 11.5 13"/>
|
||||
<polyline stroke="#6E6E6E" points="5.5 3 5.5 5.5 10.5 5.5 10.5 3"/>
|
||||
</g>
|
||||
</svg>
|
||||
*/
|
||||
|
||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||
path.append( FlatUIUtils.createPath( 11,14, 11,11, 5,11, 5,14, 2,14, 2,2, 14,2, 14,14, 11,14 ), false );
|
||||
path.append( FlatUIUtils.createPath( 4,4, 4,8, 12,8, 12,4, 4,4 ), false );
|
||||
g.fill( path );
|
||||
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
|
||||
g.setStroke( new BasicStroke( 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
|
||||
|
||||
g.fillRect( 6, 12, 4, 2 );
|
||||
g.draw( FlatUIUtils.createPath( 3.5,2.5, 11.5,2.5, 11.5,2.5, 13.5,4.5,
|
||||
13.5,12.5, FlatUIUtils.QUAD_TO, 13.5,13.5, 12.5,13.5,
|
||||
3.5,13.5, FlatUIUtils.QUAD_TO, 2.5,13.5, 2.5,12.5,
|
||||
2.5,3.5, FlatUIUtils.QUAD_TO, 2.5,2.5, 3.5,2.5 ) );
|
||||
g.draw( FlatUIUtils.createPath( false, 4.5,13, 4.5,9.5, 11.5,9.5, 11.5,13 ) );
|
||||
g.draw( FlatUIUtils.createPath( false, 5.5,3, 5.5,5.5, 10.5,5.5, 10.5,3 ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,12 @@
|
||||
|
||||
package com.formdev.flatlaf.icons;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
/**
|
||||
@@ -40,14 +42,19 @@ public class FlatFileViewHardDriveIcon
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
/*
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<path fill="#6E6E6E" fill-rule="evenodd" d="M2,6 L14,6 L14,10 L2,10 L2,6 Z M12,8 L12,9 L13,9 L13,8 L12,8 Z M10,8 L10,9 L11,9 L11,8 L10,8 Z"/>
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<rect width="11" height="5" x="2.5" y="5.5" stroke="#6E6E6E" rx="1"/>
|
||||
<circle cx="11.5" cy="8.5" r="1" fill="#6E6E6E"/>
|
||||
<circle cx="9.5" cy="8.5" r="1" fill="#6E6E6E"/>
|
||||
</g>
|
||||
</svg>
|
||||
*/
|
||||
|
||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||
path.append( new Rectangle2D.Float( 2, 6, 12, 4 ), false );
|
||||
path.append( new Rectangle2D.Float( 12, 8, 1, 1 ), false );
|
||||
path.append( new Rectangle2D.Float( 10, 8, 1, 1 ), false );
|
||||
g.fill( path );
|
||||
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
|
||||
g.setStroke( new BasicStroke( 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
|
||||
|
||||
g.draw( new RoundRectangle2D.Float( 2.5f, 5.5f, 11, 5, 2, 2 ) );
|
||||
g.fill( new Ellipse2D.Float( 10.8f, 7.8f, 1.4f, 1.4f ) );
|
||||
g.fill( new Ellipse2D.Float( 8.8f, 7.8f, 1.4f, 1.4f ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
package com.formdev.flatlaf.icons;
|
||||
|
||||
import static com.formdev.flatlaf.util.UIScale.*;
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.util.Map;
|
||||
@@ -84,6 +86,11 @@ public class FlatHelpButtonIcon
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
public Object getStyleableValue( String key ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g2 ) {
|
||||
/*
|
||||
@@ -91,13 +98,14 @@ public class FlatHelpButtonIcon
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<circle cx="11" cy="11" r="10.5" fill="#6E6E6E"/>
|
||||
<circle cx="11" cy="11" r="9.5" fill="#FFF"/>
|
||||
<path fill="#6E6E6E" d="M10,17 L12,17 L12,15 L10,15 L10,17 Z M11,5 C8.8,5 7,6.8 7,9 L9,9 C9,7.9 9.9,7 11,7 C12.1,7 13,7.9 13,9 C13,11 10,10.75 10,14 L12,14 C12,11.75 15,11.5 15,9 C15,6.8 13.2,5 11,5 Z"/>
|
||||
<path stroke="#6E6E6E" stroke-linecap="round" stroke-width="2" d="M8,8.5 C8.25,7 9.66585007,6 11,6 C12.5,6 14,7 14,8.5 C14,10.5 11,11 11,13"/>
|
||||
<circle cx="11" cy="16" r="1.2" fill="#6E6E6E"/>
|
||||
</g>
|
||||
</svg>
|
||||
*/
|
||||
|
||||
boolean enabled = c.isEnabled();
|
||||
boolean focused = FlatUIUtils.isPermanentFocusOwner( c );
|
||||
boolean enabled = c == null || c.isEnabled();
|
||||
boolean focused = c != null && FlatUIUtils.isPermanentFocusOwner( c );
|
||||
|
||||
float xy = 0.5f;
|
||||
float wh = iconSize() - 1;
|
||||
@@ -142,22 +150,19 @@ public class FlatHelpButtonIcon
|
||||
g2.fill( new Ellipse2D.Float( xy, xy, wh, wh ) );
|
||||
|
||||
// paint question mark
|
||||
Path2D q = new Path2D.Float();
|
||||
q.moveTo( 11, 5 );
|
||||
q.curveTo( 8.8,5, 7,6.8, 7,9 );
|
||||
q.lineTo( 9, 9 );
|
||||
q.curveTo( 9,7.9, 9.9,7, 11,7 );
|
||||
q.curveTo( 12.1,7, 13,7.9, 13,9 );
|
||||
q.curveTo( 13,11, 10,10.75, 10,14 );
|
||||
q.lineTo( 12, 14 );
|
||||
q.curveTo( 12,11.75, 15,11.5, 15,9 );
|
||||
q.curveTo( 15,6.8, 13.2,5, 11,5 );
|
||||
q.closePath();
|
||||
Path2D q = new Path2D.Float( Path2D.WIND_NON_ZERO, 10 );
|
||||
q.moveTo( 8,8.5 );
|
||||
q.curveTo( 8.25,7, 9.66585007,6, 11,6 );
|
||||
q.curveTo( 12.5,6, 14,7, 14,8.5 );
|
||||
q.curveTo( 14,10.5, 11,11, 11,13 );
|
||||
|
||||
g2.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
|
||||
g2.setStroke( new BasicStroke( 2, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
|
||||
|
||||
g2.translate( focusWidth, focusWidth );
|
||||
g2.setColor( enabled ? questionMarkColor : disabledQuestionMarkColor );
|
||||
g2.fill( q );
|
||||
g2.fillRect( 10, 15, 2, 2 );
|
||||
g2.draw( q );
|
||||
g2.fill( new Ellipse2D.Float( 9.8f, 14.8f, 2.4f, 2.4f ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -20,7 +20,6 @@ import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Line2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.ui.FlatButtonUI;
|
||||
@@ -54,13 +53,15 @@ public class FlatInternalFrameCloseIcon
|
||||
|
||||
g.setColor( FlatButtonUI.buttonStateColor( c, c.getForeground(), null, null, hoverForeground, pressedForeground ) );
|
||||
|
||||
float mx = width / 2;
|
||||
float my = height / 2;
|
||||
float mx = width / 2f;
|
||||
float my = height / 2f;
|
||||
float r = 3.25f;
|
||||
|
||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||
path.append( new Line2D.Float( mx - r, my - r, mx + r, my + r ), false );
|
||||
path.append( new Line2D.Float( mx - r, my + r, mx + r, my - r ), false );
|
||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD, 4 );
|
||||
path.moveTo( mx - r, my - r );
|
||||
path.lineTo( mx + r, my + r );
|
||||
path.moveTo( mx - r, my + r );
|
||||
path.lineTo( mx + r, my - r );
|
||||
g.setStroke( new BasicStroke( 1f ) );
|
||||
g.draw( path );
|
||||
}
|
||||
|
||||
@@ -61,9 +61,14 @@ public class FlatMenuArrowIcon
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
public Object getStyleableValue( String key ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
if( !c.getComponentOrientation().isLeftToRight() )
|
||||
if( c != null && !c.getComponentOrientation().isLeftToRight() )
|
||||
g.rotate( Math.toRadians( 180 ), width / 2., height / 2. );
|
||||
|
||||
g.setColor( getArrowColor( c ) );
|
||||
@@ -82,7 +87,7 @@ public class FlatMenuArrowIcon
|
||||
if( c instanceof JMenu && ((JMenu)c).isSelected() && !isUnderlineSelection() )
|
||||
return selectionForeground;
|
||||
|
||||
return c.isEnabled() ? arrowColor : disabledArrowColor;
|
||||
return c == null || c.isEnabled() ? arrowColor : disabledArrowColor;
|
||||
}
|
||||
|
||||
protected boolean isUnderlineSelection() {
|
||||
|
||||
@@ -19,7 +19,7 @@ package com.formdev.flatlaf.icons;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
|
||||
/**
|
||||
* "Error" icon for {@link javax.swing.JOptionPane}.
|
||||
@@ -40,8 +40,8 @@ public class FlatOptionPaneErrorIcon
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<circle cx="16" cy="16" r="14" fill="#DB5860"/>
|
||||
<rect width="4" height="11" x="14" y="7" fill="#FFF"/>
|
||||
<rect width="4" height="4" x="14" y="21" fill="#FFF"/>
|
||||
<rect width="4" height="12" x="14" y="7" fill="#FFF" rx="2"/>
|
||||
<circle cx="16" cy="23" r="2" fill="#FFF"/>
|
||||
</g>
|
||||
</svg>
|
||||
*/
|
||||
@@ -54,8 +54,8 @@ public class FlatOptionPaneErrorIcon
|
||||
@Override
|
||||
protected Shape createInside() {
|
||||
Path2D inside = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||
inside.append( new Rectangle2D.Float( 14, 7, 4, 11 ), false );
|
||||
inside.append( new Rectangle2D.Float( 14, 21, 4, 4 ), false );
|
||||
inside.append( new RoundRectangle2D.Float( 14, 7, 4, 12, 4, 4 ), false );
|
||||
inside.append( new Ellipse2D.Float( 14, 21, 4, 4 ), false );
|
||||
return inside;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ package com.formdev.flatlaf.icons;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
|
||||
/**
|
||||
* "Information" icon for {@link javax.swing.JOptionPane}.
|
||||
@@ -40,8 +40,8 @@ public class FlatOptionPaneInformationIcon
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<circle cx="16" cy="16" r="14" fill="#389FD6"/>
|
||||
<rect width="4" height="11" x="14" y="14" fill="#FFF"/>
|
||||
<rect width="4" height="4" x="14" y="7" fill="#FFF"/>
|
||||
<rect width="4" height="12" x="14" y="13" fill="#FFF" rx="2"/>
|
||||
<circle cx="16" cy="9" r="2" fill="#FFF"/>
|
||||
</g>
|
||||
</svg>
|
||||
*/
|
||||
@@ -54,8 +54,8 @@ public class FlatOptionPaneInformationIcon
|
||||
@Override
|
||||
protected Shape createInside() {
|
||||
Path2D inside = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||
inside.append( new Rectangle2D.Float( 14, 14, 4, 11 ), false );
|
||||
inside.append( new Rectangle2D.Float( 14, 7, 4, 4 ), false );
|
||||
inside.append( new RoundRectangle2D.Float( 14, 13, 4, 12, 4, 4 ), false );
|
||||
inside.append( new Ellipse2D.Float( 14, 7, 4, 4 ), false );
|
||||
return inside;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
|
||||
package com.formdev.flatlaf.icons;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
/**
|
||||
* "Question" icon for {@link javax.swing.JOptionPane}.
|
||||
@@ -40,8 +40,8 @@ public class FlatOptionPaneQuestionIcon
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<circle cx="16" cy="16" r="14" fill="#389FD6"/>
|
||||
<rect width="4" height="4" x="14" y="22" fill="#FFF"/>
|
||||
<path fill="#FFF" d="M14,20 C14,20 18,20 18,20 C18,16 23,16 23,12 C23,8 20,6 16,6 C12,6 9,8 9,12 C9,12 13,12 13,12 C13,10 14,9 16,9 C18,9 19,10 19,12 C19,15 14,15 14,20 Z"/>
|
||||
<circle cx="16" cy="24" r="1.7" fill="#FFF"/>
|
||||
<path stroke="#FFF" stroke-linecap="round" stroke-width="3" d="M11.5,11.75 C11.75,9.5 13.75,8 16,8 C18.25,8 20.5,9.5 20.5,11.75 C20.5,14.75 16,15.5 16,19"/>
|
||||
</g>
|
||||
</svg>
|
||||
*/
|
||||
@@ -53,21 +53,17 @@ public class FlatOptionPaneQuestionIcon
|
||||
|
||||
@Override
|
||||
protected Shape createInside() {
|
||||
Path2D q = new Path2D.Float();
|
||||
q.moveTo( 14, 20 );
|
||||
q.lineTo( 18, 20 );
|
||||
q.curveTo( 18, 16, 23, 16, 23, 12 );
|
||||
q.curveTo( 23, 8, 20, 6, 16, 6 );
|
||||
q.curveTo( 12, 6, 9, 8, 9, 12 );
|
||||
q.curveTo( 9, 12, 13, 12, 13, 12 );
|
||||
q.curveTo( 13, 10, 14, 9, 16, 9 );
|
||||
q.curveTo( 18, 9, 19, 10, 19, 12 );
|
||||
q.curveTo( 19, 15, 14, 15, 14, 20 );
|
||||
q.closePath();
|
||||
Path2D q = new Path2D.Float( Path2D.WIND_NON_ZERO, 10 );
|
||||
q.moveTo( 11.5,11.75 );
|
||||
q.curveTo( 11.75,9.5, 13.75,8, 16,8 );
|
||||
q.curveTo( 18.25,8, 20.5,9.5, 20.5,11.75 );
|
||||
q.curveTo( 20.5,14.75, 16,15.5, 16,19 );
|
||||
|
||||
BasicStroke stroke = new BasicStroke( 3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER );
|
||||
|
||||
Path2D inside = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||
inside.append( new Rectangle2D.Float( 14, 22, 4, 4 ), false );
|
||||
inside.append( q, false );
|
||||
inside.append( new Ellipse2D.Float( 14.3f, 22.3f, 3.4f, 3.4f ), false );
|
||||
inside.append( stroke.createStrokedShape( q ), false );
|
||||
return inside;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,9 @@
|
||||
package com.formdev.flatlaf.icons;
|
||||
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
|
||||
/**
|
||||
@@ -39,23 +40,24 @@ public class FlatOptionPaneWarningIcon
|
||||
/*
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<polygon fill="#EDA200" points="16 2 31 28 1 28"/>
|
||||
<rect width="4" height="8" x="14" y="10" fill="#FFF"/>
|
||||
<rect width="4" height="4" x="14" y="21" fill="#FFF"/>
|
||||
<path fill="#EDA200" d="M17.7364863,3.038851 L30.2901269,25.0077221 C30.8381469,25.966757 30.5049534,27.1884663 29.5459185,27.7364863 C29.2437231,27.9091694 28.9016945,28 28.5536406,28 L3.44635936,28 C2.34178986,28 1.44635936,27.1045695 1.44635936,26 C1.44635936,25.6519461 1.53718999,25.3099175 1.70987307,25.0077221 L14.2635137,3.038851 C14.8115337,2.0798161 16.033243,1.74662265 16.9922779,2.29464259 C17.3023404,2.47182119 17.5593077,2.72878844 17.7364863,3.038851 Z"/>
|
||||
<rect width="4" height="11" x="14" y="8" fill="#FFF" rx="2"/>
|
||||
<circle cx="16" cy="23" r="2" fill="#FFF"/>
|
||||
</g>
|
||||
</svg>
|
||||
*/
|
||||
|
||||
@Override
|
||||
protected Shape createOutside() {
|
||||
return FlatUIUtils.createPath( 16,2, 31,28, 1,28 );
|
||||
return FlatUIUtils.createRoundTrianglePath( 16,0, 32,28, 0,28, 4 );
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Shape createInside() {
|
||||
Path2D inside = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||
inside.append( new Rectangle2D.Float( 14, 10, 4, 8 ), false );
|
||||
inside.append( new Rectangle2D.Float( 14, 21, 4, 4 ), false );
|
||||
inside.append( new RoundRectangle2D.Float( 14, 8, 4, 11, 4, 4 ), false );
|
||||
inside.append( new Ellipse2D.Float( 14, 21, 4, 4 ), false );
|
||||
return inside;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ public class FlatSearchIcon
|
||||
@Styleable protected Color searchIconPressedColor = UIManager.getColor( "SearchField.searchIconPressedColor" );
|
||||
|
||||
private final boolean ignoreButtonState;
|
||||
private Area area;
|
||||
|
||||
public FlatSearchIcon() {
|
||||
this( false );
|
||||
@@ -67,6 +68,11 @@ public class FlatSearchIcon
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
public Object getStyleableValue( String key ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
/*
|
||||
@@ -84,9 +90,11 @@ public class FlatSearchIcon
|
||||
null, searchIconHoverColor, searchIconPressedColor ) );
|
||||
|
||||
// paint magnifier
|
||||
Area area = new Area( new Ellipse2D.Float( 2, 2, 10, 10 ) );
|
||||
area.subtract( new Area( new Ellipse2D.Float( 3, 3, 8, 8 ) ) );
|
||||
area.add( new Area( FlatUIUtils.createPath( 10.813,9.75, 14,12.938, 12.938,14, 9.75,10.813 ) ) );
|
||||
if( area == null ) {
|
||||
area = new Area( new Ellipse2D.Float( 2, 2, 10, 10 ) );
|
||||
area.subtract( new Area( new Ellipse2D.Float( 3, 3, 8, 8 ) ) );
|
||||
area.add( new Area( FlatUIUtils.createPath( 10.813,9.75, 14,12.938, 12.938,14, 9.75,10.813 ) ) );
|
||||
}
|
||||
g.fill( area );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Line2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.util.Map;
|
||||
import javax.swing.UIManager;
|
||||
@@ -76,6 +75,11 @@ public class FlatTabbedPaneCloseIcon
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
public Object getStyleableValue( String key ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g ) {
|
||||
// paint background
|
||||
@@ -90,14 +94,16 @@ public class FlatTabbedPaneCloseIcon
|
||||
Color fg = FlatButtonUI.buttonStateColor( c, closeForeground, null, null, closeHoverForeground, closePressedForeground );
|
||||
g.setColor( FlatUIUtils.deriveColor( fg, c.getForeground() ) );
|
||||
|
||||
float mx = width / 2;
|
||||
float my = height / 2;
|
||||
float mx = width / 2f;
|
||||
float my = height / 2f;
|
||||
float r = ((bg != null) ? closeCrossFilledSize : closeCrossPlainSize) / 2;
|
||||
|
||||
// paint cross
|
||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||
path.append( new Line2D.Float( mx - r, my - r, mx + r, my + r ), false );
|
||||
path.append( new Line2D.Float( mx - r, my + r, mx + r, my - r ), false );
|
||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD, 4 );
|
||||
path.moveTo( mx - r, my - r );
|
||||
path.lineTo( mx + r, my + r );
|
||||
path.moveTo( mx - r, my + r );
|
||||
path.lineTo( mx + r, my - r );
|
||||
g.setStroke( new BasicStroke( closeCrossLineWidth ) );
|
||||
g.draw( path );
|
||||
}
|
||||
|
||||
@@ -18,8 +18,9 @@ package com.formdev.flatlaf.icons;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.geom.Path2D;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
|
||||
/**
|
||||
* "closed" icon for {@link javax.swing.JTree} used by {@link javax.swing.tree.DefaultTreeCellRenderer}.
|
||||
@@ -31,6 +32,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
public class FlatTreeClosedIcon
|
||||
extends FlatAbstractIcon
|
||||
{
|
||||
private Path2D path;
|
||||
|
||||
public FlatTreeClosedIcon() {
|
||||
super( 16, 16, UIManager.getColor( "Tree.icon.closedColor" ) );
|
||||
}
|
||||
@@ -41,10 +44,14 @@ public class FlatTreeClosedIcon
|
||||
|
||||
/*
|
||||
<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"/>
|
||||
<path fill="none" stroke="#6E6E6E" d="M13,13.5 L3,13.5 C2.17157288,13.5 1.5,12.8284271 1.5,12 L1.5,4 C1.5,3.17157288 2.17157288,2.5 3,2.5 L6.29289322,2.5 C6.42550146,2.5 6.55267842,2.55267842 6.64644661,2.64644661 L8.5,4.5 L8.5,4.5 L13,4.5 C13.8284271,4.5 14.5,5.17157288 14.5,6 L14.5,12 C14.5,12.8284271 13.8284271,13.5 13,13.5 Z"/>
|
||||
</svg>
|
||||
*/
|
||||
|
||||
g.fill( FlatUIUtils.createPath( 1,2, 6,2, 8,4, 15,4, 15,13, 1,13 ) );
|
||||
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
|
||||
|
||||
if( path == null )
|
||||
path = FlatFileViewDirectoryIcon.createFolderPath();
|
||||
g.draw( path );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,11 @@
|
||||
|
||||
package com.formdev.flatlaf.icons;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.util.function.Function;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.SwingUtilities;
|
||||
@@ -39,6 +41,7 @@ public class FlatTreeCollapsedIcon
|
||||
extends FlatAbstractIcon
|
||||
{
|
||||
private final boolean chevron;
|
||||
private Path2D path;
|
||||
|
||||
public FlatTreeCollapsedIcon() {
|
||||
this( UIManager.getColor( "Tree.icon.collapsedColor" ) );
|
||||
@@ -59,10 +62,15 @@ public class FlatTreeCollapsedIcon
|
||||
|
||||
if( chevron ) {
|
||||
// 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.setStroke( new BasicStroke( 1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER ) );
|
||||
if( path == null )
|
||||
path = FlatUIUtils.createPath( false, 3.5,1.5, 7.5,5.5, 3.5,9.5 );
|
||||
g.draw( path );
|
||||
} else {
|
||||
// triangle arrow
|
||||
g.fill( FlatUIUtils.createPath( 2,1, 2,10, 10,5.5 ) );
|
||||
if( path == null )
|
||||
path = FlatUIUtils.createPath( 2,1, 2,10, 10,5.5 );
|
||||
g.fill( path );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +84,7 @@ public class FlatTreeCollapsedIcon
|
||||
}
|
||||
|
||||
/**
|
||||
* Because this icons are always shared for all trees,
|
||||
* 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 ) {
|
||||
|
||||
@@ -16,10 +16,14 @@
|
||||
|
||||
package com.formdev.flatlaf.icons;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.geom.Line2D;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
import com.formdev.flatlaf.util.ColorFunctions;
|
||||
|
||||
/**
|
||||
* "leaf" icon for {@link javax.swing.JTree} used by {@link javax.swing.tree.DefaultTreeCellRenderer}.
|
||||
@@ -42,13 +46,22 @@ public class FlatTreeLeafIcon
|
||||
/*
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<polygon fill="#6E6E6E" points="8 6 8 1 13 1 13 15 3 15 3 6"/>
|
||||
<polygon fill="#6E6E6E" points="3 5 7 5 7 1"/>
|
||||
<rect width="11" height="13" x="2.5" y="1.5" stroke="#6E6E6E" rx="1.5"/>
|
||||
<line x1="5.5" x2="10.5" y1="5.5" y2="5.5" stroke="#6E6E6E" stroke-linecap="round" stroke-opacity=".6"/>
|
||||
<line x1="5.5" x2="10.5" y1="8" y2="8" stroke="#6E6E6E" stroke-linecap="round" stroke-opacity=".6"/>
|
||||
<line x1="5.5" x2="10.5" y1="10.5" y2="10.5" stroke="#6E6E6E" stroke-linecap="round" stroke-opacity=".6"/>
|
||||
</g>
|
||||
</svg>
|
||||
*/
|
||||
|
||||
g.fill( FlatUIUtils.createPath( 8,6, 8,1, 13,1, 13,15, 3,15, 3,6 ) );
|
||||
g.fill( FlatUIUtils.createPath( 3,5, 7,5, 7,1 ) );
|
||||
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
|
||||
g.setStroke( new BasicStroke( 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
|
||||
|
||||
g.draw( new RoundRectangle2D.Float( 2.5f, 1.5f, 11, 13, 3, 3 ) );
|
||||
|
||||
g.setColor( ColorFunctions.fade( g.getColor(), 0.6f ) );
|
||||
g.draw( new Line2D.Float( 5.5f, 5.5f, 10.5f, 5.5f ) );
|
||||
g.draw( new Line2D.Float( 5.5f, 8, 10.5f, 8 ) );
|
||||
g.draw( new Line2D.Float( 5.5f, 10.5f, 10.5f, 10.5f ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,11 @@
|
||||
|
||||
package com.formdev.flatlaf.icons;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.geom.Path2D;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
|
||||
@@ -31,6 +34,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
public class FlatTreeOpenIcon
|
||||
extends FlatAbstractIcon
|
||||
{
|
||||
private Path2D path;
|
||||
|
||||
public FlatTreeOpenIcon() {
|
||||
super( 16, 16, UIManager.getColor( "Tree.icon.openColor" ) );
|
||||
}
|
||||
@@ -41,14 +46,38 @@ public class FlatTreeOpenIcon
|
||||
|
||||
/*
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<polygon fill="#6E6E6E" points="1 2 6 2 8 4 14 4 14 6 3.5 6 1 11"/>
|
||||
<polygon fill="#6E6E6E" points="4 7 16 7 13 13 1 13"/>
|
||||
</g>
|
||||
<path fill="none" stroke="#6E6E6E" d="M2,13.5 L4.11538462,8.42307692 C4.34828895,7.86410651 4.89444872,7.5 5.5,7.5 L14.75,7.5 C15.0261424,7.5 15.25,7.72385763 15.25,8 C15.25,8.06601301 15.2369281,8.13137261 15.2115385,8.19230769 L13.3846154,12.5769231 C13.151711,13.1358935 12.6055513,13.5 12,13.5 L3,13.5 C2.17157288,13.5 1.5,12.8284271 1.5,12 L1.5,4 C1.5,3.17157288 2.17157288,2.5 3,2.5 L6.29289322,2.5 C6.42550146,2.5 6.55267842,2.55267842 6.64644661,2.64644661 L8.5,4.5 L8.5,4.5 L12,4.5 C12.8284271,4.5 13.5,5.17157288 13.5,6 L13.5,6.5 L13.5,6.5"/>
|
||||
</svg>
|
||||
*/
|
||||
|
||||
g.fill( FlatUIUtils.createPath( 1,2, 6,2, 8,4, 14,4, 14,6, 3.5,6, 1,11 ) );
|
||||
g.fill( FlatUIUtils.createPath( 4,7, 16,7, 13,13, 1,13 ) );
|
||||
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
|
||||
g.setStroke( new BasicStroke( 1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER ) );
|
||||
|
||||
if( path == null ) {
|
||||
double arc = 1.5;
|
||||
double arc2 = 0.5;
|
||||
path = FlatUIUtils.createPath( false,
|
||||
// bottom-left of opend part
|
||||
2,13.5,
|
||||
// top-left of opend part
|
||||
FlatUIUtils.ROUNDED, 4.5,7.5, arc,
|
||||
// top-right of opend part
|
||||
FlatUIUtils.ROUNDED, 15.5,7.5, arc2,
|
||||
|
||||
// bottom-right
|
||||
FlatUIUtils.ROUNDED, 13,13.5, arc,
|
||||
// bottom-left
|
||||
1.5+arc,13.5, FlatUIUtils.QUAD_TO, 1.5,13.5, 1.5,13.5-arc,
|
||||
// top-left
|
||||
1.5,2.5+arc, FlatUIUtils.QUAD_TO, 1.5,2.5, 1.5+arc,2.5,
|
||||
// top-mid-left
|
||||
6.5-arc2,2.5, FlatUIUtils.QUAD_TO, 6.5,2.5, 6.5+arc2,2.5+arc2,
|
||||
// top-mid-right
|
||||
8.5,4.5,
|
||||
// top-right
|
||||
13.5-arc,4.5, FlatUIUtils.QUAD_TO, 13.5,4.5, 13.5,4.5+arc,
|
||||
13.5,6.5 );
|
||||
}
|
||||
g.draw( path );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.ui.FlatButtonUI;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||
@@ -30,6 +29,7 @@ import com.formdev.flatlaf.util.HiDPIUtils;
|
||||
* Base class for window icons.
|
||||
*
|
||||
* @uiDefault TitlePane.buttonSize Dimension
|
||||
* @uiDefault TitlePane.buttonSymbolHeight int
|
||||
* @uiDefault TitlePane.buttonHoverBackground Color
|
||||
* @uiDefault TitlePane.buttonPressedBackground Color
|
||||
*
|
||||
@@ -38,17 +38,22 @@ import com.formdev.flatlaf.util.HiDPIUtils;
|
||||
public abstract class FlatWindowAbstractIcon
|
||||
extends FlatAbstractIcon
|
||||
{
|
||||
private final int symbolHeight;
|
||||
private final Color hoverBackground;
|
||||
private final Color pressedBackground;
|
||||
|
||||
public FlatWindowAbstractIcon() {
|
||||
this( UIManager.getDimension( "TitlePane.buttonSize" ),
|
||||
UIManager.getColor( "TitlePane.buttonHoverBackground" ),
|
||||
UIManager.getColor( "TitlePane.buttonPressedBackground" ) );
|
||||
/** @since 3.2 */
|
||||
protected FlatWindowAbstractIcon( String windowStyle ) {
|
||||
this( FlatUIUtils.getSubUIDimension( "TitlePane.buttonSize", windowStyle ),
|
||||
FlatUIUtils.getSubUIInt( "TitlePane.buttonSymbolHeight", windowStyle, 10 ),
|
||||
FlatUIUtils.getSubUIColor( "TitlePane.buttonHoverBackground", windowStyle ),
|
||||
FlatUIUtils.getSubUIColor( "TitlePane.buttonPressedBackground", windowStyle ) );
|
||||
}
|
||||
|
||||
public FlatWindowAbstractIcon( Dimension size, Color hoverBackground, Color pressedBackground ) {
|
||||
/** @since 3.2 */
|
||||
protected FlatWindowAbstractIcon( Dimension size, int symbolHeight, Color hoverBackground, Color pressedBackground ) {
|
||||
super( size.width, size.height, null );
|
||||
this.symbolHeight = symbolHeight;
|
||||
this.hoverBackground = hoverBackground;
|
||||
this.pressedBackground = pressedBackground;
|
||||
}
|
||||
@@ -80,4 +85,9 @@ public abstract class FlatWindowAbstractIcon
|
||||
protected Color getForeground( Component c ) {
|
||||
return c.getForeground();
|
||||
}
|
||||
|
||||
/** @since 3.2 */
|
||||
protected int getSymbolHeight() {
|
||||
return symbolHeight;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,9 @@ import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Line2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.ui.FlatButtonUI;
|
||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||
import com.formdev.flatlaf.util.SystemInfo;
|
||||
|
||||
/**
|
||||
@@ -39,27 +38,38 @@ import com.formdev.flatlaf.util.SystemInfo;
|
||||
public class FlatWindowCloseIcon
|
||||
extends FlatWindowAbstractIcon
|
||||
{
|
||||
private final Color hoverForeground = UIManager.getColor( "TitlePane.closeHoverForeground" );
|
||||
private final Color pressedForeground = UIManager.getColor( "TitlePane.closePressedForeground" );
|
||||
private final Color hoverForeground;
|
||||
private final Color pressedForeground;
|
||||
|
||||
public FlatWindowCloseIcon() {
|
||||
super( UIManager.getDimension( "TitlePane.buttonSize" ),
|
||||
UIManager.getColor( "TitlePane.closeHoverBackground" ),
|
||||
UIManager.getColor( "TitlePane.closePressedBackground" ) );
|
||||
this( null );
|
||||
}
|
||||
|
||||
/** @since 3.2 */
|
||||
public FlatWindowCloseIcon( String windowStyle ) {
|
||||
super( FlatUIUtils.getSubUIDimension( "TitlePane.buttonSize", windowStyle ),
|
||||
FlatUIUtils.getSubUIInt( "TitlePane.buttonSymbolHeight", windowStyle, 10 ),
|
||||
FlatUIUtils.getSubUIColor( "TitlePane.closeHoverBackground", windowStyle ),
|
||||
FlatUIUtils.getSubUIColor( "TitlePane.closePressedBackground", windowStyle ) );
|
||||
|
||||
hoverForeground = FlatUIUtils.getSubUIColor( "TitlePane.closeHoverForeground", windowStyle );
|
||||
pressedForeground = FlatUIUtils.getSubUIColor( "TitlePane.closePressedForeground", windowStyle );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
|
||||
int iwh = (int) (10 * scaleFactor);
|
||||
int iwh = (int) (getSymbolHeight() * scaleFactor);
|
||||
int ix = x + ((width - iwh) / 2);
|
||||
int iy = y + ((height - iwh) / 2);
|
||||
int ix2 = ix + iwh - 1;
|
||||
int iy2 = iy + iwh - 1;
|
||||
float thickness = SystemInfo.isWindows_11_orLater ? (float) scaleFactor : (int) scaleFactor;
|
||||
|
||||
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, iy2, ix2, iy ), false );
|
||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD, 4 );
|
||||
path.moveTo( ix, iy );
|
||||
path.lineTo( ix2, iy2 );
|
||||
path.moveTo( ix, iy2 );
|
||||
path.lineTo( ix2, iy );
|
||||
g.setStroke( new BasicStroke( thickness ) );
|
||||
g.draw( path );
|
||||
}
|
||||
|
||||
@@ -27,11 +27,17 @@ public class FlatWindowIconifyIcon
|
||||
extends FlatWindowAbstractIcon
|
||||
{
|
||||
public FlatWindowIconifyIcon() {
|
||||
this( null );
|
||||
}
|
||||
|
||||
/** @since 3.2 */
|
||||
public FlatWindowIconifyIcon( String windowStyle ) {
|
||||
super( windowStyle );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
|
||||
int iw = (int) (10 * scaleFactor);
|
||||
int iw = (int) (getSymbolHeight() * scaleFactor);
|
||||
int ih = (int) scaleFactor;
|
||||
int ix = x + ((width - iw) / 2);
|
||||
int iy = y + ((height - ih) / 2);
|
||||
|
||||
@@ -29,11 +29,17 @@ public class FlatWindowMaximizeIcon
|
||||
extends FlatWindowAbstractIcon
|
||||
{
|
||||
public FlatWindowMaximizeIcon() {
|
||||
this( null );
|
||||
}
|
||||
|
||||
/** @since 3.2 */
|
||||
public FlatWindowMaximizeIcon( String windowStyle ) {
|
||||
super( windowStyle );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
|
||||
int iwh = (int) (10 * scaleFactor);
|
||||
int iwh = (int) (getSymbolHeight() * scaleFactor);
|
||||
int ix = x + ((width - iwh) / 2);
|
||||
int iy = y + ((height - iwh) / 2);
|
||||
float thickness = SystemInfo.isWindows_11_orLater ? (float) scaleFactor : (int) scaleFactor;
|
||||
|
||||
@@ -32,18 +32,24 @@ public class FlatWindowRestoreIcon
|
||||
extends FlatWindowAbstractIcon
|
||||
{
|
||||
public FlatWindowRestoreIcon() {
|
||||
this( null );
|
||||
}
|
||||
|
||||
/** @since 3.2 */
|
||||
public FlatWindowRestoreIcon( String windowStyle ) {
|
||||
super( windowStyle );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
|
||||
int iwh = (int) (10 * scaleFactor);
|
||||
int iwh = (int) (getSymbolHeight() * scaleFactor);
|
||||
int ix = x + ((width - iwh) / 2);
|
||||
int iy = y + ((height - iwh) / 2);
|
||||
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) ((getSymbolHeight() - 2) * scaleFactor);
|
||||
int ro2 = iwh - rwh;
|
||||
|
||||
// upper-right rectangle
|
||||
|
||||
@@ -502,9 +502,9 @@ class JsonParser {
|
||||
}
|
||||
|
||||
private boolean isHexDigit() {
|
||||
return current >= '0' && current <= '9'
|
||||
|| current >= 'a' && current <= 'f'
|
||||
|| current >= 'A' && current <= 'F';
|
||||
return (current >= '0' && current <= '9')
|
||||
|| (current >= 'a' && current <= 'f')
|
||||
|| (current >= 'A' && current <= 'F');
|
||||
}
|
||||
|
||||
private boolean isEndOfText() {
|
||||
|
||||
@@ -69,7 +69,7 @@ public class Location {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
if (!(obj instanceof Location)) {
|
||||
return false;
|
||||
}
|
||||
Location other = (Location)obj;
|
||||
|
||||
@@ -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.
|
||||
* Otherwise the compiler outputs a warning because this package is opend in module-info.java.
|
||||
* Also when using --patch-module (e.g. from an IDE), an error would occur for empty packages.
|
||||
* 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.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.themes;
|
||||
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.FlatDarkLaf;
|
||||
|
||||
/**
|
||||
* A Flat LaF that imitates macOS dark look.
|
||||
* <p>
|
||||
* The UI defaults are loaded from {@code FlatMacDarkLaf.properties},
|
||||
* {@code FlatDarkLaf.properties} and {@code FlatLaf.properties}.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
* @since 3
|
||||
*/
|
||||
public class FlatMacDarkLaf
|
||||
extends FlatDarkLaf
|
||||
{
|
||||
public static final String NAME = "FlatLaf macOS Dark";
|
||||
|
||||
/**
|
||||
* Sets the application look and feel to this LaF
|
||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||
*/
|
||||
public static boolean setup() {
|
||||
return setup( new FlatMacDarkLaf() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds this look and feel to the set of available look and feels.
|
||||
* <p>
|
||||
* Useful if your application uses {@link UIManager#getInstalledLookAndFeels()}
|
||||
* to query available LaFs and display them to the user in a combobox.
|
||||
*/
|
||||
public static void installLafInfo() {
|
||||
installLafInfo( NAME, FlatMacDarkLaf.class );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "FlatLaf macOS Dark Look and Feel";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDark() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.themes;
|
||||
|
||||
import javax.swing.UIManager;
|
||||
import com.formdev.flatlaf.FlatLightLaf;
|
||||
|
||||
/**
|
||||
* A Flat LaF that imitates macOS light look.
|
||||
* <p>
|
||||
* The UI defaults are loaded from {@code FlatMacLightLaf.properties},
|
||||
* {@code FlatLightLaf.properties} and {@code FlatLaf.properties}.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
* @since 3
|
||||
*/
|
||||
public class FlatMacLightLaf
|
||||
extends FlatLightLaf
|
||||
{
|
||||
public static final String NAME = "FlatLaf macOS Light";
|
||||
|
||||
/**
|
||||
* Sets the application look and feel to this LaF
|
||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||
*/
|
||||
public static boolean setup() {
|
||||
return setup( new FlatMacLightLaf() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds this look and feel to the set of available look and feels.
|
||||
* <p>
|
||||
* Useful if your application uses {@link UIManager#getInstalledLookAndFeels()}
|
||||
* to query available LaFs and display them to the user in a combobox.
|
||||
*/
|
||||
public static void installLafInfo() {
|
||||
installLafInfo( NAME, FlatMacLightLaf.class );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "FlatLaf macOS Light Look and Feel";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDark() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import java.awt.Graphics2D;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.plaf.UIResource;
|
||||
import javax.swing.plaf.basic.BasicArrowButton;
|
||||
|
||||
@@ -37,7 +38,7 @@ public class FlatArrowButton
|
||||
extends BasicArrowButton
|
||||
implements UIResource
|
||||
{
|
||||
public static final int DEFAULT_ARROW_WIDTH = 8;
|
||||
public static final int DEFAULT_ARROW_WIDTH = 9;
|
||||
|
||||
protected boolean chevron;
|
||||
protected Color foreground;
|
||||
@@ -48,8 +49,10 @@ public class FlatArrowButton
|
||||
protected Color pressedBackground;
|
||||
|
||||
private int arrowWidth = DEFAULT_ARROW_WIDTH;
|
||||
private float arrowThickness = 1;
|
||||
private float xOffset = 0;
|
||||
private float yOffset = 0;
|
||||
private boolean roundBorderAutoXOffset = true;
|
||||
|
||||
private boolean hover;
|
||||
private boolean pressed;
|
||||
@@ -82,14 +85,18 @@ public class FlatArrowButton
|
||||
|
||||
@Override
|
||||
public void mousePressed( MouseEvent e ) {
|
||||
pressed = true;
|
||||
repaint();
|
||||
if( SwingUtilities.isLeftMouseButton( e ) ) {
|
||||
pressed = true;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased( MouseEvent e ) {
|
||||
pressed = false;
|
||||
repaint();
|
||||
if( SwingUtilities.isLeftMouseButton( e ) ) {
|
||||
pressed = false;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
@@ -116,6 +123,16 @@ public class FlatArrowButton
|
||||
this.arrowWidth = arrowWidth;
|
||||
}
|
||||
|
||||
/** @since 3 */
|
||||
public float getArrowThickness() {
|
||||
return arrowThickness;
|
||||
}
|
||||
|
||||
/** @since 3 */
|
||||
public void setArrowThickness( float arrowThickness ) {
|
||||
this.arrowThickness = arrowThickness;
|
||||
}
|
||||
|
||||
protected boolean isHover() {
|
||||
return hover;
|
||||
}
|
||||
@@ -140,6 +157,16 @@ public class FlatArrowButton
|
||||
this.yOffset = yOffset;
|
||||
}
|
||||
|
||||
/** @since 3 */
|
||||
public boolean isRoundBorderAutoXOffset() {
|
||||
return roundBorderAutoXOffset;
|
||||
}
|
||||
|
||||
/** @since 3 */
|
||||
public void setRoundBorderAutoXOffset( boolean roundBorderAutoXOffset ) {
|
||||
this.roundBorderAutoXOffset = roundBorderAutoXOffset;
|
||||
}
|
||||
|
||||
protected Color deriveBackground( Color background ) {
|
||||
return background;
|
||||
}
|
||||
@@ -203,14 +230,17 @@ public class FlatArrowButton
|
||||
}
|
||||
|
||||
protected void paintArrow( Graphics2D g ) {
|
||||
boolean vert = (direction == NORTH || direction == SOUTH);
|
||||
int x = 0;
|
||||
|
||||
// move arrow for round borders
|
||||
Container parent = getParent();
|
||||
if( vert && parent instanceof JComponent && FlatUIUtils.hasRoundBorder( (JComponent) parent ) )
|
||||
x -= scale( parent.getComponentOrientation().isLeftToRight() ? 1 : -1 );
|
||||
if( isRoundBorderAutoXOffset() ) {
|
||||
Container parent = getParent();
|
||||
boolean vert = (direction == NORTH || direction == SOUTH);
|
||||
if( vert && parent instanceof JComponent && FlatUIUtils.hasRoundBorder( (JComponent) parent ) )
|
||||
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(), getArrowThickness(), getXOffset(), getYOffset() );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +101,12 @@ public class FlatBorder
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
@Override
|
||||
public Object getStyleableValue( String key ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
|
||||
@@ -159,7 +159,7 @@ public class FlatButtonBorder
|
||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||
if( FlatButtonUI.isToolBarButton( c ) ) {
|
||||
// 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)
|
||||
? ((AbstractButton)c).getMargin()
|
||||
: null;
|
||||
|
||||
@@ -20,6 +20,7 @@ import static com.formdev.flatlaf.FlatClientProperties.*;
|
||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.FontMetrics;
|
||||
@@ -42,13 +43,18 @@ import javax.swing.JTextField;
|
||||
import javax.swing.JToggleButton;
|
||||
import javax.swing.JToolBar;
|
||||
import javax.swing.LookAndFeel;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.plaf.ButtonUI;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.ToolBarUI;
|
||||
import javax.swing.plaf.UIResource;
|
||||
import javax.swing.plaf.basic.BasicButtonListener;
|
||||
import javax.swing.plaf.basic.BasicButtonUI;
|
||||
import javax.swing.plaf.basic.BasicHTML;
|
||||
import javax.swing.text.View;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
import com.formdev.flatlaf.FlatLaf;
|
||||
import com.formdev.flatlaf.icons.FlatHelpButtonIcon;
|
||||
@@ -77,20 +83,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.endBackground Color optional; if set, a gradient paint is used
|
||||
* @uiDefault Button.focusedBackground Color optional
|
||||
* @uiDefault Button.focusedForeground Color optional
|
||||
* @uiDefault Button.hoverBackground Color optional
|
||||
* @uiDefault Button.hoverForeground Color optional
|
||||
* @uiDefault Button.pressedBackground Color optional
|
||||
* @uiDefault Button.pressedForeground Color optional
|
||||
* @uiDefault Button.selectedBackground Color
|
||||
* @uiDefault Button.selectedForeground Color
|
||||
* @uiDefault Button.disabledBackground Color optional
|
||||
* @uiDefault Button.disabledText Color
|
||||
* @uiDefault Button.disabledSelectedBackground Color
|
||||
* @uiDefault Button.disabledSelectedForeground Color optional
|
||||
* @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.endBackground Color optional; if set, a gradient paint is used
|
||||
* @uiDefault Button.default.foreground Color
|
||||
* @uiDefault Button.default.focusedBackground Color optional
|
||||
* @uiDefault Button.default.focusedForeground Color optional
|
||||
* @uiDefault Button.default.hoverBackground Color optional
|
||||
* @uiDefault Button.default.hoverForeground Color optional
|
||||
* @uiDefault Button.default.pressedBackground Color optional
|
||||
* @uiDefault Button.default.pressedForeground Color optional
|
||||
* @uiDefault Button.default.boldText boolean
|
||||
* @uiDefault Button.paintShadow boolean default is false
|
||||
* @uiDefault Button.shadowWidth int default is 2
|
||||
@@ -98,8 +111,13 @@ import com.formdev.flatlaf.util.UIScale;
|
||||
* @uiDefault Button.default.shadowColor Color optional
|
||||
* @uiDefault Button.toolbar.spacingInsets Insets
|
||||
* @uiDefault Button.toolbar.hoverBackground Color
|
||||
* @uiDefault Button.toolbar.hoverForeground Color optional
|
||||
* @uiDefault Button.toolbar.pressedBackground Color
|
||||
* @uiDefault Button.toolbar.pressedForeground Color optional
|
||||
* @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
|
||||
*/
|
||||
@@ -116,20 +134,27 @@ public class FlatButtonUI
|
||||
protected Color startBackground;
|
||||
protected Color endBackground;
|
||||
@Styleable protected Color focusedBackground;
|
||||
/** @since 2.3 */ @Styleable protected Color focusedForeground;
|
||||
@Styleable protected Color hoverBackground;
|
||||
/** @since 2.3 */ @Styleable protected Color hoverForeground;
|
||||
@Styleable protected Color pressedBackground;
|
||||
/** @since 2.3 */ @Styleable protected Color pressedForeground;
|
||||
@Styleable protected Color selectedBackground;
|
||||
@Styleable protected Color selectedForeground;
|
||||
@Styleable protected Color disabledBackground;
|
||||
@Styleable protected Color disabledText;
|
||||
@Styleable protected Color disabledSelectedBackground;
|
||||
/** @since 2.3 */ @Styleable protected Color disabledSelectedForeground;
|
||||
|
||||
@Styleable(dot=true) protected Color defaultBackground;
|
||||
protected Color defaultEndBackground;
|
||||
@Styleable(dot=true) protected Color defaultForeground;
|
||||
@Styleable(dot=true) protected Color defaultFocusedBackground;
|
||||
/** @since 2.3 */ @Styleable(dot=true) protected Color defaultFocusedForeground;
|
||||
@Styleable(dot=true) protected Color defaultHoverBackground;
|
||||
/** @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;
|
||||
|
||||
@Styleable protected boolean paintShadow;
|
||||
@@ -138,8 +163,13 @@ public class FlatButtonUI
|
||||
@Styleable(dot=true) protected Color defaultShadowColor;
|
||||
|
||||
@Styleable(dot=true) protected Color toolbarHoverBackground;
|
||||
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarHoverForeground;
|
||||
@Styleable(dot=true) protected Color toolbarPressedBackground;
|
||||
/** @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;
|
||||
@@ -156,7 +186,7 @@ public class FlatButtonUI
|
||||
private AtomicBoolean borderShared;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return FlatUIUtils.canUseSharedUI( c )
|
||||
return FlatUIUtils.canUseSharedUI( c ) && !FlatUIUtils.needsLightAWTPeer( c )
|
||||
? FlatUIUtils.createSharedUI( FlatButtonUI.class, () -> new FlatButtonUI( true ) )
|
||||
: new FlatButtonUI( false );
|
||||
}
|
||||
@@ -168,6 +198,13 @@ public class FlatButtonUI
|
||||
|
||||
@Override
|
||||
public void installUI( JComponent c ) {
|
||||
if( FlatUIUtils.needsLightAWTPeer( c ) )
|
||||
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
|
||||
else
|
||||
installUIImpl( c );
|
||||
}
|
||||
|
||||
private void installUIImpl( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
installStyle( (AbstractButton) c );
|
||||
@@ -189,20 +226,27 @@ public class FlatButtonUI
|
||||
startBackground = UIManager.getColor( prefix + "startBackground" );
|
||||
endBackground = UIManager.getColor( prefix + "endBackground" );
|
||||
focusedBackground = UIManager.getColor( prefix + "focusedBackground" );
|
||||
focusedForeground = UIManager.getColor( prefix + "focusedForeground" );
|
||||
hoverBackground = UIManager.getColor( prefix + "hoverBackground" );
|
||||
hoverForeground = UIManager.getColor( prefix + "hoverForeground" );
|
||||
pressedBackground = UIManager.getColor( prefix + "pressedBackground" );
|
||||
pressedForeground = UIManager.getColor( prefix + "pressedForeground" );
|
||||
selectedBackground = UIManager.getColor( prefix + "selectedBackground" );
|
||||
selectedForeground = UIManager.getColor( prefix + "selectedForeground" );
|
||||
disabledBackground = UIManager.getColor( prefix + "disabledBackground" );
|
||||
disabledText = UIManager.getColor( prefix + "disabledText" );
|
||||
disabledSelectedBackground = UIManager.getColor( prefix + "disabledSelectedBackground" );
|
||||
disabledSelectedForeground = UIManager.getColor( prefix + "disabledSelectedForeground" );
|
||||
|
||||
defaultBackground = FlatUIUtils.getUIColor( "Button.default.startBackground", "Button.default.background" );
|
||||
defaultEndBackground = UIManager.getColor( "Button.default.endBackground" );
|
||||
defaultForeground = UIManager.getColor( "Button.default.foreground" );
|
||||
defaultFocusedBackground = UIManager.getColor( "Button.default.focusedBackground" );
|
||||
defaultFocusedForeground = UIManager.getColor( "Button.default.focusedForeground" );
|
||||
defaultHoverBackground = UIManager.getColor( "Button.default.hoverBackground" );
|
||||
defaultHoverForeground = UIManager.getColor( "Button.default.hoverForeground" );
|
||||
defaultPressedBackground = UIManager.getColor( "Button.default.pressedBackground" );
|
||||
defaultPressedForeground = UIManager.getColor( "Button.default.pressedForeground" );
|
||||
defaultBoldText = UIManager.getBoolean( "Button.default.boldText" );
|
||||
|
||||
paintShadow = UIManager.getBoolean( "Button.paintShadow" );
|
||||
@@ -211,8 +255,13 @@ public class FlatButtonUI
|
||||
defaultShadowColor = UIManager.getColor( "Button.default.shadowColor" );
|
||||
|
||||
toolbarHoverBackground = UIManager.getColor( prefix + "toolbar.hoverBackground" );
|
||||
toolbarHoverForeground = UIManager.getColor( prefix + "toolbar.hoverForeground" );
|
||||
toolbarPressedBackground = UIManager.getColor( prefix + "toolbar.pressedBackground" );
|
||||
toolbarPressedForeground = UIManager.getColor( prefix + "toolbar.pressedForeground" );
|
||||
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" );
|
||||
defaultMargin = UIManager.getInsets( prefix + "margin" );
|
||||
@@ -262,6 +311,10 @@ public class FlatButtonUI
|
||||
b.repaint();
|
||||
break;
|
||||
|
||||
case OUTLINE:
|
||||
b.repaint();
|
||||
break;
|
||||
|
||||
case STYLE:
|
||||
case STYLE_CLASS:
|
||||
if( shared && FlatStylingSupport.hasStyleProperty( b ) ) {
|
||||
@@ -311,6 +364,9 @@ public class FlatButtonUI
|
||||
return ((FlatHelpButtonIcon)helpButtonIcon).applyStyleProperty( key, value );
|
||||
}
|
||||
|
||||
if( "iconTextGap".equals( key ) && value instanceof Integer )
|
||||
value = UIScale.scale( (Integer) value );
|
||||
|
||||
if( borderShared == null )
|
||||
borderShared = new AtomicBoolean( true );
|
||||
return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, b, borderShared );
|
||||
@@ -325,6 +381,18 @@ public class FlatButtonUI
|
||||
return infos;
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
@Override
|
||||
public Object getStyleableValue( JComponent c, String key ) {
|
||||
if( key.startsWith( "help." ) ) {
|
||||
return (helpButtonIcon instanceof FlatHelpButtonIcon)
|
||||
? ((FlatHelpButtonIcon)helpButtonIcon).getStyleableValue( key.substring( "help.".length() ) )
|
||||
: null;
|
||||
}
|
||||
|
||||
return FlatStylingSupport.getAnnotatedStyleableValue( this, c.getBorder(), key );
|
||||
}
|
||||
|
||||
static boolean isContentAreaFilled( Component c ) {
|
||||
return !(c instanceof AbstractButton) || ((AbstractButton)c).isContentAreaFilled();
|
||||
}
|
||||
@@ -339,7 +407,7 @@ public class FlatButtonUI
|
||||
|
||||
/**
|
||||
* 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 ) {
|
||||
if( !(c instanceof JButton) && !(c instanceof JToggleButton) )
|
||||
@@ -485,9 +553,62 @@ public class FlatButtonUI
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to BasicButtonUI.paint(), but does not use zero insets for HTML text,
|
||||
* which is done in BasicButtonUI.layout() since Java 19.
|
||||
* See https://github.com/openjdk/jdk/pull/8407
|
||||
* and https://github.com/openjdk/jdk/pull/8407#issuecomment-1761583430
|
||||
*/
|
||||
@Override
|
||||
public void paint( Graphics g, JComponent c ) {
|
||||
super.paint( FlatLabelUI.createGraphicsHTMLTextYCorrection( g, c ), c );
|
||||
g = FlatLabelUI.createGraphicsHTMLTextYCorrection( g, c );
|
||||
|
||||
AbstractButton b = (AbstractButton) c;
|
||||
|
||||
// layout
|
||||
String clippedText = layout( b, b.getFontMetrics( b.getFont() ), b.getWidth(), b.getHeight() );
|
||||
|
||||
// not used in FlatLaf, but invoked for compatibility with BasicButtonUI.paint()
|
||||
clearTextShiftOffset();
|
||||
|
||||
// not used in FlatLaf, but invoked for compatibility with BasicButtonUI.paint()
|
||||
ButtonModel model = b.getModel();
|
||||
if( model.isArmed() && model.isPressed() )
|
||||
paintButtonPressed( g, b );
|
||||
|
||||
// paint icon
|
||||
if( b.getIcon() != null )
|
||||
paintIcon( g, b, iconR );
|
||||
|
||||
// paint text
|
||||
if( clippedText != null && !clippedText.isEmpty() ) {
|
||||
View view = (View) b.getClientProperty( BasicHTML.propertyKey );
|
||||
if( view != null )
|
||||
view.paint( g, textR ); // HTML text
|
||||
else
|
||||
paintText( g, b, textR, clippedText );
|
||||
}
|
||||
|
||||
// not used in FlatLaf, but invoked for compatibility with BasicButtonUI.paint()
|
||||
if( b.isFocusPainted() && b.hasFocus() )
|
||||
paintFocus( g, b, viewR, textR, iconR );
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
@@ -510,6 +631,8 @@ public class FlatButtonUI
|
||||
}
|
||||
|
||||
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() );
|
||||
int mnemonicIndex = FlatLaf.isShowMnemonics() ? b.getDisplayedMnemonicIndex() : -1;
|
||||
|
||||
@@ -523,11 +646,14 @@ public class FlatButtonUI
|
||||
|
||||
// selected state
|
||||
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
|
||||
return buttonStateColor( c,
|
||||
toolBarButton ? toolbarSelectedBackground : selectedBackground,
|
||||
toolBarButton ? toolbarSelectedBackground : disabledSelectedBackground,
|
||||
toolBarButton
|
||||
? (toolbarDisabledSelectedBackground != null ? toolbarDisabledSelectedBackground : toolbarSelectedBackground)
|
||||
: disabledSelectedBackground,
|
||||
null,
|
||||
null,
|
||||
toolBarButton ? toolbarPressedBackground : pressedBackground );
|
||||
@@ -554,6 +680,9 @@ public class FlatButtonUI
|
||||
}
|
||||
|
||||
protected Color getBackgroundBase( JComponent c, boolean def ) {
|
||||
if( FlatUIUtils.isAWTPeer( c ) )
|
||||
return background;
|
||||
|
||||
// use component background if explicitly set
|
||||
Color bg = c.getBackground();
|
||||
if( isCustomBackground( bg ) )
|
||||
@@ -569,6 +698,9 @@ public class FlatButtonUI
|
||||
public static Color buttonStateColor( Component c, Color enabledColor, Color disabledColor,
|
||||
Color focusedColor, Color hoverColor, Color pressedColor )
|
||||
{
|
||||
if( c == null )
|
||||
return enabledColor;
|
||||
|
||||
if( !c.isEnabled() )
|
||||
return disabledColor;
|
||||
|
||||
@@ -589,18 +721,48 @@ public class FlatButtonUI
|
||||
}
|
||||
|
||||
protected Color getForeground( JComponent c ) {
|
||||
if( !c.isEnabled() )
|
||||
return disabledText;
|
||||
boolean toolBarButton = isToolBarButton( c ) || isBorderlessButton( c );
|
||||
|
||||
if( ((AbstractButton)c).isSelected() && !(isToolBarButton( c ) || isBorderlessButton( c )) )
|
||||
return selectedForeground;
|
||||
// selected state
|
||||
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
|
||||
Color fg = c.getForeground();
|
||||
if( isCustomForeground( fg ) )
|
||||
return fg;
|
||||
|
||||
boolean def = isDefaultButton( c );
|
||||
return def ? defaultForeground : fg;
|
||||
}
|
||||
|
||||
@@ -617,6 +779,9 @@ public class FlatButtonUI
|
||||
if( prefSize == null )
|
||||
return null;
|
||||
|
||||
// increase width when using bold font for default button
|
||||
prefSize.width += defaultBoldPlainWidthDiff( c );
|
||||
|
||||
// make square or apply minimum width/height
|
||||
boolean isIconOnlyOrSingleCharacter = isIconOnlyOrSingleCharacterButton( c );
|
||||
if( clientPropertyBoolean( c, SQUARE_SIZE, squareSize ) ) {
|
||||
@@ -637,11 +802,89 @@ public class FlatButtonUI
|
||||
return prefSize;
|
||||
}
|
||||
|
||||
private int defaultBoldPlainWidthDiff( JComponent c ) {
|
||||
if( defaultBoldText && isDefaultButton( c ) && c.getFont() instanceof UIResource ) {
|
||||
String text = ((AbstractButton)c).getText();
|
||||
if( text == null || text.isEmpty() )
|
||||
return 0;
|
||||
|
||||
Font font = c.getFont();
|
||||
Font boldFont = font.deriveFont( Font.BOLD );
|
||||
int boldWidth = c.getFontMetrics( boldFont ).stringWidth( text );
|
||||
int plainWidth = c.getFontMetrics( font ).stringWidth( text );
|
||||
if( boldWidth > plainWidth )
|
||||
return boldWidth - plainWidth;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private boolean hasDefaultMargins( JComponent c ) {
|
||||
Insets margin = ((AbstractButton)c).getMargin();
|
||||
return margin instanceof UIResource && Objects.equals( margin, defaultMargin );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBaseline( JComponent c, int width, int height ) {
|
||||
return getBaselineImpl( c, width, height );
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to BasicButtonUI.getBaseline(), but does not use zero insets for HTML text,
|
||||
* which is done in BasicButtonUI.layout() since Java 19.
|
||||
* See https://github.com/openjdk/jdk/pull/8407
|
||||
* and https://github.com/openjdk/jdk/pull/8407#issuecomment-1761583430
|
||||
*/
|
||||
static int getBaselineImpl( JComponent c, int width, int height ) {
|
||||
if( width < 0 || height < 0 )
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
AbstractButton b = (AbstractButton) c;
|
||||
String text = b.getText();
|
||||
if( text == null || text.isEmpty() )
|
||||
return -1;
|
||||
|
||||
FontMetrics fm = b.getFontMetrics( b.getFont() );
|
||||
layout( b, fm, width, height );
|
||||
|
||||
View view = (View) b.getClientProperty( BasicHTML.propertyKey );
|
||||
if( view != null ) {
|
||||
// HTML text
|
||||
int baseline = BasicHTML.getHTMLBaseline( view, textR.width, textR.height );
|
||||
return (baseline >= 0) ? textR.y + baseline : baseline;
|
||||
} else
|
||||
return textR.y + fm.getAscent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to BasicButtonUI.layout(), but does not use zero insets for HTML text,
|
||||
* which is done in BasicButtonUI.layout() since Java 19.
|
||||
* See https://github.com/openjdk/jdk/pull/8407
|
||||
* and https://github.com/openjdk/jdk/pull/8407#issuecomment-1761583430
|
||||
*/
|
||||
private static String layout( AbstractButton b, FontMetrics fm, int width, int height ) {
|
||||
// compute view rectangle
|
||||
Insets insets = b.getInsets();
|
||||
viewR.setBounds( insets.left, insets.top,
|
||||
width - insets.left - insets.right,
|
||||
height - insets.top - insets.bottom );
|
||||
|
||||
// reset rectangles
|
||||
textR.setBounds( 0, 0, 0, 0 );
|
||||
iconR.setBounds( 0, 0, 0, 0 );
|
||||
|
||||
String text = b.getText();
|
||||
return SwingUtilities.layoutCompoundLabel( b, fm, text, b.getIcon(),
|
||||
b.getVerticalAlignment(), b.getHorizontalAlignment(),
|
||||
b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
|
||||
viewR, iconR, textR,
|
||||
(text != null) ? b.getIconTextGap() : 0 );
|
||||
}
|
||||
|
||||
private static Rectangle viewR = new Rectangle();
|
||||
private static Rectangle textR = new Rectangle();
|
||||
private static Rectangle iconR = new Rectangle();
|
||||
|
||||
//---- class FlatButtonListener -------------------------------------------
|
||||
|
||||
protected class FlatButtonListener
|
||||
@@ -659,5 +902,20 @@ public class FlatButtonUI
|
||||
super.propertyChange( e );
|
||||
FlatButtonUI.this.propertyChange( b, e );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateChanged( ChangeEvent e ) {
|
||||
super.stateChanged( e );
|
||||
|
||||
// if button is in toolbar, repaint button groups
|
||||
AbstractButton b = (AbstractButton) e.getSource();
|
||||
Container parent = b.getParent();
|
||||
if( parent instanceof JToolBar ) {
|
||||
JToolBar toolBar = (JToolBar) parent;
|
||||
ToolBarUI ui = toolBar.getUI();
|
||||
if( ui instanceof FlatToolBarUI )
|
||||
((FlatToolBarUI)ui).repaintButtonGroup( b );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ public class FlatCaret
|
||||
|
||||
// if text component is focused, then caret and selection are visible,
|
||||
// but when switching theme, the component does not yet have
|
||||
// an highlighter and the selection is not painted
|
||||
// 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() ) {
|
||||
@@ -236,7 +236,7 @@ public class FlatCaret
|
||||
if( selectAllOnFocusPolicy == null )
|
||||
selectAllOnFocusPolicy = this.selectAllOnFocusPolicy;
|
||||
|
||||
if( SELECT_ALL_ON_FOCUS_POLICY_NEVER.equals( selectAllOnFocusPolicy ) )
|
||||
if( selectAllOnFocusPolicy == null || SELECT_ALL_ON_FOCUS_POLICY_NEVER.equals( selectAllOnFocusPolicy ) )
|
||||
return;
|
||||
|
||||
if( !SELECT_ALL_ON_FOCUS_POLICY_ALWAYS.equals( selectAllOnFocusPolicy ) ) {
|
||||
@@ -256,10 +256,14 @@ public class FlatCaret
|
||||
// select all
|
||||
if( c instanceof JFormattedTextField ) {
|
||||
EventQueue.invokeLater( () -> {
|
||||
if( getComponent() == null )
|
||||
// Warning: do not use variables from outside of this runnable
|
||||
// because they may be out-of-date when this runnable is executed
|
||||
|
||||
JTextComponent c2 = getComponent();
|
||||
if( c2 == null )
|
||||
return; // was deinstalled
|
||||
|
||||
select( 0, doc.getLength() );
|
||||
select( 0, c2.getDocument().getLength() );
|
||||
} );
|
||||
} else {
|
||||
select( 0, doc.getLength() );
|
||||
|
||||
@@ -16,18 +16,20 @@
|
||||
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.Map;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.LookAndFeel;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
|
||||
import javax.swing.plaf.basic.BasicMenuItemUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
|
||||
/**
|
||||
@@ -58,9 +60,15 @@ import com.formdev.flatlaf.util.LoggingFacade;
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
@StyleableField( cls=BasicMenuItemUI.class, key="selectionBackground" )
|
||||
@StyleableField( cls=BasicMenuItemUI.class, key="selectionForeground" )
|
||||
@StyleableField( cls=BasicMenuItemUI.class, key="disabledForeground" )
|
||||
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorForeground" )
|
||||
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorSelectionForeground" )
|
||||
|
||||
public class FlatCheckBoxMenuItemUI
|
||||
extends BasicCheckBoxMenuItemUI
|
||||
implements StyleableUI
|
||||
implements StyleableUI, StyleableLookupProvider
|
||||
{
|
||||
private FlatMenuItemRenderer renderer;
|
||||
private Map<String, Object> oldStyleValues;
|
||||
@@ -119,29 +127,27 @@ public class FlatCheckBoxMenuItemUI
|
||||
|
||||
/** @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 );
|
||||
return FlatMenuItemUI.applyStyleProperty( menuItem, this, renderer, key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
return FlatMenuItemUI.getStyleableInfos( renderer );
|
||||
return FlatMenuItemUI.getStyleableInfos( this, renderer );
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
@Override
|
||||
public Object getStyleableValue( JComponent c, String key ) {
|
||||
return FlatMenuItemUI.getStyleableValue( this, renderer, key );
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
@Override
|
||||
public MethodHandles.Lookup getLookupForStyling() {
|
||||
// MethodHandles.lookup() is caller sensitive and must be invoked in this class,
|
||||
// otherwise it is not possible to access protected fields in JRE superclass
|
||||
return MethodHandles.lookup();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -43,7 +43,7 @@ public class FlatCheckBoxUI
|
||||
extends FlatRadioButtonUI
|
||||
{
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return FlatUIUtils.canUseSharedUI( c )
|
||||
return FlatUIUtils.canUseSharedUI( c ) && !FlatUIUtils.needsLightAWTPeer( c )
|
||||
? FlatUIUtils.createSharedUI( FlatCheckBoxUI.class, () -> new FlatCheckBoxUI( true ) )
|
||||
: new FlatCheckBoxUI( false );
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.awt.Component;
|
||||
import java.awt.ComponentOrientation;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
@@ -42,15 +43,18 @@ import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.CellRendererPane;
|
||||
import javax.swing.ComboBoxModel;
|
||||
import javax.swing.DefaultListCellRenderer;
|
||||
import javax.swing.InputMap;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JComboBox.KeySelectionManager;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JPanel;
|
||||
@@ -69,7 +73,10 @@ import javax.swing.plaf.basic.BasicComboBoxUI;
|
||||
import javax.swing.plaf.basic.BasicComboPopup;
|
||||
import javax.swing.plaf.basic.ComboPopup;
|
||||
import javax.swing.text.JTextComponent;
|
||||
import com.formdev.flatlaf.icons.FlatCheckBoxMenuItemIcon;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
import com.formdev.flatlaf.util.SystemInfo;
|
||||
@@ -86,14 +93,18 @@ import com.formdev.flatlaf.util.SystemInfo;
|
||||
* @uiDefault ComboBox.padding Insets
|
||||
* @uiDefault ComboBox.squareButton boolean default is true
|
||||
*
|
||||
* <!-- BasicComboPopup -->
|
||||
*
|
||||
* @uiDefault ComboBox.selectionBackground Color
|
||||
* @uiDefault ComboBox.selectionForeground Color
|
||||
*
|
||||
* <!-- FlatComboBoxUI -->
|
||||
*
|
||||
* @uiDefault ComboBox.minimumWidth int
|
||||
* @uiDefault ComboBox.editorColumns int
|
||||
* @uiDefault ComboBox.maximumRowCount int
|
||||
* @uiDefault ComboBox.buttonStyle String auto (default), button or none
|
||||
* @uiDefault ComboBox.buttonStyle String auto (default), button, mac or none
|
||||
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||
* @uiDefault Component.isIntelliJTheme boolean
|
||||
* @uiDefault ComboBox.editableBackground Color optional; defaults to ComboBox.background
|
||||
* @uiDefault ComboBox.focusedBackground Color optional
|
||||
* @uiDefault ComboBox.disabledBackground Color
|
||||
@@ -109,19 +120,24 @@ import com.formdev.flatlaf.util.SystemInfo;
|
||||
* @uiDefault ComboBox.buttonHoverArrowColor Color
|
||||
* @uiDefault ComboBox.buttonPressedArrowColor Color
|
||||
* @uiDefault ComboBox.popupBackground Color optional
|
||||
* @uiDefault ComboBox.popupInsets Insets
|
||||
* @uiDefault ComboBox.selectionInsets Insets
|
||||
* @uiDefault ComboBox.selectionArc int
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
@StyleableField( cls=BasicComboBoxUI.class, key="padding" )
|
||||
|
||||
public class FlatComboBoxUI
|
||||
extends BasicComboBoxUI
|
||||
implements StyleableUI
|
||||
implements StyleableUI, StyleableLookupProvider
|
||||
{
|
||||
@Styleable protected int minimumWidth;
|
||||
@Styleable protected int editorColumns;
|
||||
@Styleable protected String buttonStyle;
|
||||
@Styleable protected String arrowType;
|
||||
protected boolean isIntelliJTheme;
|
||||
|
||||
private Color background;
|
||||
@Styleable protected Color editableBackground;
|
||||
@Styleable protected Color focusedBackground;
|
||||
@Styleable protected Color disabledBackground;
|
||||
@@ -139,6 +155,9 @@ public class FlatComboBoxUI
|
||||
@Styleable protected Color buttonPressedArrowColor;
|
||||
|
||||
@Styleable protected Color popupBackground;
|
||||
/** @since 3 */ @Styleable protected Insets popupInsets;
|
||||
/** @since 3 */ @Styleable protected Insets selectionInsets;
|
||||
/** @since 3 */ @Styleable protected int selectionArc;
|
||||
|
||||
private MouseListener hoverListener;
|
||||
protected boolean hover;
|
||||
@@ -155,8 +174,18 @@ public class FlatComboBoxUI
|
||||
|
||||
@Override
|
||||
public void installUI( JComponent c ) {
|
||||
if( FlatUIUtils.needsLightAWTPeer( c ) )
|
||||
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
|
||||
else
|
||||
installUIImpl( c );
|
||||
}
|
||||
|
||||
private void installUIImpl( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
// install key selection manager that shows popup when Space key is pressed
|
||||
comboBox.setKeySelectionManager( new FlatKeySelectionManager( comboBox.getKeySelectionManager() ) );
|
||||
|
||||
installStyle();
|
||||
}
|
||||
|
||||
@@ -215,8 +244,8 @@ public class FlatComboBoxUI
|
||||
editorColumns = UIManager.getInt( "ComboBox.editorColumns" );
|
||||
buttonStyle = UIManager.getString( "ComboBox.buttonStyle" );
|
||||
arrowType = UIManager.getString( "Component.arrowType" );
|
||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
||||
|
||||
background = UIManager.getColor( "ComboBox.background" );
|
||||
editableBackground = UIManager.getColor( "ComboBox.editableBackground" );
|
||||
focusedBackground = UIManager.getColor( "ComboBox.focusedBackground" );
|
||||
disabledBackground = UIManager.getColor( "ComboBox.disabledBackground" );
|
||||
@@ -234,6 +263,9 @@ public class FlatComboBoxUI
|
||||
buttonPressedArrowColor = UIManager.getColor( "ComboBox.buttonPressedArrowColor" );
|
||||
|
||||
popupBackground = UIManager.getColor( "ComboBox.popupBackground" );
|
||||
popupInsets = UIManager.getInsets( "ComboBox.popupInsets" );
|
||||
selectionInsets = UIManager.getInsets( "ComboBox.selectionInsets" );
|
||||
selectionArc = UIManager.getInt( "ComboBox.selectionArc" );
|
||||
|
||||
// set maximumRowCount
|
||||
int maximumRowCount = UIManager.getInt( "ComboBox.maximumRowCount" );
|
||||
@@ -249,6 +281,7 @@ public class FlatComboBoxUI
|
||||
protected void uninstallDefaults() {
|
||||
super.uninstallDefaults();
|
||||
|
||||
background = null;
|
||||
editableBackground = null;
|
||||
focusedBackground = null;
|
||||
disabledBackground = null;
|
||||
@@ -288,11 +321,14 @@ public class FlatComboBoxUI
|
||||
// limit button width to height of a raw combobox (without insets)
|
||||
FontMetrics fm = comboBox.getFontMetrics( comboBox.getFont() );
|
||||
int maxButtonWidth = fm.getHeight() + scale( padding.top ) + scale( padding.bottom );
|
||||
int minButtonWidth = (maxButtonWidth * 3) / 4;
|
||||
|
||||
// make button square (except if width is limited)
|
||||
Insets insets = getInsets();
|
||||
int buttonWidth = Math.min( parent.getPreferredSize().height - insets.top - insets.bottom, maxButtonWidth );
|
||||
int buttonWidth = Math.min( Math.max( parent.getHeight() - insets.top - insets.bottom, minButtonWidth ), maxButtonWidth );
|
||||
|
||||
if( buttonWidth != arrowButton.getWidth() ) {
|
||||
// set width of arrow button to preferred height of combobox
|
||||
// set width of arrow button
|
||||
int xOffset = comboBox.getComponentOrientation().isLeftToRight()
|
||||
? arrowButton.getWidth() - buttonWidth
|
||||
: 0;
|
||||
@@ -354,6 +390,7 @@ public class FlatComboBoxUI
|
||||
break;
|
||||
|
||||
case COMPONENT_ROUND_RECT:
|
||||
case OUTLINE:
|
||||
comboBox.repaint();
|
||||
break;
|
||||
|
||||
@@ -485,13 +522,6 @@ public class FlatComboBoxUI
|
||||
|
||||
/** @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 );
|
||||
@@ -500,11 +530,21 @@ public class FlatComboBoxUI
|
||||
/** @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;
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this, comboBox.getBorder() );
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
@Override
|
||||
public Object getStyleableValue( JComponent c, String key ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableValue( this, comboBox.getBorder(), key );
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
@Override
|
||||
public MethodHandles.Lookup getLookupForStyling() {
|
||||
// MethodHandles.lookup() is caller sensitive and must be invoked in this class,
|
||||
// otherwise it is not possible to access protected fields in JRE superclass
|
||||
return MethodHandles.lookup();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -532,7 +572,9 @@ public class FlatComboBoxUI
|
||||
int height = c.getHeight();
|
||||
int arrowX = arrowButton.getX();
|
||||
int arrowWidth = arrowButton.getWidth();
|
||||
boolean paintButton = (comboBox.isEditable() || "button".equals( buttonStyle )) && !"none".equals( buttonStyle );
|
||||
boolean paintButton = (comboBox.isEditable() || "button".equals( buttonStyle )) &&
|
||||
!"none".equals( buttonStyle ) &&
|
||||
!isMacStyle();
|
||||
boolean enabled = comboBox.isEnabled();
|
||||
boolean isLeftToRight = comboBox.getComponentOrientation().isLeftToRight();
|
||||
|
||||
@@ -550,13 +592,21 @@ public class FlatComboBoxUI
|
||||
: buttonBackground;
|
||||
if( buttonColor != null ) {
|
||||
g2.setColor( buttonColor );
|
||||
Shape oldClip = g2.getClip();
|
||||
if( isLeftToRight )
|
||||
g2.clipRect( arrowX, 0, width - arrowX, height );
|
||||
else
|
||||
g2.clipRect( 0, 0, arrowX + arrowWidth, height );
|
||||
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
|
||||
g2.setClip( oldClip );
|
||||
if( isMacStyle() ) {
|
||||
Insets insets = comboBox.getInsets();
|
||||
int gap = scale( 2 );
|
||||
FlatUIUtils.paintComponentBackground( g2, arrowX + gap, insets.top + gap,
|
||||
arrowWidth - (gap * 2), height - insets.top - insets.bottom - (gap * 2),
|
||||
0, arc - focusWidth );
|
||||
} else {
|
||||
Shape oldClip = g2.getClip();
|
||||
if( isLeftToRight )
|
||||
g2.clipRect( arrowX, 0, width - arrowX, height );
|
||||
else
|
||||
g2.clipRect( 0, 0, arrowX + arrowWidth, height );
|
||||
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
|
||||
g2.setClip( oldClip );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -602,7 +652,7 @@ public class FlatComboBoxUI
|
||||
|
||||
boolean shouldValidate = (c instanceof JPanel);
|
||||
|
||||
paddingBorder.install( c );
|
||||
paddingBorder.install( c, 0 );
|
||||
currentValuePane.paintComponent( g, c, comboBox, bounds.x, bounds.y, bounds.width, bounds.height, shouldValidate );
|
||||
paddingBorder.uninstall();
|
||||
|
||||
@@ -617,6 +667,9 @@ public class FlatComboBoxUI
|
||||
|
||||
protected Color getBackground( boolean enabled ) {
|
||||
if( enabled ) {
|
||||
if( FlatUIUtils.isAWTPeer( comboBox ) )
|
||||
return background;
|
||||
|
||||
Color background = comboBox.getBackground();
|
||||
|
||||
// always use explicitly set color
|
||||
@@ -629,7 +682,7 @@ public class FlatComboBoxUI
|
||||
|
||||
return (editableBackground != null && comboBox.isEditable()) ? editableBackground : background;
|
||||
} else
|
||||
return isIntelliJTheme ? FlatUIUtils.getParentBackground( comboBox ) : disabledBackground;
|
||||
return disabledBackground;
|
||||
}
|
||||
|
||||
protected Color getForeground( boolean enabled ) {
|
||||
@@ -676,7 +729,7 @@ public class FlatComboBoxUI
|
||||
|
||||
@Override
|
||||
protected Dimension getSizeForComponent( Component comp ) {
|
||||
paddingBorder.install( comp );
|
||||
paddingBorder.install( comp, 0 );
|
||||
Dimension size = super.getSizeForComponent( comp );
|
||||
paddingBorder.uninstall();
|
||||
return size;
|
||||
@@ -692,6 +745,10 @@ public class FlatComboBoxUI
|
||||
return parentParent != null && !comboBox.getBackground().equals( parentParent.getBackground() );
|
||||
}
|
||||
|
||||
private boolean isMacStyle() {
|
||||
return "mac".equals( buttonStyle );
|
||||
}
|
||||
|
||||
/** @since 1.3 */
|
||||
public static boolean isPermanentFocusOwner( JComboBox<?> comboBox ) {
|
||||
if( comboBox.isEditable() ) {
|
||||
@@ -699,7 +756,7 @@ public class FlatComboBoxUI
|
||||
return true;
|
||||
|
||||
Component editorComponent = comboBox.getEditor().getEditorComponent();
|
||||
return (editorComponent != null) ? FlatUIUtils.isPermanentFocusOwner( editorComponent ) : false;
|
||||
return editorComponent != null && FlatUIUtils.isPermanentFocusOwner( editorComponent );
|
||||
} else
|
||||
return FlatUIUtils.isPermanentFocusOwner( comboBox );
|
||||
}
|
||||
@@ -726,6 +783,21 @@ public class FlatComboBoxUI
|
||||
buttonHoverArrowColor, null, buttonPressedArrowColor, null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getArrowWidth() {
|
||||
return isMacStyle() ? (getWidth() % 2 == 0 ? 6 : 7) : super.getArrowWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getArrowThickness() {
|
||||
return isMacStyle() ? 1.5f : super.getArrowThickness();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRoundBorderAutoXOffset() {
|
||||
return isMacStyle() ? false : super.isRoundBorderAutoXOffset();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isHover() {
|
||||
return super.isHover() || (!comboBox.isEditable() ? hover : false);
|
||||
@@ -743,6 +815,20 @@ public class FlatComboBoxUI
|
||||
|
||||
return super.getArrowColor();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintArrow( Graphics2D g ) {
|
||||
if( isMacStyle() && !comboBox.isEditable() ) {
|
||||
// for style "mac", paint up and down arrows if combobox is not editable
|
||||
int height = getHeight();
|
||||
int h = Math.round( height / 2f );
|
||||
FlatUIUtils.paintArrow( g, 0, 0, getWidth(), h, SwingConstants.NORTH, chevron,
|
||||
getArrowWidth(), getArrowThickness(), getXOffset(), getYOffset() + 1.25f );
|
||||
FlatUIUtils.paintArrow( g, 0, height - h, getWidth(), h, SwingConstants.SOUTH, chevron,
|
||||
getArrowWidth(), getArrowThickness(), getXOffset(), getYOffset() - 1.25f );
|
||||
} else
|
||||
super.paintArrow( g );
|
||||
}
|
||||
}
|
||||
|
||||
//---- class FlatComboPopup -----------------------------------------------
|
||||
@@ -777,12 +863,19 @@ public class FlatComboBoxUI
|
||||
}
|
||||
}
|
||||
|
||||
// for style "mac", add width of "checked item" icon
|
||||
boolean isPopupOverComboBox = isPopupOverComboBox();
|
||||
int selectedIndex = -1;
|
||||
if( isPopupOverComboBox && (selectedIndex = comboBox.getSelectedIndex()) >= 0 )
|
||||
displayWidth += MacCheckedItemIcon.INSTANCE.getIconWidth() + scale( CellPaddingBorder.MAC_STYLE_GAP );
|
||||
|
||||
// add width of vertical scroll bar
|
||||
JScrollBar verticalScrollBar = scroller.getVerticalScrollBar();
|
||||
if( verticalScrollBar != null )
|
||||
displayWidth += verticalScrollBar.getPreferredSize().width;
|
||||
|
||||
// make popup wider if necessary
|
||||
int pw0 = pw;
|
||||
if( displayWidth > pw ) {
|
||||
// limit popup width to screen width
|
||||
GraphicsConfiguration gc = comboBox.getGraphicsConfiguration();
|
||||
@@ -802,6 +895,30 @@ public class FlatComboBoxUI
|
||||
px -= diff;
|
||||
}
|
||||
|
||||
// for style "mac", place popup over combobox
|
||||
Rectangle cellBounds;
|
||||
if( isPopupOverComboBox && selectedIndex >= 0 &&
|
||||
(cellBounds = list.getCellBounds( 0, 0 )) != null )
|
||||
{
|
||||
Insets comboBoxInsets = comboBox.getInsets();
|
||||
Insets listInsets = list.getInsets();
|
||||
Insets popupInsets = getInsets();
|
||||
|
||||
// position popup so that selected item is at same Y position as combobox
|
||||
py -= (cellBounds.height * (selectedIndex + 1)) + comboBoxInsets.top + listInsets.top + popupInsets.top;
|
||||
|
||||
// position popup slightly to the left so that a small part of the right side of the combobox stays visible
|
||||
int offset = Math.min( pw - pw0, MacCheckedItemIcon.INSTANCE.getIconWidth() ) + scale( 4 );
|
||||
if( comboBox.getComponentOrientation().isLeftToRight() )
|
||||
px -= offset + comboBoxInsets.right + listInsets.right;
|
||||
else
|
||||
px += offset + comboBoxInsets.left + listInsets.left;
|
||||
|
||||
// not invoking super.computePopupBounds() here to let
|
||||
// JPopupMenu.adjustPopupLocationToFitScreen() fix the location if necessary
|
||||
return new Rectangle( px, py, pw, ph );
|
||||
}
|
||||
|
||||
return super.computePopupBounds( px, py, pw, ph );
|
||||
}
|
||||
|
||||
@@ -812,17 +929,12 @@ public class FlatComboBoxUI
|
||||
// make opaque to avoid that background shines thru border (e.g. at 150% scaling)
|
||||
setOpaque( true );
|
||||
|
||||
// set popup border
|
||||
// use non-UIResource to avoid that it is overwritten when making
|
||||
// popup visible (see JPopupMenu.setInvoker()) in theme editor preview
|
||||
// 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" );
|
||||
if( border != null )
|
||||
setBorder( FlatUIUtils.nonUIResource( border ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureList() {
|
||||
super.configureList();
|
||||
|
||||
list.setCellRenderer( new PopupListCellRenderer() );
|
||||
updateStyle();
|
||||
@@ -830,12 +942,21 @@ public class FlatComboBoxUI
|
||||
|
||||
void updateStyle() {
|
||||
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() ) );
|
||||
// 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() ) );
|
||||
|
||||
scroller.setViewportBorder( (popupInsets != null) ? new FlatEmptyBorder( popupInsets ) : null );
|
||||
scroller.setOpaque( false );
|
||||
|
||||
if( list.getUI() instanceof FlatListUI ) {
|
||||
FlatListUI ui = (FlatListUI) list.getUI();
|
||||
ui.selectionInsets = selectionInsets;
|
||||
ui.selectionArc = selectionArc;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -868,6 +989,29 @@ public class FlatComboBoxUI
|
||||
}
|
||||
}
|
||||
|
||||
// improve location of selected item in popup if list is large and scrollable
|
||||
if( list.getHeight() == 0 ) {
|
||||
// If popup is shown for the first time (or after a laf switch) and is scrollable,
|
||||
// then BasicComboPopup scrolls the selected item to the top of the visible area.
|
||||
// But for usability it would be better to have selected item somewhere
|
||||
// in the middle of the visible area so that the user can see items above
|
||||
// the selected item, which are usually more "important".
|
||||
|
||||
int selectedIndex = list.getSelectedIndex();
|
||||
if( selectedIndex >= 1 ) {
|
||||
int maximumRowCount = comboBox.getMaximumRowCount();
|
||||
if( selectedIndex < maximumRowCount ) {
|
||||
// selected item is in the first visible items --> scroll to top
|
||||
list.scrollRectToVisible( new Rectangle() );
|
||||
} else {
|
||||
// scroll the selected item to the middle of the visible area
|
||||
int firstVisibleIndex = Math.max( selectedIndex - (maximumRowCount / 2), 0 );
|
||||
if( firstVisibleIndex > 0 )
|
||||
list.ensureIndexIsVisible( firstVisibleIndex );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.show( invoker, x, y );
|
||||
}
|
||||
|
||||
@@ -877,6 +1021,15 @@ public class FlatComboBoxUI
|
||||
paddingBorder.uninstall();
|
||||
}
|
||||
|
||||
private boolean isPopupOverComboBox() {
|
||||
return isMacStyle() &&
|
||||
!comboBox.isEditable() &&
|
||||
comboBox.getItemCount() > 0 &&
|
||||
comboBox.getItemCount() <= comboBox.getMaximumRowCount() &&
|
||||
// for compatibility with Aqua Laf
|
||||
!clientPropertyBoolean( comboBox, "JComboBox.isPopDown", false );
|
||||
}
|
||||
|
||||
//---- class PopupListCellRenderer -----
|
||||
|
||||
private class PopupListCellRenderer
|
||||
@@ -894,7 +1047,14 @@ public class FlatComboBoxUI
|
||||
Component c = renderer.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
|
||||
c.applyComponentOrientation( comboBox.getComponentOrientation() );
|
||||
|
||||
paddingBorder.install( c );
|
||||
// style "mac"
|
||||
if( isPopupOverComboBox() && c instanceof JComponent ) {
|
||||
int selectedIndex = comboBox.getSelectedIndex();
|
||||
((JComponent)c).putClientProperty( CellPaddingBorder.KEY_MAC_STYLE_HINT,
|
||||
(selectedIndex >= 0) ? (index == selectedIndex) : null );
|
||||
}
|
||||
|
||||
paddingBorder.install( c, Math.round( FlatUIUtils.getBorderFocusWidth( comboBox ) ) );
|
||||
|
||||
return c;
|
||||
}
|
||||
@@ -910,13 +1070,20 @@ public class FlatComboBoxUI
|
||||
* which vertically aligns text in popup list with text in combobox.
|
||||
* <p>
|
||||
* The renderer border is painted on the outer side of this border.
|
||||
* <p>
|
||||
* For button style "mac", also used to increase insets on left side for
|
||||
* "checked item" icon and to paint "checked item" icon for selected combobox item.
|
||||
*/
|
||||
private static class CellPaddingBorder
|
||||
extends AbstractBorder
|
||||
{
|
||||
static final String KEY_MAC_STYLE_HINT = "FlatLaf.internal.FlatComboBoxUI.macStyleHint";
|
||||
static final int MAC_STYLE_GAP = 4;
|
||||
|
||||
private Insets padding;
|
||||
private JComponent rendererComponent;
|
||||
private Border rendererBorder;
|
||||
private int focusWidth;
|
||||
|
||||
CellPaddingBorder( Insets padding ) {
|
||||
this.padding = padding;
|
||||
@@ -924,10 +1091,12 @@ public class FlatComboBoxUI
|
||||
|
||||
// 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 ) {
|
||||
synchronized void install( Component c, int focusWidth ) {
|
||||
if( !(c instanceof JComponent) )
|
||||
return;
|
||||
|
||||
this.focusWidth = focusWidth;
|
||||
|
||||
JComponent jc = (JComponent) c;
|
||||
Border oldBorder = jc.getBorder();
|
||||
if( oldBorder == this )
|
||||
@@ -960,6 +1129,8 @@ public class FlatComboBoxUI
|
||||
if( rendererComponent == null )
|
||||
return;
|
||||
|
||||
rendererComponent.putClientProperty( KEY_MAC_STYLE_HINT, null );
|
||||
|
||||
if( rendererComponent.getBorder() == this )
|
||||
rendererComponent.setBorder( rendererBorder );
|
||||
rendererComponent = null;
|
||||
@@ -981,6 +1152,24 @@ public class FlatComboBoxUI
|
||||
insets.bottom = padding.bottom;
|
||||
insets.right = padding.right;
|
||||
}
|
||||
|
||||
// if used in popup list, add focus width for exact vertical alignment
|
||||
// of text in popup list with text in combobox
|
||||
insets.left += focusWidth;
|
||||
insets.right += focusWidth;
|
||||
|
||||
// style "mac"
|
||||
if( c instanceof JComponent ) {
|
||||
Boolean macStyleHint = clientPropertyBooleanStrict( (JComponent) c, KEY_MAC_STYLE_HINT, null );
|
||||
if( macStyleHint != null ) {
|
||||
int indent = MacCheckedItemIcon.INSTANCE.getIconWidth() + scale( MAC_STYLE_GAP );
|
||||
if( c.getComponentOrientation().isLeftToRight() )
|
||||
insets.left += indent;
|
||||
else
|
||||
insets.right += indent;
|
||||
}
|
||||
}
|
||||
|
||||
return insets;
|
||||
}
|
||||
|
||||
@@ -988,6 +1177,35 @@ public class FlatComboBoxUI
|
||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||
if( rendererBorder != null )
|
||||
rendererBorder.paintBorder( c, g, x, y, width, height );
|
||||
|
||||
// style "mac"
|
||||
if( c instanceof JComponent ) {
|
||||
Boolean macStyleHint = clientPropertyBooleanStrict( (JComponent) c, KEY_MAC_STYLE_HINT, null );
|
||||
if( macStyleHint == Boolean.TRUE ) {
|
||||
// paint "checked item" icon
|
||||
int ix = c.getComponentOrientation().isLeftToRight()
|
||||
? x + scale( padding.left )
|
||||
: x + width - scale( padding.right ) - MacCheckedItemIcon.INSTANCE.getIconWidth();
|
||||
MacCheckedItemIcon.INSTANCE.paintIcon( c, g, ix, y + ((height - MacCheckedItemIcon.INSTANCE.getIconHeight()) / 2) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---- class MacCheckedItemIcon -------------------------------------------
|
||||
|
||||
/**
|
||||
* Use for style "mac" to mark checked item.
|
||||
*/
|
||||
private static class MacCheckedItemIcon
|
||||
extends FlatCheckBoxMenuItemIcon
|
||||
{
|
||||
static MacCheckedItemIcon INSTANCE = new MacCheckedItemIcon();
|
||||
|
||||
@Override
|
||||
protected void paintIcon( Component c, Graphics2D g2 ) {
|
||||
g2.setColor( c.getForeground() );
|
||||
paintCheckmark( g2 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1017,4 +1235,46 @@ public class FlatComboBoxUI
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---- class FlatKeySelectionManager --------------------------------------
|
||||
|
||||
/**
|
||||
* Key selection manager that delegates to the default manager.
|
||||
* Shows the popup if Space key is pressed and "typed characters" buffer is empty.
|
||||
* If items contain spaces (e.g. "a b") it is still possible to select them
|
||||
* by pressing keys a, Space and b.
|
||||
*/
|
||||
private class FlatKeySelectionManager
|
||||
implements JComboBox.KeySelectionManager, UIResource
|
||||
{
|
||||
private final KeySelectionManager delegate;
|
||||
private final long timeFactor;
|
||||
private long lastTime;
|
||||
|
||||
FlatKeySelectionManager( JComboBox.KeySelectionManager delegate ) {
|
||||
this.delegate = delegate;
|
||||
|
||||
Long value = (Long) UIManager.get( "ComboBox.timeFactor" );
|
||||
timeFactor = (value != null) ? value : 1000;
|
||||
}
|
||||
|
||||
@SuppressWarnings( "rawtypes" )
|
||||
@Override
|
||||
public int selectionForKey( char aKey, ComboBoxModel aModel ) {
|
||||
long time = EventQueue.getMostRecentEventTime();
|
||||
long oldLastTime = lastTime;
|
||||
lastTime = time;
|
||||
|
||||
// SPACE key shows popup if not yet visible
|
||||
if( aKey == ' ' &&
|
||||
time - oldLastTime >= timeFactor &&
|
||||
!comboBox.isPopupVisible() )
|
||||
{
|
||||
comboBox.setPopupVisible( true );
|
||||
return -1;
|
||||
}
|
||||
|
||||
return delegate.selectionForKey( aKey, aModel );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,6 +107,12 @@ public class FlatDropShadowBorder
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
@Override
|
||||
public Object getStyleableValue( String key ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||
if( shadowSize <= 0 )
|
||||
|
||||
@@ -59,7 +59,6 @@ import com.formdev.flatlaf.util.LoggingFacade;
|
||||
* <!-- FlatEditorPaneUI -->
|
||||
*
|
||||
* @uiDefault Component.minimumWidth int
|
||||
* @uiDefault Component.isIntelliJTheme boolean
|
||||
* @uiDefault EditorPane.focusedBackground Color optional
|
||||
*
|
||||
* @author Karl Tauber
|
||||
@@ -69,7 +68,6 @@ public class FlatEditorPaneUI
|
||||
implements StyleableUI
|
||||
{
|
||||
@Styleable protected int minimumWidth;
|
||||
protected boolean isIntelliJTheme;
|
||||
private Color background;
|
||||
@Styleable protected Color disabledBackground;
|
||||
@Styleable protected Color inactiveBackground;
|
||||
@@ -101,7 +99,6 @@ public class FlatEditorPaneUI
|
||||
|
||||
String prefix = getPropertyPrefix();
|
||||
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
||||
background = UIManager.getColor( prefix + ".background" );
|
||||
disabledBackground = UIManager.getColor( prefix + ".disabledBackground" );
|
||||
inactiveBackground = UIManager.getColor( prefix + ".inactiveBackground" );
|
||||
@@ -209,6 +206,12 @@ public class FlatEditorPaneUI
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
@Override
|
||||
public Object getStyleableValue( JComponent c, String key ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||
}
|
||||
|
||||
private void updateBackground() {
|
||||
FlatTextFieldUI.updateBackground( getComponent(), background,
|
||||
disabledBackground, inactiveBackground,
|
||||
@@ -246,11 +249,11 @@ public class FlatEditorPaneUI
|
||||
|
||||
@Override
|
||||
protected void paintBackground( Graphics g ) {
|
||||
paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground );
|
||||
paintBackground( g, getComponent(), focusedBackground );
|
||||
}
|
||||
|
||||
static void paintBackground( Graphics g, JTextComponent c, boolean isIntelliJTheme, Color focusedBackground ) {
|
||||
g.setColor( FlatTextFieldUI.getBackground( c, isIntelliJTheme, focusedBackground ) );
|
||||
static void paintBackground( Graphics g, JTextComponent c, Color focusedBackground ) {
|
||||
g.setColor( FlatTextFieldUI.getBackground( c, focusedBackground ) );
|
||||
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ public class FlatEmptyBorder
|
||||
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 == null || c.getComponentOrientation().isLeftToRight();
|
||||
insets.left = scale( leftToRight ? left : right );
|
||||
insets.top = scale( top );
|
||||
insets.right = scale( leftToRight ? right : left );
|
||||
@@ -76,4 +76,9 @@ public class FlatEmptyBorder
|
||||
right = insets.right;
|
||||
return oldInsets;
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
public Insets getStyleableValue() {
|
||||
return new Insets( top, left, bottom, right );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,11 +19,22 @@ package com.formdev.flatlaf.ui;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Image;
|
||||
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.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.Function;
|
||||
import javax.swing.AbstractButton;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.ButtonGroup;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
@@ -34,12 +45,17 @@ import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JToggleButton;
|
||||
import javax.swing.JToolBar;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.filechooser.FileSystemView;
|
||||
import javax.swing.filechooser.FileView;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.metal.MetalFileChooserUI;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
import com.formdev.flatlaf.icons.FlatFileViewDirectoryIcon;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
import com.formdev.flatlaf.util.ScaledImageIcon;
|
||||
import com.formdev.flatlaf.util.SystemInfo;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
@@ -133,12 +149,21 @@ import com.formdev.flatlaf.util.UIScale;
|
||||
* @uiDefault FileChooser.listViewActionLabelText 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
|
||||
*/
|
||||
public class FlatFileChooserUI
|
||||
extends MetalFileChooserUI
|
||||
{
|
||||
private final FlatFileView fileView = new FlatFileView();
|
||||
private FlatShortcutsPanel shortcutsPanel;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatFileChooserUI( (JFileChooser) c );
|
||||
@@ -153,6 +178,25 @@ public class FlatFileChooserUI
|
||||
super.installComponents( 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 ) {
|
||||
@@ -192,6 +236,27 @@ public class FlatFileChooserUI
|
||||
} catch( ArrayIndexOutOfBoundsException ex ) {
|
||||
// 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 lineEnd = borderLayout.getLayoutComponent( BorderLayout.LINE_END );
|
||||
Component center = borderLayout.getLayoutComponent( BorderLayout.CENTER );
|
||||
Component south = borderLayout.getLayoutComponent( BorderLayout.SOUTH );
|
||||
if( north != null && lineEnd != null && center != null && south != null ) {
|
||||
JPanel p = new JPanel( new BorderLayout( 0, 11 ) );
|
||||
p.add( north, BorderLayout.NORTH );
|
||||
p.add( lineEnd, BorderLayout.LINE_END );
|
||||
p.add( center, BorderLayout.CENTER );
|
||||
p.add( south, BorderLayout.SOUTH );
|
||||
fc.add( p, BorderLayout.CENTER );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -250,9 +315,19 @@ public class FlatFileChooserUI
|
||||
return p;
|
||||
}
|
||||
|
||||
/** @since 2.3 */
|
||||
protected FlatShortcutsPanel createShortcutsPanel( JFileChooser fc ) {
|
||||
return new FlatShortcutsPanel( fc );
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
@@ -273,12 +348,12 @@ public class FlatFileChooserUI
|
||||
fileView.clearIconCache();
|
||||
}
|
||||
|
||||
private boolean doNotUseSystemIcons() {
|
||||
private static 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)
|
||||
// fixed in Java 18+, fix backported in Java 17.0.3+ (see https://bugs.openjdk.java.net/browse/JDK-8277299)
|
||||
return SystemInfo.isWindows &&
|
||||
SystemInfo.isX86 &&
|
||||
(SystemInfo.isJava_17_orLater && !SystemInfo.isJava_18_orLater);
|
||||
(SystemInfo.isJava_17_orLater && SystemInfo.javaVersion < SystemInfo.toVersion( 17, 0, 3, 0 ));
|
||||
}
|
||||
|
||||
//---- class FlatFileView -------------------------------------------------
|
||||
@@ -316,4 +391,248 @@ public class FlatFileChooserUI
|
||||
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 = new 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 );
|
||||
|
||||
// create toolbar buttons
|
||||
ArrayList<File> filesList = new ArrayList<>();
|
||||
ArrayList<JToggleButton> buttonsList = new ArrayList<>();
|
||||
for( File file : files ) {
|
||||
if( file == null )
|
||||
continue;
|
||||
|
||||
// wrap drive path
|
||||
if( fsv.isFileSystemRoot( file ) )
|
||||
file = fsv.createFileObject( file.getAbsolutePath() );
|
||||
|
||||
String name = getDisplayName( fsv, file );
|
||||
Icon icon = getIcon( fsv, file );
|
||||
if( name == null )
|
||||
continue;
|
||||
|
||||
// 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 );
|
||||
File f = file;
|
||||
button.addActionListener( e -> {
|
||||
fc.setCurrentDirectory( f );
|
||||
} );
|
||||
|
||||
add( button );
|
||||
buttonGroup.add( button );
|
||||
|
||||
filesList.add( file );
|
||||
buttonsList.add( button );
|
||||
}
|
||||
|
||||
this.files = filesList.toArray( new File[filesList.size()] );
|
||||
this.buttons = buttonsList.toArray( new JToggleButton[buttonsList.size()] );
|
||||
|
||||
directoryChanged( fc.getCurrentDirectory() );
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if( doNotUseSystemIcons() )
|
||||
return new FlatFileViewDirectoryIcon();
|
||||
|
||||
// Java 17+ supports getting larger system icons
|
||||
try {
|
||||
if( SystemInfo.isJava_17_orLater ) {
|
||||
Method m = fsv.getClass().getMethod( "getSystemIcon", File.class, int.class, int.class );
|
||||
return (Icon) m.invoke( fsv, file, iconSize.width, iconSize.height );
|
||||
} else if( iconSize.width > 16 || iconSize.height > 16 ) {
|
||||
Class<?> cls = Class.forName( "sun.awt.shell.ShellFolder" );
|
||||
if( cls.isInstance( file ) ) {
|
||||
Method m = file.getClass().getMethod( "getIcon", boolean.class );
|
||||
m.setAccessible( true );
|
||||
Image image = (Image) m.invoke( file, true );
|
||||
if( image != null )
|
||||
return new ImageIcon( image );
|
||||
}
|
||||
}
|
||||
} catch( Exception ex ) {
|
||||
// do not log InaccessibleObjectException because access
|
||||
// may be denied via VM option '--illegal-access=deny' (default in Java 16)
|
||||
// (not catching InaccessibleObjectException here because it is new in Java 9, but FlatLaf also runs on Java 8)
|
||||
if( !"java.lang.reflect.InaccessibleObjectException".equals( ex.getClass().getName() ) )
|
||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||
}
|
||||
|
||||
// get system icon in default size 16x16
|
||||
return fsv.getSystemIcon( file );
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ import javax.swing.plaf.ComponentUI;
|
||||
* <!-- FlatTextFieldUI -->
|
||||
*
|
||||
* @uiDefault Component.minimumWidth int
|
||||
* @uiDefault Component.isIntelliJTheme boolean
|
||||
* @uiDefault FormattedTextField.placeholderForeground Color
|
||||
* @uiDefault FormattedTextField.focusedBackground Color optional
|
||||
* @uiDefault FormattedTextField.iconTextGap int optional, default is 4
|
||||
|
||||
@@ -179,6 +179,12 @@ public class FlatInternalFrameUI
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this, frame.getBorder() );
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
@Override
|
||||
public Object getStyleableValue( JComponent c, String key ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableValue( this, frame.getBorder(), key );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update( Graphics g, JComponent c ) {
|
||||
// The internal frame actually should be opaque and fill its background,
|
||||
@@ -253,6 +259,23 @@ public class FlatInternalFrameUI
|
||||
return infos;
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
@Override
|
||||
public Object getStyleableValue( String key ) {
|
||||
switch( key ) {
|
||||
case "borderMargins": return getStyleableValue();
|
||||
|
||||
case "activeDropShadowColor": return activeDropShadowBorder.getStyleableValue( "shadowColor" );
|
||||
case "activeDropShadowInsets": return activeDropShadowBorder.getStyleableValue( "shadowInsets" );
|
||||
case "activeDropShadowOpacity": return activeDropShadowBorder.getStyleableValue( "shadowOpacity" );
|
||||
case "inactiveDropShadowColor": return inactiveDropShadowBorder.getStyleableValue( "shadowColor" );
|
||||
case "inactiveDropShadowInsets": return inactiveDropShadowBorder.getStyleableValue( "shadowInsets" );
|
||||
case "inactiveDropShadowOpacity": return inactiveDropShadowBorder.getStyleableValue( "shadowOpacity" );
|
||||
}
|
||||
|
||||
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||
if( c instanceof JInternalFrame && ((JInternalFrame)c).isMaximum() ) {
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.awt.Rectangle;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.swing.Icon;
|
||||
@@ -158,6 +159,12 @@ public class FlatLabelUI
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
@Override
|
||||
public Object getStyleableValue( JComponent c, String key ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether text contains HTML tags that use "absolute-size" keywords
|
||||
* (e.g. "x-large") for font-size in default style sheet
|
||||
@@ -173,7 +180,7 @@ public class FlatLabelUI
|
||||
// BASE_SIZE rule is parsed in javax.swing.text.html.StyleSheet.addRule()
|
||||
String style = "<style>BASE_SIZE " + c.getFont().getSize() + "</style>";
|
||||
|
||||
String lowerText = text.toLowerCase();
|
||||
String lowerText = text.toLowerCase( Locale.ENGLISH );
|
||||
int headIndex;
|
||||
int styleIndex;
|
||||
|
||||
@@ -222,7 +229,7 @@ public class FlatLabelUI
|
||||
int tagBegin = i + 1;
|
||||
for( i += 2; i < textLength; i++ ) {
|
||||
if( !Character.isLetterOrDigit( text.charAt( i ) ) ) {
|
||||
String tag = text.substring( tagBegin, i ).toLowerCase();
|
||||
String tag = text.substring( tagBegin, i ).toLowerCase( Locale.ENGLISH );
|
||||
if( tagsUseFontSizeSet.contains( tag ) )
|
||||
return true;
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ public class FlatListCellBorder
|
||||
}
|
||||
|
||||
/**
|
||||
* Because this borders are always shared for all lists,
|
||||
* 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 ) {
|
||||
|
||||
@@ -17,20 +17,34 @@
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.FocusListener;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Map;
|
||||
import javax.swing.DefaultListCellRenderer;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.ListCellRenderer;
|
||||
import javax.swing.ListModel;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.basic.BasicComboBoxRenderer;
|
||||
import javax.swing.plaf.basic.BasicListUI;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.util.Graphics2DProxy;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
/**
|
||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JList}.
|
||||
@@ -59,6 +73,8 @@ import com.formdev.flatlaf.util.LoggingFacade;
|
||||
*
|
||||
* @uiDefault List.selectionInactiveBackground Color
|
||||
* @uiDefault List.selectionInactiveForeground Color
|
||||
* @uiDefault List.selectionInsets Insets
|
||||
* @uiDefault List.selectionArc int
|
||||
*
|
||||
* <!-- FlatListCellBorder -->
|
||||
*
|
||||
@@ -76,6 +92,8 @@ public class FlatListUI
|
||||
@Styleable protected Color selectionForeground;
|
||||
@Styleable protected Color selectionInactiveBackground;
|
||||
@Styleable protected Color selectionInactiveForeground;
|
||||
/** @since 3 */ @Styleable protected Insets selectionInsets;
|
||||
/** @since 3 */ @Styleable protected int selectionArc;
|
||||
|
||||
// for FlatListCellBorder
|
||||
/** @since 2 */ @Styleable protected Insets cellMargins;
|
||||
@@ -90,6 +108,13 @@ public class FlatListUI
|
||||
|
||||
@Override
|
||||
public void installUI( JComponent c ) {
|
||||
if( FlatUIUtils.needsLightAWTPeer( c ) )
|
||||
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
|
||||
else
|
||||
installUIImpl( c );
|
||||
}
|
||||
|
||||
private void installUIImpl( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
installStyle();
|
||||
@@ -103,6 +128,8 @@ public class FlatListUI
|
||||
selectionForeground = UIManager.getColor( "List.selectionForeground" );
|
||||
selectionInactiveBackground = UIManager.getColor( "List.selectionInactiveBackground" );
|
||||
selectionInactiveForeground = UIManager.getColor( "List.selectionInactiveForeground" );
|
||||
selectionInsets = UIManager.getInsets( "List.selectionInsets" );
|
||||
selectionArc = UIManager.getInt( "List.selectionArc" );
|
||||
|
||||
toggleSelectionColors();
|
||||
}
|
||||
@@ -161,6 +188,29 @@ public class FlatListUI
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ListSelectionListener createListSelectionListener() {
|
||||
ListSelectionListener superListener = super.createListSelectionListener();
|
||||
return e -> {
|
||||
superListener.valueChanged( e );
|
||||
|
||||
// for united rounded selection, repaint parts of the rows/columns that adjoin to the changed rows/columns
|
||||
if( useUnitedRoundedSelection( true, true ) &&
|
||||
!list.isSelectionEmpty() &&
|
||||
(list.getMaxSelectionIndex() - list.getMinSelectionIndex()) >= 1 )
|
||||
{
|
||||
int size = list.getModel().getSize();
|
||||
int firstIndex = Math.min( Math.max( e.getFirstIndex(), 0 ), size - 1 );
|
||||
int lastIndex = Math.min( Math.max( e.getLastIndex(), 0 ), size - 1 );
|
||||
Rectangle r = getCellBounds( list, firstIndex, lastIndex );
|
||||
if( r != null ) {
|
||||
int arc = (int) Math.ceil( UIScale.scale( selectionArc / 2f ) );
|
||||
list.repaint( r.x - arc, r.y - arc, r.width + (arc * 2), r.height + (arc * 2) );
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
protected void installStyle() {
|
||||
try {
|
||||
@@ -209,10 +259,16 @@ public class FlatListUI
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
@Override
|
||||
public Object getStyleableValue( JComponent c, String key ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle selection colors from focused to inactive and vice versa.
|
||||
*
|
||||
* 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
|
||||
* already used in applications. Then either the inactive colors are not used,
|
||||
@@ -234,4 +290,165 @@ public class FlatListUI
|
||||
list.setSelectionForeground( selectionInactiveForeground );
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings( "rawtypes" )
|
||||
@Override
|
||||
protected void paintCell( Graphics g, int row, Rectangle rowBounds, ListCellRenderer cellRenderer,
|
||||
ListModel dataModel, ListSelectionModel selModel, int leadIndex )
|
||||
{
|
||||
boolean isSelected = selModel.isSelectedIndex( row );
|
||||
|
||||
// get renderer component
|
||||
@SuppressWarnings( "unchecked" )
|
||||
Component rendererComponent = cellRenderer.getListCellRendererComponent( list,
|
||||
dataModel.getElementAt( row ), row, isSelected,
|
||||
FlatUIUtils.isPermanentFocusOwner( list ) && (row == leadIndex) );
|
||||
|
||||
//
|
||||
boolean isFileList = Boolean.TRUE.equals( list.getClientProperty( "List.isFileList" ) );
|
||||
int cx, cw;
|
||||
if( isFileList ) {
|
||||
// see BasicListUI.paintCell()
|
||||
cw = Math.min( rowBounds.width, rendererComponent.getPreferredSize().width + 4 );
|
||||
cx = list.getComponentOrientation().isLeftToRight()
|
||||
? rowBounds.x
|
||||
: rowBounds.x + (rowBounds.width - cw);
|
||||
} else {
|
||||
cx = rowBounds.x;
|
||||
cw = rowBounds.width;
|
||||
}
|
||||
|
||||
// rounded selection or selection insets
|
||||
if( isSelected &&
|
||||
!isFileList && // rounded selection is not supported for file list
|
||||
(rendererComponent instanceof DefaultListCellRenderer ||
|
||||
rendererComponent instanceof BasicComboBoxRenderer) &&
|
||||
(selectionArc > 0 ||
|
||||
(selectionInsets != null &&
|
||||
(selectionInsets.top != 0 || selectionInsets.left != 0 || selectionInsets.bottom != 0 || selectionInsets.right != 0))) )
|
||||
{
|
||||
// Because selection painting is done in the cell renderer, it would be
|
||||
// necessary to require a FlatLaf specific renderer to implement rounded selection.
|
||||
// Using a LaF specific renderer was avoided because often a custom renderer is
|
||||
// already used in applications. Then either the rounded selection is not used,
|
||||
// or the application has to be changed to extend a FlatLaf renderer.
|
||||
//
|
||||
// To solve this, a graphics proxy is used that paints rounded selection
|
||||
// if row is selected and the renderer wants to fill the background.
|
||||
class RoundedSelectionGraphics extends Graphics2DProxy {
|
||||
// used to avoid endless loop in case that paintCellSelection() invokes
|
||||
// g.fillRect() with full bounds (selectionInsets is 0,0,0,0)
|
||||
private boolean inPaintSelection;
|
||||
|
||||
RoundedSelectionGraphics( Graphics delegate ) {
|
||||
super( (Graphics2D) delegate );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Graphics create() {
|
||||
return new RoundedSelectionGraphics( super.create() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Graphics create( int x, int y, int width, int height ) {
|
||||
return new RoundedSelectionGraphics( super.create( x, y, width, height ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillRect( int x, int y, int width, int height ) {
|
||||
if( !inPaintSelection &&
|
||||
x == 0 && y == 0 && width == rowBounds.width && height == rowBounds.height &&
|
||||
this.getColor() == rendererComponent.getBackground() )
|
||||
{
|
||||
inPaintSelection = true;
|
||||
paintCellSelection( this, row, x, y, width, height );
|
||||
inPaintSelection = false;
|
||||
} else
|
||||
super.fillRect( x, y, width, height );
|
||||
}
|
||||
}
|
||||
g = new RoundedSelectionGraphics( g );
|
||||
}
|
||||
|
||||
// paint renderer
|
||||
rendererPane.paintComponent( g, rendererComponent, list, cx, rowBounds.y, cw, rowBounds.height, true );
|
||||
}
|
||||
|
||||
/** @since 3 */
|
||||
protected void paintCellSelection( Graphics g, int row, int x, int y, int width, int height ) {
|
||||
float arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight;
|
||||
arcTopLeft = arcTopRight = arcBottomLeft = arcBottomRight = UIScale.scale( selectionArc / 2f );
|
||||
|
||||
if( list.getLayoutOrientation() == JList.VERTICAL ) {
|
||||
// layout orientation: VERTICAL
|
||||
if( useUnitedRoundedSelection( true, false ) ) {
|
||||
if( row > 0 && list.isSelectedIndex( row - 1 ) )
|
||||
arcTopLeft = arcTopRight = 0;
|
||||
if( row < list.getModel().getSize() - 1 && list.isSelectedIndex( row + 1 ) )
|
||||
arcBottomLeft = arcBottomRight = 0;
|
||||
}
|
||||
} else {
|
||||
// layout orientation: VERTICAL_WRAP or HORIZONTAL_WRAP
|
||||
Rectangle r = null;
|
||||
if( useUnitedRoundedSelection( true, false ) ) {
|
||||
// vertical: check whether cells above or below are selected
|
||||
r = getCellBounds( list, row, row );
|
||||
|
||||
int topIndex = locationToIndex( list, new Point( r.x, r.y - 1 ) );
|
||||
int bottomIndex = locationToIndex( list, new Point( r.x, r.y + r.height ) );
|
||||
|
||||
if( topIndex >= 0 && topIndex != row && list.isSelectedIndex( topIndex ) )
|
||||
arcTopLeft = arcTopRight = 0;
|
||||
if( bottomIndex >= 0 && bottomIndex != row && list.isSelectedIndex( bottomIndex ) )
|
||||
arcBottomLeft = arcBottomRight = 0;
|
||||
}
|
||||
|
||||
if( useUnitedRoundedSelection( false, true ) ) {
|
||||
// horizontal: check whether cells left or right are selected
|
||||
if( r == null )
|
||||
r = getCellBounds( list, row, row );
|
||||
|
||||
int leftIndex = locationToIndex( list, new Point( r.x - 1, r.y ) );
|
||||
int rightIndex = locationToIndex( list, new Point( r.x + r.width, r.y ) );
|
||||
|
||||
// special handling for the case that last column contains less cells than the other columns
|
||||
boolean ltr = list.getComponentOrientation().isLeftToRight();
|
||||
if( !ltr && leftIndex >= 0 && leftIndex != row && leftIndex == locationToIndex( list, new Point( r.x - 1, r.y - 1 ) ) )
|
||||
leftIndex = -1;
|
||||
if( ltr && rightIndex >= 0 && rightIndex != row && rightIndex == locationToIndex( list, new Point( r.x + r.width, r.y - 1 ) ) )
|
||||
rightIndex = -1;
|
||||
|
||||
if( leftIndex >= 0 && leftIndex != row && list.isSelectedIndex( leftIndex ) )
|
||||
arcTopLeft = arcBottomLeft = 0;
|
||||
if( rightIndex >= 0 && rightIndex != row && list.isSelectedIndex( rightIndex ) )
|
||||
arcTopRight = arcBottomRight = 0;
|
||||
}
|
||||
}
|
||||
|
||||
FlatUIUtils.paintSelection( (Graphics2D) g, x, y, width, height,
|
||||
UIScale.scale( selectionInsets ), arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight, 0 );
|
||||
}
|
||||
|
||||
private boolean useUnitedRoundedSelection( boolean vertical, boolean horizontal ) {
|
||||
return selectionArc > 0 &&
|
||||
(selectionInsets == null ||
|
||||
(vertical && selectionInsets.top == 0 && selectionInsets.bottom == 0) ||
|
||||
(horizontal && selectionInsets.left == 0 && selectionInsets.right == 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints a cell selection at the given coordinates.
|
||||
* The selection color must be set on the graphics context.
|
||||
* <p>
|
||||
* This method is intended for use in custom cell renderers.
|
||||
*
|
||||
* @since 3
|
||||
*/
|
||||
public static void paintCellSelection( JList<?> list, Graphics g, int row, int x, int y, int width, int height ) {
|
||||
if( !(list.getUI() instanceof FlatListUI) )
|
||||
return;
|
||||
|
||||
FlatListUI ui = (FlatListUI) list.getUI();
|
||||
ui.paintCellSelection( g, row, x, y, width, height );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,12 @@ public class FlatMenuBarBorder
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
@Override
|
||||
public Object getStyleableValue( String key ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||
if( !showBottomSeparator( c ) )
|
||||
|
||||
@@ -18,8 +18,10 @@ package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Insets;
|
||||
import java.awt.LayoutManager;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
@@ -27,6 +29,7 @@ import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.ActionMap;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuBar;
|
||||
@@ -40,11 +43,13 @@ import javax.swing.plaf.ActionMapUIResource;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.UIResource;
|
||||
import javax.swing.plaf.basic.BasicMenuBarUI;
|
||||
import javax.swing.plaf.basic.DefaultMenuLayout;
|
||||
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.UIScale;
|
||||
|
||||
/**
|
||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenuBar}.
|
||||
@@ -70,7 +75,12 @@ public class FlatMenuBarUI
|
||||
/** @since 2 */ @Styleable protected Insets itemMargins;
|
||||
|
||||
// used in FlatMenuUI
|
||||
/** @since 3 */ @Styleable protected Insets selectionInsets;
|
||||
/** @since 3 */ @Styleable protected Insets selectionEmbeddedInsets;
|
||||
/** @since 3 */ @Styleable protected int selectionArc = -1;
|
||||
/** @since 2 */ @Styleable protected Color hoverBackground;
|
||||
/** @since 2.5 */ @Styleable protected Color selectionBackground;
|
||||
/** @since 2.5 */ @Styleable protected Color selectionForeground;
|
||||
/** @since 2 */ @Styleable protected Color underlineSelectionBackground;
|
||||
/** @since 2 */ @Styleable protected Color underlineSelectionColor;
|
||||
/** @since 2 */ @Styleable protected int underlineSelectionHeight = -1;
|
||||
@@ -100,6 +110,10 @@ public class FlatMenuBarUI
|
||||
super.installDefaults();
|
||||
|
||||
LookAndFeel.installProperty( menuBar, "opaque", false );
|
||||
|
||||
LayoutManager layout = menuBar.getLayout();
|
||||
if( layout == null || layout instanceof UIResource )
|
||||
menuBar.setLayout( new FlatMenuBarLayout( menuBar ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -165,6 +179,12 @@ public class FlatMenuBarUI
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this, menuBar.getBorder() );
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
@Override
|
||||
public Object getStyleableValue( JComponent c, String key ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableValue( this, menuBar.getBorder(), key );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update( Graphics g, JComponent c ) {
|
||||
// paint background
|
||||
@@ -210,7 +230,7 @@ public class FlatMenuBarUI
|
||||
// check whether:
|
||||
// - TitlePane.unifiedBackground is true and
|
||||
// - menu bar is the "main" menu bar and
|
||||
// - window has custom decorations enabled
|
||||
// - window root pane has custom decoration style
|
||||
|
||||
JRootPane rootPane;
|
||||
// (not storing value of "TitlePane.unifiedBackground" in class to allow changing at runtime)
|
||||
@@ -218,7 +238,131 @@ public class FlatMenuBarUI
|
||||
(rootPane = SwingUtilities.getRootPane( c )) != null &&
|
||||
rootPane.getParent() instanceof Window &&
|
||||
rootPane.getJMenuBar() == c &&
|
||||
FlatNativeWindowBorder.hasCustomDecoration( (Window) rootPane.getParent() );
|
||||
rootPane.getWindowDecorationStyle() != JRootPane.NONE;
|
||||
}
|
||||
|
||||
//---- class FlatMenuBarLayout --------------------------------------------
|
||||
|
||||
/**
|
||||
* @since 2.4
|
||||
*/
|
||||
protected static class FlatMenuBarLayout
|
||||
extends DefaultMenuLayout
|
||||
{
|
||||
public FlatMenuBarLayout( Container target ) {
|
||||
super( target, BoxLayout.LINE_AXIS );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void layoutContainer( Container target ) {
|
||||
super.layoutContainer( target );
|
||||
|
||||
|
||||
// The only purpose of the code below is to make sure that a horizontal glue,
|
||||
// which can be used to move window and displays the window title in embedded menu bar,
|
||||
// is always visible within the menu bar bounds and has a minimum width.
|
||||
// If this is not the case, the horizontal glue is made larger and
|
||||
// components that are on the left side of the glue are made smaller.
|
||||
|
||||
|
||||
// get root pane and check whether this menu bar is the root pane menu bar
|
||||
JRootPane rootPane = SwingUtilities.getRootPane( target );
|
||||
if( rootPane == null || rootPane.getJMenuBar() != target )
|
||||
return;
|
||||
|
||||
// get title pane and check whether menu bar is embedded
|
||||
FlatTitlePane titlePane = FlatRootPaneUI.getTitlePane( rootPane );
|
||||
if( titlePane == null || !titlePane.isMenuBarEmbedded() )
|
||||
return;
|
||||
|
||||
// check whether there is a horizontal glue (used for window title in embedded menu bar)
|
||||
// and check minimum width of horizontal glue
|
||||
Component horizontalGlue = titlePane.findHorizontalGlue( (JMenuBar) target );
|
||||
int minTitleWidth = UIScale.scale( titlePane.titleMinimumWidth );
|
||||
if( horizontalGlue != null && horizontalGlue.getWidth() < minTitleWidth ) {
|
||||
// get index of glue component
|
||||
int glueIndex = -1;
|
||||
Component[] components = target.getComponents();
|
||||
for( int i = components.length - 1; i >= 0; i-- ) {
|
||||
if( components[i] == horizontalGlue ) {
|
||||
glueIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( glueIndex < 0 )
|
||||
return; // should never happen
|
||||
|
||||
if( target.getComponentOrientation().isLeftToRight() ) {
|
||||
// left-to-right
|
||||
|
||||
// make horizontal glue wider (minimum title width)
|
||||
int offset = minTitleWidth - horizontalGlue.getWidth();
|
||||
horizontalGlue.setSize( minTitleWidth, horizontalGlue.getHeight() );
|
||||
|
||||
// check whether glue is fully visible
|
||||
int minGlueX = target.getWidth() - target.getInsets().right - minTitleWidth;
|
||||
if( minGlueX < horizontalGlue.getX() ) {
|
||||
// move glue to the left to make it fully visible
|
||||
offset -= (horizontalGlue.getX() - minGlueX);
|
||||
horizontalGlue.setLocation( minGlueX, horizontalGlue.getY() );
|
||||
|
||||
// shrink and move components that are on the left side of the glue
|
||||
for( int i = glueIndex - 1; i >= 0; i-- ) {
|
||||
Component c = components[i];
|
||||
if( c.getX() > minGlueX ) {
|
||||
// move component and set width to zero
|
||||
c.setBounds( minGlueX, c.getY(), 0, c.getHeight() );
|
||||
} else {
|
||||
// reduce size of component
|
||||
c.setSize( minGlueX - c.getX(), c.getHeight() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// move components that are on the right side of the glue
|
||||
for( int i = glueIndex + 1; i < components.length; i++ ) {
|
||||
Component c = components[i];
|
||||
c.setLocation( c.getX() + offset, c.getY() );
|
||||
}
|
||||
} else {
|
||||
// right-to-left
|
||||
|
||||
// make horizontal glue wider (minimum title width)
|
||||
int offset = minTitleWidth - horizontalGlue.getWidth();
|
||||
horizontalGlue.setBounds( horizontalGlue.getX() - offset, horizontalGlue.getY(),
|
||||
minTitleWidth, horizontalGlue.getHeight() );
|
||||
|
||||
// check whether glue is fully visible
|
||||
int minGlueX = target.getInsets().left;
|
||||
if( minGlueX > horizontalGlue.getX() ) {
|
||||
// move glue to the right to make it fully visible
|
||||
offset -= (horizontalGlue.getX() - minGlueX);
|
||||
horizontalGlue.setLocation( minGlueX, horizontalGlue.getY() );
|
||||
|
||||
// shrink and move components that are on the right side of the glue
|
||||
int x = horizontalGlue.getX() + horizontalGlue.getWidth();
|
||||
for( int i = glueIndex - 1; i >= 0; i-- ) {
|
||||
Component c = components[i];
|
||||
if( c.getX() + c.getWidth() < x ) {
|
||||
// move component and set width to zero
|
||||
c.setBounds( x, c.getY(), 0, c.getHeight() );
|
||||
} else {
|
||||
// move component and reduce size
|
||||
c.setBounds( x, c.getY(), c.getWidth() - (x - c.getX()), c.getHeight() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// move components that are on the left side of the glue
|
||||
for( int i = glueIndex + 1; i < components.length; i++ ) {
|
||||
Component c = components[i];
|
||||
c.setLocation( c.getX() - offset, c.getY() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---- class TakeFocus ----------------------------------------------------
|
||||
|
||||
@@ -35,7 +35,7 @@ import javax.swing.plaf.MenuBarUI;
|
||||
public class FlatMenuItemBorder
|
||||
extends FlatMarginBorder
|
||||
{
|
||||
// only used if parent menubar is not a instance of FlatMenuBarUI
|
||||
// only used if parent menubar is not an instance of FlatMenuBarUI
|
||||
private final Insets menuBarItemMargins = UIManager.getInsets( "MenuBar.itemMargins" );
|
||||
|
||||
@Override
|
||||
|
||||
@@ -63,6 +63,8 @@ import com.formdev.flatlaf.util.SystemInfo;
|
||||
* @uiDefault MenuItem.acceleratorArrowGap int
|
||||
* @uiDefault MenuItem.checkBackground Color
|
||||
* @uiDefault MenuItem.checkMargins Insets
|
||||
* @uiDefault MenuItem.selectionInsets Insets
|
||||
* @uiDefault MenuItem.selectionArc int
|
||||
* @uiDefault MenuItem.selectionType String null (default) or underline
|
||||
* @uiDefault MenuItem.underlineSelectionBackground Color
|
||||
* @uiDefault MenuItem.underlineSelectionCheckBackground Color
|
||||
@@ -91,12 +93,16 @@ public class FlatMenuItemRenderer
|
||||
@Styleable protected Color checkBackground = UIManager.getColor( "MenuItem.checkBackground" );
|
||||
@Styleable protected Insets checkMargins = UIManager.getInsets( "MenuItem.checkMargins" );
|
||||
|
||||
/** @since 3 */ @Styleable protected Insets selectionInsets = UIManager.getInsets( "MenuItem.selectionInsets" );
|
||||
/** @since 3 */ @Styleable protected int selectionArc = UIManager.getInt( "MenuItem.selectionArc" );
|
||||
|
||||
@Styleable protected Color underlineSelectionBackground = UIManager.getColor( "MenuItem.underlineSelectionBackground" );
|
||||
@Styleable protected Color underlineSelectionCheckBackground = UIManager.getColor( "MenuItem.underlineSelectionCheckBackground" );
|
||||
@Styleable protected Color underlineSelectionColor = UIManager.getColor( "MenuItem.underlineSelectionColor" );
|
||||
@Styleable protected int underlineSelectionHeight = UIManager.getInt( "MenuItem.underlineSelectionHeight" );
|
||||
|
||||
private boolean iconsShared = true;
|
||||
private final Font menuFont = UIManager.getFont( "Menu.font" );
|
||||
|
||||
protected FlatMenuItemRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
|
||||
Font acceleratorFont, String acceleratorDelimiter )
|
||||
@@ -169,6 +175,19 @@ public class FlatMenuItemRenderer
|
||||
return infos;
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
public Object getStyleableValue( String key ) {
|
||||
if( key.startsWith( "icon." ) ) {
|
||||
String key2 = key.substring( "icon.".length() );
|
||||
if( checkIcon instanceof FlatCheckBoxMenuItemIcon )
|
||||
return ((FlatCheckBoxMenuItemIcon)checkIcon).getStyleableValue( key2 );
|
||||
if( arrowIcon instanceof FlatMenuArrowIcon )
|
||||
return ((FlatMenuArrowIcon)arrowIcon).getStyleableValue( key2 );
|
||||
}
|
||||
|
||||
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||
}
|
||||
|
||||
protected Dimension getPreferredMenuItemSize() {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
@@ -180,7 +199,8 @@ public class FlatMenuItemRenderer
|
||||
|
||||
// layout icon and text
|
||||
SwingUtilities.layoutCompoundLabel( menuItem,
|
||||
menuItem.getFontMetrics( menuItem.getFont() ), menuItem.getText(), getIconForLayout(),
|
||||
menuItem.getFontMetrics( isTopLevelMenu ? getTopLevelFont() : menuItem.getFont() ),
|
||||
menuItem.getText(), getIconForLayout(),
|
||||
menuItem.getVerticalAlignment(), menuItem.getHorizontalAlignment(),
|
||||
menuItem.getVerticalTextPosition(), menuItem.getHorizontalTextPosition(),
|
||||
viewRect, iconRect, textRect, scale( menuItem.getIconTextGap() ) );
|
||||
@@ -282,7 +302,8 @@ public class FlatMenuItemRenderer
|
||||
|
||||
// layout icon and text
|
||||
SwingUtilities.layoutCompoundLabel( menuItem,
|
||||
menuItem.getFontMetrics( menuItem.getFont() ), menuItem.getText(), getIconForLayout(),
|
||||
menuItem.getFontMetrics( isTopLevelMenu ? getTopLevelFont() : menuItem.getFont() ),
|
||||
menuItem.getText(), getIconForLayout(),
|
||||
menuItem.getVerticalAlignment(), menuItem.getHorizontalAlignment(),
|
||||
menuItem.getVerticalTextPosition(), menuItem.getHorizontalTextPosition(),
|
||||
labelRect, iconRect, textRect, scale( menuItem.getIconTextGap() ) );
|
||||
@@ -321,10 +342,16 @@ public class FlatMenuItemRenderer
|
||||
g.setColor( Color.orange ); g.drawRect( arrowRect.x, arrowRect.y, arrowRect.width - 1, arrowRect.height - 1 );
|
||||
debug*/
|
||||
|
||||
boolean armedOrSelected = isArmedOrSelected( menuItem );
|
||||
boolean underlineSelection = isUnderlineSelection();
|
||||
paintBackground( g, underlineSelection ? underlineSelectionBackground : selectionBackground );
|
||||
if( underlineSelection && isArmedOrSelected( menuItem ) )
|
||||
paintUnderlineSelection( g, underlineSelectionColor, underlineSelectionHeight );
|
||||
|
||||
paintBackground( g );
|
||||
if( armedOrSelected ) {
|
||||
if( underlineSelection )
|
||||
paintUnderlineSelection( g, underlineSelectionBackground, underlineSelectionColor, underlineSelectionHeight );
|
||||
else
|
||||
paintSelection( g, selectionBackground, selectionInsets, selectionArc );
|
||||
}
|
||||
paintIcon( g, iconRect, getIconForPainting(), underlineSelection ? underlineSelectionCheckBackground : checkBackground, selectionBackground );
|
||||
paintText( g, textRect, menuItem.getText(), selectionForeground, disabledForeground );
|
||||
paintAccelerator( g, accelRect, getAcceleratorText(), acceleratorForeground, acceleratorSelectionForeground, disabledForeground );
|
||||
@@ -332,21 +359,35 @@ debug*/
|
||||
paintArrowIcon( g, arrowRect, arrowIcon );
|
||||
}
|
||||
|
||||
protected void paintBackground( Graphics g, Color selectionBackground ) {
|
||||
boolean armedOrSelected = isArmedOrSelected( menuItem );
|
||||
if( menuItem.isOpaque() || armedOrSelected ) {
|
||||
// paint background
|
||||
g.setColor( armedOrSelected
|
||||
? deriveBackground( selectionBackground )
|
||||
: menuItem.getBackground() );
|
||||
/** @since 3 */
|
||||
protected void paintBackground( Graphics g ) {
|
||||
if( menuItem.isOpaque() ) {
|
||||
g.setColor( menuItem.getBackground() );
|
||||
g.fillRect( 0, 0, menuItem.getWidth(), menuItem.getHeight() );
|
||||
}
|
||||
}
|
||||
|
||||
protected void paintUnderlineSelection( Graphics g, Color underlineSelectionColor, int underlineSelectionHeight ) {
|
||||
/** @since 3 */
|
||||
protected void paintSelection( Graphics g, Color selectionBackground, Insets selectionInsets, int selectionArc ) {
|
||||
float arc = scale( selectionArc / 2f );
|
||||
|
||||
g.setColor( deriveBackground( selectionBackground ) );
|
||||
FlatUIUtils.paintSelection( (Graphics2D) g, 0, 0, menuItem.getWidth(), menuItem.getHeight(),
|
||||
scale( selectionInsets ), arc, arc, arc, arc, 0 );
|
||||
}
|
||||
|
||||
/** @since 3 */
|
||||
protected void paintUnderlineSelection( Graphics g, Color underlineSelectionBackground,
|
||||
Color underlineSelectionColor, int underlineSelectionHeight )
|
||||
{
|
||||
int width = menuItem.getWidth();
|
||||
int height = menuItem.getHeight();
|
||||
|
||||
// paint background
|
||||
g.setColor( deriveBackground( underlineSelectionBackground ) );
|
||||
g.fillRect( 0, 0, width, height );
|
||||
|
||||
// paint underline
|
||||
int underlineHeight = scale( underlineSelectionHeight );
|
||||
g.setColor( underlineSelectionColor );
|
||||
if( isTopLevelMenu( menuItem ) ) {
|
||||
@@ -392,9 +433,10 @@ debug*/
|
||||
}
|
||||
|
||||
int mnemonicIndex = FlatLaf.isShowMnemonics() ? menuItem.getDisplayedMnemonicIndex() : -1;
|
||||
Color foreground = (isTopLevelMenu( menuItem ) ? menuItem.getParent() : menuItem).getForeground();
|
||||
boolean isTopLevelMenu = isTopLevelMenu( menuItem );
|
||||
Color foreground = (isTopLevelMenu ? menuItem.getParent() : menuItem).getForeground();
|
||||
|
||||
paintText( g, menuItem, textRect, text, mnemonicIndex, menuItem.getFont(),
|
||||
paintText( g, menuItem, textRect, text, mnemonicIndex, isTopLevelMenu ? getTopLevelFont() : menuItem.getFont(),
|
||||
foreground, isUnderlineSelection() ? foreground : selectionForeground, disabledForeground );
|
||||
}
|
||||
|
||||
@@ -414,10 +456,11 @@ debug*/
|
||||
return;
|
||||
|
||||
// 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() );
|
||||
|
||||
// paint
|
||||
icon.paintIcon( menuItem, g, iconRect.x, y );
|
||||
icon.paintIcon( menuItem, g, x, y );
|
||||
}
|
||||
|
||||
protected static void paintText( Graphics g, JMenuItem menuItem,
|
||||
@@ -446,6 +489,19 @@ debug*/
|
||||
protected static void paintHTMLText( Graphics g, JMenuItem menuItem,
|
||||
Rectangle textRect, View htmlView, Color selectionForeground )
|
||||
{
|
||||
// On Windows, using Segoe UI font, Java 15 or older and scaling greater than 1,
|
||||
// the width of the HTML view may be initially too small (because component
|
||||
// is not connected to a GraphicsConfiguration when getPreferredSize() is invoked).
|
||||
// Since Java 16, this seems to be fixed somehow by doing popup menu layout twice.
|
||||
//
|
||||
// If using a too small width for htmlView.paint(), the view would rearrange
|
||||
// its children and wrap them to two lines. To avoid this, use view preferred X span
|
||||
// for painting. Core Lafs actually do the same in class MenuItemLayoutHelper.
|
||||
//
|
||||
// Example HTML text that causes the problem: "<html>some <b>HTML</b> <i>text</i></html>"
|
||||
textRect = new Rectangle( textRect );
|
||||
textRect.width = (int) htmlView.getPreferredSpan( View.X_AXIS );
|
||||
|
||||
if( isArmedOrSelected( menuItem ) && selectionForeground != null )
|
||||
g = new GraphicsProxyWithTextColor( (Graphics2D) g, selectionForeground );
|
||||
|
||||
@@ -468,6 +524,15 @@ debug*/
|
||||
return "underline".equals( UIManager.getString( "MenuItem.selectionType" ) );
|
||||
}
|
||||
|
||||
private Font getTopLevelFont() {
|
||||
Font font = menuItem.getFont();
|
||||
// menu item parent may be null if JMenu.isTopLevelMenu() is overridden
|
||||
// and does not check parent (e.g. com.jidesoft.swing.JideMenu.isTopLevelMenu())
|
||||
return (font != menuFont || menuItem.getParent() == null)
|
||||
? font
|
||||
: menuItem.getParent().getFont();
|
||||
}
|
||||
|
||||
private Icon getIconForPainting() {
|
||||
Icon icon = menuItem.getIcon();
|
||||
|
||||
|
||||
@@ -16,16 +16,19 @@
|
||||
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.Map;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.LookAndFeel;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.basic.BasicMenuItemUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
@@ -58,9 +61,15 @@ import com.formdev.flatlaf.util.LoggingFacade;
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
@StyleableField( cls=BasicMenuItemUI.class, key="selectionBackground" )
|
||||
@StyleableField( cls=BasicMenuItemUI.class, key="selectionForeground" )
|
||||
@StyleableField( cls=BasicMenuItemUI.class, key="disabledForeground" )
|
||||
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorForeground" )
|
||||
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorSelectionForeground" )
|
||||
|
||||
public class FlatMenuItemUI
|
||||
extends BasicMenuItemUI
|
||||
implements StyleableUI
|
||||
implements StyleableUI, StyleableLookupProvider
|
||||
{
|
||||
private FlatMenuItemRenderer renderer;
|
||||
private Map<String, Object> oldStyleValues;
|
||||
@@ -119,42 +128,54 @@ public class FlatMenuItemUI
|
||||
|
||||
/** @since 2 */
|
||||
protected Object applyStyleProperty( String key, Object value ) {
|
||||
return applyStyleProperty( menuItem, this, renderer, key, value );
|
||||
}
|
||||
|
||||
static Object applyStyleProperty( JMenuItem menuItem, BasicMenuItemUI ui,
|
||||
FlatMenuItemRenderer renderer, String key, Object value )
|
||||
{
|
||||
try {
|
||||
return renderer.applyStyleProperty( key, value );
|
||||
} catch ( UnknownStyleException ex ) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
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 );
|
||||
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( ui, menuItem, key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
return getStyleableInfos( renderer );
|
||||
return getStyleableInfos( this, 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 );
|
||||
static Map<String, Class<?>> getStyleableInfos( BasicMenuItemUI ui, FlatMenuItemRenderer renderer ) {
|
||||
Map<String, Class<?>> infos = FlatStylingSupport.getAnnotatedStyleableInfos( ui );
|
||||
infos.putAll( renderer.getStyleableInfos() );
|
||||
return infos;
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
@Override
|
||||
public Object getStyleableValue( JComponent c, String key ) {
|
||||
return getStyleableValue( this, renderer, key );
|
||||
}
|
||||
|
||||
static Object getStyleableValue( BasicMenuItemUI ui, FlatMenuItemRenderer renderer, String key ) {
|
||||
Object value = renderer.getStyleableValue( key );
|
||||
if( value == null )
|
||||
value = FlatStylingSupport.getAnnotatedStyleableValue( ui, key );
|
||||
return value;
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
@Override
|
||||
public MethodHandles.Lookup getLookupForStyling() {
|
||||
// MethodHandles.lookup() is caller sensitive and must be invoked in this class,
|
||||
// otherwise it is not possible to access protected fields in JRE superclass
|
||||
return MethodHandles.lookup();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
|
||||
return renderer.getPreferredMenuItemSize();
|
||||
|
||||
@@ -20,8 +20,12 @@ import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import javax.swing.ButtonModel;
|
||||
@@ -30,14 +34,18 @@ import javax.swing.JComponent;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuBar;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JRootPane;
|
||||
import javax.swing.LookAndFeel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.event.MouseInputListener;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.MenuBarUI;
|
||||
import javax.swing.plaf.basic.BasicMenuItemUI;
|
||||
import javax.swing.plaf.basic.BasicMenuUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
|
||||
/**
|
||||
@@ -71,16 +79,27 @@ import com.formdev.flatlaf.util.LoggingFacade;
|
||||
*
|
||||
* <!-- FlatMenuRenderer -->
|
||||
*
|
||||
* @uiDefault MenuBar.selectionInsets Insets
|
||||
* @uiDefault MenuBar.selectionEmbeddedInsets Insets
|
||||
* @uiDefault MenuBar.selectionArc int
|
||||
* @uiDefault MenuBar.hoverBackground Color
|
||||
* @uiDefault MenuBar.selectionBackground Color optional; defaults to Menu.selectionBackground
|
||||
* @uiDefault MenuBar.selectionForeground Color optional; defaults to Menu.selectionForeground
|
||||
* @uiDefault MenuBar.underlineSelectionBackground Color
|
||||
* @uiDefault MenuBar.underlineSelectionColor Color
|
||||
* @uiDefault MenuBar.underlineSelectionHeight int
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
@StyleableField( cls=BasicMenuItemUI.class, key="selectionBackground" )
|
||||
@StyleableField( cls=BasicMenuItemUI.class, key="selectionForeground" )
|
||||
@StyleableField( cls=BasicMenuItemUI.class, key="disabledForeground" )
|
||||
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorForeground" )
|
||||
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorSelectionForeground" )
|
||||
|
||||
public class FlatMenuUI
|
||||
extends BasicMenuUI
|
||||
implements StyleableUI
|
||||
implements StyleableUI, StyleableLookupProvider
|
||||
{
|
||||
private FlatMenuItemRenderer renderer;
|
||||
private Map<String, Object> oldStyleValues;
|
||||
@@ -166,29 +185,27 @@ public class FlatMenuUI
|
||||
|
||||
/** @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 );
|
||||
return FlatMenuItemUI.applyStyleProperty( menuItem, this, renderer, key, value );
|
||||
}
|
||||
|
||||
/** @since 2 */
|
||||
@Override
|
||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||
return FlatMenuItemUI.getStyleableInfos( renderer );
|
||||
return FlatMenuItemUI.getStyleableInfos( this, renderer );
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
@Override
|
||||
public Object getStyleableValue( JComponent c, String key ) {
|
||||
return FlatMenuItemUI.getStyleableValue( this, renderer, key );
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
@Override
|
||||
public MethodHandles.Lookup getLookupForStyling() {
|
||||
// MethodHandles.lookup() is caller sensitive and must be invoked in this class,
|
||||
// otherwise it is not possible to access protected fields in JRE superclass
|
||||
return MethodHandles.lookup();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -215,10 +232,15 @@ public class FlatMenuUI
|
||||
protected class FlatMenuRenderer
|
||||
extends FlatMenuItemRenderer
|
||||
{
|
||||
/** @since 3 */ protected Insets menuBarSelectionInsets = UIManager.getInsets( "MenuBar.selectionInsets" );
|
||||
/** @since 3 */ protected Insets menuBarSelectionEmbeddedInsets = UIManager.getInsets( "MenuBar.selectionEmbeddedInsets" );
|
||||
/** @since 3 */ protected int menuBarSelectionArc = UIManager.getInt( "MenuBar.selectionArc" );
|
||||
protected Color hoverBackground = UIManager.getColor( "MenuBar.hoverBackground" );
|
||||
protected Color menuBarUnderlineSelectionBackground = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionBackground", underlineSelectionBackground );
|
||||
protected Color menuBarUnderlineSelectionColor = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionColor", underlineSelectionColor );
|
||||
protected int menuBarUnderlineSelectionHeight = FlatUIUtils.getUIInt( "MenuBar.underlineSelectionHeight", underlineSelectionHeight );
|
||||
/** @since 2.5 */ protected Color menuBarSelectionBackground = UIManager.getColor( "MenuBar.selectionBackground" );
|
||||
/** @since 2.5 */ protected Color menuBarSelectionForeground = UIManager.getColor( "MenuBar.selectionForeground" );
|
||||
protected Color menuBarUnderlineSelectionBackground = UIManager.getColor( "MenuBar.underlineSelectionBackground" );
|
||||
protected Color menuBarUnderlineSelectionColor = UIManager.getColor( "MenuBar.underlineSelectionColor" );
|
||||
protected int menuBarUnderlineSelectionHeight = FlatUIUtils.getUIInt( "MenuBar.underlineSelectionHeight", -1 );
|
||||
|
||||
protected FlatMenuRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
|
||||
Font acceleratorFont, String acceleratorDelimiter )
|
||||
@@ -226,32 +248,76 @@ public class FlatMenuUI
|
||||
super( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
||||
}
|
||||
|
||||
/** @since 3 */
|
||||
@Override
|
||||
protected void paintBackground( Graphics g, Color selectionBackground ) {
|
||||
if( ((JMenu)menuItem).isTopLevelMenu() ) {
|
||||
if( isUnderlineSelection() )
|
||||
selectionBackground = getStyleFromMenuBarUI( ui -> ui.underlineSelectionBackground, menuBarUnderlineSelectionBackground );
|
||||
protected void paintBackground( Graphics g ) {
|
||||
super.paintBackground( g );
|
||||
|
||||
ButtonModel model = menuItem.getModel();
|
||||
if( model.isRollover() && !model.isArmed() && !model.isSelected() && model.isEnabled() ) {
|
||||
g.setColor( deriveBackground( getStyleFromMenuBarUI( ui -> ui.hoverBackground, hoverBackground ) ) );
|
||||
if( ((JMenu)menuItem).isTopLevelMenu() && isHover() ) {
|
||||
// paint hover background
|
||||
Color color = deriveBackground( getStyleFromMenuBarUI( ui -> ui.hoverBackground, hoverBackground ) );
|
||||
if( isUnderlineSelection() ) {
|
||||
g.setColor( color );
|
||||
g.fillRect( 0, 0, menuItem.getWidth(), menuItem.getHeight() );
|
||||
return;
|
||||
}
|
||||
} else
|
||||
paintSelection( g, color, selectionInsets, selectionArc );
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 3 */
|
||||
@Override
|
||||
protected void paintSelection( Graphics g, Color selectionBackground, Insets selectionInsets, int selectionArc ) {
|
||||
if( ((JMenu)menuItem).isTopLevelMenu() ) {
|
||||
if( !isHover() )
|
||||
selectionBackground = getStyleFromMenuBarUI( ui -> ui.selectionBackground, menuBarSelectionBackground, selectionBackground );
|
||||
|
||||
JMenuBar menuBar = (JMenuBar) menuItem.getParent();
|
||||
JRootPane rootPane = SwingUtilities.getRootPane( menuBar );
|
||||
if( rootPane != null && rootPane.getParent() instanceof Window &&
|
||||
rootPane.getJMenuBar() == menuBar &&
|
||||
FlatRootPaneUI.isMenuBarEmbedded( rootPane ) )
|
||||
{
|
||||
selectionInsets = getStyleFromMenuBarUI( ui -> ui.selectionEmbeddedInsets, menuBarSelectionEmbeddedInsets );
|
||||
} else
|
||||
selectionInsets = getStyleFromMenuBarUI( ui -> ui.selectionInsets, menuBarSelectionInsets );
|
||||
|
||||
selectionArc = getStyleFromMenuBarUI( ui -> (ui.selectionArc != -1)
|
||||
? ui.selectionArc : null, menuBarSelectionArc );
|
||||
}
|
||||
|
||||
super.paintBackground( g, selectionBackground );
|
||||
super.paintSelection( g, selectionBackground, selectionInsets, selectionArc );
|
||||
}
|
||||
|
||||
/** @since 3 */
|
||||
@Override
|
||||
protected void paintUnderlineSelection( Graphics g, Color underlineSelectionBackground,
|
||||
Color underlineSelectionColor, int underlineSelectionHeight )
|
||||
{
|
||||
if( ((JMenu)menuItem).isTopLevelMenu() ) {
|
||||
underlineSelectionBackground = getStyleFromMenuBarUI( ui -> ui.underlineSelectionBackground, menuBarUnderlineSelectionBackground, underlineSelectionBackground );
|
||||
underlineSelectionColor = getStyleFromMenuBarUI( ui -> ui.underlineSelectionColor, menuBarUnderlineSelectionColor, underlineSelectionColor );
|
||||
underlineSelectionHeight = getStyleFromMenuBarUI( ui -> (ui.underlineSelectionHeight != -1) ? ui.underlineSelectionHeight : null,
|
||||
(menuBarUnderlineSelectionHeight != -1) ? menuBarUnderlineSelectionHeight : underlineSelectionHeight );
|
||||
}
|
||||
|
||||
super.paintUnderlineSelection( g, underlineSelectionBackground, underlineSelectionColor, underlineSelectionHeight );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintUnderlineSelection( Graphics g, Color underlineSelectionColor, int underlineSelectionHeight ) {
|
||||
if( ((JMenu)menuItem).isTopLevelMenu() ) {
|
||||
underlineSelectionColor = getStyleFromMenuBarUI( ui -> ui.underlineSelectionColor, menuBarUnderlineSelectionColor );
|
||||
underlineSelectionHeight = getStyleFromMenuBarUI( ui -> (ui.underlineSelectionHeight != -1)
|
||||
? ui.underlineSelectionHeight : null, menuBarUnderlineSelectionHeight );
|
||||
}
|
||||
protected void paintText( Graphics g, Rectangle textRect, String text, Color selectionForeground, Color disabledForeground ) {
|
||||
if( ((JMenu)menuItem).isTopLevelMenu() && !isUnderlineSelection() )
|
||||
selectionForeground = getStyleFromMenuBarUI( ui -> ui.selectionForeground, menuBarSelectionForeground, selectionForeground );
|
||||
|
||||
super.paintUnderlineSelection( g, underlineSelectionColor, underlineSelectionHeight );
|
||||
super.paintText( g, textRect, text, selectionForeground, disabledForeground );
|
||||
}
|
||||
|
||||
private boolean isHover() {
|
||||
ButtonModel model = menuItem.getModel();
|
||||
return model.isRollover() && !model.isArmed() && !model.isSelected() && model.isEnabled();
|
||||
}
|
||||
|
||||
private <T> T getStyleFromMenuBarUI( Function<FlatMenuBarUI, T> f, T defaultValue, T defaultValue2 ) {
|
||||
return getStyleFromMenuBarUI( f, (defaultValue != null) ? defaultValue : defaultValue2 );
|
||||
}
|
||||
|
||||
private <T> T getStyleFromMenuBarUI( Function<FlatMenuBarUI, T> f, T defaultValue ) {
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* 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 java.net.URL;
|
||||
import java.security.CodeSource;
|
||||
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 boolean initialized;
|
||||
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( initialized )
|
||||
return;
|
||||
initialized = true;
|
||||
|
||||
if( !FlatSystemProperties.getBoolean( FlatSystemProperties.USE_NATIVE_LIBRARY, true ) )
|
||||
return;
|
||||
|
||||
String classifier;
|
||||
String ext;
|
||||
if( SystemInfo.isWindows_10_orLater && (SystemInfo.isX86 || SystemInfo.isX86_64 || SystemInfo.isAARCH64) ) {
|
||||
// Windows: requires Windows 10/11 (x86, x86_64 or aarch64)
|
||||
|
||||
if( SystemInfo.isAARCH64 )
|
||||
classifier = "windows-arm64";
|
||||
else if( SystemInfo.isX86_64 )
|
||||
classifier = "windows-x86_64";
|
||||
else
|
||||
classifier = "windows-x86";
|
||||
|
||||
ext = "dll";
|
||||
|
||||
// Do not load jawt.dll (part of JRE) here explicitly because
|
||||
// the FlatLaf native library flatlaf.dll may be loaded very early on Windows
|
||||
// (e.g. from class com.formdev.flatlaf.util.SystemInfo) and before AWT is
|
||||
// initialized (and awt.dll is loaded). Loading jawt.dll also loads awt.dll.
|
||||
// In Java 8, loading jawt.dll before AWT is initialized may load
|
||||
// a wrong version of awt.dll if a newer Java version (e.g. 19)
|
||||
// is in PATH environment variable. Then Java 19 awt.dll and Java 8 awt.dll
|
||||
// are loaded at same time and calling JAWT_GetAWT() crashes the application.
|
||||
//
|
||||
// To avoid this, flatlaf.dll is not linked to jawt.dll,
|
||||
// which avoids loading jawt.dll when flatlaf.dll is loaded.
|
||||
// Instead flatlaf.dll dynamically loads jawt.dll when first used,
|
||||
// which is guaranteed after AWT initialization.
|
||||
|
||||
} else if( SystemInfo.isLinux && SystemInfo.isX86_64 ) {
|
||||
// Linux: requires x86_64
|
||||
|
||||
classifier = "linux-x86_64";
|
||||
ext = "so";
|
||||
|
||||
// Load libjawt.so (part of JRE) explicitly because it is not found
|
||||
// in all Java versions/distributions.
|
||||
// E.g. not found in Java 13 and later from openjdk.java.net.
|
||||
// There seems to be also differences between distributions.
|
||||
// E.g. Adoptium Java 17 does not need this, but Java 17 from openjdk.java.net does.
|
||||
loadJAWT();
|
||||
} else
|
||||
return; // no native library available for current OS or CPU architecture
|
||||
|
||||
// load native library
|
||||
nativeLibrary = createNativeLibrary( classifier, ext );
|
||||
}
|
||||
|
||||
private static NativeLibrary createNativeLibrary( String classifier, String ext ) {
|
||||
String libraryName = "flatlaf-" + classifier;
|
||||
|
||||
// load from "java.library.path" or from path specified in system property "flatlaf.nativeLibraryPath"
|
||||
String libraryPath = System.getProperty( FlatSystemProperties.NATIVE_LIBRARY_PATH );
|
||||
if( libraryPath != null ) {
|
||||
if( "system".equals( libraryPath ) ) {
|
||||
NativeLibrary library = new NativeLibrary( libraryName, true );
|
||||
if( library.isLoaded() )
|
||||
return library;
|
||||
|
||||
LoggingFacade.INSTANCE.logSevere( "Did not find library " + libraryName + " in java.library.path, using extracted library instead", null );
|
||||
} else {
|
||||
File libraryFile = new File( libraryPath, System.mapLibraryName( libraryName ) );
|
||||
if( libraryFile.exists() )
|
||||
return new NativeLibrary( libraryFile, true );
|
||||
|
||||
LoggingFacade.INSTANCE.logSevere( "Did not find external library " + libraryFile + ", using extracted library instead", null );
|
||||
}
|
||||
}
|
||||
|
||||
// load from beside the FlatLaf jar
|
||||
// e.g. for flatlaf-3.1.jar, load flatlaf-3.1-windows-x86_64.dll (from same directory)
|
||||
File libraryFile = findLibraryBesideJar( classifier, ext );
|
||||
if( libraryFile != null )
|
||||
return new NativeLibrary( libraryFile, true );
|
||||
|
||||
// load from FlatLaf jar (extract native library to temp folder)
|
||||
return new NativeLibrary( "com/formdev/flatlaf/natives/" + libraryName, null, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for a native library beside the jar that contains this class
|
||||
* (usually the FlatLaf jar).
|
||||
* The native library must be in the same directory (or in "../bin" if jar is in "lib")
|
||||
* as the jar and have the same basename as the jar.
|
||||
* If FlatLaf jar is repackaged into fat/uber application jar, "-flatlaf" is appended to jar basename.
|
||||
* The classifier and the extension are appended to the jar basename.
|
||||
*
|
||||
* E.g.
|
||||
* flatlaf-3.1.jar
|
||||
* flatlaf-3.1-windows-x86_64.dll
|
||||
* flatlaf-3.1-linux-x86_64.so
|
||||
*/
|
||||
private static File findLibraryBesideJar( String classifier, String ext ) {
|
||||
try {
|
||||
// get location of FlatLaf jar
|
||||
CodeSource codeSource = FlatNativeLibrary.class.getProtectionDomain().getCodeSource();
|
||||
URL jarUrl = (codeSource != null) ? codeSource.getLocation() : null;
|
||||
if( jarUrl == null )
|
||||
return null;
|
||||
|
||||
// if url is not a file, then we're running in a special environment (e.g. WebStart)
|
||||
if( !"file".equals( jarUrl.getProtocol() ) )
|
||||
return null;
|
||||
|
||||
File jarFile = new File( jarUrl.toURI() );
|
||||
|
||||
// if jarFile is a directory, then we're in a development environment
|
||||
if( !jarFile.isFile() )
|
||||
return null;
|
||||
|
||||
// build library file
|
||||
String jarName = jarFile.getName();
|
||||
String jarBasename = jarName.substring( 0, jarName.lastIndexOf( '.' ) );
|
||||
File parent = jarFile.getParentFile();
|
||||
String libraryName = jarBasename
|
||||
+ (jarBasename.contains( "flatlaf" ) ? "" : "-flatlaf")
|
||||
+ '-' + classifier + '.' + ext;
|
||||
|
||||
// check whether native library exists in same directory as jar
|
||||
File libraryFile = new File( parent, libraryName );
|
||||
if( libraryFile.isFile() )
|
||||
return libraryFile;
|
||||
|
||||
// if jar is in "lib" directory, then also check whether library exists
|
||||
// in "../bin" directory
|
||||
if( parent.getName().equalsIgnoreCase( "lib" ) ) {
|
||||
libraryFile = new File( parent.getParentFile(), "bin/" + libraryName );
|
||||
if( libraryFile.isFile() )
|
||||
return libraryFile;
|
||||
}
|
||||
} catch( Exception ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( ex.getMessage(), ex );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void loadJAWT() {
|
||||
try {
|
||||
System.loadLibrary( "jawt" );
|
||||
} catch( UnsatisfiedLinkError ex ) {
|
||||
// log error only if native library jawt.dll not already loaded
|
||||
String message = ex.getMessage();
|
||||
if( message == null || !message.contains( "already loaded in another classloader" ) )
|
||||
LoggingFacade.INSTANCE.logSevere( message, ex );
|
||||
} catch( Exception ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( ex.getMessage(), ex );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2022 FormDev Software GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFrame;
|
||||
import com.formdev.flatlaf.util.SystemInfo;
|
||||
|
||||
/**
|
||||
* Native methods for Linux.
|
||||
* <p>
|
||||
* <b>Note</b>: This is private API. Do not use!
|
||||
*
|
||||
* @author Karl Tauber
|
||||
* @since 2.5
|
||||
*/
|
||||
class FlatNativeLinuxLibrary
|
||||
{
|
||||
static boolean isLoaded() {
|
||||
return SystemInfo.isLinux && FlatNativeLibrary.isLoaded();
|
||||
}
|
||||
|
||||
// direction for _NET_WM_MOVERESIZE message
|
||||
// see https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html
|
||||
static final int MOVE = 8;
|
||||
|
||||
private static Boolean isXWindowSystem;
|
||||
|
||||
private static boolean isXWindowSystem() {
|
||||
if( isXWindowSystem == null )
|
||||
isXWindowSystem = Toolkit.getDefaultToolkit().getClass().getName().endsWith( ".XToolkit" );
|
||||
return isXWindowSystem;
|
||||
}
|
||||
|
||||
static boolean isWMUtilsSupported( Window window ) {
|
||||
return hasCustomDecoration( window ) && isXWindowSystem() && isLoaded();
|
||||
}
|
||||
|
||||
static boolean moveOrResizeWindow( Window window, MouseEvent e, int direction ) {
|
||||
Point pt = scale( window, e.getLocationOnScreen() );
|
||||
return xMoveOrResizeWindow( window, pt.x, pt.y, direction );
|
||||
|
||||
/*
|
||||
try {
|
||||
Class<?> cls = Class.forName( "com.formdev.flatlaf.natives.jna.linux.X11WmUtils" );
|
||||
java.lang.reflect.Method m = cls.getMethod( "xMoveOrResizeWindow", Window.class, int.class, int.class, int.class );
|
||||
return (Boolean) m.invoke( null, window, pt.x, pt.y, direction );
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
static boolean showWindowMenu( Window window, MouseEvent e ) {
|
||||
Point pt = scale( window, e.getLocationOnScreen() );
|
||||
return xShowWindowMenu( window, pt.x, pt.y );
|
||||
|
||||
/*
|
||||
try {
|
||||
Class<?> cls = Class.forName( "com.formdev.flatlaf.natives.jna.linux.X11WmUtils" );
|
||||
java.lang.reflect.Method m = cls.getMethod( "xShowWindowMenu", Window.class, int.class, int.class );
|
||||
return (Boolean) m.invoke( null, window, pt.x, pt.y );
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
private static Point scale( Window window, Point pt ) {
|
||||
AffineTransform transform = window.getGraphicsConfiguration().getDefaultTransform();
|
||||
int x = (int) Math.round( pt.x * transform.getScaleX() );
|
||||
int y = (int) Math.round( pt.y * transform.getScaleY() );
|
||||
return new Point( x, y );
|
||||
}
|
||||
|
||||
// X Window System
|
||||
private static native boolean xMoveOrResizeWindow( Window window, int x, int y, int direction );
|
||||
private static native boolean xShowWindowMenu( Window window, int x, int y );
|
||||
|
||||
private static boolean hasCustomDecoration( Window window ) {
|
||||
return (window instanceof JFrame && JFrame.isDefaultLookAndFeelDecorated() && ((JFrame)window).isUndecorated()) ||
|
||||
(window instanceof JDialog && JDialog.isDefaultLookAndFeelDecorated() && ((JDialog)window).isUndecorated());
|
||||
}
|
||||
}
|
||||
@@ -42,11 +42,13 @@ import com.formdev.flatlaf.util.SystemInfo;
|
||||
public class FlatNativeWindowBorder
|
||||
{
|
||||
// can use window decorations if:
|
||||
// - on Windows 10
|
||||
// - on Windows 10 or later
|
||||
// - not if system property "sun.java2d.opengl" is true on Windows 10
|
||||
// - not when running in JetBrains Projector, Webswing or WinPE
|
||||
// - not disabled via system property
|
||||
private static final boolean canUseWindowDecorations =
|
||||
SystemInfo.isWindows_10_orLater &&
|
||||
(SystemInfo.isWindows_11_orLater || !FlatSystemProperties.getBoolean( "sun.java2d.opengl", false )) &&
|
||||
!SystemInfo.isProjector &&
|
||||
!SystemInfo.isWebswing &&
|
||||
!SystemInfo.isWinPE &&
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2022 FormDev Software GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Window;
|
||||
import com.formdev.flatlaf.util.SystemInfo;
|
||||
|
||||
/**
|
||||
* Native methods for Windows.
|
||||
* <p>
|
||||
* <b>Note</b>: This is private API. Do not use!
|
||||
*
|
||||
* @author Karl Tauber
|
||||
* @since 3.1
|
||||
*/
|
||||
public class FlatNativeWindowsLibrary
|
||||
{
|
||||
private static long osBuildNumber = Long.MIN_VALUE;
|
||||
|
||||
public static boolean isLoaded() {
|
||||
return SystemInfo.isWindows && FlatNativeLibrary.isLoaded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Windows operating system build number.
|
||||
* <p>
|
||||
* Invokes Win32 API method {@code GetVersionEx()} and returns {@code OSVERSIONINFO.dwBuildNumber}.
|
||||
* See https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getversionexa
|
||||
*/
|
||||
public static long getOSBuildNumber() {
|
||||
if( osBuildNumber == Long.MIN_VALUE )
|
||||
osBuildNumber = getOSBuildNumberImpl();
|
||||
return osBuildNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes Win32 API method {@code GetVersionEx()} and returns {@code OSVERSIONINFO.dwBuildNumber}.
|
||||
* See https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getversionexa
|
||||
*/
|
||||
private native static long getOSBuildNumberImpl();
|
||||
|
||||
/**
|
||||
* Gets the Windows window handle (HWND) for the given Swing window.
|
||||
* <p>
|
||||
* Note that the underlying Windows window must be already created,
|
||||
* otherwise this method returns zero. Use following to ensure this:
|
||||
* <pre>{@code
|
||||
* if( !window.isDisplayable() )
|
||||
* window.addNotify();
|
||||
* }</pre>
|
||||
* or invoke this method after packing the window. E.g.
|
||||
* <pre>{@code
|
||||
* window.pack();
|
||||
* long hwnd = getHWND( window );
|
||||
* }</pre>
|
||||
*/
|
||||
public native static long getHWND( Window window );
|
||||
|
||||
/**
|
||||
* DWM_WINDOW_CORNER_PREFERENCE
|
||||
* see https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_window_corner_preference
|
||||
*/
|
||||
public static final int
|
||||
DWMWCP_DEFAULT = 0,
|
||||
DWMWCP_DONOTROUND = 1,
|
||||
DWMWCP_ROUND = 2,
|
||||
DWMWCP_ROUNDSMALL = 3;
|
||||
|
||||
/**
|
||||
* Sets the rounded corner preference for the window.
|
||||
* Allowed values are {@link #DWMWCP_DEFAULT}, {@link #DWMWCP_DONOTROUND},
|
||||
* {@link #DWMWCP_ROUND} and {@link #DWMWCP_ROUNDSMALL}.
|
||||
* <p>
|
||||
* Invokes Win32 API method {@code DwmSetWindowAttribute(DWMWA_WINDOW_CORNER_PREFERENCE)}.
|
||||
* See https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute
|
||||
* <p>
|
||||
* Supported since Windows 11 Build 22000.
|
||||
*/
|
||||
public native static boolean setWindowCornerPreference( long hwnd, int cornerPreference );
|
||||
|
||||
/**
|
||||
* Sets the color of the window border.
|
||||
* The red/green/blue values must be in range {@code 0 - 255}.
|
||||
* If red is {@code -1}, then the system default border color is used (useful to reset the border color).
|
||||
* If red is {@code -2}, then no border is painted.
|
||||
* <p>
|
||||
* Invokes Win32 API method {@code DwmSetWindowAttribute(DWMWA_BORDER_COLOR)}.
|
||||
* See https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute
|
||||
* <p>
|
||||
* Supported since Windows 11 Build 22000.
|
||||
*/
|
||||
public native static boolean setWindowBorderColor( long hwnd, int red, int green, int blue );
|
||||
}
|
||||
@@ -18,12 +18,14 @@ 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.JPanel;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
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;
|
||||
@@ -43,13 +45,12 @@ import com.formdev.flatlaf.util.UIScale;
|
||||
*/
|
||||
public class FlatPanelUI
|
||||
extends BasicPanelUI
|
||||
implements StyleableUI
|
||||
implements StyleableUI, PropertyChangeListener
|
||||
{
|
||||
// only used via styling (not in UI defaults)
|
||||
/** @since 2 */ @Styleable protected int arc = -1;
|
||||
|
||||
private final boolean shared;
|
||||
private PropertyChangeListener propertyChangeListener;
|
||||
private Map<String, Object> oldStyleValues;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
@@ -67,9 +68,7 @@ public class FlatPanelUI
|
||||
public void installUI( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
propertyChangeListener = FlatStylingSupport.createPropertyChangeListener(
|
||||
c, () -> stylePropertyChange( (JPanel) c ), null );
|
||||
c.addPropertyChangeListener( propertyChangeListener );
|
||||
c.addPropertyChangeListener( this );
|
||||
|
||||
installStyle( (JPanel) c );
|
||||
}
|
||||
@@ -78,21 +77,28 @@ public class FlatPanelUI
|
||||
public void uninstallUI( JComponent c ) {
|
||||
super.uninstallUI( c );
|
||||
|
||||
c.removePropertyChangeListener( propertyChangeListener );
|
||||
propertyChangeListener = null;
|
||||
c.removePropertyChangeListener( this );
|
||||
|
||||
oldStyleValues = null;
|
||||
}
|
||||
|
||||
private void stylePropertyChange( JPanel c ) {
|
||||
if( shared && FlatStylingSupport.hasStyleProperty( c ) ) {
|
||||
// unshare component UI if necessary
|
||||
// updateUI() invokes installStyle() from installUI()
|
||||
c.updateUI();
|
||||
} else
|
||||
installStyle( c );
|
||||
c.revalidate();
|
||||
c.repaint();
|
||||
/** @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 */
|
||||
@@ -121,6 +127,12 @@ public class FlatPanelUI
|
||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
@Override
|
||||
public Object getStyleableValue( JComponent c, String key ) {
|
||||
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update( Graphics g, JComponent c ) {
|
||||
// fill background
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.awt.Toolkit;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.KeyListener;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.util.Map;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.ActionMap;
|
||||
@@ -65,7 +66,6 @@ import com.formdev.flatlaf.util.UIScale;
|
||||
* <!-- FlatTextFieldUI -->
|
||||
*
|
||||
* @uiDefault Component.minimumWidth int
|
||||
* @uiDefault Component.isIntelliJTheme boolean
|
||||
* @uiDefault PasswordField.placeholderForeground Color
|
||||
* @uiDefault PasswordField.focusedBackground Color optional
|
||||
* @uiDefault PasswordField.iconTextGap int optional, default is 4
|
||||
@@ -233,6 +233,14 @@ public class FlatPasswordFieldUI
|
||||
return infos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getStyleableValue( JComponent c, String key ) {
|
||||
if( key.equals( "capsLockIconColor" ) && capsLockIcon instanceof FlatCapsLockIcon )
|
||||
return ((FlatCapsLockIcon)capsLockIcon).getStyleableValue( key );
|
||||
|
||||
return super.getStyleableValue( c, key );
|
||||
}
|
||||
|
||||
@Override
|
||||
public View create( Element elem ) {
|
||||
return new PasswordView( elem );
|
||||
@@ -283,6 +291,7 @@ public class FlatPasswordFieldUI
|
||||
protected void installRevealButton() {
|
||||
if( showRevealButton ) {
|
||||
revealButton = createRevealButton();
|
||||
updateRevealButton();
|
||||
installLayout();
|
||||
getComponent().add( revealButton );
|
||||
}
|
||||
@@ -290,28 +299,64 @@ public class FlatPasswordFieldUI
|
||||
|
||||
/** @since 2 */
|
||||
protected JToggleButton createRevealButton() {
|
||||
JToggleButton button = new JToggleButton( revealIcon );
|
||||
JPasswordField c = (JPasswordField) getComponent();
|
||||
JToggleButton button = new JToggleButton( revealIcon, !c.echoCharIsSet() );
|
||||
button.setName( "PasswordField.revealButton" );
|
||||
prepareLeadingOrTrailingComponent( button );
|
||||
button.putClientProperty( FlatClientProperties.STYLE_CLASS, "inTextField revealButton" );
|
||||
if( FlatClientProperties.clientPropertyBoolean( getComponent(), KEY_REVEAL_SELECTED, false ) ) {
|
||||
if( FlatClientProperties.clientPropertyBoolean( c, KEY_REVEAL_SELECTED, false ) ) {
|
||||
button.setSelected( true );
|
||||
updateEchoChar( true );
|
||||
}
|
||||
button.addActionListener( e -> {
|
||||
boolean selected = button.isSelected();
|
||||
updateEchoChar( selected );
|
||||
getComponent().putClientProperty( KEY_REVEAL_SELECTED, selected );
|
||||
c.putClientProperty( KEY_REVEAL_SELECTED, selected );
|
||||
} );
|
||||
return button;
|
||||
}
|
||||
|
||||
/** @since 2.5 */
|
||||
protected void updateRevealButton() {
|
||||
if( revealButton == null )
|
||||
return;
|
||||
|
||||
JTextComponent c = getComponent();
|
||||
boolean visible = c.isEnabled();
|
||||
if( visible != revealButton.isVisible() ) {
|
||||
revealButton.setVisible( visible );
|
||||
c.revalidate();
|
||||
c.repaint();
|
||||
|
||||
if( !visible ) {
|
||||
revealButton.setSelected( false );
|
||||
updateEchoChar( false );
|
||||
getComponent().putClientProperty( KEY_REVEAL_SELECTED, null );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void propertyChange( PropertyChangeEvent e ) {
|
||||
super.propertyChange( e );
|
||||
|
||||
switch( e.getPropertyName() ) {
|
||||
case "enabled":
|
||||
updateRevealButton();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateEchoChar( boolean selected ) {
|
||||
char newEchoChar = selected
|
||||
? 0
|
||||
: (echoChar != null ? echoChar : '*');
|
||||
|
||||
JPasswordField c = (JPasswordField) getComponent();
|
||||
if( newEchoChar == c.getEchoChar() )
|
||||
return;
|
||||
|
||||
// set echo char
|
||||
LookAndFeel.installProperty( c, "echoChar", newEchoChar );
|
||||
|
||||
// check whether was able to set echo char via LookAndFeel.installProperty()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user